mirror of
https://git.vladimir.cc/vladimir/libfec.git
synced 2026-08-25 17:27:34 +00:00
init commit
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
INSTALLATION INSTRUCTIONS
|
||||
|
||||
To build and install the libfec libraries, simply say
|
||||
|
||||
./configure
|
||||
make
|
||||
make test (optional)
|
||||
make install (as root)
|
||||
|
||||
By default, "make install" puts the libfec libraries in
|
||||
/usr/local/lib, the include files in /usr/local/include, and the
|
||||
manual page in /usr/local/man.
|
||||
|
||||
You may have an old version of the GNU assembler that cannot handle
|
||||
the relatively new SSE2 mnemonics. Update your version of the GNU
|
||||
"binutils" package.
|
||||
|
||||
You may obtain the latest binutils package through your normal
|
||||
distribution channels or from:
|
||||
|
||||
http://sources.redhat.com/binutils/
|
||||
|
||||
TESTING THE FEC LIBRARY
|
||||
|
||||
After running the ./configure script, optional tests can be built and
|
||||
run as follows:
|
||||
|
||||
make test
|
||||
|
||||
"make test" tests each routine, using the SIMD versions as
|
||||
appropriate, verifying correct operation and estimating Viterbi
|
||||
decoding speeds. These tests should always succeed unless something is
|
||||
broken.
|
||||
|
||||
28 Mar 2004
|
||||
Phil Karn, karn@ka9q.net
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
COPYRIGHT
|
||||
|
||||
This package is copyright 2004 by Phil Karn, KA9Q. It may be used
|
||||
under the terms of the GNU Lesser General Public License (LGPL). See
|
||||
the file "lesser.txt" in this package for license details.
|
||||
|
||||
INTRODUCTION
|
||||
|
||||
This package provides a set of functions that implement several
|
||||
popular forward error correction (FEC) algorithms and several low-level routines
|
||||
useful in modems implemented with digital signal processing (DSP).
|
||||
|
||||
The following routines are provided:
|
||||
|
||||
1. Viterbi decoders for the following convolutional codes:
|
||||
|
||||
r=1/2 k=7 ("Voyager" code, now a widely used industry standard)
|
||||
r=1/2 k=9 (Used on the IS-95 CDMA forward link)
|
||||
r=1/6 k=15 ("Cassini" code, used by several NASA/JPL deep space missions)
|
||||
|
||||
2. Reed-Solomon encoders and decoders for any user-specified code.
|
||||
|
||||
3. Optimized encoder and decoder for the CCSDS-standard (255,223)
|
||||
Reed-Solomon code, with and without the CCSDS-standard "dual basis"
|
||||
symbol representation.
|
||||
|
||||
4. Compute dot product between a 16-bit buffer and a set of 16-bit
|
||||
coefficients. This is the basic DSP primitive for digital filtering
|
||||
and correlation.
|
||||
|
||||
4. Compute sum of squares of a buffer of 16-bit signed integers. This is
|
||||
useful in DSP for finding the total energy in a signal.
|
||||
|
||||
5. Find peak value in a buffer of 16-bit signed integers, useful for
|
||||
scaling a signal to prevent overflow.
|
||||
|
||||
SIMD SUPPORT
|
||||
|
||||
This package automatically makes use of various SIMD (Single
|
||||
Instruction stream, Multiple Data stream) instruction sets, when
|
||||
available: MMX, SSE and SSE2 on the IA-32 (Intel) architecture, and
|
||||
Altivec on the PowerPC G4 and G5 used by Power Macintoshes.
|
||||
|
||||
"Altivec" is a Motorola trademark; Apple calls it "Velocity Engine",
|
||||
and IBM calls it "VMX". Altivec is roughly comparable to SSE2 on the
|
||||
IA-32.
|
||||
|
||||
Many of the SIMD versions run more than an order of
|
||||
magnitude faster than their portable C versions. The available SIMD
|
||||
instruction sets, if any, are determined at run time and the proper
|
||||
version of each routine is automatically selected. If no SIMD
|
||||
instructions are available, the portable C version is invoked by
|
||||
default. On targets other than IA-32 and PPC, only the portable C
|
||||
version is built.
|
||||
|
||||
The SIMD-assisted versions generally produce the same results as the C
|
||||
versions, with a few minor exceptions. The Viterbi decoders in C have
|
||||
a very slightly greater Eb/No performance due to their use of 32-bit
|
||||
path metrics. On the other hand, the SIMD versions use the
|
||||
"saturating" arithmetic available in these instructions to avoid the
|
||||
integer wraparounds that can occur in C when argument ranges are not
|
||||
properly constrained. This applies primarily to the "dotprod" (dot
|
||||
product) function.
|
||||
|
||||
The MMX (MultiMedia eXtensions) instruction set was introduced on
|
||||
later Pentium CPUs; it is also implemented on the Pentium II and most
|
||||
AMD CPUs starting with the K6. SSE (SIMD Streaming Extensions) was
|
||||
introduced in the Pentium III; AMD calls it "3D Now! Professional".
|
||||
Intel introduced SSE2 on the Pentium 4, and it has been picked up by
|
||||
later AMD CPUs. SSE support implies MMX support, while SSE2 support
|
||||
implies both SSE and MMX support.
|
||||
|
||||
The latest IA-32 SIMD instruction set, SSE3 (also known as "Prescott
|
||||
New Instructions") was introduced in early 2004 with the latest
|
||||
("Prescott") revision of the Pentium 4. Relatively little was
|
||||
introduced with SSE3, and this library currently makes no use of it.
|
||||
|
||||
See the various manual pages for details on how to use the library
|
||||
routines.
|
||||
|
||||
Copyright 2004, Phil Karn, KA9Q
|
||||
karn@ka9q.net
|
||||
http://people.qualcomm.com/karn
|
||||
|
||||
This software may be used under the terms of the GNU Lesser General
|
||||
Public License (LGPL); see the file lesser.txt for details.
|
||||
|
||||
Revision history:
|
||||
Version 1.0 released 29 May 2001
|
||||
|
||||
Version 2.0 released 3 Dec 2001:
|
||||
Restructured to add support for shared libraries.
|
||||
|
||||
Version 2.0.1 released 8 Dec 2001:
|
||||
Includes autoconf/configure script
|
||||
|
||||
Version 2.0.2 released 4 Feb 2002:
|
||||
Add SIMD version override options
|
||||
Test for lack of SSE2 mnemonic support in 'as'
|
||||
Build only selected version
|
||||
|
||||
Version 2.0.3 released 6 Feb 2002:
|
||||
Fix to parityb function in parity.h
|
||||
|
||||
feclib version 1.0 released November 2003
|
||||
Merged SIMD-Viterbi, RS and DSP libraries
|
||||
Changed SIMD Viterbi decoder to detect SSE2/SSE/MMX at runtime rather than build time
|
||||
|
||||
feclib version 2.0 (unreleased) Mar 2004
|
||||
General speedups and cleanups
|
||||
Switch from 4 to 8-bit input symbols on all Viterbi decoders
|
||||
Support for Altivec on PowerPC
|
||||
Support for k=15 r=1/6 Cassini/Mars Pathfinder/Mars Exploration Rover/STEREO code
|
||||
Changed license to GNU Lesser General Public License (LGPL)
|
||||
@@ -0,0 +1,5 @@
|
||||
typedef unsigned char data_t;
|
||||
extern unsigned char Taltab[],Tal1tab[];
|
||||
#define NN 255
|
||||
#define NROOTS 32
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/* Stuff specific to the 8-bit symbol version of the general purpose RS codecs
|
||||
*
|
||||
* Copyright 2003, Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
typedef unsigned char data_t;
|
||||
|
||||
#define MODNN(x) modnn(rs,x)
|
||||
|
||||
#define MM (rs->mm)
|
||||
#define NN (rs->nn)
|
||||
#define ALPHA_TO (rs->alpha_to)
|
||||
#define INDEX_OF (rs->index_of)
|
||||
#define GENPOLY (rs->genpoly)
|
||||
#define NROOTS (rs->nroots)
|
||||
#define FCR (rs->fcr)
|
||||
#define PRIM (rs->prim)
|
||||
#define IPRIM (rs->iprim)
|
||||
#define PAD (rs->pad)
|
||||
#define A0 (NN)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
|
||||
#define MCQLI24 1 // used by ICE
|
||||
//#define RJ1 1
|
||||
//#define RJ2 1
|
||||
//#define BJ24 1
|
||||
//#define MJ 1
|
||||
//#define MCQLI24 1 // k=24 r=1/2 code for ICE
|
||||
//#define JQLIODP48 1
|
||||
//#define MCQLI48 1
|
||||
//#define OT24 1
|
||||
//#define QR24 1
|
||||
//#define LL 1
|
||||
//#define J60 1
|
||||
//#define J50 1
|
||||
|
||||
//#define MCQLI32 1 // NASA standard
|
||||
//#define JP24 1
|
||||
//#define JSODP47 1
|
||||
//#define BLLF47 1
|
||||
|
||||
// Convolutional coding polynomials. All are rate 1/2
|
||||
|
||||
#if defined(MCQLI32)
|
||||
//"NASA standard" code by Massey & Costello
|
||||
// Shortened from MCQLI-48:
|
||||
// Nonsystematic, quick look-in, dmin=11, dfree=23
|
||||
// used on Pioneer 10-12, Helios A,B
|
||||
#define POLY1 0xbbef6bb7 // 111 011 011 101 011 011 110 111 110 111 010 = 73353367672
|
||||
#define POLY2 0xbbef6bb5 // 101 011 011 101 011 011 110 111 110 111 010 = 53353367672
|
||||
#define K 32
|
||||
#define G1FLIP 0
|
||||
#define G2FLIP 0
|
||||
|
||||
#elif defined(MJ)
|
||||
// Massey-Johannesson code
|
||||
// Nonsystematic, quick look-in, dmin=13, dfree>=23
|
||||
// Purported to be more computationally efficient than Massey-Costello
|
||||
// Appears related to 1JQLIODP-24
|
||||
#define POLY1 0xb840a20f // 111 100 000 100 010 100 000 010 000 111 010 = 74042402072
|
||||
#define POLY2 0xb840a20d // 101 100 000 100 010 100 000 010 000 111 010 = 54042402072
|
||||
#define K 32
|
||||
#define G1FLIP 0
|
||||
#define G2FLIP 0
|
||||
|
||||
#elif defined(LL)
|
||||
// Layland-Lushbaugh code
|
||||
// Nonsystematic, non-quick look-in, dmin=?, dfree=?
|
||||
#define POLY1 0xf2d05351 // 100 010 101 100 101 000 001 011 010 011 110 = 42545013236
|
||||
#define POLY2 0xe4613c47 // 111 000 100 011 110 010 000 110 001 001 110 = 70436206116
|
||||
#define K 32
|
||||
#define G1FLIP 0
|
||||
#define G2FLIP 0
|
||||
|
||||
|
||||
#elif defined(MCQLI24)
|
||||
// MCQLI-24
|
||||
// k=24 r=1/2 Massey QLI code for ISEE-3/International Comet Explorer
|
||||
// The ref gives the polynomials as 073353367 and 053353367 (octal)
|
||||
// Shortened from MCQLI-48
|
||||
#define POLY1 073665667
|
||||
#define POLY2 073665665
|
||||
#define K 24
|
||||
#define G1FLIP 0
|
||||
#define G2FLIP 1 // Invert the second symbol
|
||||
|
||||
// 1JQLIODP-24
|
||||
// Johannassen QLI ODP k=24
|
||||
#elif defined(RJ1)
|
||||
#define POLY1 074121017
|
||||
#define POLY2 074121015
|
||||
#define K 24
|
||||
#define G1FLIP 0
|
||||
#define G2FLIP 0
|
||||
|
||||
// 2JQLIODP-24
|
||||
// Johannassen QLI ODP k=24
|
||||
#elif defined(RJ2)
|
||||
#define POLY1 073541017
|
||||
#define POLY2 073541015
|
||||
#define K 24
|
||||
#define G1FLIP 0
|
||||
#define G2FLIP 0
|
||||
|
||||
// BJ-24
|
||||
// Bahl-Jelinek k=24 complementary code
|
||||
#elif defined(BJ24)
|
||||
#define POLY1 054220245
|
||||
#define POLY2 063557533
|
||||
#define K 24
|
||||
#define G1FLIP 0
|
||||
#define G2FLIP 0
|
||||
|
||||
// QR-24 code
|
||||
// Massey, Costello, Justesen Quadratic k=24 residue code
|
||||
#elif defined(QR24)
|
||||
#define POLY1 026241177
|
||||
#define POLY2 037620515
|
||||
#define K 24
|
||||
#define G1FLIP 0
|
||||
#define G2FLIP 0
|
||||
|
||||
// OT-24
|
||||
// Massey k=24 optimally truncatable code
|
||||
#elif defined(OT24)
|
||||
#define POLY1 062650457
|
||||
#define POLY2 062650455
|
||||
#define K 24
|
||||
#define G1FLIP 0
|
||||
#define G2FLIP 0
|
||||
|
||||
// MCQLI-48
|
||||
// Massey-Costelli QLI code k=48
|
||||
#elif defined(MCQLI48)
|
||||
#define POLY1 06556767373665667LL
|
||||
#define POLY2 06556767373665665LL
|
||||
#define K 48
|
||||
#define G1FLIP 0
|
||||
#define G2FLIP 0
|
||||
|
||||
// JQLIODP-48
|
||||
// Johannesson QLI ODP code with k=48
|
||||
#elif defined(JQLIODP48)
|
||||
#define POLY1 05634247020121017LL
|
||||
#define POLY2 05634247020121015LL
|
||||
#define K 48
|
||||
#define G1FLIP 0
|
||||
#define G2FLIP 0
|
||||
|
||||
//BLLF-47
|
||||
// Bussgang, Lin, Lynn, Forney k=47
|
||||
// Systematic
|
||||
#elif defined(BLLF47)
|
||||
#define POLY1 1
|
||||
#define POLY2 0531746407671547LL
|
||||
#define K 45
|
||||
#define G1FLIP 0
|
||||
#define G2FLIP 0
|
||||
|
||||
// JSODP-47
|
||||
// Johannesson's ODP k=47
|
||||
// Systematic
|
||||
#elif defined(JSODP47)
|
||||
#define POLY1 1
|
||||
#define POLY2 03331355751514473LL
|
||||
#define K 47
|
||||
#define G1FLIP 0
|
||||
#define G2FLIP 0
|
||||
|
||||
// JP-24
|
||||
// Mentioned in addendum to Massey
|
||||
// Non-QLI, non-systematic
|
||||
#elif defined (JP24)
|
||||
#define POLY1 052431655
|
||||
#define POLY2 061411757
|
||||
#define K 24
|
||||
#define G1FLIP 0
|
||||
#define G2FLIP 0
|
||||
|
||||
// Monster k=60 Johannesson systematic
|
||||
#elif defined (J60)
|
||||
#define POLY1 1
|
||||
#define POLY2 073607331355751514473LL
|
||||
#define K 60
|
||||
#define G1FLIP 0
|
||||
#define G2FLIP 0
|
||||
|
||||
// Monster k=50 Johannesson QLI
|
||||
#elif defined (J50)
|
||||
#define POLY1 075634247020121017
|
||||
#define POLY2 075634247020121015
|
||||
#define K 50
|
||||
#define G1FLIP 0
|
||||
#define G2FLIP 0
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
int encode(
|
||||
unsigned char *symbols, // Output buffer, 2*8*nbytes
|
||||
const unsigned char *data, // Input buffer, nbytes
|
||||
unsigned int nbytes); // Number of bytes in data
|
||||
@@ -0,0 +1,29 @@
|
||||
# This file is a shell script that caches the results of configure
|
||||
# tests run on this system so they can be shared between configure
|
||||
# scripts and configure runs. It is not useful on other systems.
|
||||
# If it contains results you don't want to keep, you may remove or edit it.
|
||||
#
|
||||
# By default, configure uses ./config.cache as the cache file,
|
||||
# creating it if it does not exist already. You can give configure
|
||||
# the --cache-file=FILE option to use a different cache file; that is
|
||||
# what configure does when it calls configure scripts in
|
||||
# subdirectories, so they share the cache.
|
||||
# Giving --cache-file=/dev/null disables caching, for debugging configure.
|
||||
# config.status only pays attention to the cache file if you give it the
|
||||
# --recheck option to rerun configure.
|
||||
#
|
||||
ac_cv_func_getopt_long=${ac_cv_func_getopt_long=yes}
|
||||
ac_cv_func_memmove=${ac_cv_func_memmove=yes}
|
||||
ac_cv_func_memset=${ac_cv_func_memset=yes}
|
||||
ac_cv_header_getopt_h=${ac_cv_header_getopt_h=yes}
|
||||
ac_cv_header_memory_h=${ac_cv_header_memory_h=yes}
|
||||
ac_cv_header_stdio_h=${ac_cv_header_stdio_h=yes}
|
||||
ac_cv_header_stdlib_h=${ac_cv_header_stdlib_h=yes}
|
||||
ac_cv_header_string_h=${ac_cv_header_string_h=yes}
|
||||
ac_cv_lib_c_malloc=${ac_cv_lib_c_malloc=yes}
|
||||
ac_cv_prog_CC=${ac_cv_prog_CC=gcc}
|
||||
ac_cv_prog_CPP=${ac_cv_prog_CPP='gcc -E'}
|
||||
ac_cv_prog_cc_cross=${ac_cv_prog_cc_cross=no}
|
||||
ac_cv_prog_cc_g=${ac_cv_prog_cc_g=yes}
|
||||
ac_cv_prog_cc_works=${ac_cv_prog_cc_works=yes}
|
||||
ac_cv_prog_gcc=${ac_cv_prog_gcc=yes}
|
||||
+1371
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
/* config.h. Generated automatically by configure. */
|
||||
/* config.h.in. Generated automatically from configure.in by autoheader. */
|
||||
|
||||
/* Define if you have the getopt_long function. */
|
||||
#define HAVE_GETOPT_LONG 1
|
||||
|
||||
/* Define if you have the <getopt.h> header file. */
|
||||
#define HAVE_GETOPT_H 1
|
||||
|
||||
/* Define if you have the <memory.h> header file. */
|
||||
#define HAVE_MEMORY_H 1
|
||||
|
||||
/* Define if you have the <stdio.h> header file. */
|
||||
#define HAVE_STDIO_H 1
|
||||
|
||||
/* Define if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define if you have the c library (-lc). */
|
||||
#define HAVE_LIBC 1
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/* config.h.in. Generated automatically from configure.in by autoheader. */
|
||||
|
||||
/* Define if you have the getopt_long function. */
|
||||
#undef HAVE_GETOPT_LONG
|
||||
|
||||
/* Define if you have the <getopt.h> header file. */
|
||||
#undef HAVE_GETOPT_H
|
||||
|
||||
/* Define if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
/* Define if you have the <stdio.h> header file. */
|
||||
#undef HAVE_STDIO_H
|
||||
|
||||
/* Define if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define if you have the c library (-lc). */
|
||||
#undef HAVE_LIBC
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
configure:534: checking for gcc
|
||||
configure:647: checking whether the C compiler (gcc -O2 -fPIC -Wno-implicit-int -Wno-implicit-function-declaration -Wno-int-conversion ) works
|
||||
configure:663: gcc -o conftest -O2 -fPIC -Wno-implicit-int -Wno-implicit-function-declaration -Wno-int-conversion conftest.c 1>&5
|
||||
configure:689: checking whether the C compiler (gcc -O2 -fPIC -Wno-implicit-int -Wno-implicit-function-declaration -Wno-int-conversion ) is a cross-compiler
|
||||
configure:694: checking whether we are using GNU C
|
||||
configure:703: gcc -E conftest.c
|
||||
configure:722: checking whether gcc accepts -g
|
||||
configure:758: checking for malloc in -lc
|
||||
configure:777: gcc -o conftest -O2 -fPIC -Wno-implicit-int -Wno-implicit-function-declaration -Wno-int-conversion conftest.c -lc 1>&5
|
||||
configure:770:6: warning: conflicting types for built-in function 'malloc'; expected 'void *(long unsigned int)' [-Wbuiltin-declaration-mismatch]
|
||||
770 | char malloc();
|
||||
| ^~~~~~
|
||||
configure:767:1: note: 'malloc' is declared in header '<stdlib.h>'
|
||||
766 | #include "confdefs.h"
|
||||
+++ |+#include <stdlib.h>
|
||||
767 | /* Override any gcc2 internal prototype to avoid an error. */
|
||||
configure:806: checking how to run the C preprocessor
|
||||
configure:827: gcc -E conftest.c >/dev/null 2>conftest.out
|
||||
configure:889: checking for getopt.h
|
||||
configure:899: gcc -E conftest.c >/dev/null 2>conftest.out
|
||||
configure:889: checking for stdio.h
|
||||
configure:899: gcc -E conftest.c >/dev/null 2>conftest.out
|
||||
configure:889: checking for stdlib.h
|
||||
configure:899: gcc -E conftest.c >/dev/null 2>conftest.out
|
||||
configure:889: checking for memory.h
|
||||
configure:899: gcc -E conftest.c >/dev/null 2>conftest.out
|
||||
configure:889: checking for string.h
|
||||
configure:899: gcc -E conftest.c >/dev/null 2>conftest.out
|
||||
configure:989: checking host system type
|
||||
configure:1010: checking target system type
|
||||
configure:1028: checking build system type
|
||||
configure:1093: checking for getopt_long
|
||||
configure:1121: gcc -o conftest -O2 -fPIC -Wno-implicit-int -Wno-implicit-function-declaration -Wno-int-conversion conftest.c -lc 1>&5
|
||||
configure:1093: checking for memset
|
||||
configure:1121: gcc -o conftest -O2 -fPIC -Wno-implicit-int -Wno-implicit-function-declaration -Wno-int-conversion conftest.c -lc 1>&5
|
||||
configure:1105:6: warning: conflicting types for built-in function 'memset'; expected 'void *(void *, int, long unsigned int)' [-Wbuiltin-declaration-mismatch]
|
||||
1105 | char $ac_func();
|
||||
| ^~~~~~
|
||||
configure:1102:1: note: 'memset' is declared in header '<string.h>'
|
||||
1101 | #include <assert.h>
|
||||
+++ |+#include <string.h>
|
||||
1102 | /* Override any gcc2 internal prototype to avoid an error. */
|
||||
configure:1093: checking for memmove
|
||||
configure:1121: gcc -o conftest -O2 -fPIC -Wno-implicit-int -Wno-implicit-function-declaration -Wno-int-conversion conftest.c -lc 1>&5
|
||||
configure:1105:6: warning: conflicting types for built-in function 'memmove'; expected 'void *(void *, const void *, long unsigned int)' [-Wbuiltin-declaration-mismatch]
|
||||
1105 | char $ac_func();
|
||||
| ^~~~~~~
|
||||
configure:1102:1: note: 'memmove' is declared in header '<string.h>'
|
||||
1101 | #include <assert.h>
|
||||
+++ |+#include <string.h>
|
||||
1102 | /* Override any gcc2 internal prototype to avoid an error. */
|
||||
Executable
+273
@@ -0,0 +1,273 @@
|
||||
#! /bin/sh
|
||||
# Generated automatically by configure.
|
||||
# Run this file to recreate the current configuration.
|
||||
# This directory was configured as follows,
|
||||
# on host Uladzimir-PC:
|
||||
#
|
||||
# ./configure
|
||||
#
|
||||
# Compiler output produced by configure, useful for debugging
|
||||
# configure, is in ./config.log if it exists.
|
||||
|
||||
ac_cs_usage="Usage: ./config.status [--recheck] [--version] [--help]"
|
||||
for ac_option
|
||||
do
|
||||
case "$ac_option" in
|
||||
-recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
|
||||
echo "running ${CONFIG_SHELL-/bin/sh} ./configure --no-create --no-recursion"
|
||||
exec ${CONFIG_SHELL-/bin/sh} ./configure --no-create --no-recursion ;;
|
||||
-version | --version | --versio | --versi | --vers | --ver | --ve | --v)
|
||||
echo "./config.status generated by autoconf version 2.13"
|
||||
exit 0 ;;
|
||||
-help | --help | --hel | --he | --h)
|
||||
echo "$ac_cs_usage"; exit 0 ;;
|
||||
*) echo "$ac_cs_usage"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
ac_given_srcdir=.
|
||||
|
||||
trap 'rm -fr makefile config.h conftest*; exit 1' 1 2 15
|
||||
|
||||
# Protect against being on the right side of a sed subst in config.status.
|
||||
sed 's/%@/@@/; s/@%/@@/; s/%g$/@g/; /@g$/s/[\\&%]/\\&/g;
|
||||
s/@@/%@/; s/@@/@%/; s/@g$/%g/' > conftest.subs <<\CEOF
|
||||
/^[ ]*VPATH[ ]*=[^:]*$/d
|
||||
|
||||
s%@SHELL@%/bin/sh%g
|
||||
s%@CFLAGS@%-O2 -fPIC -Wno-implicit-int -Wno-implicit-function-declaration -Wno-int-conversion%g
|
||||
s%@CPPFLAGS@%%g
|
||||
s%@CXXFLAGS@%%g
|
||||
s%@FFLAGS@%%g
|
||||
s%@DEFS@%-DHAVE_CONFIG_H%g
|
||||
s%@LDFLAGS@%%g
|
||||
s%@LIBS@%-lc %g
|
||||
s%@exec_prefix@%${prefix}%g
|
||||
s%@prefix@%/usr/local%g
|
||||
s%@program_transform_name@%s,x,x,%g
|
||||
s%@bindir@%${exec_prefix}/bin%g
|
||||
s%@sbindir@%${exec_prefix}/sbin%g
|
||||
s%@libexecdir@%${exec_prefix}/libexec%g
|
||||
s%@datadir@%${prefix}/share%g
|
||||
s%@sysconfdir@%${prefix}/etc%g
|
||||
s%@sharedstatedir@%${prefix}/com%g
|
||||
s%@localstatedir@%${prefix}/var%g
|
||||
s%@libdir@%${exec_prefix}/lib%g
|
||||
s%@includedir@%${prefix}/include%g
|
||||
s%@oldincludedir@%/usr/include%g
|
||||
s%@infodir@%${prefix}/info%g
|
||||
s%@mandir@%${prefix}/man%g
|
||||
s%@SO_NAME@%3%g
|
||||
s%@VERSION@%3.0.0%g
|
||||
s%@CC@%gcc%g
|
||||
s%@CPP@%gcc -E%g
|
||||
s%@host@%x86_64-unknown-linux-gnu%g
|
||||
s%@host_alias@%x86_64-unknown-linux-gnu%g
|
||||
s%@host_cpu@%x86_64%g
|
||||
s%@host_vendor@%unknown%g
|
||||
s%@host_os@%linux-gnu%g
|
||||
s%@target@%x86_64-unknown-linux-gnu%g
|
||||
s%@target_alias@%x86_64-unknown-linux-gnu%g
|
||||
s%@target_cpu@%x86_64%g
|
||||
s%@target_vendor@%unknown%g
|
||||
s%@target_os@%linux-gnu%g
|
||||
s%@build@%x86_64-unknown-linux-gnu%g
|
||||
s%@build_alias@%x86_64-unknown-linux-gnu%g
|
||||
s%@build_cpu@%x86_64%g
|
||||
s%@build_vendor@%unknown%g
|
||||
s%@build_os@%linux-gnu%g
|
||||
s%@SH_LIB@%libfec.so%g
|
||||
s%@REBIND@%ldconfig%g
|
||||
s%@MLIBS@%%g
|
||||
s%@ARCH_OPTION@%%g
|
||||
|
||||
CEOF
|
||||
|
||||
# Split the substitutions into bite-sized pieces for seds with
|
||||
# small command number limits, like on Digital OSF/1 and HP-UX.
|
||||
ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
|
||||
ac_file=1 # Number of current file.
|
||||
ac_beg=1 # First line for current file.
|
||||
ac_end=$ac_max_sed_cmds # Line after last line for current file.
|
||||
ac_more_lines=:
|
||||
ac_sed_cmds=""
|
||||
while $ac_more_lines; do
|
||||
if test $ac_beg -gt 1; then
|
||||
sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
|
||||
else
|
||||
sed "${ac_end}q" conftest.subs > conftest.s$ac_file
|
||||
fi
|
||||
if test ! -s conftest.s$ac_file; then
|
||||
ac_more_lines=false
|
||||
rm -f conftest.s$ac_file
|
||||
else
|
||||
if test -z "$ac_sed_cmds"; then
|
||||
ac_sed_cmds="sed -f conftest.s$ac_file"
|
||||
else
|
||||
ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
|
||||
fi
|
||||
ac_file=`expr $ac_file + 1`
|
||||
ac_beg=$ac_end
|
||||
ac_end=`expr $ac_end + $ac_max_sed_cmds`
|
||||
fi
|
||||
done
|
||||
if test -z "$ac_sed_cmds"; then
|
||||
ac_sed_cmds=cat
|
||||
fi
|
||||
|
||||
CONFIG_FILES=${CONFIG_FILES-"makefile"}
|
||||
for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
|
||||
# Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
|
||||
case "$ac_file" in
|
||||
*:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
|
||||
ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
|
||||
*) ac_file_in="${ac_file}.in" ;;
|
||||
esac
|
||||
|
||||
# Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
|
||||
|
||||
# Remove last slash and all that follows it. Not all systems have dirname.
|
||||
ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
|
||||
if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
|
||||
# The file is in a subdirectory.
|
||||
test ! -d "$ac_dir" && mkdir "$ac_dir"
|
||||
ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
|
||||
# A "../" for each directory in $ac_dir_suffix.
|
||||
ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
|
||||
else
|
||||
ac_dir_suffix= ac_dots=
|
||||
fi
|
||||
|
||||
case "$ac_given_srcdir" in
|
||||
.) srcdir=.
|
||||
if test -z "$ac_dots"; then top_srcdir=.
|
||||
else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
|
||||
/*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
|
||||
*) # Relative path.
|
||||
srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
|
||||
top_srcdir="$ac_dots$ac_given_srcdir" ;;
|
||||
esac
|
||||
|
||||
|
||||
echo creating "$ac_file"
|
||||
rm -f "$ac_file"
|
||||
configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
|
||||
case "$ac_file" in
|
||||
*Makefile*) ac_comsub="1i\\
|
||||
# $configure_input" ;;
|
||||
*) ac_comsub= ;;
|
||||
esac
|
||||
|
||||
ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
|
||||
sed -e "$ac_comsub
|
||||
s%@configure_input@%$configure_input%g
|
||||
s%@srcdir@%$srcdir%g
|
||||
s%@top_srcdir@%$top_srcdir%g
|
||||
" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
|
||||
fi; done
|
||||
rm -f conftest.s*
|
||||
|
||||
# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
|
||||
# NAME is the cpp macro being defined and VALUE is the value it is being given.
|
||||
#
|
||||
# ac_d sets the value in "#define NAME VALUE" lines.
|
||||
ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)'
|
||||
ac_dB='\([ ][ ]*\)[^ ]*%\1#\2'
|
||||
ac_dC='\3'
|
||||
ac_dD='%g'
|
||||
# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
|
||||
ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
|
||||
ac_uB='\([ ]\)%\1#\2define\3'
|
||||
ac_uC=' '
|
||||
ac_uD='\4%g'
|
||||
# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
|
||||
ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
|
||||
ac_eB='$%\1#\2define\3'
|
||||
ac_eC=' '
|
||||
ac_eD='%g'
|
||||
|
||||
if test "${CONFIG_HEADERS+set}" != set; then
|
||||
CONFIG_HEADERS="config.h"
|
||||
fi
|
||||
for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
|
||||
# Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
|
||||
case "$ac_file" in
|
||||
*:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
|
||||
ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
|
||||
*) ac_file_in="${ac_file}.in" ;;
|
||||
esac
|
||||
|
||||
echo creating $ac_file
|
||||
|
||||
rm -f conftest.frag conftest.in conftest.out
|
||||
ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
|
||||
cat $ac_file_inputs > conftest.in
|
||||
|
||||
cat > conftest.frag <<CEOF
|
||||
${ac_dA}HAVE_LIBC${ac_dB}HAVE_LIBC${ac_dC}1${ac_dD}
|
||||
${ac_uA}HAVE_LIBC${ac_uB}HAVE_LIBC${ac_uC}1${ac_uD}
|
||||
${ac_eA}HAVE_LIBC${ac_eB}HAVE_LIBC${ac_eC}1${ac_eD}
|
||||
${ac_dA}HAVE_GETOPT_H${ac_dB}HAVE_GETOPT_H${ac_dC}1${ac_dD}
|
||||
${ac_uA}HAVE_GETOPT_H${ac_uB}HAVE_GETOPT_H${ac_uC}1${ac_uD}
|
||||
${ac_eA}HAVE_GETOPT_H${ac_eB}HAVE_GETOPT_H${ac_eC}1${ac_eD}
|
||||
${ac_dA}HAVE_STDIO_H${ac_dB}HAVE_STDIO_H${ac_dC}1${ac_dD}
|
||||
${ac_uA}HAVE_STDIO_H${ac_uB}HAVE_STDIO_H${ac_uC}1${ac_uD}
|
||||
${ac_eA}HAVE_STDIO_H${ac_eB}HAVE_STDIO_H${ac_eC}1${ac_eD}
|
||||
${ac_dA}HAVE_STDLIB_H${ac_dB}HAVE_STDLIB_H${ac_dC}1${ac_dD}
|
||||
${ac_uA}HAVE_STDLIB_H${ac_uB}HAVE_STDLIB_H${ac_uC}1${ac_uD}
|
||||
${ac_eA}HAVE_STDLIB_H${ac_eB}HAVE_STDLIB_H${ac_eC}1${ac_eD}
|
||||
CEOF
|
||||
sed -f conftest.frag conftest.in > conftest.out
|
||||
rm -f conftest.in
|
||||
mv conftest.out conftest.in
|
||||
|
||||
cat > conftest.frag <<CEOF
|
||||
${ac_dA}HAVE_MEMORY_H${ac_dB}HAVE_MEMORY_H${ac_dC}1${ac_dD}
|
||||
${ac_uA}HAVE_MEMORY_H${ac_uB}HAVE_MEMORY_H${ac_uC}1${ac_uD}
|
||||
${ac_eA}HAVE_MEMORY_H${ac_eB}HAVE_MEMORY_H${ac_eC}1${ac_eD}
|
||||
${ac_dA}HAVE_STRING_H${ac_dB}HAVE_STRING_H${ac_dC}1${ac_dD}
|
||||
${ac_uA}HAVE_STRING_H${ac_uB}HAVE_STRING_H${ac_uC}1${ac_uD}
|
||||
${ac_eA}HAVE_STRING_H${ac_eB}HAVE_STRING_H${ac_eC}1${ac_eD}
|
||||
${ac_dA}HAVE_GETOPT_LONG${ac_dB}HAVE_GETOPT_LONG${ac_dC}1${ac_dD}
|
||||
${ac_uA}HAVE_GETOPT_LONG${ac_uB}HAVE_GETOPT_LONG${ac_uC}1${ac_uD}
|
||||
${ac_eA}HAVE_GETOPT_LONG${ac_eB}HAVE_GETOPT_LONG${ac_eC}1${ac_eD}
|
||||
${ac_dA}HAVE_MEMSET${ac_dB}HAVE_MEMSET${ac_dC}1${ac_dD}
|
||||
${ac_uA}HAVE_MEMSET${ac_uB}HAVE_MEMSET${ac_uC}1${ac_uD}
|
||||
${ac_eA}HAVE_MEMSET${ac_eB}HAVE_MEMSET${ac_eC}1${ac_eD}
|
||||
CEOF
|
||||
sed -f conftest.frag conftest.in > conftest.out
|
||||
rm -f conftest.in
|
||||
mv conftest.out conftest.in
|
||||
|
||||
cat > conftest.frag <<CEOF
|
||||
${ac_dA}HAVE_MEMMOVE${ac_dB}HAVE_MEMMOVE${ac_dC}1${ac_dD}
|
||||
${ac_uA}HAVE_MEMMOVE${ac_uB}HAVE_MEMMOVE${ac_uC}1${ac_uD}
|
||||
${ac_eA}HAVE_MEMMOVE${ac_eB}HAVE_MEMMOVE${ac_eC}1${ac_eD}
|
||||
s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
|
||||
CEOF
|
||||
sed -f conftest.frag conftest.in > conftest.out
|
||||
rm -f conftest.in
|
||||
mv conftest.out conftest.in
|
||||
|
||||
rm -f conftest.frag conftest.h
|
||||
echo "/* $ac_file. Generated automatically by configure. */" > conftest.h
|
||||
cat conftest.in >> conftest.h
|
||||
rm -f conftest.in
|
||||
if cmp -s $ac_file conftest.h 2>/dev/null; then
|
||||
echo "$ac_file is unchanged"
|
||||
rm -f conftest.h
|
||||
else
|
||||
# Remove last slash and all that follows it. Not all systems have dirname.
|
||||
ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
|
||||
if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
|
||||
# The file is in a subdirectory.
|
||||
test ! -d "$ac_dir" && mkdir "$ac_dir"
|
||||
fi
|
||||
rm -f $ac_file
|
||||
mv conftest.h $ac_file
|
||||
fi
|
||||
fi; done
|
||||
|
||||
|
||||
|
||||
exit 0
|
||||
+1362
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,81 @@
|
||||
dnl Process this file with autoconf to produce a configure script.
|
||||
AC_INIT(viterbi27.c)
|
||||
AC_CONFIG_HEADER(config.h)
|
||||
SO_NAME=3
|
||||
VERSION=3.0.0
|
||||
AC_SUBST(SO_NAME)
|
||||
AC_SUBST(VERSION)
|
||||
|
||||
dnl Checks for programs.
|
||||
AC_PROG_CC
|
||||
if test $GCC != "yes"
|
||||
then
|
||||
AC_MSG_ERROR([Need GNU C compiler])
|
||||
fi
|
||||
dnl Checks for libraries.
|
||||
AC_CHECK_LIB(c, malloc)
|
||||
|
||||
dnl Checks for header files.
|
||||
AC_CHECK_HEADERS(getopt.h stdio.h stdlib.h memory.h string.h)
|
||||
if test -z "$HAVE_stdio.h"
|
||||
then
|
||||
AC_MSG_ERROR([Need stdio.h!])
|
||||
fi
|
||||
if test -z "$HAVE_stdlib.h"
|
||||
then
|
||||
AC_MSG_ERROR([Need stdlib.h!])
|
||||
fi
|
||||
if test -z "$HAVE_stdlib.h"
|
||||
then
|
||||
AC_MSG_ERROR([Need memory.h!])
|
||||
fi
|
||||
if test -z "$HAVE_string.h"
|
||||
then
|
||||
AC_MSG_ERROR([Need string.h])
|
||||
fi
|
||||
|
||||
AC_CANONICAL_SYSTEM
|
||||
case $target_cpu in
|
||||
i386|i486|i586|i686)
|
||||
ARCH_OPTION="-march=$target_cpu"
|
||||
MLIBS="viterbi27_mmx.o mmxbfly27.o viterbi27_sse.o ssebfly27.o viterbi27_sse2.o sse2bfly27.o \
|
||||
viterbi29_mmx.o mmxbfly29.o viterbi29_sse.o ssebfly29.o viterbi29_sse2.o sse2bfly29.o \
|
||||
viterbi615_mmx.o viterbi615_sse.o viterbi615_sse2.o \
|
||||
dotprod_mmx.o dotprod_mmx_assist.o \
|
||||
dotprod_sse2.o dotprod_sse2_assist.o \
|
||||
peakval_mmx.o peakval_mmx_assist.o \
|
||||
peakval_sse.o peakval_sse_assist.o \
|
||||
peakval_sse2.o peakval_sse2_assist.o \
|
||||
sumsq.o sumsq_port.o \
|
||||
sumsq_sse2.o sumsq_sse2_assist.o \
|
||||
sumsq_mmx.o sumsq_mmx_assist.o \
|
||||
cpu_features.o cpu_mode_x86.o"
|
||||
;;
|
||||
powerpc*)
|
||||
ARCH_OPTION="-fno-common -faltivec"
|
||||
MLIBS="viterbi27_av.o viterbi29_av.o viterbi615_av.o encode_rs_av.o \
|
||||
dotprod_av.o sumsq_av.o peakval_av.o cpu_mode_ppc.o"
|
||||
;;
|
||||
*)
|
||||
MLIBS=
|
||||
esac
|
||||
case $target_os in
|
||||
darwin*)
|
||||
SH_LIB=libfec.dylib
|
||||
REBIND=""
|
||||
;;
|
||||
*)
|
||||
SH_LIB=libfec.so
|
||||
REBIND=ldconfig
|
||||
;;
|
||||
esac
|
||||
AC_SUBST(SH_LIB)
|
||||
AC_SUBST(REBIND)
|
||||
AC_SUBST(MLIBS)
|
||||
AC_SUBST(ARCH_OPTION)
|
||||
|
||||
|
||||
dnl Checks for library functions.
|
||||
AC_CHECK_FUNCS(getopt_long memset memmove)
|
||||
|
||||
AC_OUTPUT(makefile)
|
||||
@@ -0,0 +1,15 @@
|
||||
.text
|
||||
.global cpu_features
|
||||
.type cpu_features,@function
|
||||
cpu_features:
|
||||
pushl %ebx
|
||||
pushl %ecx
|
||||
pushl %edx
|
||||
movl $1,%eax
|
||||
cpuid
|
||||
movl %edx,%eax
|
||||
popl %edx
|
||||
popl %ecx
|
||||
popl %ebx
|
||||
ret
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/* Portable CPU-mode selection for libfec.
|
||||
*
|
||||
* The legacy SIMD path (cpu_features.s + cpu_mode_x86.c) is 32-bit x86
|
||||
* assembly and does not assemble on x86_64 or other architectures. The
|
||||
* viterbi27/29/615 dispatchers only compile their SSE/MMX/Altivec cases
|
||||
* under __i386__ / __VEC__, so on every other target the PORT (generic C)
|
||||
* path is the only one available — forcing PORT here is correct and fully
|
||||
* portable. Replaces both cpu_mode_x86.o and cpu_features.o.
|
||||
*/
|
||||
#include "fec.h"
|
||||
|
||||
char *Cpu_modes[] = {"Unknown","Portable C","MMX","SSE","SSE2","Altivec"};
|
||||
enum cpu_mode Cpu_mode;
|
||||
|
||||
void find_cpu_mode(void){
|
||||
Cpu_mode = PORT;
|
||||
}
|
||||
|
||||
int cpu_features(void){
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/* Determine CPU support for SIMD on Power PC
|
||||
* Copyright 2004 Phil Karn, KA9Q
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "fec.h"
|
||||
#ifdef __VEC__
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
/* Various SIMD instruction set names */
|
||||
char *Cpu_modes[] = {"Unknown","Portable C","x86 Multi Media Extensions (MMX)",
|
||||
"x86 Streaming SIMD Extensions (SSE)",
|
||||
"x86 Streaming SIMD Extensions 2 (SSE2)",
|
||||
"PowerPC G4/G5 Altivec/Velocity Engine"};
|
||||
|
||||
enum cpu_mode Cpu_mode;
|
||||
|
||||
void find_cpu_mode(void){
|
||||
|
||||
if(Cpu_mode != UNKNOWN)
|
||||
return;
|
||||
|
||||
#ifdef __VEC__
|
||||
{
|
||||
/* Ask the OS if we have Altivec support */
|
||||
int selectors[2] = { CTL_HW, HW_VECTORUNIT };
|
||||
int hasVectorUnit = 0;
|
||||
size_t length = sizeof(hasVectorUnit);
|
||||
int error = sysctl(selectors, 2, &hasVectorUnit, &length, NULL, 0);
|
||||
if(0 == error && hasVectorUnit)
|
||||
Cpu_mode = ALTIVEC;
|
||||
else
|
||||
Cpu_mode = PORT;
|
||||
}
|
||||
#else
|
||||
Cpu_mode = PORT;
|
||||
#endif
|
||||
|
||||
fprintf(stderr,"SIMD CPU detect: %s\n",Cpu_modes[Cpu_mode]);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Determine CPU support for SIMD
|
||||
* Copyright 2004 Phil Karn, KA9Q
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "fec.h"
|
||||
|
||||
/* Various SIMD instruction set names */
|
||||
char *Cpu_modes[] = {"Unknown","Portable C","x86 Multi Media Extensions (MMX)",
|
||||
"x86 Streaming SIMD Extensions (SSE)",
|
||||
"x86 Streaming SIMD Extensions 2 (SSE2)",
|
||||
"PowerPC G4/G5 Altivec/Velocity Engine"};
|
||||
|
||||
enum cpu_mode Cpu_mode;
|
||||
|
||||
void find_cpu_mode(void){
|
||||
|
||||
if(Cpu_mode != UNKNOWN)
|
||||
return;
|
||||
|
||||
{
|
||||
int f;
|
||||
/* Figure out what kind of CPU we have */
|
||||
f = cpu_features();
|
||||
if(f & (1<<26)){ /* SSE2 is present */
|
||||
Cpu_mode = SSE2;
|
||||
} else if(f & (1<<25)){ /* SSE is present */
|
||||
Cpu_mode = SSE;
|
||||
} else if(f & (1<<23)){ /* MMX is present */
|
||||
Cpu_mode = MMX;
|
||||
} else { /* No SIMD at all */
|
||||
Cpu_mode = PORT;
|
||||
}
|
||||
}
|
||||
fprintf(stderr,"SIMD CPU detect: %s\n",Cpu_modes[Cpu_mode]);
|
||||
}
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
/* Reed-Solomon decoder
|
||||
* Copyright 2002 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define NULL ((void *)0)
|
||||
#define min(a,b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
#ifdef FIXED
|
||||
#include "fixed.h"
|
||||
#elif defined(BIGSYM)
|
||||
#include "int.h"
|
||||
#else
|
||||
#include "char.h"
|
||||
#endif
|
||||
|
||||
int DECODE_RS(
|
||||
#ifdef FIXED
|
||||
data_t *data, int *eras_pos, int no_eras,int pad){
|
||||
#else
|
||||
void *p,data_t *data, int *eras_pos, int no_eras){
|
||||
struct rs *rs = (struct rs *)p;
|
||||
#endif
|
||||
int deg_lambda, el, deg_omega;
|
||||
int i, j, r,k;
|
||||
data_t u,q,tmp,num1,num2,den,discr_r;
|
||||
data_t lambda[NROOTS+1], s[NROOTS]; /* Err+Eras Locator poly
|
||||
* and syndrome poly */
|
||||
data_t b[NROOTS+1], t[NROOTS+1], omega[NROOTS+1];
|
||||
data_t root[NROOTS], reg[NROOTS+1], loc[NROOTS];
|
||||
int syn_error, count;
|
||||
|
||||
#ifdef FIXED
|
||||
/* Check pad parameter for validity */
|
||||
if(pad < 0 || pad >= NN)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
/* form the syndromes; i.e., evaluate data(x) at roots of g(x) */
|
||||
for(i=0;i<NROOTS;i++)
|
||||
s[i] = data[0];
|
||||
|
||||
for(j=1;j<NN-PAD;j++){
|
||||
for(i=0;i<NROOTS;i++){
|
||||
if(s[i] == 0){
|
||||
s[i] = data[j];
|
||||
} else {
|
||||
s[i] = data[j] ^ ALPHA_TO[MODNN(INDEX_OF[s[i]] + (FCR+i)*PRIM)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert syndromes to index form, checking for nonzero condition */
|
||||
syn_error = 0;
|
||||
for(i=0;i<NROOTS;i++){
|
||||
syn_error |= s[i];
|
||||
s[i] = INDEX_OF[s[i]];
|
||||
}
|
||||
|
||||
if (!syn_error) {
|
||||
/* if syndrome is zero, data[] is a codeword and there are no
|
||||
* errors to correct. So return data[] unmodified
|
||||
*/
|
||||
count = 0;
|
||||
goto finish;
|
||||
}
|
||||
memset(&lambda[1],0,NROOTS*sizeof(lambda[0]));
|
||||
lambda[0] = 1;
|
||||
|
||||
if (no_eras > 0) {
|
||||
/* Init lambda to be the erasure locator polynomial */
|
||||
lambda[1] = ALPHA_TO[MODNN(PRIM*(NN-1-eras_pos[0]))];
|
||||
for (i = 1; i < no_eras; i++) {
|
||||
u = MODNN(PRIM*(NN-1-eras_pos[i]));
|
||||
for (j = i+1; j > 0; j--) {
|
||||
tmp = INDEX_OF[lambda[j - 1]];
|
||||
if(tmp != A0)
|
||||
lambda[j] ^= ALPHA_TO[MODNN(u + tmp)];
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG >= 1
|
||||
/* Test code that verifies the erasure locator polynomial just constructed
|
||||
Needed only for decoder debugging. */
|
||||
|
||||
/* find roots of the erasure location polynomial */
|
||||
for(i=1;i<=no_eras;i++)
|
||||
reg[i] = INDEX_OF[lambda[i]];
|
||||
|
||||
count = 0;
|
||||
for (i = 1,k=IPRIM-1; i <= NN; i++,k = MODNN(k+IPRIM)) {
|
||||
q = 1;
|
||||
for (j = 1; j <= no_eras; j++)
|
||||
if (reg[j] != A0) {
|
||||
reg[j] = MODNN(reg[j] + j);
|
||||
q ^= ALPHA_TO[reg[j]];
|
||||
}
|
||||
if (q != 0)
|
||||
continue;
|
||||
/* store root and error location number indices */
|
||||
root[count] = i;
|
||||
loc[count] = k;
|
||||
count++;
|
||||
}
|
||||
if (count != no_eras) {
|
||||
printf("count = %d no_eras = %d\n lambda(x) is WRONG\n",count,no_eras);
|
||||
count = -1;
|
||||
goto finish;
|
||||
}
|
||||
#if DEBUG >= 2
|
||||
printf("\n Erasure positions as determined by roots of Eras Loc Poly:\n");
|
||||
for (i = 0; i < count; i++)
|
||||
printf("%d ", loc[i]);
|
||||
printf("\n");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
for(i=0;i<NROOTS+1;i++)
|
||||
b[i] = INDEX_OF[lambda[i]];
|
||||
|
||||
/*
|
||||
* Begin Berlekamp-Massey algorithm to determine error+erasure
|
||||
* locator polynomial
|
||||
*/
|
||||
r = no_eras;
|
||||
el = no_eras;
|
||||
while (++r <= NROOTS) { /* r is the step number */
|
||||
/* Compute discrepancy at the r-th step in poly-form */
|
||||
discr_r = 0;
|
||||
for (i = 0; i < r; i++){
|
||||
if ((lambda[i] != 0) && (s[r-i-1] != A0)) {
|
||||
discr_r ^= ALPHA_TO[MODNN(INDEX_OF[lambda[i]] + s[r-i-1])];
|
||||
}
|
||||
}
|
||||
discr_r = INDEX_OF[discr_r]; /* Index form */
|
||||
if (discr_r == A0) {
|
||||
/* 2 lines below: B(x) <-- x*B(x) */
|
||||
memmove(&b[1],b,NROOTS*sizeof(b[0]));
|
||||
b[0] = A0;
|
||||
} else {
|
||||
/* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */
|
||||
t[0] = lambda[0];
|
||||
for (i = 0 ; i < NROOTS; i++) {
|
||||
if(b[i] != A0)
|
||||
t[i+1] = lambda[i+1] ^ ALPHA_TO[MODNN(discr_r + b[i])];
|
||||
else
|
||||
t[i+1] = lambda[i+1];
|
||||
}
|
||||
if (2 * el <= r + no_eras - 1) {
|
||||
el = r + no_eras - el;
|
||||
/*
|
||||
* 2 lines below: B(x) <-- inv(discr_r) *
|
||||
* lambda(x)
|
||||
*/
|
||||
for (i = 0; i <= NROOTS; i++)
|
||||
b[i] = (lambda[i] == 0) ? A0 : MODNN(INDEX_OF[lambda[i]] - discr_r + NN);
|
||||
} else {
|
||||
/* 2 lines below: B(x) <-- x*B(x) */
|
||||
memmove(&b[1],b,NROOTS*sizeof(b[0]));
|
||||
b[0] = A0;
|
||||
}
|
||||
memcpy(lambda,t,(NROOTS+1)*sizeof(t[0]));
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert lambda to index form and compute deg(lambda(x)) */
|
||||
deg_lambda = 0;
|
||||
for(i=0;i<NROOTS+1;i++){
|
||||
lambda[i] = INDEX_OF[lambda[i]];
|
||||
if(lambda[i] != A0)
|
||||
deg_lambda = i;
|
||||
}
|
||||
/* Find roots of the error+erasure locator polynomial by Chien search */
|
||||
memcpy(®[1],&lambda[1],NROOTS*sizeof(reg[0]));
|
||||
count = 0; /* Number of roots of lambda(x) */
|
||||
for (i = 1,k=IPRIM-1; i <= NN; i++,k = MODNN(k+IPRIM)) {
|
||||
q = 1; /* lambda[0] is always 0 */
|
||||
for (j = deg_lambda; j > 0; j--){
|
||||
if (reg[j] != A0) {
|
||||
reg[j] = MODNN(reg[j] + j);
|
||||
q ^= ALPHA_TO[reg[j]];
|
||||
}
|
||||
}
|
||||
if (q != 0)
|
||||
continue; /* Not a root */
|
||||
/* store root (index-form) and error location number */
|
||||
#if DEBUG>=2
|
||||
printf("count %d root %d loc %d\n",count,i,k);
|
||||
#endif
|
||||
root[count] = i;
|
||||
loc[count] = k;
|
||||
/* If we've already found max possible roots,
|
||||
* abort the search to save time
|
||||
*/
|
||||
if(++count == deg_lambda)
|
||||
break;
|
||||
}
|
||||
if (deg_lambda != count) {
|
||||
/*
|
||||
* deg(lambda) unequal to number of roots => uncorrectable
|
||||
* error detected
|
||||
*/
|
||||
count = -1;
|
||||
goto finish;
|
||||
}
|
||||
/*
|
||||
* Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
|
||||
* x**NROOTS). in index form. Also find deg(omega).
|
||||
*/
|
||||
deg_omega = deg_lambda-1;
|
||||
for (i = 0; i <= deg_omega;i++){
|
||||
tmp = 0;
|
||||
for(j=i;j >= 0; j--){
|
||||
if ((s[i - j] != A0) && (lambda[j] != A0))
|
||||
tmp ^= ALPHA_TO[MODNN(s[i - j] + lambda[j])];
|
||||
}
|
||||
omega[i] = INDEX_OF[tmp];
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
|
||||
* inv(X(l))**(FCR-1) and den = lambda_pr(inv(X(l))) all in poly-form
|
||||
*/
|
||||
for (j = count-1; j >=0; j--) {
|
||||
num1 = 0;
|
||||
for (i = deg_omega; i >= 0; i--) {
|
||||
if (omega[i] != A0)
|
||||
num1 ^= ALPHA_TO[MODNN(omega[i] + i * root[j])];
|
||||
}
|
||||
num2 = ALPHA_TO[MODNN(root[j] * (FCR - 1) + NN)];
|
||||
den = 0;
|
||||
|
||||
/* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
|
||||
for (i = min(deg_lambda,NROOTS-1) & ~1; i >= 0; i -=2) {
|
||||
if(lambda[i+1] != A0)
|
||||
den ^= ALPHA_TO[MODNN(lambda[i+1] + i * root[j])];
|
||||
}
|
||||
#if DEBUG >= 1
|
||||
if (den == 0) {
|
||||
printf("\n ERROR: denominator = 0\n");
|
||||
count = -1;
|
||||
goto finish;
|
||||
}
|
||||
#endif
|
||||
/* Apply error to data */
|
||||
if (num1 != 0 && loc[j] >= PAD) {
|
||||
data[loc[j]-PAD] ^= ALPHA_TO[MODNN(INDEX_OF[num1] + INDEX_OF[num2] + NN - INDEX_OF[den])];
|
||||
}
|
||||
}
|
||||
finish:
|
||||
if(eras_pos != NULL){
|
||||
for(i=0;i<count;i++)
|
||||
eras_pos[i] = loc[i];
|
||||
}
|
||||
return count;
|
||||
}
|
||||
+298
@@ -0,0 +1,298 @@
|
||||
/* The guts of the Reed-Solomon decoder, meant to be #included
|
||||
* into a function body with the following typedefs, macros and variables supplied
|
||||
* according to the code parameters:
|
||||
|
||||
* data_t - a typedef for the data symbol
|
||||
* data_t data[] - array of NN data and parity symbols to be corrected in place
|
||||
* retval - an integer lvalue into which the decoder's return code is written
|
||||
* NROOTS - the number of roots in the RS code generator polynomial,
|
||||
* which is the same as the number of parity symbols in a block.
|
||||
Integer variable or literal.
|
||||
* NN - the total number of symbols in a RS block. Integer variable or literal.
|
||||
* PAD - the number of pad symbols in a block. Integer variable or literal.
|
||||
* ALPHA_TO - The address of an array of NN elements to convert Galois field
|
||||
* elements in index (log) form to polynomial form. Read only.
|
||||
* INDEX_OF - The address of an array of NN elements to convert Galois field
|
||||
* elements in polynomial form to index (log) form. Read only.
|
||||
* MODNN - a function to reduce its argument modulo NN. May be inline or a macro.
|
||||
* FCR - An integer literal or variable specifying the first consecutive root of the
|
||||
* Reed-Solomon generator polynomial. Integer variable or literal.
|
||||
* PRIM - The primitive root of the generator poly. Integer variable or literal.
|
||||
* DEBUG - If set to 1 or more, do various internal consistency checking. Leave this
|
||||
* undefined for production code
|
||||
|
||||
* The memset(), memmove(), and memcpy() functions are used. The appropriate header
|
||||
* file declaring these functions (usually <string.h>) must be included by the calling
|
||||
* program.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(NROOTS)
|
||||
#error "NROOTS not defined"
|
||||
#endif
|
||||
|
||||
#if !defined(NN)
|
||||
#error "NN not defined"
|
||||
#endif
|
||||
|
||||
#if !defined(PAD)
|
||||
#error "PAD not defined"
|
||||
#endif
|
||||
|
||||
#if !defined(ALPHA_TO)
|
||||
#error "ALPHA_TO not defined"
|
||||
#endif
|
||||
|
||||
#if !defined(INDEX_OF)
|
||||
#error "INDEX_OF not defined"
|
||||
#endif
|
||||
|
||||
#if !defined(MODNN)
|
||||
#error "MODNN not defined"
|
||||
#endif
|
||||
|
||||
#if !defined(FCR)
|
||||
#error "FCR not defined"
|
||||
#endif
|
||||
|
||||
#if !defined(PRIM)
|
||||
#error "PRIM not defined"
|
||||
#endif
|
||||
|
||||
#if !defined(NULL)
|
||||
#define NULL ((void *)0)
|
||||
#endif
|
||||
|
||||
#undef MIN
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
#undef A0
|
||||
#define A0 (NN)
|
||||
|
||||
{
|
||||
int deg_lambda, el, deg_omega;
|
||||
int i, j, r,k;
|
||||
data_t u,q,tmp,num1,num2,den,discr_r;
|
||||
data_t lambda[NROOTS+1], s[NROOTS]; /* Err+Eras Locator poly
|
||||
* and syndrome poly */
|
||||
data_t b[NROOTS+1], t[NROOTS+1], omega[NROOTS+1];
|
||||
data_t root[NROOTS], reg[NROOTS+1], loc[NROOTS];
|
||||
int syn_error, count;
|
||||
|
||||
/* form the syndromes; i.e., evaluate data(x) at roots of g(x) */
|
||||
for(i=0;i<NROOTS;i++)
|
||||
s[i] = data[0];
|
||||
|
||||
for(j=1;j<NN-PAD;j++){
|
||||
for(i=0;i<NROOTS;i++){
|
||||
if(s[i] == 0){
|
||||
s[i] = data[j];
|
||||
} else {
|
||||
s[i] = data[j] ^ ALPHA_TO[MODNN(INDEX_OF[s[i]] + (FCR+i)*PRIM)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert syndromes to index form, checking for nonzero condition */
|
||||
syn_error = 0;
|
||||
for(i=0;i<NROOTS;i++){
|
||||
syn_error |= s[i];
|
||||
s[i] = INDEX_OF[s[i]];
|
||||
}
|
||||
|
||||
if (!syn_error) {
|
||||
/* if syndrome is zero, data[] is a codeword and there are no
|
||||
* errors to correct. So return data[] unmodified
|
||||
*/
|
||||
count = 0;
|
||||
goto finish;
|
||||
}
|
||||
memset(&lambda[1],0,NROOTS*sizeof(lambda[0]));
|
||||
lambda[0] = 1;
|
||||
|
||||
if (no_eras > 0) {
|
||||
/* Init lambda to be the erasure locator polynomial */
|
||||
lambda[1] = ALPHA_TO[MODNN(PRIM*(NN-1-eras_pos[0]))];
|
||||
for (i = 1; i < no_eras; i++) {
|
||||
u = MODNN(PRIM*(NN-1-eras_pos[i]));
|
||||
for (j = i+1; j > 0; j--) {
|
||||
tmp = INDEX_OF[lambda[j - 1]];
|
||||
if(tmp != A0)
|
||||
lambda[j] ^= ALPHA_TO[MODNN(u + tmp)];
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG >= 1
|
||||
/* Test code that verifies the erasure locator polynomial just constructed
|
||||
Needed only for decoder debugging. */
|
||||
|
||||
/* find roots of the erasure location polynomial */
|
||||
for(i=1;i<=no_eras;i++)
|
||||
reg[i] = INDEX_OF[lambda[i]];
|
||||
|
||||
count = 0;
|
||||
for (i = 1,k=IPRIM-1; i <= NN; i++,k = MODNN(k+IPRIM)) {
|
||||
q = 1;
|
||||
for (j = 1; j <= no_eras; j++)
|
||||
if (reg[j] != A0) {
|
||||
reg[j] = MODNN(reg[j] + j);
|
||||
q ^= ALPHA_TO[reg[j]];
|
||||
}
|
||||
if (q != 0)
|
||||
continue;
|
||||
/* store root and error location number indices */
|
||||
root[count] = i;
|
||||
loc[count] = k;
|
||||
count++;
|
||||
}
|
||||
if (count != no_eras) {
|
||||
printf("count = %d no_eras = %d\n lambda(x) is WRONG\n",count,no_eras);
|
||||
count = -1;
|
||||
goto finish;
|
||||
}
|
||||
#if DEBUG >= 2
|
||||
printf("\n Erasure positions as determined by roots of Eras Loc Poly:\n");
|
||||
for (i = 0; i < count; i++)
|
||||
printf("%d ", loc[i]);
|
||||
printf("\n");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
for(i=0;i<NROOTS+1;i++)
|
||||
b[i] = INDEX_OF[lambda[i]];
|
||||
|
||||
/*
|
||||
* Begin Berlekamp-Massey algorithm to determine error+erasure
|
||||
* locator polynomial
|
||||
*/
|
||||
r = no_eras;
|
||||
el = no_eras;
|
||||
while (++r <= NROOTS) { /* r is the step number */
|
||||
/* Compute discrepancy at the r-th step in poly-form */
|
||||
discr_r = 0;
|
||||
for (i = 0; i < r; i++){
|
||||
if ((lambda[i] != 0) && (s[r-i-1] != A0)) {
|
||||
discr_r ^= ALPHA_TO[MODNN(INDEX_OF[lambda[i]] + s[r-i-1])];
|
||||
}
|
||||
}
|
||||
discr_r = INDEX_OF[discr_r]; /* Index form */
|
||||
if (discr_r == A0) {
|
||||
/* 2 lines below: B(x) <-- x*B(x) */
|
||||
memmove(&b[1],b,NROOTS*sizeof(b[0]));
|
||||
b[0] = A0;
|
||||
} else {
|
||||
/* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */
|
||||
t[0] = lambda[0];
|
||||
for (i = 0 ; i < NROOTS; i++) {
|
||||
if(b[i] != A0)
|
||||
t[i+1] = lambda[i+1] ^ ALPHA_TO[MODNN(discr_r + b[i])];
|
||||
else
|
||||
t[i+1] = lambda[i+1];
|
||||
}
|
||||
if (2 * el <= r + no_eras - 1) {
|
||||
el = r + no_eras - el;
|
||||
/*
|
||||
* 2 lines below: B(x) <-- inv(discr_r) *
|
||||
* lambda(x)
|
||||
*/
|
||||
for (i = 0; i <= NROOTS; i++)
|
||||
b[i] = (lambda[i] == 0) ? A0 : MODNN(INDEX_OF[lambda[i]] - discr_r + NN);
|
||||
} else {
|
||||
/* 2 lines below: B(x) <-- x*B(x) */
|
||||
memmove(&b[1],b,NROOTS*sizeof(b[0]));
|
||||
b[0] = A0;
|
||||
}
|
||||
memcpy(lambda,t,(NROOTS+1)*sizeof(t[0]));
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert lambda to index form and compute deg(lambda(x)) */
|
||||
deg_lambda = 0;
|
||||
for(i=0;i<NROOTS+1;i++){
|
||||
lambda[i] = INDEX_OF[lambda[i]];
|
||||
if(lambda[i] != A0)
|
||||
deg_lambda = i;
|
||||
}
|
||||
/* Find roots of the error+erasure locator polynomial by Chien search */
|
||||
memcpy(®[1],&lambda[1],NROOTS*sizeof(reg[0]));
|
||||
count = 0; /* Number of roots of lambda(x) */
|
||||
for (i = 1,k=IPRIM-1; i <= NN; i++,k = MODNN(k+IPRIM)) {
|
||||
q = 1; /* lambda[0] is always 0 */
|
||||
for (j = deg_lambda; j > 0; j--){
|
||||
if (reg[j] != A0) {
|
||||
reg[j] = MODNN(reg[j] + j);
|
||||
q ^= ALPHA_TO[reg[j]];
|
||||
}
|
||||
}
|
||||
if (q != 0)
|
||||
continue; /* Not a root */
|
||||
/* store root (index-form) and error location number */
|
||||
#if DEBUG>=2
|
||||
printf("count %d root %d loc %d\n",count,i,k);
|
||||
#endif
|
||||
root[count] = i;
|
||||
loc[count] = k;
|
||||
/* If we've already found max possible roots,
|
||||
* abort the search to save time
|
||||
*/
|
||||
if(++count == deg_lambda)
|
||||
break;
|
||||
}
|
||||
if (deg_lambda != count) {
|
||||
/*
|
||||
* deg(lambda) unequal to number of roots => uncorrectable
|
||||
* error detected
|
||||
*/
|
||||
count = -1;
|
||||
goto finish;
|
||||
}
|
||||
/*
|
||||
* Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
|
||||
* x**NROOTS). in index form. Also find deg(omega).
|
||||
*/
|
||||
deg_omega = deg_lambda-1;
|
||||
for (i = 0; i <= deg_omega;i++){
|
||||
tmp = 0;
|
||||
for(j=i;j >= 0; j--){
|
||||
if ((s[i - j] != A0) && (lambda[j] != A0))
|
||||
tmp ^= ALPHA_TO[MODNN(s[i - j] + lambda[j])];
|
||||
}
|
||||
omega[i] = INDEX_OF[tmp];
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
|
||||
* inv(X(l))**(FCR-1) and den = lambda_pr(inv(X(l))) all in poly-form
|
||||
*/
|
||||
for (j = count-1; j >=0; j--) {
|
||||
num1 = 0;
|
||||
for (i = deg_omega; i >= 0; i--) {
|
||||
if (omega[i] != A0)
|
||||
num1 ^= ALPHA_TO[MODNN(omega[i] + i * root[j])];
|
||||
}
|
||||
num2 = ALPHA_TO[MODNN(root[j] * (FCR - 1) + NN)];
|
||||
den = 0;
|
||||
|
||||
/* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
|
||||
for (i = MIN(deg_lambda,NROOTS-1) & ~1; i >= 0; i -=2) {
|
||||
if(lambda[i+1] != A0)
|
||||
den ^= ALPHA_TO[MODNN(lambda[i+1] + i * root[j])];
|
||||
}
|
||||
#if DEBUG >= 1
|
||||
if (den == 0) {
|
||||
printf("\n ERROR: denominator = 0\n");
|
||||
count = -1;
|
||||
goto finish;
|
||||
}
|
||||
#endif
|
||||
/* Apply error to data */
|
||||
if (num1 != 0 && loc[j] >= PAD) {
|
||||
data[loc[j]-PAD] ^= ALPHA_TO[MODNN(INDEX_OF[num1] + INDEX_OF[num2] + NN - INDEX_OF[den])];
|
||||
}
|
||||
}
|
||||
finish:
|
||||
if(eras_pos != NULL){
|
||||
for(i=0;i<count;i++)
|
||||
eras_pos[i] = loc[i];
|
||||
}
|
||||
retval = count;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/* General purpose Reed-Solomon decoder for 8-bit symbols or less
|
||||
* Copyright 2003 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "fixed.h"
|
||||
|
||||
int decode_rs_8(data_t *data, int *eras_pos, int no_eras, int pad){
|
||||
int retval;
|
||||
|
||||
if(pad < 0 || pad > 222){
|
||||
return -1;
|
||||
}
|
||||
|
||||
#include "decode_rs.h"
|
||||
|
||||
return retval;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* This function wraps around the fixed 8-bit decoder, performing the
|
||||
* basis transformations necessary to meet the CCSDS standard
|
||||
*
|
||||
* Copyright 2002, Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
#include "ccsds.h"
|
||||
#include "fec.h"
|
||||
|
||||
int decode_rs_ccsds(data_t *data,int *eras_pos,int no_eras,int pad){
|
||||
int i,r;
|
||||
data_t cdata[NN];
|
||||
|
||||
/* Convert data from dual basis to conventional */
|
||||
for(i=0;i<NN-pad;i++)
|
||||
cdata[i] = Tal1tab[data[i]];
|
||||
|
||||
r = decode_rs_8(cdata,eras_pos,no_eras,pad);
|
||||
|
||||
if(r > 0){
|
||||
/* Convert from conventional to dual basis */
|
||||
for(i=0;i<NN-pad;i++)
|
||||
data[i] = Taltab[cdata[i]];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/* General purpose Reed-Solomon decoder for 8-bit symbols or less
|
||||
* Copyright 2003 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "char.h"
|
||||
#include "rs-common.h"
|
||||
|
||||
int decode_rs_char(void *p, data_t *data, int *eras_pos, int no_eras){
|
||||
int retval;
|
||||
struct rs *rs = (struct rs *)p;
|
||||
|
||||
#include "decode_rs.h"
|
||||
|
||||
return retval;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/* General purpose Reed-Solomon decoder
|
||||
* Copyright 2003 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "int.h"
|
||||
#include "rs-common.h"
|
||||
|
||||
int decode_rs_int(void *p, data_t *data, int *eras_pos, int no_eras){
|
||||
int retval;
|
||||
struct rs *rs = (struct rs *)p;
|
||||
|
||||
#include "decode_rs.h"
|
||||
|
||||
return retval;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/* 16-bit signed integer dot product
|
||||
* Switch to appropriate versions
|
||||
* Copyright 2004 Phil Karn
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include "fec.h"
|
||||
|
||||
void *initdp_port(signed short coeffs[],int len);
|
||||
long dotprod_port(void *p,signed short *b);
|
||||
void freedp_port(void *p);
|
||||
|
||||
#ifdef __i386__
|
||||
void *initdp_mmx(signed short coeffs[],int len);
|
||||
void *initdp_sse2(signed short coeffs[],int len);
|
||||
long dotprod_mmx(void *p,signed short *b);
|
||||
long dotprod_sse2(void *p,signed short *b);
|
||||
void freedp_mmx(void *p);
|
||||
void freedp_sse2(void *p);
|
||||
#endif
|
||||
|
||||
#ifdef __VEC__
|
||||
void *initdp_av(signed short coeffs[],int len);
|
||||
long dotprod_av(void *p,signed short *b);
|
||||
void freedp_av(void *p);
|
||||
#endif
|
||||
|
||||
/* Create and return a descriptor for use with the dot product function */
|
||||
void *initdp(signed short coeffs[],int len){
|
||||
find_cpu_mode();
|
||||
|
||||
switch(Cpu_mode){
|
||||
case PORT:
|
||||
default:
|
||||
return initdp_port(coeffs,len);
|
||||
#ifdef __i386__
|
||||
case MMX:
|
||||
case SSE:
|
||||
return initdp_mmx(coeffs,len);
|
||||
case SSE2:
|
||||
return initdp_sse2(coeffs,len);
|
||||
#endif
|
||||
|
||||
#ifdef __VEC__
|
||||
case ALTIVEC:
|
||||
return initdp_av(coeffs,len);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Free a dot product descriptor created earlier */
|
||||
void freedp(void *p){
|
||||
switch(Cpu_mode){
|
||||
case PORT:
|
||||
default:
|
||||
#ifdef __i386__
|
||||
case MMX:
|
||||
case SSE:
|
||||
return freedp_mmx(p);
|
||||
case SSE2:
|
||||
return freedp_sse2(p);
|
||||
#endif
|
||||
#ifdef __VEC__
|
||||
case ALTIVEC:
|
||||
return freedp_av(p);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute a dot product given a descriptor and an input array
|
||||
* The length is taken from the descriptor
|
||||
*/
|
||||
long dotprod(void *p,signed short a[]){
|
||||
switch(Cpu_mode){
|
||||
case PORT:
|
||||
default:
|
||||
return dotprod_port(p,a);
|
||||
#ifdef __i386__
|
||||
case MMX:
|
||||
case SSE:
|
||||
return dotprod_mmx(p,a);
|
||||
case SSE2:
|
||||
return dotprod_sse2(p,a);
|
||||
#endif
|
||||
|
||||
#ifdef __VEC__
|
||||
case ALTIVEC:
|
||||
return dotprod_av(p,a);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/* Internal definitions for dotproduct function */
|
||||
|
||||
struct dotprod {
|
||||
int len; /* Number of coefficients */
|
||||
|
||||
/* On a MMX or SSE machine, these hold 4 copies of the coefficients,
|
||||
* preshifted by 0,1,2,3 words to meet all possible input data
|
||||
* alignments (see Intel ap559 on MMX dot products).
|
||||
*
|
||||
* SSE2 is similar, but with 8 words at a time
|
||||
*
|
||||
* On a non-MMX machine, only one copy is present
|
||||
*/
|
||||
signed short *coeffs[8];
|
||||
};
|
||||
@@ -0,0 +1,93 @@
|
||||
/* 16-bit signed integer dot product
|
||||
* Altivec-assisted version
|
||||
* Copyright 2004 Phil Karn
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include "fec.h"
|
||||
|
||||
struct dotprod {
|
||||
int len; /* Number of coefficients */
|
||||
|
||||
/* On an Altivec machine, these hold 8 copies of the coefficients,
|
||||
* preshifted by 0,1,..7 words to meet all possible input data
|
||||
*/
|
||||
signed short *coeffs[8];
|
||||
};
|
||||
|
||||
/* Create and return a descriptor for use with the dot product function */
|
||||
void *initdp_av(signed short coeffs[],int len){
|
||||
struct dotprod *dp;
|
||||
int i,j;
|
||||
|
||||
if(len == 0)
|
||||
return NULL;
|
||||
|
||||
dp = (struct dotprod *)calloc(1,sizeof(struct dotprod));
|
||||
dp->len = len;
|
||||
|
||||
/* Make 8 copies of coefficients, one for each data alignment,
|
||||
* each aligned to 16-byte boundary
|
||||
*/
|
||||
for(i=0;i<8;i++){
|
||||
dp->coeffs[i] = calloc(1+(len+i-1)/8,sizeof(vector signed short));
|
||||
for(j=0;j<len;j++)
|
||||
dp->coeffs[i][j+i] = coeffs[j];
|
||||
}
|
||||
return (void *)dp;
|
||||
}
|
||||
|
||||
|
||||
/* Free a dot product descriptor created earlier */
|
||||
void freedp_av(void *p){
|
||||
struct dotprod *dp = (struct dotprod *)p;
|
||||
int i;
|
||||
|
||||
for(i=0;i<8;i++)
|
||||
if(dp->coeffs[i] != NULL)
|
||||
free(dp->coeffs[i]);
|
||||
free(dp);
|
||||
}
|
||||
|
||||
/* Compute a dot product given a descriptor and an input array
|
||||
* The length is taken from the descriptor
|
||||
*/
|
||||
long dotprod_av(void *p,signed short a[]){
|
||||
struct dotprod *dp = (struct dotprod *)p;
|
||||
int al;
|
||||
vector signed short *ar,*d;
|
||||
vector signed int sums0,sums1,sums2,sums3;
|
||||
union { vector signed int v; signed int w[4];} s;
|
||||
int nblocks;
|
||||
|
||||
/* round ar down to beginning of 16-byte block containing 0th element of
|
||||
* input buffer. Then set d to one of 8 sets of shifted coefficients
|
||||
*/
|
||||
ar = (vector signed short *)((int)a & ~15);
|
||||
al = ((int)a & 15)/sizeof(signed short);
|
||||
d = (vector signed short *)dp->coeffs[al];
|
||||
|
||||
nblocks = (dp->len+al-1)/8+1;
|
||||
|
||||
/* Sum into four vectors each holding four 32-bit partial sums */
|
||||
sums3 = sums2 = sums1 = sums0 = (vector signed int)(0);
|
||||
while(nblocks >= 4){
|
||||
sums0 = vec_msums(ar[nblocks-1],d[nblocks-1],sums0);
|
||||
sums1 = vec_msums(ar[nblocks-2],d[nblocks-2],sums1);
|
||||
sums2 = vec_msums(ar[nblocks-3],d[nblocks-3],sums2);
|
||||
sums3 = vec_msums(ar[nblocks-4],d[nblocks-4],sums3);
|
||||
nblocks -= 4;
|
||||
}
|
||||
sums0 = vec_adds(sums0,sums1);
|
||||
sums2 = vec_adds(sums2,sums3);
|
||||
sums0 = vec_adds(sums0,sums2);
|
||||
while(nblocks-- > 0){
|
||||
sums0 = vec_msums(ar[nblocks],d[nblocks],sums0);
|
||||
}
|
||||
/* Sum 4 partial sums into final result */
|
||||
s.v = vec_sums(sums0,(vector signed int)(0));
|
||||
|
||||
return s.w[3];
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/* 16-bit signed integer dot product
|
||||
* MMX assisted version; also for SSE
|
||||
*
|
||||
* Copyright 2004 Phil Karn
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include "fec.h"
|
||||
|
||||
struct dotprod {
|
||||
int len; /* Number of coefficients */
|
||||
|
||||
/* On a MMX or SSE machine, these hold 4 copies of the coefficients,
|
||||
* preshifted by 0,1,2,3 words to meet all possible input data
|
||||
* alignments (see Intel ap559 on MMX dot products).
|
||||
*/
|
||||
signed short *coeffs[4];
|
||||
};
|
||||
long dotprod_mmx_assist(signed short *a,signed short *b,int cnt);
|
||||
|
||||
/* Create and return a descriptor for use with the dot product function */
|
||||
void *initdp_mmx(signed short coeffs[],int len){
|
||||
struct dotprod *dp;
|
||||
int i,j;
|
||||
|
||||
|
||||
if(len == 0)
|
||||
return NULL;
|
||||
|
||||
dp = (struct dotprod *)calloc(1,sizeof(struct dotprod));
|
||||
dp->len = len;
|
||||
|
||||
/* Make 4 copies of coefficients, one for each data alignment */
|
||||
for(i=0;i<4;i++){
|
||||
dp->coeffs[i] = (signed short *)calloc(1+(len+i-1)/4,
|
||||
4*sizeof(signed short));
|
||||
for(j=0;j<len;j++)
|
||||
dp->coeffs[i][j+i] = coeffs[j];
|
||||
}
|
||||
return (void *)dp;
|
||||
}
|
||||
|
||||
|
||||
/* Free a dot product descriptor created earlier */
|
||||
void freedp_mmx(void *p){
|
||||
struct dotprod *dp = (struct dotprod *)p;
|
||||
int i;
|
||||
|
||||
for(i=0;i<4;i++)
|
||||
if(dp->coeffs[i] != NULL)
|
||||
free(dp->coeffs[i]);
|
||||
free(dp);
|
||||
}
|
||||
|
||||
/* Compute a dot product given a descriptor and an input array
|
||||
* The length is taken from the descriptor
|
||||
*/
|
||||
long dotprod_mmx(void *p,signed short a[]){
|
||||
struct dotprod *dp = (struct dotprod *)p;
|
||||
int al;
|
||||
signed short *ar;
|
||||
|
||||
/* Round input data address down to 8 byte boundary
|
||||
* NB: depending on the alignment of a[], memory
|
||||
* before a[] will be accessed. The contents don't matter since they'll
|
||||
* be multiplied by zero coefficients. I can't conceive of any
|
||||
* situation where this could cause a segfault since memory protection
|
||||
* in the x86 machines is done on much larger boundaries
|
||||
*/
|
||||
ar = (signed short *)((int)a & ~7);
|
||||
|
||||
/* Choose one of 4 sets of pre-shifted coefficients. al is both the
|
||||
* index into dp->coeffs[] and the number of 0 words padded onto
|
||||
* that coefficients array for alignment purposes
|
||||
*/
|
||||
al = a - ar;
|
||||
|
||||
/* Call assembler routine to do the work, passing number of 4-word blocks */
|
||||
return dotprod_mmx_assist(ar,dp->coeffs[al],(dp->len+al-1)/4+1);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
# SIMD MMX dot product
|
||||
# Equivalent to the following C code:
|
||||
# long dotprod(signed short *a,signed short *b,int cnt)
|
||||
# {
|
||||
# long sum = 0;
|
||||
# cnt *= 4;
|
||||
# while(cnt--)
|
||||
# sum += *a++ + *b++;
|
||||
# return sum;
|
||||
# }
|
||||
# a and b should also be 64-bit aligned, or speed will suffer greatly
|
||||
# Copyright 1999, Phil Karn KA9Q
|
||||
# May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
|
||||
.text
|
||||
.global dotprod_mmx_assist
|
||||
.type dotprod_mmx_assist,@function
|
||||
dotprod_mmx_assist:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
pushl %ecx
|
||||
pushl %ebx
|
||||
movl 8(%ebp),%esi # a
|
||||
movl 12(%ebp),%edi # b
|
||||
movl 16(%ebp),%ecx # cnt
|
||||
pxor %mm0,%mm0 # clear running sum (in two 32-bit halves)
|
||||
|
||||
# MMX dot product loop unrolled 4 times, crunching 16 terms per loop
|
||||
.align 16
|
||||
.Loop1: subl $4,%ecx
|
||||
jl .Loop1Done
|
||||
|
||||
movq (%esi),%mm1 # mm1 = a[3],a[2],a[1],a[0]
|
||||
pmaddwd (%edi),%mm1 # mm1 = b[3]*a[3]+b[2]*a[2],b[1]*a[1]+b[0]*a[0]
|
||||
paddd %mm1,%mm0
|
||||
|
||||
movq 8(%esi),%mm1
|
||||
pmaddwd 8(%edi),%mm1
|
||||
paddd %mm1,%mm0
|
||||
|
||||
movq 16(%esi),%mm1
|
||||
pmaddwd 16(%edi),%mm1
|
||||
paddd %mm1,%mm0
|
||||
|
||||
movq 24(%esi),%mm1
|
||||
addl $32,%esi
|
||||
pmaddwd 24(%edi),%mm1
|
||||
addl $32,%edi
|
||||
paddd %mm1,%mm0
|
||||
|
||||
jmp .Loop1
|
||||
.Loop1Done:
|
||||
|
||||
addl $4,%ecx
|
||||
|
||||
# MMX dot product loop, not unrolled, crunching 4 terms per loop
|
||||
# This could be redone as Duff's Device on the unrolled loop above
|
||||
.Loop2: subl $1,%ecx
|
||||
jl .Loop2Done
|
||||
|
||||
movq (%esi),%mm1
|
||||
addl $8,%esi
|
||||
pmaddwd (%edi),%mm1
|
||||
addl $8,%edi
|
||||
paddd %mm1,%mm0
|
||||
jmp .Loop2
|
||||
.Loop2Done:
|
||||
|
||||
movd %mm0,%ebx # right-hand word to ebx
|
||||
punpckhdq %mm0,%mm0 # left-hand word to right side of %mm0
|
||||
movd %mm0,%eax
|
||||
addl %ebx,%eax # running sum now in %eax
|
||||
emms # done with MMX
|
||||
|
||||
popl %ebx
|
||||
popl %ecx
|
||||
popl %edi
|
||||
popl %esi
|
||||
movl %ebp,%esp
|
||||
popl %ebp
|
||||
ret
|
||||
@@ -0,0 +1,58 @@
|
||||
/* 16-bit signed integer dot product
|
||||
* Portable C version
|
||||
* Copyright 2004 Phil Karn
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include "fec.h"
|
||||
|
||||
struct dotprod {
|
||||
int len; /* Number of coefficients */
|
||||
|
||||
signed short *coeffs;
|
||||
};
|
||||
|
||||
/* Create and return a descriptor for use with the dot product function */
|
||||
void *initdp_port(signed short coeffs[],int len){
|
||||
struct dotprod *dp;
|
||||
int j;
|
||||
|
||||
if(len == 0)
|
||||
return NULL;
|
||||
|
||||
dp = (struct dotprod *)calloc(1,sizeof(struct dotprod));
|
||||
dp->len = len;
|
||||
|
||||
/* Just one copy of the coefficients for the C version */
|
||||
dp->coeffs = (signed short *)calloc(len,sizeof(signed short));
|
||||
for(j=0;j<len;j++)
|
||||
dp->coeffs[j] = coeffs[j];
|
||||
return (void *)dp;
|
||||
}
|
||||
|
||||
|
||||
/* Free a dot product descriptor created earlier */
|
||||
void freedp_port(void *p){
|
||||
struct dotprod *dp = (struct dotprod *)p;
|
||||
|
||||
if(dp->coeffs != NULL)
|
||||
free(dp->coeffs);
|
||||
free(dp);
|
||||
}
|
||||
|
||||
/* Compute a dot product given a descriptor and an input array
|
||||
* The length is taken from the descriptor
|
||||
*/
|
||||
long dotprod_port(void *p,signed short a[]){
|
||||
struct dotprod *dp = (struct dotprod *)p;
|
||||
long corr;
|
||||
int i;
|
||||
|
||||
corr = 0;
|
||||
for(i=0;i<dp->len;i++){
|
||||
corr += (long)a[i] * dp->coeffs[i];
|
||||
}
|
||||
return corr;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/* 16-bit signed integer dot product
|
||||
* SSE2 version
|
||||
* Copyright 2004 Phil Karn
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
#define _XOPEN_SOURCE 600
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include "fec.h"
|
||||
|
||||
struct dotprod {
|
||||
int len; /* Number of coefficients */
|
||||
|
||||
/* On a SSE2 machine, these hold 8 copies of the coefficients,
|
||||
* preshifted by 0,1,..7 words to meet all possible input data
|
||||
* alignments (see Intel ap559 on MMX dot products).
|
||||
*/
|
||||
signed short *coeffs[8];
|
||||
};
|
||||
|
||||
long dotprod_sse2_assist(signed short *a,signed short *b,int cnt);
|
||||
|
||||
/* Create and return a descriptor for use with the dot product function */
|
||||
void *initdp_sse2(signed short coeffs[],int len){
|
||||
struct dotprod *dp;
|
||||
int i,j,blksize;
|
||||
|
||||
if(len == 0)
|
||||
return NULL;
|
||||
|
||||
dp = (struct dotprod *)calloc(1,sizeof(struct dotprod));
|
||||
dp->len = len;
|
||||
|
||||
/* Make 8 copies of coefficients, one for each data alignment,
|
||||
* each aligned to 16-byte boundary
|
||||
*/
|
||||
for(i=0;i<8;i++){
|
||||
blksize = (1+(len+i-1)/8) * 8*sizeof(signed short);
|
||||
posix_memalign((void **)&dp->coeffs[i],16,blksize);
|
||||
memset(dp->coeffs[i],0,blksize);
|
||||
for(j=0;j<len;j++)
|
||||
dp->coeffs[i][j+i] = coeffs[j];
|
||||
}
|
||||
return (void *)dp;
|
||||
}
|
||||
|
||||
|
||||
/* Free a dot product descriptor created earlier */
|
||||
void freedp_sse2(void *p){
|
||||
struct dotprod *dp = (struct dotprod *)p;
|
||||
int i;
|
||||
|
||||
for(i=0;i<8;i++)
|
||||
if(dp->coeffs[i] != NULL)
|
||||
free(dp->coeffs[i]);
|
||||
free(dp);
|
||||
}
|
||||
|
||||
/* Compute a dot product given a descriptor and an input array
|
||||
* The length is taken from the descriptor
|
||||
*/
|
||||
long dotprod_sse2(void *p,signed short a[]){
|
||||
struct dotprod *dp = (struct dotprod *)p;
|
||||
int al;
|
||||
signed short *ar;
|
||||
|
||||
ar = (signed short *)((int)a & ~15);
|
||||
al = a - ar;
|
||||
|
||||
/* Call assembler routine to do the work, passing number of 8-word blocks */
|
||||
return dotprod_sse2_assist(ar,dp->coeffs[al],(dp->len+al-1)/8+1);
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
# SIMD SSE2 dot product
|
||||
# Equivalent to the following C code:
|
||||
# long dotprod(signed short *a,signed short *b,int cnt)
|
||||
# {
|
||||
# long sum = 0;
|
||||
# cnt *= 8;
|
||||
# while(cnt--)
|
||||
# sum += *a++ + *b++;
|
||||
# return sum;
|
||||
# }
|
||||
# a and b must be 128-bit aligned
|
||||
# Copyright 2001, Phil Karn KA9Q
|
||||
# May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
|
||||
.text
|
||||
.global dotprod_sse2_assist
|
||||
.type dotprod_sse2_assist,@function
|
||||
dotprod_sse2_assist:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
pushl %ecx
|
||||
pushl %ebx
|
||||
movl 8(%ebp),%esi # a
|
||||
movl 12(%ebp),%edi # b
|
||||
movl 16(%ebp),%ecx # cnt
|
||||
pxor %xmm0,%xmm0 # clear running sum (in two 32-bit halves)
|
||||
|
||||
# SSE2 dot product loop unrolled 4 times, crunching 32 terms per loop
|
||||
.align 16
|
||||
.Loop1: subl $4,%ecx
|
||||
jl .Loop1Done
|
||||
|
||||
movdqa (%esi),%xmm1
|
||||
pmaddwd (%edi),%xmm1
|
||||
paddd %xmm1,%xmm0
|
||||
|
||||
movdqa 16(%esi),%xmm1
|
||||
pmaddwd 16(%edi),%xmm1
|
||||
paddd %xmm1,%xmm0
|
||||
|
||||
movdqa 32(%esi),%xmm1
|
||||
pmaddwd 32(%edi),%xmm1
|
||||
paddd %xmm1,%xmm0
|
||||
|
||||
movdqa 48(%esi),%xmm1
|
||||
addl $64,%esi
|
||||
pmaddwd 48(%edi),%xmm1
|
||||
addl $64,%edi
|
||||
paddd %xmm1,%xmm0
|
||||
|
||||
jmp .Loop1
|
||||
.Loop1Done:
|
||||
|
||||
addl $4,%ecx
|
||||
|
||||
# SSE2 dot product loop, not unrolled, crunching 4 terms per loop
|
||||
# This could be redone as Duff's Device on the unrolled loop above
|
||||
.Loop2: subl $1,%ecx
|
||||
jl .Loop2Done
|
||||
|
||||
movdqa (%esi),%xmm1
|
||||
addl $16,%esi
|
||||
pmaddwd (%edi),%xmm1
|
||||
addl $16,%edi
|
||||
paddd %xmm1,%xmm0
|
||||
jmp .Loop2
|
||||
.Loop2Done:
|
||||
|
||||
movdqa %xmm0,%xmm1
|
||||
psrldq $8,%xmm0
|
||||
paddd %xmm1,%xmm0
|
||||
movd %xmm0,%eax # right-hand word to eax
|
||||
psrldq $4,%xmm0
|
||||
movd %xmm0,%ebx
|
||||
addl %ebx,%eax
|
||||
|
||||
popl %ebx
|
||||
popl %ecx
|
||||
popl %edi
|
||||
popl %esi
|
||||
movl %ebp,%esp
|
||||
popl %ebp
|
||||
ret
|
||||
@@ -0,0 +1,63 @@
|
||||
.TH DSP 3
|
||||
.SH NAME
|
||||
initdp, freedp, dotprod, sumsq, peakval -\ SIMD-assisted
|
||||
digital signal processing primitives
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
.ft
|
||||
#include "fec.h"
|
||||
|
||||
void *initdp(signed short *coeffs,int len);
|
||||
long dotprod(void *p,signed short *a);
|
||||
void freedp(void *p);
|
||||
|
||||
unsigned long long sumsq(signed short *in,int cnt);
|
||||
|
||||
int peakval(signed short *b,int cnt);
|
||||
|
||||
.SH DESCRIPTION
|
||||
These functions provide several basic primitives useful in digital
|
||||
signal processing (DSP), especially in modems. The \fBinitdp\fR,
|
||||
\fBdotprod\fR and \fBfreedp\fR functions implement an integer dot
|
||||
product useful in correlation and filtering operations on signed
|
||||
16-bit integers. \fBsumsq\fR computes the sum
|
||||
of the squares of an array of signed 16-bit integers,
|
||||
useful for measuring the energy of a signal. \fBpeakval\fR returns the
|
||||
absolute value of the largest magitude element in the input array,
|
||||
useful for scaling a signal's amplitude.
|
||||
|
||||
Each function uses IA32 or PowerPC Altivec instructions when
|
||||
available; otherwise, a portable C version is used.
|
||||
|
||||
.SH USAGE
|
||||
To create a FIR filter or correlator, call \fBinitdp\fR with the
|
||||
coefficients in \fBcoeff\fR and their number in \fBlen\fR. This
|
||||
creates the appropriate data structures and returns a handle.
|
||||
|
||||
To compute a dot product, pass the handle from \fBinitdp\fR and the
|
||||
input array to \fBdotprod\fR. No length field is needed as the number
|
||||
of samples will be taken from the \fBlen\fR parameter originally given
|
||||
to \fBinitdp\fR. There must be at least as many samples in the input
|
||||
array as there were coefficients passed to \fBinitdp\fR.
|
||||
|
||||
When the filter or correlator is no longer needed, the data structures
|
||||
may be freed by passing the handle to \fBfreedp\fR.
|
||||
|
||||
The user is responsible for scaling the inputs to \fBinitdp\fR and
|
||||
\fBdotprod\fR, as the 32-bit result from \fBdotprod\fR will silently
|
||||
wrap around in the event of overflow.
|
||||
|
||||
To compute the sum of the squares of an array of signed 16-bit
|
||||
integers, use sumsq\fR. This returns a 64 bit sum.
|
||||
|
||||
\fBpeakval\fR computes the absolute value of each 16-bit element in
|
||||
the input array and returns the largest.
|
||||
|
||||
.SH RETURN VALUES
|
||||
|
||||
\fBinitdp\fR returns a handle that points to a control block, or NULL in
|
||||
the event of an error (such as a memory allocation failure). \fBsumsq\fR
|
||||
and \fBpeakval\fR have no error returns.
|
||||
|
||||
.SH AUTHOR and COPYRIGHT
|
||||
Phil Karn, KA9Q (karn@ka9q.net)
|
||||
@@ -0,0 +1,99 @@
|
||||
/* Test dot-product function */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <math.h>
|
||||
#include "config.h"
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
#include "fec.h"
|
||||
|
||||
#if HAVE_GETOPT_LONG
|
||||
struct option Options[] = {
|
||||
{"force-altivec",0,NULL,'a'},
|
||||
{"force-port",0,NULL,'p'},
|
||||
{"force-mmx",0,NULL,'m'},
|
||||
{"force-sse",0,NULL,'s'},
|
||||
{"force-sse2",0,NULL,'t'},
|
||||
{"trials",0,NULL,'n'},
|
||||
{NULL},
|
||||
};
|
||||
#endif
|
||||
|
||||
int main(int argc,char *argv[]){
|
||||
short coeffs[512];
|
||||
short input[2048];
|
||||
int trials=1000,d;
|
||||
int errors = 0;
|
||||
|
||||
#if HAVE_GETOPT_LONG
|
||||
while((d = getopt_long(argc,argv,"apmstn:",Options,NULL)) != EOF){
|
||||
#else
|
||||
while((d = getopt(argc,argv,"apmstn:")) != EOF){
|
||||
#endif
|
||||
switch(d){
|
||||
case 'a':
|
||||
Cpu_mode = ALTIVEC;
|
||||
break;
|
||||
case 'p':
|
||||
Cpu_mode = PORT;
|
||||
break;
|
||||
case 'm':
|
||||
Cpu_mode = MMX;
|
||||
break;
|
||||
case 's':
|
||||
Cpu_mode = SSE;
|
||||
break;
|
||||
case 't':
|
||||
Cpu_mode = SSE2;
|
||||
break;
|
||||
case 'n':
|
||||
trials = atoi(optarg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while(trials--){
|
||||
long port_result;
|
||||
long simd_result;
|
||||
int ntaps;
|
||||
int i;
|
||||
int csum = 0;
|
||||
int offset;
|
||||
void *dp_simd,*dp_port;
|
||||
|
||||
/* Generate set of coefficients
|
||||
* limit sum of absolute values to 32767 to avoid overflow
|
||||
*/
|
||||
memset(coeffs,0,sizeof(coeffs));
|
||||
for(i=0;i<512;i++){
|
||||
double gv;
|
||||
|
||||
gv = normal_rand(0.,100.);
|
||||
if(csum + fabs(gv) > 32767)
|
||||
break;
|
||||
coeffs[i] = gv;
|
||||
csum += fabs(gv);
|
||||
}
|
||||
ntaps = i;
|
||||
|
||||
/* Compare results to portable C version for a bunch of random data buffers and offsets */
|
||||
dp_simd = initdp(coeffs,ntaps);
|
||||
dp_port = initdp_port(coeffs,ntaps);
|
||||
|
||||
for(i=0;i<2048;i++)
|
||||
input[i] = random();
|
||||
|
||||
offset = random() & 511;
|
||||
|
||||
simd_result = dotprod(dp_simd,input+offset);
|
||||
port_result = dotprod_port(dp_port,input+offset);
|
||||
if(simd_result != port_result){
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
printf("dtest: %d errors\n",errors);
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "code.h"
|
||||
|
||||
|
||||
static inline int parity(unsigned long long x){
|
||||
return __builtin_parityll(x);
|
||||
}
|
||||
|
||||
|
||||
// Convolutionally encode a packet. The input data bytes are read
|
||||
// high bit first and the encoded packet is written into 'symbols',
|
||||
// one symbol per byte. The first symbol is generated from POLY1,
|
||||
// the second from POLY2.
|
||||
|
||||
// Storing only one symbol per byte uses more space, but it is faster
|
||||
// and easier than trying to pack them more compactly.
|
||||
int encode(
|
||||
unsigned char *symbols, // Output buffer, 2*8*nbytes
|
||||
const unsigned char *data, // Input buffer, nbytes
|
||||
unsigned int nbytes) // Number of bytes in data
|
||||
{
|
||||
unsigned long long encstate;
|
||||
int i;
|
||||
|
||||
encstate = 0;
|
||||
while(nbytes-- != 0){
|
||||
for(i=7;i>=0;i--){ // Transmit MSB first
|
||||
encstate = ((encstate << 1) | ((*data >> i) & 1));
|
||||
*symbols++ = G1FLIP ^ parity(encstate & POLY1);
|
||||
*symbols++ = G2FLIP ^ parity(encstate & POLY2);
|
||||
}
|
||||
data++;
|
||||
}
|
||||
if((encstate & ((1LL << K) -1)) != 0)
|
||||
return -1; // Warn if encoder wasn't tailed back to 0
|
||||
return 0;
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/* Reed-Solomon encoder
|
||||
* Copyright 2002, Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
#ifdef FIXED
|
||||
#include "fixed.h"
|
||||
#elif defined(BIGSYM)
|
||||
#include "int.h"
|
||||
#else
|
||||
#include "char.h"
|
||||
#endif
|
||||
|
||||
void ENCODE_RS(
|
||||
#ifdef FIXED
|
||||
data_t *data, data_t *bb,int pad){
|
||||
#else
|
||||
void *p,data_t *data, data_t *bb){
|
||||
struct rs *rs = (struct rs *)p;
|
||||
#endif
|
||||
int i, j;
|
||||
data_t feedback;
|
||||
|
||||
#ifdef FIXED
|
||||
/* Check pad parameter for validity */
|
||||
if(pad < 0 || pad >= NN)
|
||||
return;
|
||||
#endif
|
||||
|
||||
memset(bb,0,NROOTS*sizeof(data_t));
|
||||
|
||||
for(i=0;i<NN-NROOTS-PAD;i++){
|
||||
feedback = INDEX_OF[data[i] ^ bb[0]];
|
||||
if(feedback != A0){ /* feedback term is non-zero */
|
||||
#ifdef UNNORMALIZED
|
||||
/* This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
|
||||
* always be for the polynomials constructed by init_rs()
|
||||
*/
|
||||
feedback = MODNN(NN - GENPOLY[NROOTS] + feedback);
|
||||
#endif
|
||||
for(j=1;j<NROOTS;j++)
|
||||
bb[j] ^= ALPHA_TO[MODNN(feedback + GENPOLY[NROOTS-j])];
|
||||
}
|
||||
/* Shift */
|
||||
memmove(&bb[0],&bb[1],sizeof(data_t)*(NROOTS-1));
|
||||
if(feedback != A0)
|
||||
bb[NROOTS-1] = ALPHA_TO[MODNN(feedback + GENPOLY[0])];
|
||||
else
|
||||
bb[NROOTS-1] = 0;
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/* The guts of the Reed-Solomon encoder, meant to be #included
|
||||
* into a function body with the following typedefs, macros and variables supplied
|
||||
* according to the code parameters:
|
||||
|
||||
* data_t - a typedef for the data symbol
|
||||
* data_t data[] - array of NN-NROOTS-PAD and type data_t to be encoded
|
||||
* data_t parity[] - an array of NROOTS and type data_t to be written with parity symbols
|
||||
* NROOTS - the number of roots in the RS code generator polynomial,
|
||||
* which is the same as the number of parity symbols in a block.
|
||||
Integer variable or literal.
|
||||
*
|
||||
* NN - the total number of symbols in a RS block. Integer variable or literal.
|
||||
* PAD - the number of pad symbols in a block. Integer variable or literal.
|
||||
* ALPHA_TO - The address of an array of NN elements to convert Galois field
|
||||
* elements in index (log) form to polynomial form. Read only.
|
||||
* INDEX_OF - The address of an array of NN elements to convert Galois field
|
||||
* elements in polynomial form to index (log) form. Read only.
|
||||
* MODNN - a function to reduce its argument modulo NN. May be inline or a macro.
|
||||
* GENPOLY - an array of NROOTS+1 elements containing the generator polynomial in index form
|
||||
|
||||
* The memset() and memmove() functions are used. The appropriate header
|
||||
* file declaring these functions (usually <string.h>) must be included by the calling
|
||||
* program.
|
||||
|
||||
* Copyright 2004, Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
|
||||
|
||||
#undef A0
|
||||
#define A0 (NN) /* Special reserved value encoding zero in index form */
|
||||
|
||||
{
|
||||
int i, j;
|
||||
data_t feedback;
|
||||
|
||||
memset(parity,0,NROOTS*sizeof(data_t));
|
||||
|
||||
for(i=0;i<NN-NROOTS-PAD;i++){
|
||||
feedback = INDEX_OF[data[i] ^ parity[0]];
|
||||
if(feedback != A0){ /* feedback term is non-zero */
|
||||
#ifdef UNNORMALIZED
|
||||
/* This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
|
||||
* always be for the polynomials constructed by init_rs()
|
||||
*/
|
||||
feedback = MODNN(NN - GENPOLY[NROOTS] + feedback);
|
||||
#endif
|
||||
for(j=1;j<NROOTS;j++)
|
||||
parity[j] ^= ALPHA_TO[MODNN(feedback + GENPOLY[NROOTS-j])];
|
||||
}
|
||||
/* Shift */
|
||||
memmove(&parity[0],&parity[1],sizeof(data_t)*(NROOTS-1));
|
||||
if(feedback != A0)
|
||||
parity[NROOTS-1] = ALPHA_TO[MODNN(feedback + GENPOLY[0])];
|
||||
else
|
||||
parity[NROOTS-1] = 0;
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
/* Reed-Solomon encoder
|
||||
* Copyright 2004, Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "fixed.h"
|
||||
#ifdef __VEC__
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
|
||||
static enum {UNKNOWN=0,MMX,SSE,SSE2,ALTIVEC,PORT} cpu_mode;
|
||||
|
||||
static void encode_rs_8_c(data_t *data, data_t *parity,int pad);
|
||||
#if __vec__
|
||||
static void encode_rs_8_av(data_t *data, data_t *parity,int pad);
|
||||
#endif
|
||||
#if __i386__
|
||||
int cpu_features(void);
|
||||
#endif
|
||||
|
||||
void encode_rs_8(data_t *data, data_t *parity,int pad){
|
||||
if(cpu_mode == UNKNOWN){
|
||||
#ifdef __i386__
|
||||
int f;
|
||||
/* Figure out what kind of CPU we have */
|
||||
f = cpu_features();
|
||||
if(f & (1<<26)){ /* SSE2 is present */
|
||||
cpu_mode = SSE2;
|
||||
} else if(f & (1<<25)){ /* SSE is present */
|
||||
cpu_mode = SSE;
|
||||
} else if(f & (1<<23)){ /* MMX is present */
|
||||
cpu_mode = MMX;
|
||||
} else { /* No SIMD at all */
|
||||
cpu_mode = PORT;
|
||||
}
|
||||
#elif __VEC__
|
||||
/* Ask the OS if we have Altivec support */
|
||||
int selectors[2] = { CTL_HW, HW_VECTORUNIT };
|
||||
int hasVectorUnit = 0;
|
||||
size_t length = sizeof(hasVectorUnit);
|
||||
int error = sysctl(selectors, 2, &hasVectorUnit, &length, NULL, 0);
|
||||
if(0 == error && hasVectorUnit)
|
||||
cpu_mode = ALTIVEC;
|
||||
else
|
||||
cpu_mode = PORT;
|
||||
#else
|
||||
cpu_mode = PORT;
|
||||
#endif
|
||||
}
|
||||
switch(cpu_mode){
|
||||
#if __vec__
|
||||
case ALTIVEC:
|
||||
encode_rs_8_av(data,parity,pad);
|
||||
return;
|
||||
#endif
|
||||
#if __i386__
|
||||
case MMX:
|
||||
case SSE:
|
||||
case SSE2:
|
||||
#endif
|
||||
default:
|
||||
encode_rs_8_c(data,parity,pad);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#if __vec__ /* PowerPC G4/G5 Altivec instructions are available */
|
||||
|
||||
static vector unsigned char reverse = (vector unsigned char)(0,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1);
|
||||
static vector unsigned char shift_right = (vector unsigned char)(15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30);
|
||||
|
||||
/* Lookup table for feedback multiplications
|
||||
* These are the low half of the coefficients. Since the generator polynomial is
|
||||
* palindromic, we form the other half by reversing this one
|
||||
*/
|
||||
extern static union { vector unsigned char v; unsigned char c[16]; } table[256];
|
||||
|
||||
static void encode_rs_8_av(data_t *data, data_t *parity,int pad){
|
||||
union { vector unsigned char v[2]; unsigned char c[32]; } shift_register;
|
||||
int i;
|
||||
|
||||
shift_register.v[0] = (vector unsigned char)(0);
|
||||
shift_register.v[1] = (vector unsigned char)(0);
|
||||
|
||||
for(i=0;i<NN-NROOTS-pad;i++){
|
||||
vector unsigned char feedback0,feedback1;
|
||||
unsigned char f;
|
||||
|
||||
f = data[i] ^ shift_register.c[31];
|
||||
feedback1 = table[f].v;
|
||||
feedback0 = vec_perm(feedback1,feedback1,reverse);
|
||||
|
||||
/* Shift right one byte */
|
||||
shift_register.v[1] = vec_perm(shift_register.v[0],shift_register.v[1],shift_right) ^ feedback1;
|
||||
shift_register.v[0] = vec_sro(shift_register.v[0],(vector unsigned char)(8)) ^ feedback0;
|
||||
shift_register.c[0] = f;
|
||||
}
|
||||
for(i=0;i<NROOTS;i++)
|
||||
parity[NROOTS-i-1] = shift_register.c[i];
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Portable C version */
|
||||
static void encode_rs_8_c(data_t *data, data_t *parity,int pad){
|
||||
|
||||
#include "encode_rs.h"
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/* Fast Reed-Solomon encoder for (255,223) CCSDS code on PowerPC G4/G5 using Altivec instructions
|
||||
* Copyright 2004, Phil Karn KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "fixed.h"
|
||||
|
||||
/* Lookup table for feedback multiplications
|
||||
* These are the low half of the coefficients. Since the generator polynomial is
|
||||
* palindromic, we form it by reversing these on the fly
|
||||
*/
|
||||
static union { vector unsigned char v; unsigned char c[16]; } table[256];
|
||||
|
||||
static vector unsigned char reverse = (vector unsigned char)(0,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1);
|
||||
static vector unsigned char shift_right = (vector unsigned char)(15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30);
|
||||
|
||||
extern data_t CCSDS_alpha_to[];
|
||||
extern data_t CCSDS_index_of[];
|
||||
extern data_t CCSDS_poly[];
|
||||
|
||||
void rs_init_av(){
|
||||
int i,j;
|
||||
|
||||
/* The PowerPC is big-endian, so the low-order byte of each vector contains the highest order term in the polynomial */
|
||||
for(j=0;j<16;j++){
|
||||
table[0].c[j] = 0;
|
||||
for(i=1;i<256;i++){
|
||||
table[i].c[16-j-1] = CCSDS_alpha_to[MODNN(CCSDS_poly[j+1] + CCSDS_index_of[i])];
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
for(i=0;i<256;i++){
|
||||
printf("table[%3d] = %3vu\n",i,table[i].v);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void encode_rs_av(unsigned char *data,unsigned char *parity,int pad){
|
||||
union { vector unsigned char v[2]; unsigned char c[32]; } shift_register;
|
||||
int i;
|
||||
|
||||
shift_register.v[0] = (vector unsigned char)(0);
|
||||
shift_register.v[1] = (vector unsigned char)(0);
|
||||
|
||||
for(i=0;i<NN-NROOTS-pad;i++){
|
||||
vector unsigned char feedback0,feedback1;
|
||||
unsigned char f;
|
||||
|
||||
f = data[i] ^ shift_register.c[31];
|
||||
feedback1 = table[f].v;
|
||||
feedback0 = vec_perm(feedback1,feedback1,reverse);
|
||||
|
||||
/* Shift right one byte */
|
||||
shift_register.v[1] = vec_perm(shift_register.v[0],shift_register.v[1],shift_right) ^ feedback1;
|
||||
shift_register.v[0] = vec_sro(shift_register.v[0],(vector unsigned char)(8)) ^ feedback0;
|
||||
shift_register.c[0] = f;
|
||||
}
|
||||
for(i=0;i<NROOTS;i++)
|
||||
parity[NROOTS-i-1] = shift_register.c[i];
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/* This function wraps around the fixed 8-bit encoder, performing the
|
||||
* basis transformations necessary to meet the CCSDS standard
|
||||
*
|
||||
* Copyright 2002, Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
#include "ccsds.h"
|
||||
#include "fec.h"
|
||||
|
||||
void encode_rs_ccsds(data_t *data,data_t *parity,int pad){
|
||||
int i;
|
||||
data_t cdata[NN-NROOTS];
|
||||
|
||||
/* Convert data from dual basis to conventional */
|
||||
for(i=0;i<NN-NROOTS-pad;i++)
|
||||
cdata[i] = Tal1tab[data[i]];
|
||||
|
||||
encode_rs_8(cdata,parity,pad);
|
||||
|
||||
/* Convert parity from conventional to dual basis */
|
||||
for(i=0;i<NN-NROOTS-pad;i++)
|
||||
parity[i] = Taltab[parity[i]];
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/* Reed-Solomon encoder
|
||||
* Copyright 2002, Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
#include "char.h"
|
||||
#include "rs-common.h"
|
||||
|
||||
void encode_rs_char(void *p,data_t *data, data_t *parity){
|
||||
struct rs *rs = (struct rs *)p;
|
||||
|
||||
#include "encode_rs.h"
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/* Reed-Solomon encoder
|
||||
* Copyright 2003, Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
#include "int.h"
|
||||
#include "rs-common.h"
|
||||
|
||||
void encode_rs_int(void *p,data_t *data, data_t *parity){
|
||||
struct rs *rs = (struct rs *)p;
|
||||
|
||||
#include "encode_rs.h"
|
||||
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/* Exercise an RS codec a specified number of times using random
|
||||
* data and error patterns
|
||||
*
|
||||
* Copyright 2002 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
#define FLAG_ERASURE 1 /* Randomly flag 50% of errors as erasures */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef FIXED
|
||||
#include "fixed.h"
|
||||
#define EXERCISE exercise_8
|
||||
#elif defined(CCSDS)
|
||||
#include "fixed.h"
|
||||
#include "ccsds.h"
|
||||
#define EXERCISE exercise_ccsds
|
||||
#elif defined(BIGSYM)
|
||||
#include "int.h"
|
||||
#define EXERCISE exercise_int
|
||||
#else
|
||||
#include "char.h"
|
||||
#define EXERCISE exercise_char
|
||||
#endif
|
||||
|
||||
#ifdef FIXED
|
||||
#define PRINTPARM printf("(255,223):");
|
||||
#elif defined(CCSDS)
|
||||
#define PRINTPARM printf("CCSDS (255,223):");
|
||||
#else
|
||||
#define PRINTPARM printf("(%d,%d):",rs->nn,rs->nn-rs->nroots);
|
||||
#endif
|
||||
|
||||
/* Exercise the RS codec passed as an argument */
|
||||
int EXERCISE(
|
||||
#if !defined(CCSDS) && !defined(FIXED)
|
||||
void *p,
|
||||
#endif
|
||||
int trials){
|
||||
#if !defined(CCSDS) && !defined(FIXED)
|
||||
struct rs *rs = (struct rs *)p;
|
||||
#endif
|
||||
data_t block[NN],tblock[NN];
|
||||
int i;
|
||||
int errors;
|
||||
int errlocs[NN];
|
||||
int derrlocs[NROOTS];
|
||||
int derrors;
|
||||
int errval,errloc;
|
||||
int erasures;
|
||||
int decoder_errors = 0;
|
||||
|
||||
while(trials-- != 0){
|
||||
/* Test up to the error correction capacity of the code */
|
||||
for(errors=0;errors <= NROOTS/2;errors++){
|
||||
|
||||
/* Load block with random data and encode */
|
||||
for(i=0;i<NN-NROOTS;i++)
|
||||
block[i] = random() & NN;
|
||||
|
||||
#if defined(CCSDS) || defined(FIXED)
|
||||
ENCODE_RS(&block[0],&block[NN-NROOTS],0);
|
||||
#else
|
||||
ENCODE_RS(rs,&block[0],&block[NN-NROOTS]);
|
||||
#endif
|
||||
|
||||
/* Make temp copy, seed with errors */
|
||||
memcpy(tblock,block,sizeof(tblock));
|
||||
memset(errlocs,0,sizeof(errlocs));
|
||||
memset(derrlocs,0,sizeof(derrlocs));
|
||||
erasures=0;
|
||||
for(i=0;i<errors;i++){
|
||||
do {
|
||||
errval = random() & NN;
|
||||
} while(errval == 0); /* Error value must be nonzero */
|
||||
|
||||
do {
|
||||
errloc = random() % NN;
|
||||
} while(errlocs[errloc] != 0); /* Must not choose the same location twice */
|
||||
|
||||
errlocs[errloc] = 1;
|
||||
|
||||
#if FLAG_ERASURE
|
||||
if(random() & 1) /* 50-50 chance */
|
||||
derrlocs[erasures++] = errloc;
|
||||
#endif
|
||||
tblock[errloc] ^= errval;
|
||||
}
|
||||
|
||||
/* Decode the errored block */
|
||||
#if defined(CCSDS) || defined(FIXED)
|
||||
derrors = DECODE_RS(tblock,derrlocs,erasures,0);
|
||||
#else
|
||||
derrors = DECODE_RS(rs,tblock,derrlocs,erasures);
|
||||
#endif
|
||||
|
||||
if(derrors != errors){
|
||||
PRINTPARM
|
||||
printf(" decoder says %d errors, true number is %d\n",derrors,errors);
|
||||
decoder_errors++;
|
||||
}
|
||||
for(i=0;i<derrors;i++){
|
||||
if(errlocs[derrlocs[i]] == 0){
|
||||
PRINTPARM
|
||||
printf(" decoder indicates error in location %d without error\n",derrlocs[i]);
|
||||
decoder_errors++;
|
||||
}
|
||||
}
|
||||
if(memcmp(tblock,block,sizeof(tblock)) != 0){
|
||||
PRINTPARM
|
||||
printf(" uncorrected errors! output ^ input:");
|
||||
decoder_errors++;
|
||||
for(i=0;i<NN;i++)
|
||||
printf(" %02x",tblock[i] ^ block[i]);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return decoder_errors;
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
// Soft decision Fano sequential decoder for r=1/2 convolutional codes
|
||||
// Copyright 1994, Phil Karn, KA9Q
|
||||
// Updated March 2014 (!!) for r=1/2 k=24 ICE code
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include "fano.h"
|
||||
#include "code.h"
|
||||
|
||||
struct node {
|
||||
unsigned long long encstate; // Encoder state of next node
|
||||
long gamma; // Cumulative metric to this node
|
||||
int metrics[4]; // Metrics indexed by all possible tx syms
|
||||
int tm[2]; // Sorted metrics for current hypotheses
|
||||
int i; // Current branch being tested
|
||||
};
|
||||
|
||||
static inline int parity(unsigned long long x){
|
||||
return __builtin_parityll(x);
|
||||
}
|
||||
|
||||
// Given an encoder state, return a rate 1/2 symbol pair.
|
||||
// The POLY1 symbol goes into the next-to-LSB
|
||||
// of the result and the POLY2 symbol goes into the LSB.
|
||||
static inline int makesyms(unsigned long long state){
|
||||
int result;
|
||||
|
||||
result = (parity(state & POLY1) << 1) ^ G1FLIP;
|
||||
result |= parity(state & POLY2) ^ G2FLIP;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Decode packet with the Fano algorithm.
|
||||
// Return 0 on success, -1 on timeout
|
||||
int fano(
|
||||
unsigned long *metric, // Final path metric (returned value)
|
||||
unsigned long *cycles, // Cycle count (returned value)
|
||||
unsigned char *data, // Decoded output data
|
||||
const unsigned char *symbols, // Raw deinterleaved input symbols
|
||||
unsigned int nbits, // Number of output bits, including tail
|
||||
int mettab[2][256], // Metric table, [sent sym][rx symbol]
|
||||
int delta, // Threshold adjust parameter
|
||||
unsigned long maxcycles)// Decoding timeout in cycles per bit
|
||||
{
|
||||
struct node *nodes; // First node
|
||||
register struct node *np; // Current node
|
||||
struct node *lastnode; // Last node
|
||||
struct node *tail; // First node of tail
|
||||
long t; // Threshold
|
||||
long m0,m1;
|
||||
long ngamma;
|
||||
unsigned int lsym;
|
||||
unsigned long i;
|
||||
|
||||
if((nodes = (struct node *)malloc(nbits*sizeof(struct node))) == NULL){
|
||||
fprintf(stderr,"alloc failed\n");
|
||||
return 0;
|
||||
}
|
||||
lastnode = &nodes[nbits];
|
||||
tail = &nodes[nbits-(K-1)];
|
||||
|
||||
// Compute all possible branch metrics for each symbol pair
|
||||
// This is the only place we actually look at the raw input symbols
|
||||
for(np=nodes;np < lastnode;np++){
|
||||
np->metrics[0] = mettab[0][symbols[0]] + mettab[0][symbols[1]];
|
||||
np->metrics[1] = mettab[0][symbols[0]] + mettab[1][symbols[1]];
|
||||
np->metrics[2] = mettab[1][symbols[0]] + mettab[0][symbols[1]];
|
||||
np->metrics[3] = mettab[1][symbols[0]] + mettab[1][symbols[1]];
|
||||
#if 0
|
||||
printf("k=%ld metrics %d %d %d %d\n",np-nodes,
|
||||
np->metrics[0],np->metrics[1],np->metrics[2],np->metrics[3]);
|
||||
#endif
|
||||
symbols += 2;
|
||||
}
|
||||
np = nodes;
|
||||
np->encstate = 0;
|
||||
|
||||
// Compute and sort branch metrics from root node
|
||||
lsym = makesyms(np->encstate); // 0-branch (LSB is 0)
|
||||
|
||||
m0 = np->metrics[lsym];
|
||||
|
||||
// Now do the 1-branch. To save another makesyms call here and
|
||||
// inside the loop, we assume that both polynomials are odd,
|
||||
// i.e., the least significant bits are 1, providing complementary pairs of branch symbols.
|
||||
|
||||
// This code could be sped up if a systematic code were used.
|
||||
m1 = np->metrics[3^lsym];
|
||||
if(m0 > m1){
|
||||
// 0-branch has better metric
|
||||
np->tm[0] = m0;
|
||||
np->tm[1] = m1;
|
||||
} else {
|
||||
// 1-branch is better
|
||||
np->tm[0] = m1;
|
||||
np->tm[1] = m0;
|
||||
np->encstate |= 1; // Set low bit
|
||||
}
|
||||
np->i = 0; // Start with best branch
|
||||
maxcycles *= nbits;
|
||||
np->gamma = t = 0;
|
||||
|
||||
// Start the Fano decoder
|
||||
for(i=1;i <= maxcycles;i++){
|
||||
|
||||
//#define debug 1
|
||||
#ifdef debug
|
||||
fprintf(stdout,"k=%d, encoder 0x%06llx, metric=%ld, thresh=%ld, m[%d]=%d\n",
|
||||
(int)(np-nodes),np->encstate & ((1LL<<K)-1),np->gamma,t,np->i,np->tm[np->i]);
|
||||
#endif
|
||||
// Look forward
|
||||
ngamma = np->gamma + np->tm[np->i];
|
||||
// printf("np->gamma = %ld, ngamma = %ld\n",np->gamma,ngamma);
|
||||
if(ngamma >= t){
|
||||
// Node is acceptable
|
||||
if(np->gamma < t + delta){
|
||||
// First time we've visited this node; tighten threshold.
|
||||
|
||||
// This loop could be replaced with
|
||||
// t += delta * ((ngamma - t)/delta);
|
||||
// but the multiply and divide are slower.
|
||||
while(ngamma >= t + delta)
|
||||
t += delta;
|
||||
}
|
||||
// Move forward
|
||||
if(++np == lastnode){
|
||||
np--;
|
||||
break; // Done!
|
||||
}
|
||||
np->gamma = ngamma;
|
||||
np->encstate = np[-1].encstate << 1;
|
||||
|
||||
// Compute and sort metrics, starting with the zero branch
|
||||
lsym = makesyms(np->encstate);
|
||||
if(np >= tail){
|
||||
// The tail must be all zeroes, so don't even
|
||||
// bother computing the 1-branches there.
|
||||
np->tm[0] = np->metrics[lsym];
|
||||
} else {
|
||||
m0 = np->metrics[lsym];
|
||||
m1 = np->metrics[3^lsym];
|
||||
#if 0
|
||||
printf("m0 = %ld, m1 = %ld\n",m0,m1);
|
||||
#endif
|
||||
if(m0 > m1){
|
||||
// 0-branch is better
|
||||
np->tm[0] = m0;
|
||||
np->tm[1] = m1;
|
||||
} else {
|
||||
// 1-branch is better
|
||||
np->tm[0] = m1;
|
||||
np->tm[1] = m0;
|
||||
np->encstate++; // Set low bit
|
||||
}
|
||||
}
|
||||
np->i = 0; // Start with best branch
|
||||
continue;
|
||||
}
|
||||
// Threshold violated, can't go forward
|
||||
for(;;){
|
||||
// Look backward
|
||||
if(np == nodes || np[-1].gamma < t){
|
||||
// Can't back up either.
|
||||
// Relax threshold and and look forward again to better branch.
|
||||
t -= delta;
|
||||
if(np->i != 0){
|
||||
np->i = 0;
|
||||
np->encstate ^= 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Back up
|
||||
if(--np < tail && np->i != 1){
|
||||
// Search next best branch
|
||||
np->i++;
|
||||
np->encstate ^= 1;
|
||||
break;
|
||||
} // else keep looking back
|
||||
}
|
||||
}
|
||||
*metric = np->gamma; // Return final path metric
|
||||
|
||||
// Copy decoded data to user's buffer
|
||||
nbits = nbits/8; // Copy tail, which should be 0's
|
||||
np = &nodes[7]; // Start with first full byte
|
||||
while(nbits-- != 0){
|
||||
*data++ = np->encstate;
|
||||
np += 8;
|
||||
}
|
||||
|
||||
free(nodes);
|
||||
*cycles = i;
|
||||
if(i > maxcycles)
|
||||
return -1; // Decoder timed out
|
||||
return 0; // Successful completion
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
int fano(unsigned long *metric, unsigned long *cycles,
|
||||
unsigned char *data,const unsigned char *symbols,
|
||||
unsigned int nbits,int mettab[2][256],int delta,
|
||||
unsigned long maxcycles);
|
||||
int encode(unsigned char *symbols,const unsigned char *data,unsigned int nbytes);
|
||||
void gen_met(int mettab[2][256],double signal,double noise,double bias,double scale);
|
||||
|
||||
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <getopt.h>
|
||||
#include "fano.h"
|
||||
#include "sim.h"
|
||||
|
||||
|
||||
|
||||
struct option Options[] = {
|
||||
{"scale",1, NULL, 'S'},
|
||||
{"delta",1, NULL, 'd'},
|
||||
{"max-cycles", 1, NULL, 'm'},
|
||||
{"frame-length",1,NULL,'l'},
|
||||
{"frame-count",1,NULL,'n'},
|
||||
{"ebn0",1,NULL,'e'},
|
||||
{"gain",1,NULL,'g'},
|
||||
{"verbose",0,NULL,'v'},
|
||||
{NULL},
|
||||
};
|
||||
|
||||
|
||||
int Nbits = 1024; // Bits per frame, including tail that must be 0
|
||||
double Signal = 30; // Signal amplitude (scaled to 8 bit byte)
|
||||
int Scale = 8; // Fano metric scaling factor
|
||||
const double Rate = 0.5; // Code rate = 1/2
|
||||
double Ebn0 = 2.0; // Digital signal-to-noise ratio per bit in dB
|
||||
unsigned long Maxcycles = 1000; // Maximum number of decoder moves per bit
|
||||
int Trials = 1000; // Number of frames to test
|
||||
int Verbose; // diag diarrhea
|
||||
int Zerodata; // Use all 0's data (no effect on linear codes)
|
||||
|
||||
int main(int argc,char *argv[]){
|
||||
int mettab[2][256];
|
||||
unsigned char data[Nbits/8],symbols[Nbits*2],decode_data[Nbits/8];
|
||||
unsigned long cycles, metric;
|
||||
int delta = 4;
|
||||
time_t t;
|
||||
int trial,i,r,good=0,bad=0,undetected=0;
|
||||
long totcycles = 0,histogram[256];
|
||||
double noise_amp; // Actual noise amplitude, computed from Signal amplitude & Eb/N0
|
||||
|
||||
while((i = getopt_long(argc,argv,"d:S:l:n:e:s:m:vz",Options,NULL)) != EOF){
|
||||
switch(i){
|
||||
case 'd':
|
||||
delta = atoi(optarg);
|
||||
break;
|
||||
case 'S':
|
||||
Scale = atoi(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
Maxcycles = atoi(optarg);
|
||||
break;
|
||||
case 'l':
|
||||
Nbits = atoi(optarg);
|
||||
break;
|
||||
case 'n':
|
||||
Trials = atoi(optarg);
|
||||
break;
|
||||
case 'e':
|
||||
Ebn0 = atof(optarg);
|
||||
break;
|
||||
case 's':
|
||||
Signal = atof(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
Verbose++;
|
||||
break;
|
||||
case 'z':
|
||||
Zerodata++;
|
||||
break;
|
||||
default:
|
||||
printf("Usage: %s [-m maxcycles/bit] [-l bits/frame] [-n numframes] [-e Eb/No] [-s signal_amplitude] [-v] [-z]\n",
|
||||
argv[0]);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(Nbits < 64){
|
||||
printf("bits/frame must be >= 64\n");
|
||||
exit(1);
|
||||
}
|
||||
delta *= Scale;
|
||||
|
||||
// Compute noise voltage. The factor of 2 accounts for BPSK seeing
|
||||
// only half the noise power, and the sqrt() converts power to voltage
|
||||
noise_amp = Signal / sqrt(2*Rate*pow(10.,Ebn0/10.));
|
||||
gen_met(mettab,Signal,noise_amp,Rate,Scale);
|
||||
|
||||
// Generate channel transition matrix
|
||||
setup_channel(Signal,noise_amp);
|
||||
|
||||
printf("Code rate %.2f, Nbits = %d, Maxcycles/bit %ld\n",Rate,Nbits,Maxcycles);
|
||||
printf("Eb/N0 = %.3lf dB, Signal = %lg, Noise = %lg, BER@Eb/N0 = %lg, BER@Es/N0 = %lg\n",
|
||||
Ebn0,Signal,noise_amp,0.5*erfc(pow(10.,Ebn0/20.)),0.5*erfc(sqrt(Rate*pow(10.,Ebn0/10.))));
|
||||
|
||||
srandom(time(&t));
|
||||
|
||||
memset(histogram,0,sizeof(histogram));
|
||||
memset(data,0,sizeof(data));
|
||||
for(trial = 0; trial < Trials; trial++){
|
||||
|
||||
if(!Zerodata){
|
||||
// Generate random data
|
||||
// Note last 3 bytes must be 0 to tail off the encoder
|
||||
for(i=0;i<(Nbits-64)/8;i++) // allow room on end for max length tail
|
||||
data[i] = random() & 0xff;
|
||||
|
||||
for(;i<Nbits/8;i++)
|
||||
data[i] = 0;
|
||||
}
|
||||
i = encode(symbols,data,sizeof(data));
|
||||
assert(i == 0);
|
||||
|
||||
#if 0
|
||||
printf("raw data and symbols, no noise:\n");
|
||||
for(i=0;i<Nbits;i++){
|
||||
if((i % 8) == 0)
|
||||
printf("data[%d] = %02x; symbols = ",i/8,data[i/8]);
|
||||
printf("%d%d",symbols[2*i],symbols[2*i+1]);
|
||||
if((i % 8) == 7)
|
||||
putchar('\n');
|
||||
}
|
||||
putchar('\n');
|
||||
#endif
|
||||
|
||||
// Add noise & scale, build histogram
|
||||
for(i=0;i<2*Nbits;i++){
|
||||
symbols[i] = simulate(symbols[i]);
|
||||
histogram[symbols[i]]++;
|
||||
}
|
||||
#if 0
|
||||
printf("data and symbols, noisy & scaled:\n");
|
||||
for(i=0;i<Nbits;i++){
|
||||
if((i % 8) == 0)
|
||||
printf("data[%d] = %02x; symbols = ",i/8,data[i/8]);
|
||||
printf(" %03d %03d",symbols[2*i],symbols[2*i+1]);
|
||||
if((i % 8) == 7)
|
||||
putchar('\n');
|
||||
}
|
||||
putchar('\n');
|
||||
#endif
|
||||
if(Verbose > 2){
|
||||
printf("Cumulative symbol histogram:\n");
|
||||
for(i=0;i<256;i++){
|
||||
printf(" %6ld",histogram[i]);
|
||||
if((i % 16) == 15)
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
memset(decode_data,0,sizeof(decode_data));
|
||||
r = fano(&metric,&cycles,decode_data,symbols,Nbits,mettab,delta,Maxcycles);
|
||||
totcycles += cycles;
|
||||
i = memcmp(data,decode_data,sizeof(data));
|
||||
bad += (i != 0);
|
||||
undetected += (r == 0 && i != 0);
|
||||
good += (i == 0);
|
||||
|
||||
if(Verbose > 1 || (Verbose && r != 0)){
|
||||
printf("trial %d fano returns %d, metric = %ld, cycles = %ld",trial,r,metric,cycles);
|
||||
if(i != 0 && (Verbose > 1 || r == 0)){
|
||||
// Error in data
|
||||
putchar(' ');
|
||||
for(i=0;i<Nbits/8;i++)
|
||||
printf("%02x",decode_data[i] ^ data[i]);
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
printf("trials %d avg cycles/bit %lg good %d bad %d undetected %d deletion rate %lg%%\n",
|
||||
trial,(double)totcycles/(trial*Nbits),good,bad,undetected,100.*bad/trial);
|
||||
|
||||
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/* Utility routines for FEC support
|
||||
* Copyright 2004, Phil Karn, KA9Q
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "fec.h"
|
||||
|
||||
unsigned char Partab[256];
|
||||
int P_init;
|
||||
|
||||
/* Create 256-entry odd-parity lookup table
|
||||
* Needed only on non-ia32 machines
|
||||
*/
|
||||
void partab_init(void){
|
||||
int i,cnt,ti;
|
||||
|
||||
/* Initialize parity lookup table */
|
||||
for(i=0;i<256;i++){
|
||||
cnt = 0;
|
||||
ti = i;
|
||||
while(ti){
|
||||
if(ti & 1)
|
||||
cnt++;
|
||||
ti >>= 1;
|
||||
}
|
||||
Partab[i] = cnt & 1;
|
||||
}
|
||||
P_init=1;
|
||||
}
|
||||
|
||||
/* Lookup table giving count of 1 bits for integers 0-255 */
|
||||
int Bitcnt[] = {
|
||||
0, 1, 1, 2, 1, 2, 2, 3,
|
||||
1, 2, 2, 3, 2, 3, 3, 4,
|
||||
1, 2, 2, 3, 2, 3, 3, 4,
|
||||
2, 3, 3, 4, 3, 4, 4, 5,
|
||||
1, 2, 2, 3, 2, 3, 3, 4,
|
||||
2, 3, 3, 4, 3, 4, 4, 5,
|
||||
2, 3, 3, 4, 3, 4, 4, 5,
|
||||
3, 4, 4, 5, 4, 5, 5, 6,
|
||||
1, 2, 2, 3, 2, 3, 3, 4,
|
||||
2, 3, 3, 4, 3, 4, 4, 5,
|
||||
2, 3, 3, 4, 3, 4, 4, 5,
|
||||
3, 4, 4, 5, 4, 5, 5, 6,
|
||||
2, 3, 3, 4, 3, 4, 4, 5,
|
||||
3, 4, 4, 5, 4, 5, 5, 6,
|
||||
3, 4, 4, 5, 4, 5, 5, 6,
|
||||
4, 5, 5, 6, 5, 6, 6, 7,
|
||||
1, 2, 2, 3, 2, 3, 3, 4,
|
||||
2, 3, 3, 4, 3, 4, 4, 5,
|
||||
2, 3, 3, 4, 3, 4, 4, 5,
|
||||
3, 4, 4, 5, 4, 5, 5, 6,
|
||||
2, 3, 3, 4, 3, 4, 4, 5,
|
||||
3, 4, 4, 5, 4, 5, 5, 6,
|
||||
3, 4, 4, 5, 4, 5, 5, 6,
|
||||
4, 5, 5, 6, 5, 6, 6, 7,
|
||||
2, 3, 3, 4, 3, 4, 4, 5,
|
||||
3, 4, 4, 5, 4, 5, 5, 6,
|
||||
3, 4, 4, 5, 4, 5, 5, 6,
|
||||
4, 5, 5, 6, 5, 6, 6, 7,
|
||||
3, 4, 4, 5, 4, 5, 5, 6,
|
||||
4, 5, 5, 6, 5, 6, 6, 7,
|
||||
4, 5, 5, 6, 5, 6, 6, 7,
|
||||
5, 6, 6, 7, 6, 7, 7, 8,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
/* User include file for libfec
|
||||
* Copyright 2004, Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
|
||||
#ifndef _FEC_H_
|
||||
#define _FEC_H_
|
||||
|
||||
/* r=1/2 k=7 convolutional encoder polynomials */
|
||||
#define V27POLYA 0x6d
|
||||
#define V27POLYB 0x4f
|
||||
|
||||
void *create_viterbi27(int len);
|
||||
int init_viterbi27(void *vp,int starting_state);
|
||||
int update_viterbi27_blk(void *vp,unsigned char sym[],int npairs);
|
||||
int chainback_viterbi27(void *vp, unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi27(void *vp);
|
||||
|
||||
#ifdef __VEC__
|
||||
void *create_viterbi27_av(int len);
|
||||
int init_viterbi27_av(void *p,int starting_state);
|
||||
int chainback_viterbi27_av(void *p,unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi27_av(void *p);
|
||||
int update_viterbi27_blk_av(void *p,unsigned char *syms,int nbits);
|
||||
#endif
|
||||
|
||||
#ifdef __i386__
|
||||
void *create_viterbi27_mmx(int len);
|
||||
int init_viterbi27_mmx(void *p,int starting_state);
|
||||
int chainback_viterbi27_mmx(void *p,unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi27_mmx(void *p);
|
||||
int update_viterbi27_blk_mmx(void *p,unsigned char *syms,int nbits);
|
||||
|
||||
void *create_viterbi27_sse(int len);
|
||||
int init_viterbi27_sse(void *p,int starting_state);
|
||||
int chainback_viterbi27_sse(void *p,unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi27_sse(void *p);
|
||||
int update_viterbi27_blk_sse(void *p,unsigned char *syms,int nbits);
|
||||
|
||||
void *create_viterbi27_sse2(int len);
|
||||
int init_viterbi27_sse2(void *p,int starting_state);
|
||||
int chainback_viterbi27_sse2(void *p,unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi27_sse2(void *p);
|
||||
int update_viterbi27_blk_sse2(void *p,unsigned char *syms,int nbits);
|
||||
#endif
|
||||
|
||||
void *create_viterbi27_port(int len);
|
||||
int init_viterbi27_port(void *p,int starting_state);
|
||||
int chainback_viterbi27_port(void *p,unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi27_port(void *p);
|
||||
int update_viterbi27_blk_port(void *p,unsigned char *syms,int nbits);
|
||||
|
||||
/* r=1/2 k=9 convolutional encoder polynomials */
|
||||
#define V29POLYA 0x1af
|
||||
#define V29POLYB 0x11d
|
||||
|
||||
void *create_viterbi29(int len);
|
||||
int init_viterbi29(void *vp,int starting_state);
|
||||
int update_viterbi29_blk(void *vp,unsigned char syms[],int nbits);
|
||||
int chainback_viterbi29(void *vp, unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi29(void *vp);
|
||||
|
||||
#ifdef __VEC__
|
||||
void *create_viterbi29_av(int len);
|
||||
int init_viterbi29_av(void *p,int starting_state);
|
||||
int chainback_viterbi29_av(void *p,unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi29_av(void *p);
|
||||
int update_viterbi29_blk_av(void *p,unsigned char *syms,int nbits);
|
||||
#endif
|
||||
|
||||
#ifdef __i386__
|
||||
void *create_viterbi29_mmx(int len);
|
||||
int init_viterbi29_mmx(void *p,int starting_state);
|
||||
int chainback_viterbi29_mmx(void *p,unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi29_mmx(void *p);
|
||||
int update_viterbi29_blk_mmx(void *p,unsigned char *syms,int nbits);
|
||||
|
||||
void *create_viterbi29_sse(int len);
|
||||
int init_viterbi29_sse(void *p,int starting_state);
|
||||
int chainback_viterbi29_sse(void *p,unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi29_sse(void *p);
|
||||
int update_viterbi29_blk_sse(void *p,unsigned char *syms,int nbits);
|
||||
|
||||
void *create_viterbi29_sse2(int len);
|
||||
int init_viterbi29_sse2(void *p,int starting_state);
|
||||
int chainback_viterbi29_sse2(void *p,unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi29_sse2(void *p);
|
||||
int update_viterbi29_blk_sse2(void *p,unsigned char *syms,int nbits);
|
||||
#endif
|
||||
|
||||
void *create_viterbi29_port(int len);
|
||||
int init_viterbi29_port(void *p,int starting_state);
|
||||
int chainback_viterbi29_port(void *p,unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi29_port(void *p);
|
||||
int update_viterbi29_blk_port(void *p,unsigned char *syms,int nbits);
|
||||
|
||||
|
||||
/* r=1/6 k=15 Cassini convolutional encoder polynomials
|
||||
* dfree = 56
|
||||
* These bits may be left-right flipped from some textbook representations;
|
||||
* here I have the bits entering the shift register from the right (low) end
|
||||
*/
|
||||
#define V615POLYA 042631
|
||||
#define V615POLYB 047245
|
||||
#define V615POLYC 056507
|
||||
#define V615POLYD 073363
|
||||
#define V615POLYE 077267
|
||||
#define V615POLYF 064537
|
||||
|
||||
void *create_viterbi615(int len);
|
||||
int init_viterbi615(void *vp,int starting_state);
|
||||
int update_viterbi615_blk(void *vp,unsigned char *syms,int nbits);
|
||||
int chainback_viterbi615(void *vp, unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi615(void *vp);
|
||||
|
||||
#ifdef __VEC__
|
||||
void *create_viterbi615_av(int len);
|
||||
int init_viterbi615_av(void *p,int starting_state);
|
||||
int chainback_viterbi615_av(void *p,unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi615_av(void *p);
|
||||
int update_viterbi615_blk_av(void *p,unsigned char *syms,int nbits);
|
||||
#endif
|
||||
|
||||
#ifdef __i386__
|
||||
void *create_viterbi615_mmx(int len);
|
||||
int init_viterbi615_mmx(void *p,int starting_state);
|
||||
int chainback_viterbi615_mmx(void *p,unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi615_mmx(void *p);
|
||||
int update_viterbi615_blk_mmx(void *p,unsigned char *syms,int nbits);
|
||||
|
||||
void *create_viterbi615_sse(int len);
|
||||
int init_viterbi615_sse(void *p,int starting_state);
|
||||
int chainback_viterbi615_sse(void *p,unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi615_sse(void *p);
|
||||
int update_viterbi615_blk_sse(void *p,unsigned char *syms,int nbits);
|
||||
|
||||
void *create_viterbi615_sse2(int len);
|
||||
int init_viterbi615_sse2(void *p,int starting_state);
|
||||
int chainback_viterbi615_sse2(void *p,unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi615_sse2(void *p);
|
||||
int update_viterbi615_blk_sse2(void *p,unsigned char *syms,int nbits);
|
||||
|
||||
#endif
|
||||
|
||||
void *create_viterbi615_port(int len);
|
||||
int init_viterbi615_port(void *p,int starting_state);
|
||||
int chainback_viterbi615_port(void *p,unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi615_port(void *p);
|
||||
int update_viterbi615_blk_port(void *p,unsigned char *syms,int nbits);
|
||||
|
||||
|
||||
/* General purpose RS codec, 8-bit symbols */
|
||||
void encode_rs_char(void *rs,unsigned char *data,unsigned char *parity);
|
||||
int decode_rs_char(void *rs,unsigned char *data,int *eras_pos,
|
||||
int no_eras);
|
||||
void *init_rs_char(int symsize,int gfpoly,
|
||||
int fcr,int prim,int nroots,
|
||||
int pad);
|
||||
void free_rs_char(void *rs);
|
||||
|
||||
/* General purpose RS codec, integer symbols */
|
||||
void encode_rs_int(void *rs,int *data,int *parity);
|
||||
int decode_rs_int(void *rs,int *data,int *eras_pos,int no_eras);
|
||||
void *init_rs_int(int symsize,int gfpoly,int fcr,
|
||||
int prim,int nroots,int pad);
|
||||
void free_rs_int(void *rs);
|
||||
|
||||
/* CCSDS standard (255,223) RS codec with conventional (*not* dual-basis)
|
||||
* symbol representation
|
||||
*/
|
||||
void encode_rs_8(unsigned char *data,unsigned char *parity,int pad);
|
||||
int decode_rs_8(unsigned char *data,int *eras_pos,int no_eras,int pad);
|
||||
|
||||
/* CCSDS standard (255,223) RS codec with dual-basis symbol representation */
|
||||
void encode_rs_ccsds(unsigned char *data,unsigned char *parity,int pad);
|
||||
int decode_rs_ccsds(unsigned char *data,int *eras_pos,int no_eras,int pad);
|
||||
|
||||
/* Tables to map from conventional->dual (Taltab) and
|
||||
* dual->conventional (Tal1tab) bases
|
||||
*/
|
||||
extern unsigned char Taltab[],Tal1tab[];
|
||||
|
||||
|
||||
/* CPU SIMD instruction set available */
|
||||
extern enum cpu_mode {UNKNOWN=0,PORT,MMX,SSE,SSE2,ALTIVEC} Cpu_mode;
|
||||
void find_cpu_mode(void); /* Call this once at startup to set Cpu_mode */
|
||||
|
||||
/* Determine parity of argument: 1 = odd, 0 = even */
|
||||
#ifdef __i386__
|
||||
static inline int parityb(unsigned char x){
|
||||
__asm__ __volatile__ ("test %1,%1;setpo %0" : "=g"(x) : "r" (x));
|
||||
return x;
|
||||
}
|
||||
#else
|
||||
void partab_init();
|
||||
|
||||
static inline int parityb(unsigned char x){
|
||||
extern unsigned char Partab[256];
|
||||
extern int P_init;
|
||||
if(!P_init){
|
||||
partab_init();
|
||||
}
|
||||
return Partab[x];
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static inline int parity(int x){
|
||||
/* Fold down to one byte */
|
||||
x ^= (x >> 16);
|
||||
x ^= (x >> 8);
|
||||
return parityb(x);
|
||||
}
|
||||
|
||||
/* Useful utilities for simulation */
|
||||
double normal_rand(double mean, double std_dev);
|
||||
unsigned char addnoise(int sym,double amp,double gain,double offset,int clip);
|
||||
|
||||
extern int Bitcnt[];
|
||||
|
||||
/* Dot product functions */
|
||||
void *initdp(signed short coeffs[],int len);
|
||||
void freedp(void *dp);
|
||||
long dotprod(void *dp,signed short a[]);
|
||||
|
||||
void *initdp_port(signed short coeffs[],int len);
|
||||
void freedp_port(void *dp);
|
||||
long dotprod_port(void *dp,signed short a[]);
|
||||
|
||||
#ifdef __i386__
|
||||
void *initdp_mmx(signed short coeffs[],int len);
|
||||
void freedp_mmx(void *dp);
|
||||
long dotprod_mmx(void *dp,signed short a[]);
|
||||
|
||||
void *initdp_sse(signed short coeffs[],int len);
|
||||
void freedp_sse(void *dp);
|
||||
long dotprod_sse(void *dp,signed short a[]);
|
||||
|
||||
void *initdp_sse2(signed short coeffs[],int len);
|
||||
void freedp_sse2(void *dp);
|
||||
long dotprod_sse2(void *dp,signed short a[]);
|
||||
#endif
|
||||
|
||||
#ifdef __VEC__
|
||||
void *initdp_av(signed short coeffs[],int len);
|
||||
void freedp_av(void *dp);
|
||||
long dotprod_av(void *dp,signed short a[]);
|
||||
#endif
|
||||
|
||||
/* Sum of squares - accepts signed shorts, produces unsigned long long */
|
||||
unsigned long long sumsq(signed short *in,int cnt);
|
||||
unsigned long long sumsq_port(signed short *in,int cnt);
|
||||
|
||||
#ifdef __i386__
|
||||
unsigned long long sumsq_mmx(signed short *in,int cnt);
|
||||
unsigned long long sumsq_sse(signed short *in,int cnt);
|
||||
unsigned long long sumsq_sse2(signed short *in,int cnt);
|
||||
#endif
|
||||
#ifdef __VEC__
|
||||
unsigned long long sumsq_av(signed short *in,int cnt);
|
||||
#endif
|
||||
|
||||
|
||||
/* Low-level data structures and routines */
|
||||
|
||||
int cpu_features(void);
|
||||
|
||||
#endif /* _FEC_H_ */
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
// User include file for libfec
|
||||
// Copyright 2004, 2014 Phil Karn, KA9Q
|
||||
// May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
|
||||
#ifndef _FEC_H_
|
||||
#define _FEC_H_
|
||||
|
||||
// r=1/2 k=7 convolutional encoder polynomials
|
||||
// The NASA-DSN convention is to use V27POLYA inverted, then V27POLYB
|
||||
// The CCSDS/NASA-GSFC convention is to use V27POLYB, then V27POLYA inverted
|
||||
#define V27POLYA 0x6d
|
||||
#define V27POLYB 0x4f
|
||||
|
||||
void *create_viterbi27(int len);
|
||||
int init_viterbi27(void *vp,int starting_state);
|
||||
int update_viterbi27_blk(void *vp,unsigned char sym[],int npairs);
|
||||
int chainback_viterbi27(void *vp, unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi27(void *vp);
|
||||
|
||||
// r=1/2 k=9 convolutional encoder polynomials
|
||||
#define V29POLYA 0x1af
|
||||
#define V29POLYB 0x11d
|
||||
|
||||
void *create_viterbi29(int len);
|
||||
int init_viterbi29(void *vp,int starting_state);
|
||||
int update_viterbi29_blk(void *vp,unsigned char syms[],int nbits);
|
||||
int chainback_viterbi29(void *vp, unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi29(void *vp);
|
||||
|
||||
// r=1/3 k=9 convolutional encoder polynomials
|
||||
#define V39POLYA 0x1ed
|
||||
#define V39POLYB 0x19b
|
||||
#define V39POLYC 0x127
|
||||
|
||||
void *create_viterbi39(int len);
|
||||
int init_viterbi39(void *vp,int starting_state);
|
||||
int update_viterbi39_blk(void *vp,unsigned char syms[],int nbits);
|
||||
int chainback_viterbi39(void *vp, unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi39(void *vp);
|
||||
|
||||
// r=1/6 k=15 Cassini convolutional encoder polynomials without symbol inversion
|
||||
// dfree = 56
|
||||
// These bits may be left-right flipped from some textbook representations;
|
||||
// here I have the bits entering the shift register from the right (low) end
|
||||
|
||||
// Some other spacecraft use the same code, but with the polynomials in a different order.
|
||||
// E.g., Mars Pathfinder and STEREO swap POLYC and POLYD. All use alternate symbol inversion.
|
||||
#define V615POLYA 042631
|
||||
#define V615POLYB 047245
|
||||
#define V615POLYC 056507
|
||||
#define V615POLYD 073363
|
||||
#define V615POLYE 077267
|
||||
#define V615POLYF 064537
|
||||
|
||||
void *create_viterbi615(int len);
|
||||
int init_viterbi615(void *vp,int starting_state);
|
||||
int update_viterbi615_blk(void *vp,unsigned char *syms,int nbits);
|
||||
int chainback_viterbi615(void *vp, unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi615(void *vp);
|
||||
|
||||
// r=1/2 k=24 MCQLI-24 r=1/2 k=24 convolutional code for ISEE-3/ICE, launched 1978
|
||||
// Obviously this code was originally intended for sequential decoding, but here's
|
||||
// a monster Viterbi decoder for this code!
|
||||
// The ref gives the polynomials as 073353367 and 053353367 (octal)
|
||||
// Shortened from MCQLI-48
|
||||
#define V224POLY1 073665667
|
||||
#define V224POLY2 073665665
|
||||
#define G1FLIP 0
|
||||
#define G2FLIP 1 // Invert the second symbol
|
||||
|
||||
void *create_viterbi224(int len);
|
||||
int init_viterbi224(void *p,int starting_state);
|
||||
int update_viterbi224_blk(void *p,const unsigned char *syms,int nbits);
|
||||
int chainback_viterbi224(void *p,unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi224(void *p);
|
||||
|
||||
// General purpose RS codec, 8-bit symbols
|
||||
void encode_rs_char(void *rs,unsigned char *data,unsigned char *parity);
|
||||
int decode_rs_char(void *rs,unsigned char *data,int *eras_pos,
|
||||
int no_eras);
|
||||
void *init_rs_char(int symsize,int gfpoly,
|
||||
int fcr,int prim,int nroots,
|
||||
int pad);
|
||||
void free_rs_char(void *rs);
|
||||
|
||||
// General purpose RS codec, integer symbols
|
||||
void encode_rs_int(void *rs,int *data,int *parity);
|
||||
int decode_rs_int(void *rs,int *data,int *eras_pos,int no_eras);
|
||||
void *init_rs_int(int symsize,int gfpoly,int fcr,
|
||||
int prim,int nroots,int pad);
|
||||
void free_rs_int(void *rs);
|
||||
|
||||
// CCSDS standard (255,223) RS codec with conventional (*not* dual-basis) representation
|
||||
void encode_rs_8(unsigned char *data,unsigned char *parity,int pad);
|
||||
int decode_rs_8(unsigned char *data,int *eras_pos,int no_eras,int pad);
|
||||
|
||||
// CCSDS standard (255,223) RS codec with dual-basis symbol representation
|
||||
void encode_rs_ccsds(unsigned char *data,unsigned char *parity,int pad);
|
||||
int decode_rs_ccsds(unsigned char *data,int *eras_pos,int no_eras,int pad);
|
||||
|
||||
// Tables to map from conventional->dual (Taltab) and dual->conventional (Tal1tab) bases
|
||||
extern unsigned char Taltab[],Tal1tab[];
|
||||
|
||||
#endif /* _FEC_H_ */
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/* Stuff specific to the CCSDS (255,223) RS codec
|
||||
* (255,223) code over GF(256). Note: the conventional basis is still
|
||||
* used; the dual-basis mappings are performed in [en|de]code_rs_ccsds.c
|
||||
*
|
||||
* Copyright 2003 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
typedef unsigned char data_t;
|
||||
|
||||
static inline int mod255(int x){
|
||||
while (x >= 255) {
|
||||
x -= 255;
|
||||
x = (x >> 8) + (x & 255);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
#define MODNN(x) mod255(x)
|
||||
|
||||
extern data_t CCSDS_alpha_to[];
|
||||
extern data_t CCSDS_index_of[];
|
||||
extern data_t CCSDS_poly[];
|
||||
|
||||
#define MM 8
|
||||
#define NN 255
|
||||
#define ALPHA_TO CCSDS_alpha_to
|
||||
#define INDEX_OF CCSDS_index_of
|
||||
#define GENPOLY CCSDS_poly
|
||||
#define NROOTS 32
|
||||
#define FCR 112
|
||||
#define PRIM 11
|
||||
#define IPRIM 116
|
||||
#define PAD pad
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/* Generate tables for CCSDS code
|
||||
* Copyright 2002 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "char.h"
|
||||
#include "rs-common.h"
|
||||
#include "fec.h"
|
||||
|
||||
int main(){
|
||||
struct rs *rs;
|
||||
int i;
|
||||
|
||||
rs = init_rs_char(8,0x187,112,11,32,0); /* CCSDS standard */
|
||||
assert(rs != NULL);
|
||||
printf("char CCSDS_alpha_to[] = {");
|
||||
for(i=0;i<256;i++){
|
||||
if((i % 16) == 0)
|
||||
printf("\n");
|
||||
printf("0x%02x,",rs->alpha_to[i]);
|
||||
}
|
||||
printf("\n};\n\nchar CCSDS_index_of[] = {");
|
||||
for(i=0;i<256;i++){
|
||||
if((i % 16) == 0)
|
||||
printf("\n");
|
||||
printf("%3d,",rs->index_of[i]);
|
||||
}
|
||||
printf("\n};\n\nchar CCSDS_poly[] = {");
|
||||
for(i=0;i<33;i++){
|
||||
if((i % 16) == 0)
|
||||
printf("\n");
|
||||
|
||||
printf("%3d,",rs->genpoly[i]);
|
||||
}
|
||||
printf("\n};\n");
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/* Conversion lookup tables from conventional alpha to Berlekamp's
|
||||
* dual-basis representation. Used in the CCSDS version only.
|
||||
* taltab[] -- convert conventional to dual basis
|
||||
* tal1tab[] -- convert dual basis to conventional
|
||||
|
||||
* Note: the actual RS encoder/decoder works with the conventional basis.
|
||||
* So data is converted from dual to conventional basis before either
|
||||
* encoding or decoding and then converted back.
|
||||
*
|
||||
* Copyright 2002 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DTYPE unsigned char
|
||||
DTYPE Taltab[256],Tal1tab[256];
|
||||
|
||||
static DTYPE tal[] = { 0x8d, 0xef, 0xec, 0x86, 0xfa, 0x99, 0xaf, 0x7b };
|
||||
|
||||
/* Generate conversion lookup tables between conventional alpha representation
|
||||
* (@**7, @**6, ...@**0)
|
||||
* and Berlekamp's dual basis representation
|
||||
* (l0, l1, ...l7)
|
||||
*/
|
||||
int main(){
|
||||
int i,j,k;
|
||||
|
||||
for(i=0;i<256;i++){/* For each value of input */
|
||||
Taltab[i] = 0;
|
||||
for(j=0;j<8;j++) /* for each column of matrix */
|
||||
for(k=0;k<8;k++){ /* for each row of matrix */
|
||||
if(i & (1<<k))
|
||||
Taltab[i] ^= tal[7-k] & (1<<j);
|
||||
}
|
||||
Tal1tab[Taltab[i]] = i;
|
||||
}
|
||||
printf("unsigned char Taltab[] = {\n");
|
||||
for(i=0;i<256;i++){
|
||||
if((i % 16) == 0)
|
||||
printf("\n");
|
||||
printf("0x%02x,",Taltab[i]);
|
||||
}
|
||||
printf("\n};\n\nunsigned char Tal1tab[] = {");
|
||||
for(i=0;i<256;i++){
|
||||
if((i % 16) == 0)
|
||||
printf("\n");
|
||||
printf("0x%02x,",Tal1tab[i]);
|
||||
}
|
||||
printf("\n};\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <getopt.h>
|
||||
#include "viterbi224.h"
|
||||
#include "fano.h"
|
||||
#include "sim.h"
|
||||
|
||||
static inline int popcount(int x){
|
||||
return __builtin_popcount(x);
|
||||
}
|
||||
|
||||
struct option Options[] = {
|
||||
{"scale",1, NULL, 'S'},
|
||||
{"delta",1, NULL, 'd'},
|
||||
{"max-cycles", 1, NULL, 'm'},
|
||||
{"frame-length",1,NULL,'l'},
|
||||
{"frame-count",1,NULL,'n'},
|
||||
{"ebn0",1,NULL,'e'},
|
||||
{"gain",1,NULL,'g'},
|
||||
{"verbose",0,NULL,'v'},
|
||||
{NULL},
|
||||
};
|
||||
|
||||
|
||||
int Nbits = 1024; // Bits per frame, INCLUDING TAIL that must be 0's
|
||||
double Signal = 30; // Signal amplitude (scaled to 8 bit byte)
|
||||
int Scale = 8; // Fano metric scaling factor
|
||||
const double Rate = 0.5; // Code rate = 1/2
|
||||
double Ebn0 = 2.0; // Digital signal-to-noise ratio per bit in dB
|
||||
unsigned long Maxcycles = 1000; // Maximum number of decoder moves per bit
|
||||
int Trials = 1000; // Number of frames to test
|
||||
int Verbose; // diag diarrhea
|
||||
int Zerodata; // Use all 0's data (no effect on linear codes)
|
||||
|
||||
int main(int argc,char *argv[]){
|
||||
int mettab[2][256];
|
||||
unsigned char data[Nbits/8],symbols[Nbits*2],decode_data[Nbits/8];
|
||||
unsigned char xordata[Nbits/8];
|
||||
unsigned long cycles, metric;
|
||||
int delta = 4;
|
||||
time_t t;
|
||||
int trial,i,r;
|
||||
int errcnt;
|
||||
int fano_good_frames = 0;
|
||||
int fano_failures = 0;
|
||||
int fano_frame_errors = 0;
|
||||
int fano_bit_errors = 0;
|
||||
int viterbi_attempts = 0;
|
||||
int viterbi_good_frames = 0;
|
||||
int viterbi_frame_errors = 0;
|
||||
int viterbi_bit_errors = 0;
|
||||
long totcycles = 0;
|
||||
long histogram[256];
|
||||
double noise_amp; // Actual noise amplitude, computed from Signal amplitude & Eb/N0
|
||||
void *vp;
|
||||
|
||||
while((i = getopt_long(argc,argv,"d:S:l:n:e:s:m:vz",Options,NULL)) != EOF){
|
||||
switch(i){
|
||||
case 'd':
|
||||
delta = atoi(optarg);
|
||||
break;
|
||||
case 'S':
|
||||
Scale = atoi(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
Maxcycles = atoi(optarg);
|
||||
break;
|
||||
case 'l':
|
||||
Nbits = atoi(optarg);
|
||||
break;
|
||||
case 'n':
|
||||
Trials = atoi(optarg);
|
||||
break;
|
||||
case 'e':
|
||||
Ebn0 = atof(optarg);
|
||||
break;
|
||||
case 's':
|
||||
Signal = atof(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
Verbose++;
|
||||
break;
|
||||
case 'z':
|
||||
Zerodata++;
|
||||
break;
|
||||
default:
|
||||
printf("Usage: %s [-m maxcycles/bit] [-l bits/frame] [-n numframes] [-e Eb/No] [-s signal_amplitude] [-v] [-z]\n",
|
||||
argv[0]);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(Nbits < 64){
|
||||
printf("bits/frame must be >= 64\n");
|
||||
exit(1);
|
||||
}
|
||||
delta *= Scale;
|
||||
|
||||
// Compute noise voltage. The factor of 2 accounts for BPSK seeing
|
||||
// only half the noise power, and the sqrt() converts power to voltage
|
||||
noise_amp = Signal / sqrt(2*Rate*pow(10.,Ebn0/10.));
|
||||
gen_met(mettab,Signal,noise_amp,Rate,Scale);
|
||||
|
||||
// Generate channel transition matrix
|
||||
setup_channel(Signal,noise_amp);
|
||||
|
||||
printf("Code rate %.2f, Nbits = %d, Maxcycles/bit %ld\n",Rate,Nbits,Maxcycles);
|
||||
printf("Eb/N0 = %.3lf dB, Signal = %lg, Noise = %lg, BER@Eb/N0 = %lg, BER@Es/N0 = %lg\n",
|
||||
Ebn0,Signal,noise_amp,0.5*erfc(pow(10.,Ebn0/20.)),0.5*erfc(sqrt(Rate*pow(10.,Ebn0/10.))));
|
||||
|
||||
srandom(time(&t));
|
||||
|
||||
memset(histogram,0,sizeof(histogram));
|
||||
memset(data,0,sizeof(data));
|
||||
for(trial = 0; trial < Trials; trial++){
|
||||
|
||||
if(!Zerodata){
|
||||
// Generate random data
|
||||
// Note last 3 bytes must be 0 to tail off the encoder
|
||||
for(i=0;i<(Nbits-64)/8;i++) // allow room on end for max length tail
|
||||
data[i] = random() & 0xff;
|
||||
|
||||
for(;i<Nbits/8;i++)
|
||||
data[i] = 0;
|
||||
}
|
||||
i = encode(symbols,data,sizeof(data));
|
||||
assert(i == 0);
|
||||
|
||||
// Add noise & scale, build histogram
|
||||
for(i=0;i<2*Nbits;i++){
|
||||
symbols[i] = simulate(symbols[i]);
|
||||
histogram[symbols[i]]++;
|
||||
}
|
||||
if(Verbose > 2){
|
||||
printf("Cumulative symbol histogram:\n");
|
||||
for(i=0;i<256;i++){
|
||||
printf(" %6ld",histogram[i]);
|
||||
if((i % 16) == 15)
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
memset(decode_data,0,sizeof(decode_data));
|
||||
r = fano(&metric,&cycles,decode_data,symbols,Nbits,mettab,delta,Maxcycles);
|
||||
totcycles += cycles;
|
||||
if(r != 0){
|
||||
++fano_failures;
|
||||
if(Verbose)
|
||||
printf("trial %d fano: decode failure\n",trial);
|
||||
} else { // Fano finished, look for undetected errors
|
||||
errcnt = 0;
|
||||
for(i=0;i<Nbits/8;i++){
|
||||
int e;
|
||||
|
||||
e = popcount(xordata[i] = decode_data[i] ^ data[i]);
|
||||
fano_bit_errors += e;
|
||||
errcnt += e;
|
||||
}
|
||||
if(errcnt != 0){
|
||||
fano_frame_errors++;
|
||||
fano_bit_errors += errcnt;
|
||||
if(Verbose)
|
||||
printf("trial %d fano: metric %ld, cycles %ld, bit errors %d\n",
|
||||
trial,metric,cycles,errcnt);
|
||||
|
||||
if(Verbose > 1){
|
||||
for(i=0;i<Nbits/8;i++)
|
||||
printf("%02x",xordata[i]);
|
||||
putchar('\n');
|
||||
}
|
||||
} else {
|
||||
++fano_good_frames;
|
||||
if(Verbose > 1)
|
||||
printf("trial %d fano: metric = %ld, cycles %ld; data OK\n",trial,metric,cycles);
|
||||
continue; // Fano OK, next frame
|
||||
}
|
||||
}
|
||||
// Fano failed. Try Viterbi
|
||||
viterbi_attempts++;
|
||||
if(Verbose){
|
||||
printf("Trying Viterbi...");
|
||||
fflush(stdout);
|
||||
}
|
||||
if((vp = create_viterbi224(Nbits)) == NULL){
|
||||
printf("create_viterbi224 failed\n");
|
||||
exit(1);
|
||||
}
|
||||
init_viterbi224(vp,0); // Initialize Viterbi decoder
|
||||
update_viterbi224_blk(vp,symbols,Nbits); // Process symbols
|
||||
chainback_viterbi224(vp,decode_data,Nbits,0); // Viterbi chainback
|
||||
delete_viterbi224(vp);
|
||||
|
||||
// look for Viterbi errors
|
||||
errcnt = 0;
|
||||
for(i=0;i<Nbits/8;i++){
|
||||
int e = popcount(xordata[i] = data[i] ^ decode_data[i]);
|
||||
errcnt += e;
|
||||
viterbi_bit_errors += e;
|
||||
}
|
||||
if(!errcnt){
|
||||
viterbi_good_frames++;
|
||||
if(Verbose)
|
||||
printf(" Success\n");
|
||||
continue;
|
||||
}
|
||||
// Viterbi made errors
|
||||
if(Verbose)
|
||||
printf(" %d bit errors\n",errcnt);
|
||||
viterbi_frame_errors++;
|
||||
viterbi_bit_errors += errcnt;
|
||||
if(Verbose > 1){
|
||||
for(i=0;i<Nbits/8;i++)
|
||||
printf("%02x",xordata[i]);
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
printf("Fano good frames: %d, decode failures %d, frame errors %d, bit errors %d cycles/bit %lf\n",
|
||||
fano_good_frames,fano_failures,fano_frame_errors,fano_bit_errors,(double)totcycles/(trial*Nbits));
|
||||
if(viterbi_attempts != 0){
|
||||
printf("Viterbi attempts %d good frames: %d frame errors %d (%lg%%) bit errors %d (%lg%%)\n",
|
||||
viterbi_attempts,
|
||||
viterbi_good_frames,viterbi_frame_errors,100.*viterbi_frame_errors/viterbi_attempts,
|
||||
viterbi_bit_errors,100.*viterbi_bit_errors/(Nbits*viterbi_attempts));
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/* Initialize a RS codec
|
||||
*
|
||||
* Copyright 2002 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include "fec.h"
|
||||
|
||||
#if !defined(NULL)
|
||||
#define NULL ((void *)0)
|
||||
#endif
|
||||
|
||||
#include "rs-common.h"
|
||||
|
||||
void free_rs(void *p){
|
||||
struct rs *rs = (struct rs *)p;
|
||||
|
||||
free(rs->alpha_to);
|
||||
free(rs->index_of);
|
||||
free(rs->genpoly);
|
||||
free(rs);
|
||||
}
|
||||
|
||||
/* Initialize a Reed-Solomon codec
|
||||
* symsize = symbol size, bits
|
||||
* gfpoly = Field generator polynomial coefficients
|
||||
* fcr = first root of RS code generator polynomial, index form
|
||||
* prim = primitive element to generate polynomial roots
|
||||
* nroots = RS code generator polynomial degree (number of roots)
|
||||
* pad = padding bytes at front of shortened block
|
||||
*/
|
||||
void *init_rs_common(int symsize,int gfpoly,int fcr,int prim,
|
||||
int nroots,int pad){
|
||||
struct rs *rs;
|
||||
|
||||
#include "init_rs.h"
|
||||
|
||||
return rs;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/* Common code for intializing a Reed-Solomon control block (char or int symbols)
|
||||
* Copyright 2004 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
#undef NULL
|
||||
#define NULL ((void *)0)
|
||||
|
||||
{
|
||||
int i, j, sr,root,iprim;
|
||||
|
||||
rs = NULL;
|
||||
/* Check parameter ranges */
|
||||
if(symsize < 0 || symsize > 8*sizeof(data_t)){
|
||||
goto done;
|
||||
}
|
||||
|
||||
if(fcr < 0 || fcr >= (1<<symsize))
|
||||
goto done;
|
||||
if(prim <= 0 || prim >= (1<<symsize))
|
||||
goto done;
|
||||
if(nroots < 0 || nroots >= (1<<symsize))
|
||||
goto done; /* Can't have more roots than symbol values! */
|
||||
if(pad < 0 || pad >= ((1<<symsize) -1 - nroots))
|
||||
goto done; /* Too much padding */
|
||||
|
||||
rs = (struct rs *)calloc(1,sizeof(struct rs));
|
||||
if(rs == NULL)
|
||||
goto done;
|
||||
|
||||
rs->mm = symsize;
|
||||
rs->nn = (1<<symsize)-1;
|
||||
rs->pad = pad;
|
||||
|
||||
rs->alpha_to = (data_t *)malloc(sizeof(data_t)*(rs->nn+1));
|
||||
if(rs->alpha_to == NULL){
|
||||
free(rs);
|
||||
rs = NULL;
|
||||
goto done;
|
||||
}
|
||||
rs->index_of = (data_t *)malloc(sizeof(data_t)*(rs->nn+1));
|
||||
if(rs->index_of == NULL){
|
||||
free(rs->alpha_to);
|
||||
free(rs);
|
||||
rs = NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Generate Galois field lookup tables */
|
||||
rs->index_of[0] = A0; /* log(zero) = -inf */
|
||||
rs->alpha_to[A0] = 0; /* alpha**-inf = 0 */
|
||||
sr = 1;
|
||||
for(i=0;i<rs->nn;i++){
|
||||
rs->index_of[sr] = i;
|
||||
rs->alpha_to[i] = sr;
|
||||
sr <<= 1;
|
||||
if(sr & (1<<symsize))
|
||||
sr ^= gfpoly;
|
||||
sr &= rs->nn;
|
||||
}
|
||||
if(sr != 1){
|
||||
/* field generator polynomial is not primitive! */
|
||||
free(rs->alpha_to);
|
||||
free(rs->index_of);
|
||||
free(rs);
|
||||
rs = NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Form RS code generator polynomial from its roots */
|
||||
rs->genpoly = (data_t *)malloc(sizeof(data_t)*(nroots+1));
|
||||
if(rs->genpoly == NULL){
|
||||
free(rs->alpha_to);
|
||||
free(rs->index_of);
|
||||
free(rs);
|
||||
rs = NULL;
|
||||
goto done;
|
||||
}
|
||||
rs->fcr = fcr;
|
||||
rs->prim = prim;
|
||||
rs->nroots = nroots;
|
||||
|
||||
/* Find prim-th root of 1, used in decoding */
|
||||
for(iprim=1;(iprim % prim) != 0;iprim += rs->nn)
|
||||
;
|
||||
rs->iprim = iprim / prim;
|
||||
|
||||
rs->genpoly[0] = 1;
|
||||
for (i = 0,root=fcr*prim; i < nroots; i++,root += prim) {
|
||||
rs->genpoly[i+1] = 1;
|
||||
|
||||
/* Multiply rs->genpoly[] by @**(root + x) */
|
||||
for (j = i; j > 0; j--){
|
||||
if (rs->genpoly[j] != 0)
|
||||
rs->genpoly[j] = rs->genpoly[j-1] ^ rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[j]] + root)];
|
||||
else
|
||||
rs->genpoly[j] = rs->genpoly[j-1];
|
||||
}
|
||||
/* rs->genpoly[0] can never be zero */
|
||||
rs->genpoly[0] = rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[0]] + root)];
|
||||
}
|
||||
/* convert rs->genpoly[] to index form for quicker encoding */
|
||||
for (i = 0; i <= nroots; i++)
|
||||
rs->genpoly[i] = rs->index_of[rs->genpoly[i]];
|
||||
done:;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Initialize a RS codec
|
||||
*
|
||||
* Copyright 2002 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "char.h"
|
||||
#include "rs-common.h"
|
||||
|
||||
void free_rs_char(void *p){
|
||||
struct rs *rs = (struct rs *)p;
|
||||
|
||||
free(rs->alpha_to);
|
||||
free(rs->index_of);
|
||||
free(rs->genpoly);
|
||||
free(rs);
|
||||
}
|
||||
|
||||
/* Initialize a Reed-Solomon codec
|
||||
* symsize = symbol size, bits
|
||||
* gfpoly = Field generator polynomial coefficients
|
||||
* fcr = first root of RS code generator polynomial, index form
|
||||
* prim = primitive element to generate polynomial roots
|
||||
* nroots = RS code generator polynomial degree (number of roots)
|
||||
* pad = padding bytes at front of shortened block
|
||||
*/
|
||||
void *init_rs_char(int symsize,int gfpoly,int fcr,int prim,
|
||||
int nroots,int pad){
|
||||
struct rs *rs;
|
||||
|
||||
#include "init_rs.h"
|
||||
|
||||
return rs;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Initialize a RS codec
|
||||
*
|
||||
* Copyright 2002 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "int.h"
|
||||
#include "rs-common.h"
|
||||
|
||||
void free_rs_int(void *p){
|
||||
struct rs *rs = (struct rs *)p;
|
||||
|
||||
free(rs->alpha_to);
|
||||
free(rs->index_of);
|
||||
free(rs->genpoly);
|
||||
free(rs);
|
||||
}
|
||||
|
||||
/* Initialize a Reed-Solomon codec
|
||||
* symsize = symbol size, bits
|
||||
* gfpoly = Field generator polynomial coefficients
|
||||
* fcr = first root of RS code generator polynomial, index form
|
||||
* prim = primitive element to generate polynomial roots
|
||||
* nroots = RS code generator polynomial degree (number of roots)
|
||||
* pad = padding bytes at front of shortened block
|
||||
*/
|
||||
void *init_rs_int(int symsize,int gfpoly,int fcr,int prim,
|
||||
int nroots,int pad){
|
||||
struct rs *rs;
|
||||
|
||||
#include "init_rs.h"
|
||||
|
||||
return rs;
|
||||
}
|
||||
Executable
+251
@@ -0,0 +1,251 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# install - install a program, script, or datafile
|
||||
# This comes from X11R5 (mit/util/scripts/install.sh).
|
||||
#
|
||||
# Copyright 1991 by the Massachusetts Institute of Technology
|
||||
#
|
||||
# Permission to use, copy, modify, distribute, and sell this software and its
|
||||
# documentation for any purpose is hereby granted without fee, provided that
|
||||
# the above copyright notice appear in all copies and that both that
|
||||
# copyright notice and this permission notice appear in supporting
|
||||
# documentation, and that the name of M.I.T. not be used in advertising or
|
||||
# publicity pertaining to distribution of the software without specific,
|
||||
# written prior permission. M.I.T. makes no representations about the
|
||||
# suitability of this software for any purpose. It is provided "as is"
|
||||
# without express or implied warranty.
|
||||
#
|
||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||
# `make' implicit rules from creating a file called install from it
|
||||
# when there is no Makefile.
|
||||
#
|
||||
# This script is compatible with the BSD install script, but was written
|
||||
# from scratch. It can only install one file at a time, a restriction
|
||||
# shared with many OS's install programs.
|
||||
|
||||
|
||||
# set DOITPROG to echo to test this script
|
||||
|
||||
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||
doit="${DOITPROG-}"
|
||||
|
||||
|
||||
# put in absolute paths if you don't have them in your path; or use env. vars.
|
||||
|
||||
mvprog="${MVPROG-mv}"
|
||||
cpprog="${CPPROG-cp}"
|
||||
chmodprog="${CHMODPROG-chmod}"
|
||||
chownprog="${CHOWNPROG-chown}"
|
||||
chgrpprog="${CHGRPPROG-chgrp}"
|
||||
stripprog="${STRIPPROG-strip}"
|
||||
rmprog="${RMPROG-rm}"
|
||||
mkdirprog="${MKDIRPROG-mkdir}"
|
||||
|
||||
transformbasename=""
|
||||
transform_arg=""
|
||||
instcmd="$mvprog"
|
||||
chmodcmd="$chmodprog 0755"
|
||||
chowncmd=""
|
||||
chgrpcmd=""
|
||||
stripcmd=""
|
||||
rmcmd="$rmprog -f"
|
||||
mvcmd="$mvprog"
|
||||
src=""
|
||||
dst=""
|
||||
dir_arg=""
|
||||
|
||||
while [ x"$1" != x ]; do
|
||||
case $1 in
|
||||
-c) instcmd="$cpprog"
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-d) dir_arg=true
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-m) chmodcmd="$chmodprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-s) stripcmd="$stripprog"
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
|
||||
shift
|
||||
continue;;
|
||||
|
||||
*) if [ x"$src" = x ]
|
||||
then
|
||||
src=$1
|
||||
else
|
||||
# this colon is to work around a 386BSD /bin/sh bug
|
||||
:
|
||||
dst=$1
|
||||
fi
|
||||
shift
|
||||
continue;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ x"$src" = x ]
|
||||
then
|
||||
echo "install: no input file specified"
|
||||
exit 1
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
if [ x"$dir_arg" != x ]; then
|
||||
dst=$src
|
||||
src=""
|
||||
|
||||
if [ -d $dst ]; then
|
||||
instcmd=:
|
||||
chmodcmd=""
|
||||
else
|
||||
instcmd=mkdir
|
||||
fi
|
||||
else
|
||||
|
||||
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
|
||||
# might cause directories to be created, which would be especially bad
|
||||
# if $src (and thus $dsttmp) contains '*'.
|
||||
|
||||
if [ -f $src -o -d $src ]
|
||||
then
|
||||
true
|
||||
else
|
||||
echo "install: $src does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ x"$dst" = x ]
|
||||
then
|
||||
echo "install: no destination specified"
|
||||
exit 1
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
# If destination is a directory, append the input filename; if your system
|
||||
# does not like double slashes in filenames, you may need to add some logic
|
||||
|
||||
if [ -d $dst ]
|
||||
then
|
||||
dst="$dst"/`basename $src`
|
||||
else
|
||||
true
|
||||
fi
|
||||
fi
|
||||
|
||||
## this sed command emulates the dirname command
|
||||
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
|
||||
|
||||
# Make sure that the destination directory exists.
|
||||
# this part is taken from Noah Friedman's mkinstalldirs script
|
||||
|
||||
# Skip lots of stat calls in the usual case.
|
||||
if [ ! -d "$dstdir" ]; then
|
||||
defaultIFS='
|
||||
'
|
||||
IFS="${IFS-${defaultIFS}}"
|
||||
|
||||
oIFS="${IFS}"
|
||||
# Some sh's can't handle IFS=/ for some reason.
|
||||
IFS='%'
|
||||
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
|
||||
IFS="${oIFS}"
|
||||
|
||||
pathcomp=''
|
||||
|
||||
while [ $# -ne 0 ] ; do
|
||||
pathcomp="${pathcomp}${1}"
|
||||
shift
|
||||
|
||||
if [ ! -d "${pathcomp}" ] ;
|
||||
then
|
||||
$mkdirprog "${pathcomp}"
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
pathcomp="${pathcomp}/"
|
||||
done
|
||||
fi
|
||||
|
||||
if [ x"$dir_arg" != x ]
|
||||
then
|
||||
$doit $instcmd $dst &&
|
||||
|
||||
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
|
||||
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
|
||||
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
|
||||
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
|
||||
else
|
||||
|
||||
# If we're going to rename the final executable, determine the name now.
|
||||
|
||||
if [ x"$transformarg" = x ]
|
||||
then
|
||||
dstfile=`basename $dst`
|
||||
else
|
||||
dstfile=`basename $dst $transformbasename |
|
||||
sed $transformarg`$transformbasename
|
||||
fi
|
||||
|
||||
# don't allow the sed command to completely eliminate the filename
|
||||
|
||||
if [ x"$dstfile" = x ]
|
||||
then
|
||||
dstfile=`basename $dst`
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
# Make a temp file name in the proper directory.
|
||||
|
||||
dsttmp=$dstdir/#inst.$$#
|
||||
|
||||
# Move or copy the file name to the temp name
|
||||
|
||||
$doit $instcmd $src $dsttmp &&
|
||||
|
||||
trap "rm -f ${dsttmp}" 0 &&
|
||||
|
||||
# and set any options; do chmod last to preserve setuid bits
|
||||
|
||||
# If any of these fail, we abort the whole thing. If we want to
|
||||
# ignore errors from any of these, just make sure not to ignore
|
||||
# errors from the above "$doit $instcmd $src $dsttmp" command.
|
||||
|
||||
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
|
||||
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
|
||||
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
|
||||
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
|
||||
$doit $rmcmd -f $dstdir/$dstfile &&
|
||||
$doit $mvcmd $dsttmp $dstdir/$dstfile
|
||||
|
||||
fi &&
|
||||
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,22 @@
|
||||
/* Stuff specific to the general (integer) version of the Reed-Solomon codecs
|
||||
*
|
||||
* Copyright 2003, Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
typedef unsigned int data_t;
|
||||
|
||||
#define MODNN(x) modnn(rs,x)
|
||||
|
||||
#define MM (rs->mm)
|
||||
#define NN (rs->nn)
|
||||
#define ALPHA_TO (rs->alpha_to)
|
||||
#define INDEX_OF (rs->index_of)
|
||||
#define GENPOLY (rs->genpoly)
|
||||
#define NROOTS (rs->nroots)
|
||||
#define FCR (rs->fcr)
|
||||
#define PRIM (rs->prim)
|
||||
#define IPRIM (rs->iprim)
|
||||
#define PAD (rs->pad)
|
||||
#define A0 (NN)
|
||||
|
||||
|
||||
+504
@@ -0,0 +1,504 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
# Makefile prototype for configure
|
||||
# Copyright 2004 Phil Karn, KA9Q
|
||||
# May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
|
||||
# Generated automatically from makefile.in by configure.
|
||||
srcdir = .
|
||||
prefix = /usr/local
|
||||
exec_prefix=${prefix}
|
||||
CC=gcc
|
||||
LIBS= cpu_mode_generic.o fec.o sim.o viterbi27.o viterbi27_port.o viterbi29.o viterbi29_port.o \
|
||||
viterbi615.o viterbi615_port.o encode_rs_char.o encode_rs_int.o encode_rs_8.o \
|
||||
decode_rs_char.o decode_rs_int.o decode_rs_8.o \
|
||||
init_rs_char.o init_rs_int.o ccsds_tab.o \
|
||||
encode_rs_ccsds.o decode_rs_ccsds.o ccsds_tal.o \
|
||||
dotprod.o dotprod_port.o \
|
||||
peakval.o peakval_port.o \
|
||||
sumsq.o sumsq_port.o
|
||||
|
||||
CFLAGS=-O2 -fPIC -Wno-implicit-int -Wno-implicit-function-declaration -Wno-int-conversion -I. -Wall
|
||||
|
||||
SHARED_LIB=libfec.so
|
||||
|
||||
all: libfec.a $(SHARED_LIB)
|
||||
|
||||
test: vtest27 vtest29 vtest615 rstest dtest sumsq_test peaktest
|
||||
./vtest27 -e 3.0 -n 1000 -v
|
||||
./vtest27
|
||||
./vtest29 -e 2.5 -n 1000 -v
|
||||
./vtest29
|
||||
./vtest615 -e 1.0 -n 100 -v
|
||||
./vtest615
|
||||
./rstest
|
||||
./dtest
|
||||
./sumsq_test
|
||||
./peaktest
|
||||
|
||||
install: all
|
||||
mkdir -p ${exec_prefix}/lib
|
||||
install -m 644 -p $(SHARED_LIB) libfec.a ${exec_prefix}/lib
|
||||
# (cd ${exec_prefix}/lib;ln -f -s $(SHARED_LIB) libfec.so)
|
||||
ldconfig
|
||||
mkdir -p ${prefix}/include
|
||||
install -m 644 -p fec.h ${prefix}/include
|
||||
mkdir -m 0755 -p ${prefix}/man/man3
|
||||
install -m 644 -p simd-viterbi.3 rs.3 dsp.3 ${prefix}/man/man3
|
||||
|
||||
peaktest: peaktest.o libfec.a
|
||||
gcc -g -o $@ $^
|
||||
|
||||
sumsq_test: sumsq_test.o libfec.a
|
||||
gcc -g -o $@ $^
|
||||
|
||||
dtest: dtest.o libfec.a
|
||||
gcc -g -o $@ $^ -lm
|
||||
|
||||
vtest27: vtest27.o libfec.a
|
||||
gcc -g -o $@ $^ -lm
|
||||
|
||||
vtest29: vtest29.o libfec.a
|
||||
gcc -g -o $@ $^ -lm
|
||||
|
||||
vtest615: vtest615.o libfec.a
|
||||
gcc -g -o $@ $^ -lm
|
||||
|
||||
rstest: rstest.o libfec.a
|
||||
gcc -g -o $@ $^
|
||||
|
||||
# for some reason, the test programs without args segfault on the PPC with -O2 optimization. Dunno why - compiler bug?
|
||||
vtest27.o: vtest27.c fec.h
|
||||
gcc -g -c $<
|
||||
|
||||
vtest29.o: vtest29.c fec.h
|
||||
gcc -g -c $<
|
||||
|
||||
vtest615.o: vtest615.c fec.h
|
||||
gcc -g -c $<
|
||||
|
||||
libfec.a: $(LIBS)
|
||||
ar rv $@ $^
|
||||
ranlib libfec.a
|
||||
|
||||
# for Darwin
|
||||
libfec.dylib: $(LIBS)
|
||||
$(CC) -dynamiclib -install_name $@ -o $@ $^
|
||||
|
||||
# for Linux et al
|
||||
libfec.so: $(LIBS)
|
||||
gcc -shared -Xlinker -soname=$@ -o $@ -Wl,-whole-archive $^ -Wl,-no-whole-archive -lc -lm
|
||||
|
||||
dotprod.o: dotprod.c fec.h
|
||||
|
||||
dotprod_port.o: dotprod_port.c fec.h
|
||||
|
||||
viterbi27.o: viterbi27.c fec.h
|
||||
|
||||
viterbi27_port.o: viterbi27_port.c fec.h
|
||||
|
||||
viterbi29.o: viterbi29.c fec.h
|
||||
|
||||
encode_rs_char.o: encode_rs_char.c char.h rs-common.h
|
||||
|
||||
encode_rs_int.o: encode_rs_int.c int.h rs-common.h
|
||||
|
||||
encode_rs_8.o: encode_rs_8.c fixed.h
|
||||
|
||||
encode_rs_av.o: encode_rs_av.c fixed.h
|
||||
|
||||
decode_rs_char.o: decode_rs_char.c char.h rs-common.h
|
||||
|
||||
decode_rs_int.o: decode_rs_int.c int.h rs-common.h
|
||||
|
||||
decode_rs_8.o: decode_rs_8.c fixed.h
|
||||
|
||||
init_rs_char.o: init_rs_char.c char.h rs-common.h
|
||||
|
||||
init_rs_int.o: init_rs_int.c int.h rs-common.h
|
||||
|
||||
ccsds_tab.o: ccsds_tab.c
|
||||
|
||||
ccsds_tab.c: gen_ccsds
|
||||
./gen_ccsds > ccsds_tab.c
|
||||
|
||||
gen_ccsds: gen_ccsds.o init_rs_char.o
|
||||
gcc -o $@ $^
|
||||
|
||||
gen_ccsds.o: gen_ccsds.c
|
||||
gcc $(CFLAGS) -c -o $@ $<
|
||||
|
||||
ccsds_tal.o: ccsds_tal.c
|
||||
|
||||
ccsds_tal.c: gen_ccsds_tal
|
||||
./gen_ccsds_tal > ccsds_tal.c
|
||||
|
||||
exercise_char.o: exercise.c
|
||||
gcc $(CFLAGS) -c -o $@ $<
|
||||
|
||||
exercise_int.o: exercise.c
|
||||
gcc -DBIGSYM=1 $(CFLAGS) -c -o $@ $<
|
||||
|
||||
exercise_8.o: exercise.c
|
||||
gcc -DFIXED=1 $(CFLAGS) -c -o $@ $<
|
||||
|
||||
exercise_ccsds.o: exercise.c
|
||||
gcc -DCCSDS=1 $(CFLAGS) -c -o $@ $<
|
||||
|
||||
viterbi27.o: viterbi27.c fec.h
|
||||
|
||||
viterbi27_port.o: viterbi27_port.c fec.h
|
||||
|
||||
viterbi27_av.o: viterbi27_av.c fec.h
|
||||
|
||||
viterbi27_mmx.o: viterbi27_mmx.c fec.h
|
||||
gcc $(CFLAGS) -mmmx -c -o $@ $<
|
||||
|
||||
viterbi27_sse.o: viterbi27_sse.c fec.h
|
||||
gcc $(CFLAGS) -msse -c -o $@ $<
|
||||
|
||||
viterbi27_sse2.o: viterbi27_sse2.c fec.h
|
||||
gcc $(CFLAGS) -msse2 -c -o $@ $<
|
||||
|
||||
viterbi29.o: viterbi29.c fec.h
|
||||
|
||||
viterbi29_port.o: viterbi29_port.c fec.h
|
||||
|
||||
viterbi29_av.o: viterbi29_av.c fec.h
|
||||
|
||||
viterbi29_mmx.o: viterbi29_mmx.c fec.h
|
||||
gcc $(CFLAGS) -mmmx -c -o $@ $<
|
||||
|
||||
viterbi29_sse.o: viterbi29_sse.c fec.h
|
||||
gcc $(CFLAGS) -msse -c -o $@ $<
|
||||
|
||||
viterbi29_sse2.o: viterbi29_sse2.c fec.h
|
||||
gcc $(CFLAGS) -msse2 -c -o $@ $<
|
||||
|
||||
viterbi615.o: viterbi615.c fec.h
|
||||
|
||||
viterbi615_port.o: viterbi615_port.c fec.h
|
||||
|
||||
viterbi615_av.o: viterbi615_av.c fec.h
|
||||
|
||||
viterbi615_mmx.o: viterbi615_mmx.c fec.h
|
||||
gcc $(CFLAGS) -mmmx -c -o $@ $<
|
||||
|
||||
viterbi615_sse.o: viterbi615_sse.c fec.h
|
||||
gcc $(CFLAGS) -msse -c -o $@ $<
|
||||
|
||||
viterbi615_sse2.o: viterbi615_sse2.c fec.h
|
||||
gcc $(CFLAGS) -msse2 -c -o $@ $<
|
||||
|
||||
cpu_mode_generic.o: cpu_mode_generic.c fec.h
|
||||
gcc $(CFLAGS) -c -o $@ $<
|
||||
|
||||
cpu_mode_x86.o: cpu_mode_x86.c fec.h
|
||||
|
||||
cpu_mode_ppc.o: cpu_mode_ppc.c fec.h
|
||||
|
||||
|
||||
clean:
|
||||
rm -f *.o $(SHARED_LIB) *.a peaktest sumsq_test dtest vtest27 vtest29 vtest615 rstest ccsds_tab.c ccsds_tal.c gen_ccsds gen_ccsds_tal core
|
||||
rm -rf autom4te.cache
|
||||
|
||||
distclean: clean
|
||||
rm -f config.log config.cache config.status config.h makefile
|
||||
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
# Makefile prototype for configure
|
||||
# Copyright 2004 Phil Karn, KA9Q
|
||||
# May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
|
||||
# @configure_input@
|
||||
srcdir = @srcdir@
|
||||
prefix = @prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
VPATH = @srcdir@
|
||||
CC=@CC@
|
||||
LIBS=@MLIBS@ cpu_mode_generic.o fec.o sim.o viterbi27.o viterbi27_port.o viterbi29.o viterbi29_port.o \
|
||||
viterbi615.o viterbi615_port.o encode_rs_char.o encode_rs_int.o encode_rs_8.o \
|
||||
decode_rs_char.o decode_rs_int.o decode_rs_8.o \
|
||||
init_rs_char.o init_rs_int.o ccsds_tab.o \
|
||||
encode_rs_ccsds.o decode_rs_ccsds.o ccsds_tal.o \
|
||||
dotprod.o dotprod_port.o \
|
||||
peakval.o peakval_port.o \
|
||||
sumsq.o sumsq_port.o
|
||||
|
||||
CFLAGS=@CFLAGS@ -I. -Wall @ARCH_OPTION@
|
||||
|
||||
SHARED_LIB=@SH_LIB@
|
||||
|
||||
all: libfec.a $(SHARED_LIB)
|
||||
|
||||
test: vtest27 vtest29 vtest615 rstest dtest sumsq_test peaktest
|
||||
./vtest27 -e 3.0 -n 1000 -v
|
||||
./vtest27
|
||||
./vtest29 -e 2.5 -n 1000 -v
|
||||
./vtest29
|
||||
./vtest615 -e 1.0 -n 100 -v
|
||||
./vtest615
|
||||
./rstest
|
||||
./dtest
|
||||
./sumsq_test
|
||||
./peaktest
|
||||
|
||||
install: all
|
||||
mkdir -p @libdir@
|
||||
install -m 644 -p $(SHARED_LIB) libfec.a @libdir@
|
||||
# (cd @libdir@;ln -f -s $(SHARED_LIB) libfec.so)
|
||||
@REBIND@
|
||||
mkdir -p @includedir@
|
||||
install -m 644 -p fec.h @includedir@
|
||||
mkdir -m 0755 -p @mandir@/man3
|
||||
install -m 644 -p simd-viterbi.3 rs.3 dsp.3 @mandir@/man3
|
||||
|
||||
peaktest: peaktest.o libfec.a
|
||||
gcc -g -o $@ $^
|
||||
|
||||
sumsq_test: sumsq_test.o libfec.a
|
||||
gcc -g -o $@ $^
|
||||
|
||||
dtest: dtest.o libfec.a
|
||||
gcc -g -o $@ $^ -lm
|
||||
|
||||
vtest27: vtest27.o libfec.a
|
||||
gcc -g -o $@ $^ -lm
|
||||
|
||||
vtest29: vtest29.o libfec.a
|
||||
gcc -g -o $@ $^ -lm
|
||||
|
||||
vtest615: vtest615.o libfec.a
|
||||
gcc -g -o $@ $^ -lm
|
||||
|
||||
rstest: rstest.o libfec.a
|
||||
gcc -g -o $@ $^
|
||||
|
||||
# for some reason, the test programs without args segfault on the PPC with -O2 optimization. Dunno why - compiler bug?
|
||||
vtest27.o: vtest27.c fec.h
|
||||
gcc -g -c $<
|
||||
|
||||
vtest29.o: vtest29.c fec.h
|
||||
gcc -g -c $<
|
||||
|
||||
vtest615.o: vtest615.c fec.h
|
||||
gcc -g -c $<
|
||||
|
||||
libfec.a: $(LIBS)
|
||||
ar rv $@ $^
|
||||
ranlib libfec.a
|
||||
|
||||
# for Darwin
|
||||
libfec.dylib: $(LIBS)
|
||||
$(CC) -dynamiclib -install_name $@ -o $@ $^
|
||||
|
||||
# for Linux et al
|
||||
libfec.so: $(LIBS)
|
||||
gcc -shared -Xlinker -soname=$@ -o $@ -Wl,-whole-archive $^ -Wl,-no-whole-archive -lc -lm
|
||||
|
||||
dotprod.o: dotprod.c fec.h
|
||||
|
||||
dotprod_port.o: dotprod_port.c fec.h
|
||||
|
||||
viterbi27.o: viterbi27.c fec.h
|
||||
|
||||
viterbi27_port.o: viterbi27_port.c fec.h
|
||||
|
||||
viterbi29.o: viterbi29.c fec.h
|
||||
|
||||
encode_rs_char.o: encode_rs_char.c char.h rs-common.h
|
||||
|
||||
encode_rs_int.o: encode_rs_int.c int.h rs-common.h
|
||||
|
||||
encode_rs_8.o: encode_rs_8.c fixed.h
|
||||
|
||||
encode_rs_av.o: encode_rs_av.c fixed.h
|
||||
|
||||
decode_rs_char.o: decode_rs_char.c char.h rs-common.h
|
||||
|
||||
decode_rs_int.o: decode_rs_int.c int.h rs-common.h
|
||||
|
||||
decode_rs_8.o: decode_rs_8.c fixed.h
|
||||
|
||||
init_rs_char.o: init_rs_char.c char.h rs-common.h
|
||||
|
||||
init_rs_int.o: init_rs_int.c int.h rs-common.h
|
||||
|
||||
ccsds_tab.o: ccsds_tab.c
|
||||
|
||||
ccsds_tab.c: gen_ccsds
|
||||
./gen_ccsds > ccsds_tab.c
|
||||
|
||||
gen_ccsds: gen_ccsds.o init_rs_char.o
|
||||
gcc -o $@ $^
|
||||
|
||||
gen_ccsds.o: gen_ccsds.c
|
||||
gcc $(CFLAGS) -c -o $@ $<
|
||||
|
||||
ccsds_tal.o: ccsds_tal.c
|
||||
|
||||
ccsds_tal.c: gen_ccsds_tal
|
||||
./gen_ccsds_tal > ccsds_tal.c
|
||||
|
||||
exercise_char.o: exercise.c
|
||||
gcc $(CFLAGS) -c -o $@ $<
|
||||
|
||||
exercise_int.o: exercise.c
|
||||
gcc -DBIGSYM=1 $(CFLAGS) -c -o $@ $<
|
||||
|
||||
exercise_8.o: exercise.c
|
||||
gcc -DFIXED=1 $(CFLAGS) -c -o $@ $<
|
||||
|
||||
exercise_ccsds.o: exercise.c
|
||||
gcc -DCCSDS=1 $(CFLAGS) -c -o $@ $<
|
||||
|
||||
viterbi27.o: viterbi27.c fec.h
|
||||
|
||||
viterbi27_port.o: viterbi27_port.c fec.h
|
||||
|
||||
viterbi27_av.o: viterbi27_av.c fec.h
|
||||
|
||||
viterbi27_mmx.o: viterbi27_mmx.c fec.h
|
||||
gcc $(CFLAGS) -mmmx -c -o $@ $<
|
||||
|
||||
viterbi27_sse.o: viterbi27_sse.c fec.h
|
||||
gcc $(CFLAGS) -msse -c -o $@ $<
|
||||
|
||||
viterbi27_sse2.o: viterbi27_sse2.c fec.h
|
||||
gcc $(CFLAGS) -msse2 -c -o $@ $<
|
||||
|
||||
viterbi29.o: viterbi29.c fec.h
|
||||
|
||||
viterbi29_port.o: viterbi29_port.c fec.h
|
||||
|
||||
viterbi29_av.o: viterbi29_av.c fec.h
|
||||
|
||||
viterbi29_mmx.o: viterbi29_mmx.c fec.h
|
||||
gcc $(CFLAGS) -mmmx -c -o $@ $<
|
||||
|
||||
viterbi29_sse.o: viterbi29_sse.c fec.h
|
||||
gcc $(CFLAGS) -msse -c -o $@ $<
|
||||
|
||||
viterbi29_sse2.o: viterbi29_sse2.c fec.h
|
||||
gcc $(CFLAGS) -msse2 -c -o $@ $<
|
||||
|
||||
viterbi615.o: viterbi615.c fec.h
|
||||
|
||||
viterbi615_port.o: viterbi615_port.c fec.h
|
||||
|
||||
viterbi615_av.o: viterbi615_av.c fec.h
|
||||
|
||||
viterbi615_mmx.o: viterbi615_mmx.c fec.h
|
||||
gcc $(CFLAGS) -mmmx -c -o $@ $<
|
||||
|
||||
viterbi615_sse.o: viterbi615_sse.c fec.h
|
||||
gcc $(CFLAGS) -msse -c -o $@ $<
|
||||
|
||||
viterbi615_sse2.o: viterbi615_sse2.c fec.h
|
||||
gcc $(CFLAGS) -msse2 -c -o $@ $<
|
||||
|
||||
cpu_mode_generic.o: cpu_mode_generic.c fec.h
|
||||
gcc $(CFLAGS) -c -o $@ $<
|
||||
|
||||
cpu_mode_x86.o: cpu_mode_x86.c fec.h
|
||||
|
||||
cpu_mode_ppc.o: cpu_mode_ppc.c fec.h
|
||||
|
||||
|
||||
clean:
|
||||
rm -f *.o $(SHARED_LIB) *.a peaktest sumsq_test dtest vtest27 vtest29 vtest615 rstest ccsds_tab.c ccsds_tal.c gen_ccsds gen_ccsds_tal core
|
||||
rm -rf autom4te.cache
|
||||
|
||||
distclean: clean
|
||||
rm -f config.log config.cache config.status config.h makefile
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// Generate metric tables for a soft-decision convolutional decoder
|
||||
// assuming gaussian noise on a PSK channel.
|
||||
|
||||
// Works from "first principles" by evaluating the normal probability
|
||||
// function and then computing the log-likelihood function
|
||||
// for every possible received symbol value
|
||||
|
||||
// Copyright 1995 Phil Karn, KA9Q
|
||||
// Updated March 2014 (!)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern int Verbose;
|
||||
|
||||
// Normal function integrated from -Inf to x. Range: 0-1
|
||||
static double normal(double x){
|
||||
return 0.5 + 0.5*erf(x/M_SQRT2);
|
||||
}
|
||||
|
||||
// Generate log-likelihood metrics for 8-bit soft quantized channel assuming AWGN and BPSK
|
||||
void gen_met(
|
||||
int mettab[2][256], // Metric table, [sent sym][rx symbol]
|
||||
double signal, // Signal amplitude, units
|
||||
double noise, // Noise amplitude, units (absolute, no longer relative!)
|
||||
double bias, // Metric bias; 0 for viterbi, rate for sequential
|
||||
double scale // Metric scale factor */
|
||||
){
|
||||
int s;
|
||||
double metrics0,metrics1,p0,p1;
|
||||
double left0,left1,right0,right1;
|
||||
double inv_noise;
|
||||
|
||||
inv_noise = 1./noise;
|
||||
|
||||
// Compute the channel transition probabilities, i.e., the probability of receiving each of the
|
||||
// 256 possible values when 0s and 1s were sent.
|
||||
// The bins are assumed to be centered on their nominal values:
|
||||
// Bin 0; -infinity < v < -127.5
|
||||
// Bin 1: -127.5 < v < -126.5
|
||||
// Bin 128: -0.5 < v < +0.5
|
||||
// Bin 255: +126.5 < v < +infinity
|
||||
left0 = left1 = 0.0; // area below bin 0 is zero
|
||||
for(s=0;s<256;s++){
|
||||
|
||||
// Find the area below and in this bin, subtract the area below and in the previous bin,
|
||||
// leaving just the area of this bin.
|
||||
// The area above bin 255 is zero.
|
||||
right0 = (s != 255) ? normal((s - 128 + 0.5 + signal) * inv_noise) : 1.0;
|
||||
right1 = (s != 255) ? normal((s - 128 + 0.5 - signal) * inv_noise) : 1.0;
|
||||
|
||||
p0 = right0 - left0; // p0 = P(s|0), prob of receiving s given that a 0 was sent
|
||||
p1 = right1 - left1; // p1 = P(s|1), prob of recieving s given that a 1 was sent
|
||||
|
||||
left1 = right1;
|
||||
left0 = right0;
|
||||
|
||||
// Compute log-likelihood ratios assuming even balance of 0's and 1's on channel
|
||||
if(p0 == p1){
|
||||
// At high SNR, extremal sample values may underflow to p0 == p1 == 0, giving
|
||||
// infinitely bad metrics for what might actually be very good samples if
|
||||
// actually encountered. Not sure what's right here, so I punt and treat both as erasures
|
||||
metrics0 = metrics1 = -bias;
|
||||
} else {
|
||||
// The smallest value from log2() is about -32, so approximate log2(0) as -33.
|
||||
// Alternatively I could represent it as -INT_MAX, the worst possible metric, but that seems excessive
|
||||
metrics0 = (p0 == 0) ? -33.0 : log2(2*p0/(p1+p0)) - bias;
|
||||
metrics1 = (p1 == 0) ? -33.0 : log2(2*p1/(p1+p0)) - bias;
|
||||
// Equivalent:
|
||||
metrics0 = (p0 == 0) ? -33.0 : 1 + log2(p0) - log2(p1+p0) - bias;
|
||||
metrics1 = (p1 == 0) ? -33.0 : 1 + log2(p1) - log2(p1+p0) - bias;
|
||||
}
|
||||
// Scale and round for table
|
||||
mettab[0][s] = lrint(metrics0 * scale);
|
||||
mettab[1][s] = lrint(metrics1 * scale);
|
||||
if(Verbose){
|
||||
printf("s=%3d P(s|0) = %-12lg P(s|1) = %-12lg P(s) = %-12lg",
|
||||
s,p0,p1,(p1+p0)/2.);
|
||||
printf(" metrics0 = %-12lg [%4d]",metrics0,mettab[0][s]);
|
||||
printf(" metrics1 = %-12lg [%4d]\n",metrics1,mettab[1][s]);
|
||||
}
|
||||
}
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
/* Intel SIMD MMX implementation of Viterbi ACS butterflies
|
||||
for 64-state (k=7) convolutional code
|
||||
Copyright 2004 Phil Karn, KA9Q
|
||||
This code may be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
|
||||
void update_viterbi27_blk_mmx(struct v27 *vp,unsigned char *syms,int nbits) ;
|
||||
*/
|
||||
# MMX (64-bit SIMD) version
|
||||
# requires Pentium-MMX, Pentium-II or better
|
||||
|
||||
# These are offsets into struct v27, defined in viterbi27_mmx.c
|
||||
.set DP,128
|
||||
.set OLDMETRICS,132
|
||||
.set NEWMETRICS,136
|
||||
.text
|
||||
.global update_viterbi27_blk_mmx,Mettab27_1,Mettab27_2
|
||||
.type update_viterbi27_blk_mmx,@function
|
||||
.align 16
|
||||
|
||||
update_viterbi27_blk_mmx:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
pushl %edx
|
||||
pushl %ebx
|
||||
|
||||
movl 8(%ebp),%edx # edx = vp
|
||||
movl OLDMETRICS(%edx),%esi # esi -> old metrics
|
||||
movl NEWMETRICS(%edx),%edi # edi -> new metrics
|
||||
movl DP(%edx),%edx # edx -> decisions
|
||||
|
||||
1: movl 16(%ebp),%eax # eax = nbits
|
||||
decl %eax
|
||||
jl 2f # passed zero, we're done
|
||||
movl %eax,16(%ebp)
|
||||
|
||||
movl 12(%ebp),%ebx # ebx = syms
|
||||
movw (%ebx),%ax # ax = second symbol : first symbol
|
||||
addl $2,%ebx
|
||||
movl %ebx,12(%ebp)
|
||||
|
||||
movb %ah,%bl
|
||||
andl $255,%eax
|
||||
andl $255,%ebx
|
||||
|
||||
# shift into first array index dimension slot
|
||||
shll $5,%eax
|
||||
shll $5,%ebx
|
||||
|
||||
# each invocation of this macro will do 8 butterflies in parallel
|
||||
.MACRO butterfly GROUP
|
||||
# Compute branch metrics
|
||||
movq (Mettab27_1+8*\GROUP)(%eax),%mm3
|
||||
movq fifteens,%mm0
|
||||
|
||||
paddb (Mettab27_2+8*\GROUP)(%ebx),%mm3
|
||||
paddb ones,%mm3 # emulate pavgb - this may not be necessary
|
||||
psrlq $1,%mm3
|
||||
pand %mm0,%mm3
|
||||
|
||||
movq (8*\GROUP)(%esi),%mm6 # Incoming path metric, high bit = 0
|
||||
movq ((8*\GROUP)+32)(%esi),%mm2 # Incoming path metric, high bit = 1
|
||||
movq %mm6,%mm1
|
||||
movq %mm2,%mm7
|
||||
|
||||
paddb %mm3,%mm6
|
||||
paddb %mm3,%mm2
|
||||
pxor %mm0,%mm3 # invert branch metric
|
||||
paddb %mm3,%mm7 # path metric for inverted symbols
|
||||
paddb %mm3,%mm1
|
||||
|
||||
# live registers 1 2 6 7
|
||||
# Compare mm6 and mm7; mm1 and mm2
|
||||
pxor %mm3,%mm3
|
||||
movq %mm6,%mm4
|
||||
movq %mm1,%mm5
|
||||
psubb %mm7,%mm4 # mm4 = mm6 - mm7
|
||||
psubb %mm2,%mm5 # mm5 = mm1 - mm2
|
||||
pcmpgtb %mm3,%mm4 # mm4 = first set of decisions (ff = 1 better)
|
||||
pcmpgtb %mm3,%mm5 # mm5 = second set of decisions
|
||||
|
||||
# live registers 1 2 4 5 6 7
|
||||
# select survivors
|
||||
movq %mm4,%mm0
|
||||
pand %mm4,%mm7
|
||||
movq %mm5,%mm3
|
||||
pand %mm5,%mm2
|
||||
pandn %mm6,%mm0
|
||||
pandn %mm1,%mm3
|
||||
por %mm0,%mm7 # mm7 = first set of survivors
|
||||
por %mm3,%mm2 # mm2 = second set of survivors
|
||||
|
||||
# live registers 2 4 5 7
|
||||
# interleave & store decisions in mm4, mm5
|
||||
# interleave & store new branch metrics in mm2, mm7
|
||||
movq %mm4,%mm3
|
||||
movq %mm7,%mm0
|
||||
punpckhbw %mm5,%mm4
|
||||
punpcklbw %mm5,%mm3
|
||||
punpcklbw %mm2,%mm7 # interleave second 8 new metrics
|
||||
punpckhbw %mm2,%mm0 # interleave first 8 new metrics
|
||||
movq %mm4,(16*\GROUP+8)(%edx)
|
||||
movq %mm3,(16*\GROUP)(%edx)
|
||||
movq %mm7,(16*\GROUP)(%edi)
|
||||
movq %mm0,(16*\GROUP+8)(%edi)
|
||||
|
||||
.endm
|
||||
|
||||
# invoke macro 4 times for a total of 32 butterflies
|
||||
butterfly GROUP=0
|
||||
butterfly GROUP=1
|
||||
butterfly GROUP=2
|
||||
butterfly GROUP=3
|
||||
|
||||
addl $64,%edx # bump decision pointer
|
||||
|
||||
# swap metrics
|
||||
movl %esi,%eax
|
||||
movl %edi,%esi
|
||||
movl %eax,%edi
|
||||
jmp 1b
|
||||
|
||||
2: emms
|
||||
movl 8(%ebp),%ebx # ebx = vp
|
||||
# stash metric pointers
|
||||
movl %esi,OLDMETRICS(%ebx)
|
||||
movl %edi,NEWMETRICS(%ebx)
|
||||
movl %edx,DP(%ebx) # stash incremented value of vp->dp
|
||||
popl %ebx
|
||||
popl %edx
|
||||
popl %edi
|
||||
popl %esi
|
||||
popl %ebp
|
||||
ret
|
||||
|
||||
.data
|
||||
.align 8
|
||||
fifteens:
|
||||
.byte 15,15,15,15,15,15,15,15
|
||||
|
||||
.align 8
|
||||
ones: .byte 1,1,1,1,1,1,1,1
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
/* Intel SIMD MMX implementation of Viterbi ACS butterflies
|
||||
for 256-state (k=9) convolutional code
|
||||
Copyright 2004 Phil Karn, KA9Q
|
||||
This code may be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
|
||||
void update_viterbi29_blk_mmx(struct v29 *vp,unsigned char *syms,int nbits);
|
||||
*/
|
||||
|
||||
# These are offsets into struct v29, defined in viterbi29.h
|
||||
.set DP,512
|
||||
.set OLDMETRICS,516
|
||||
.set NEWMETRICS,520
|
||||
.text
|
||||
.global update_viterbi29_blk_mmx,Mettab29_1,Mettab29_2
|
||||
.type update_viterbi29_blk_mmx,@function
|
||||
.align 16
|
||||
|
||||
# MMX (64-bit SIMD) version
|
||||
# requires Pentium-MMX, Pentium-II or better
|
||||
|
||||
update_viterbi29_blk_mmx:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
pushl %edx
|
||||
pushl %ebx
|
||||
|
||||
movl 8(%ebp),%edx # edx = vp
|
||||
movl OLDMETRICS(%edx),%esi # esi -> old metrics
|
||||
movl NEWMETRICS(%edx),%edi # edi -> new metrics
|
||||
movl DP(%edx),%edx # edx -> decisions
|
||||
|
||||
1: movl 16(%ebp),%eax # eax = nbits
|
||||
decl %eax
|
||||
jl 2f # passed zero, we're done
|
||||
movl %eax,16(%ebp)
|
||||
|
||||
movl 12(%ebp),%ebx # ebx = syms
|
||||
movw (%ebx),%ax # ax = second symbol : first symbol
|
||||
addl $2,%ebx
|
||||
movl %ebx,12(%ebp)
|
||||
|
||||
movb %ah,%bl
|
||||
andl $255,%eax
|
||||
andl $255,%ebx
|
||||
|
||||
# shift into first array index dimension slot
|
||||
shll $7,%eax
|
||||
shll $7,%ebx
|
||||
|
||||
# each invocation of this macro will do 8 butterflies in parallel
|
||||
.MACRO butterfly GROUP
|
||||
# Compute branch metrics
|
||||
movq (Mettab29_1+8*\GROUP)(%eax),%mm3
|
||||
movq fifteens,%mm0
|
||||
paddb (Mettab29_2+8*\GROUP)(%ebx),%mm3
|
||||
paddb ones,%mm3 # emulate pavgb - this may not be necessary
|
||||
psrlq $1,%mm3
|
||||
pand %mm0,%mm3
|
||||
|
||||
movq (8*\GROUP)(%esi),%mm6 # Incoming path metric, high bit = 0
|
||||
movq ((8*\GROUP)+128)(%esi),%mm2 # Incoming path metric, high bit = 1
|
||||
movq %mm6,%mm1
|
||||
movq %mm2,%mm7
|
||||
|
||||
paddb %mm3,%mm6
|
||||
paddb %mm3,%mm2
|
||||
pxor %mm0,%mm3 # invert branch metric
|
||||
paddb %mm3,%mm7 # path metric for inverted symbols
|
||||
paddb %mm3,%mm1
|
||||
|
||||
# live registers 1 2 6 7
|
||||
# Compare mm6 and mm7; mm1 and mm2
|
||||
pxor %mm3,%mm3
|
||||
movq %mm6,%mm4
|
||||
movq %mm1,%mm5
|
||||
psubb %mm7,%mm4 # mm4 = mm6 - mm7
|
||||
psubb %mm2,%mm5 # mm5 = mm1 - mm2
|
||||
pcmpgtb %mm3,%mm4 # mm4 = first set of decisions (ff = 1 better)
|
||||
pcmpgtb %mm3,%mm5 # mm5 = second set of decisions
|
||||
|
||||
# live registers 1 2 4 5 6 7
|
||||
# select survivors
|
||||
movq %mm4,%mm0
|
||||
pand %mm4,%mm7
|
||||
movq %mm5,%mm3
|
||||
pand %mm5,%mm2
|
||||
pandn %mm6,%mm0
|
||||
pandn %mm1,%mm3
|
||||
por %mm0,%mm7 # mm7 = first set of survivors
|
||||
por %mm3,%mm2 # mm2 = second set of survivors
|
||||
|
||||
# live registers 2 4 5 7
|
||||
# interleave & store decisions in mm4, mm5
|
||||
# interleave & store new branch metrics in mm2, mm7
|
||||
movq %mm4,%mm3
|
||||
movq %mm7,%mm0
|
||||
punpckhbw %mm5,%mm4
|
||||
punpcklbw %mm5,%mm3
|
||||
punpcklbw %mm2,%mm7 # interleave second 8 new metrics
|
||||
punpckhbw %mm2,%mm0 # interleave first 8 new metrics
|
||||
movq %mm4,(16*\GROUP+8)(%edx)
|
||||
movq %mm3,(16*\GROUP)(%edx)
|
||||
movq %mm7,(16*\GROUP)(%edi)
|
||||
movq %mm0,(16*\GROUP+8)(%edi)
|
||||
|
||||
.endm
|
||||
|
||||
# invoke macro 16 times for a total of 128 butterflies
|
||||
butterfly GROUP=0
|
||||
butterfly GROUP=1
|
||||
butterfly GROUP=2
|
||||
butterfly GROUP=3
|
||||
butterfly GROUP=4
|
||||
butterfly GROUP=5
|
||||
butterfly GROUP=6
|
||||
butterfly GROUP=7
|
||||
butterfly GROUP=8
|
||||
butterfly GROUP=9
|
||||
butterfly GROUP=10
|
||||
butterfly GROUP=11
|
||||
butterfly GROUP=12
|
||||
butterfly GROUP=13
|
||||
butterfly GROUP=14
|
||||
butterfly GROUP=15
|
||||
|
||||
addl $256,%edx # bump decision pointer
|
||||
|
||||
# swap metrics
|
||||
movl %esi,%eax
|
||||
movl %edi,%esi
|
||||
movl %eax,%edi
|
||||
jmp 1b
|
||||
|
||||
2: emms
|
||||
movl 8(%ebp),%ebx # ebx = vp
|
||||
# stash metric pointers
|
||||
movl %esi,OLDMETRICS(%ebx)
|
||||
movl %edi,NEWMETRICS(%ebx)
|
||||
movl %edx,DP(%ebx) # stash incremented value of vp->dp
|
||||
popl %ebx
|
||||
popl %edx
|
||||
popl %edi
|
||||
popl %esi
|
||||
popl %ebp
|
||||
ret
|
||||
|
||||
.data
|
||||
.align 8
|
||||
fifteens:
|
||||
.byte 15,15,15,15,15,15,15,15
|
||||
|
||||
.align 8
|
||||
ones: .byte 1,1,1,1,1,1,1,1
|
||||
@@ -0,0 +1,70 @@
|
||||
# MMX assist routines for peakval
|
||||
# Copyright 2001 Phil Karn, KA9Q
|
||||
# May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
|
||||
.text
|
||||
|
||||
# Find peak value in signed 16-bit input samples
|
||||
# int peakval_mmx(signed short *in,int cnt);
|
||||
.global peakval_mmx
|
||||
.type peakval_mmx,@function
|
||||
.align 16
|
||||
peakval_mmx:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
pushl %esi
|
||||
pushl %ecx
|
||||
pushl %ebx
|
||||
|
||||
movl 8(%ebp),%esi
|
||||
movl 12(%ebp),%ecx
|
||||
|
||||
pxor %mm7,%mm7 # clear peak
|
||||
|
||||
1: subl $4,%ecx
|
||||
jl 2f
|
||||
movq (%esi),%mm0
|
||||
movq %mm0,%mm1
|
||||
psraw $15,%mm1 # mm1 = 1's if negative, 0's if positive
|
||||
pxor %mm1,%mm0 # complement negatives
|
||||
psubw %mm1,%mm0 # add 1 to negatives
|
||||
movq %mm7,%mm6 # copy previous peak
|
||||
pcmpgtw %mm0,%mm6 # ff == old peak greater
|
||||
pand %mm6,%mm7 # select old peaks that are greater
|
||||
pandn %mm0,%mm6 # select new values that are greater
|
||||
por %mm6,%mm7
|
||||
|
||||
addl $8,%esi
|
||||
jmp 1b
|
||||
|
||||
2: movd %mm7,%eax
|
||||
psrlq $16,%mm7
|
||||
andl $0xffff,%eax
|
||||
|
||||
movd %mm7,%edx
|
||||
psrlq $16,%mm7
|
||||
andl $0xffff,%edx
|
||||
cmpl %edx,%eax
|
||||
jnl 3f
|
||||
movl %edx,%eax
|
||||
3:
|
||||
movd %mm7,%edx
|
||||
psrlq $16,%mm7
|
||||
andl $0xffff,%edx
|
||||
cmpl %edx,%eax
|
||||
jnl 4f
|
||||
movl %edx,%eax
|
||||
4:
|
||||
movd %mm7,%edx
|
||||
andl $0xffff,%edx
|
||||
cmpl %edx,%eax
|
||||
jnl 5f
|
||||
movl %edx,%eax
|
||||
5:
|
||||
emms
|
||||
popl %ebx
|
||||
popl %ecx
|
||||
popl %esi
|
||||
popl %ebp
|
||||
ret
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
# SSE2 assist routines for peakval
|
||||
# Copyright 2001 Phil Karn, KA9Q
|
||||
# May be used under the terms of the GNU Public License (GPL)
|
||||
|
||||
.text
|
||||
|
||||
# Find peak absolute value in signed 16-bit input samples
|
||||
# int peakval_sse2(signed short *in,int cnt);
|
||||
.global peakval_sse2
|
||||
.type peakval_sse2,@function
|
||||
.align 16
|
||||
peakval_sse2:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
pushl %esi
|
||||
pushl %ecx
|
||||
|
||||
movl 8(%ebp),%esi
|
||||
movl 12(%ebp),%ecx
|
||||
|
||||
pxor %xmm7,%xmm7 # clear peak
|
||||
|
||||
1: subl $8,%ecx
|
||||
jl 2f
|
||||
movaps (%esi),%xmm0
|
||||
movaps %xmm0,%xmm1
|
||||
psraw $15,%xmm1 # xmm1 = 1's if negative, 0's if positive
|
||||
pxor %xmm1,%xmm0 # complement negatives
|
||||
psubw %xmm1,%xmm0 # add 1 to negatives
|
||||
pmaxsw %xmm0,%xmm7 # store peak
|
||||
|
||||
addl $16,%esi
|
||||
jmp 1b
|
||||
|
||||
2: movaps %xmm7,%xmm0
|
||||
psrldq $8,%xmm0
|
||||
pmaxsw %xmm0,%xmm7
|
||||
movaps %xmm7,%xmm0
|
||||
psrlq $32,%xmm0
|
||||
pmaxsw %xmm0,%xmm7
|
||||
movaps %xmm7,%xmm0
|
||||
psrlq $16,%xmm0
|
||||
pmaxsw %xmm0,%xmm7 # min value in low word of %xmm7
|
||||
|
||||
movd %xmm7,%eax
|
||||
andl $0xffff,%eax
|
||||
|
||||
popl %ecx
|
||||
popl %esi
|
||||
popl %ebp
|
||||
ret
|
||||
@@ -0,0 +1,49 @@
|
||||
# SSE assist routines for peakval
|
||||
# Copyright 2001 Phil Karn, KA9Q
|
||||
# May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
|
||||
.text
|
||||
|
||||
# Find peak absolute value in signed 16-bit input samples
|
||||
# int peakval_sse(signed short *in,int cnt);
|
||||
.global peakval_sse
|
||||
.type peakval_sse,@function
|
||||
.align 16
|
||||
peakval_sse:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
pushl %esi
|
||||
pushl %ecx
|
||||
|
||||
movl 8(%ebp),%esi
|
||||
movl 12(%ebp),%ecx
|
||||
|
||||
pxor %mm7,%mm7 # clear peak
|
||||
|
||||
1: subl $4,%ecx
|
||||
jl 2f
|
||||
movq (%esi),%mm0
|
||||
movq %mm0,%mm1
|
||||
psraw $15,%mm1 # mm1 = 1's if negative, 0's if positive
|
||||
pxor %mm1,%mm0 # complement negatives
|
||||
psubw %mm1,%mm0 # add 1 to negatives
|
||||
pmaxsw %mm0,%mm7 # store peak
|
||||
|
||||
addl $8,%esi
|
||||
jmp 1b
|
||||
|
||||
2: movq %mm7,%mm0
|
||||
psrlq $32,%mm0
|
||||
pmaxsw %mm0,%mm7
|
||||
movq %mm7,%mm0
|
||||
psrlq $16,%mm0
|
||||
pmaxsw %mm0,%mm7 # min value in low word of %mm7
|
||||
|
||||
movd %mm7,%eax
|
||||
andl $0xffff,%eax
|
||||
|
||||
emms
|
||||
popl %ecx
|
||||
popl %esi
|
||||
popl %ebp
|
||||
ret
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/* Verify correctness of the peak routine
|
||||
* Copyright 2004 Phil Karn, KA9Q
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
/* These values should trigger leading/trailing array fragment handling */
|
||||
#define NSAMP 200002
|
||||
#define OFFSET 1
|
||||
|
||||
int peakval(signed short *,int);
|
||||
int peakval_port(signed short *,int);
|
||||
|
||||
int main(){
|
||||
int i,s;
|
||||
int result,rresult;
|
||||
signed short samples[NSAMP];
|
||||
|
||||
srandom(time(NULL));
|
||||
|
||||
for(i=0;i<NSAMP;i++){
|
||||
do {
|
||||
s = random() & 0x0fff;
|
||||
} while(s == 0x8000);
|
||||
samples[i] = s;
|
||||
}
|
||||
samples[5] = 25000;
|
||||
|
||||
rresult = peakval_port(&samples[OFFSET],NSAMP-OFFSET);
|
||||
result = peakval(&samples[OFFSET],NSAMP-OFFSET);
|
||||
if(result == rresult){
|
||||
printf("OK\n");
|
||||
} else {
|
||||
printf("peak mismatch: %d != %d\n",result,rresult);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/* Switch to appropriate version of peakval routine
|
||||
* Copyright 2004, Phil Karn, KA9Q
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "fec.h"
|
||||
|
||||
int peakval_port(signed short *b,int cnt);
|
||||
#ifdef __i386__
|
||||
int peakval_mmx(signed short *b,int cnt);
|
||||
int peakval_sse(signed short *b,int cnt);
|
||||
int peakval_sse2(signed short *b,int cnt);
|
||||
#endif
|
||||
|
||||
#ifdef __VEC__
|
||||
int peakval_av(signed short *b,int cnt);
|
||||
#endif
|
||||
|
||||
int peakval(signed short *b,int cnt){
|
||||
find_cpu_mode();
|
||||
|
||||
switch(Cpu_mode){
|
||||
case PORT:
|
||||
default:
|
||||
return peakval_port(b,cnt);
|
||||
#ifdef __i386__
|
||||
case MMX:
|
||||
return peakval_mmx(b,cnt);
|
||||
case SSE:
|
||||
return peakval_sse(b,cnt);
|
||||
case SSE2:
|
||||
return peakval_sse2(b,cnt);
|
||||
#endif
|
||||
#ifdef __VEC__
|
||||
case ALTIVEC:
|
||||
return peakval_av(b,cnt);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/* Return the largest absolute value of a vector of signed shorts
|
||||
|
||||
* This is the Altivec SIMD version.
|
||||
|
||||
* Copyright 2004 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
|
||||
#include "fec.h"
|
||||
|
||||
signed short peakval_av(signed short *in,int cnt){
|
||||
vector signed short x;
|
||||
int pad;
|
||||
union { vector signed char cv; vector signed short hv; signed short s[8]; signed char c[16];} s;
|
||||
vector signed short smallest,largest;
|
||||
|
||||
smallest = (vector signed short)(0);
|
||||
largest = (vector signed short)(0);
|
||||
if((pad = (int)in & 15)!=0){
|
||||
/* Load unaligned leading word */
|
||||
x = vec_perm(vec_ld(0,in),(vector signed short)(0),vec_lvsl(0,in));
|
||||
if(cnt < 8){ /* Shift right to chop stuff beyond end of short block */
|
||||
s.c[15] = (8-cnt)<<4;
|
||||
x = vec_sro(x,s.cv);
|
||||
}
|
||||
smallest = vec_min(smallest,x);
|
||||
largest = vec_max(largest,x);
|
||||
in += 8-pad/2;
|
||||
cnt -= 8-pad/2;
|
||||
}
|
||||
/* Everything is now aligned, rip through most of the block */
|
||||
while(cnt >= 8){
|
||||
x = vec_ld(0,in);
|
||||
smallest = vec_min(smallest,x);
|
||||
largest = vec_max(largest,x);
|
||||
in += 8;
|
||||
cnt -= 8;
|
||||
}
|
||||
/* Handle trailing fragment, if any */
|
||||
if(cnt > 0){
|
||||
x = vec_ld(0,in);
|
||||
s.c[15] = (8-cnt)<<4;
|
||||
x = vec_sro(x,s.cv);
|
||||
smallest = vec_min(smallest,x);
|
||||
largest = vec_max(largest,x);
|
||||
}
|
||||
/* Combine and extract result */
|
||||
largest = vec_max(largest,vec_abs(smallest));
|
||||
|
||||
s.c[15] = 64; /* Shift right four 16-bit words */
|
||||
largest = vec_max(largest,vec_sro(largest,s.cv));
|
||||
|
||||
s.c[15] = 32; /* Shift right two 16-bit words */
|
||||
largest = vec_max(largest,vec_sro(largest,s.cv));
|
||||
|
||||
s.c[15] = 16; /* Shift right one 16-bit word */
|
||||
largest = vec_max(largest,vec_sro(largest,s.cv));
|
||||
|
||||
s.hv = largest;
|
||||
return s.s[7];
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/* Wrapper for the MMX version of peakval
|
||||
* Copyright 2004 Phil Karn, KA9Q
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int peakval_mmx_assist(signed short *,int);
|
||||
|
||||
int peakval_mmx(signed short *b,int cnt){
|
||||
int peak = 0;
|
||||
int a;
|
||||
|
||||
while(((int)b & 7) != 0 && cnt != 0){
|
||||
a = abs(*b);
|
||||
if(a > peak)
|
||||
peak = a;
|
||||
b++;
|
||||
cnt--;
|
||||
}
|
||||
a = peakval_mmx_assist(b,cnt);
|
||||
if(a > peak)
|
||||
peak = a;
|
||||
b += cnt & ~3;
|
||||
cnt &= 3;
|
||||
|
||||
while(cnt != 0){
|
||||
a = abs(*b);
|
||||
if(a > peak)
|
||||
peak = a;
|
||||
b++;
|
||||
cnt--;
|
||||
}
|
||||
return peak;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
# MMX assist routines for peakval
|
||||
# Copyright 2001 Phil Karn, KA9Q
|
||||
# May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
|
||||
.text
|
||||
|
||||
# Find peak value in signed 16-bit input samples
|
||||
# int peakval_mmx_assist(signed short *in,int cnt);
|
||||
.global peakval_mmx_assist
|
||||
.type peakval_mmx_assist,@function
|
||||
.align 16
|
||||
peakval_mmx_assist:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
pushl %esi
|
||||
pushl %ecx
|
||||
pushl %ebx
|
||||
|
||||
movl 8(%ebp),%esi
|
||||
movl 12(%ebp),%ecx
|
||||
|
||||
pxor %mm7,%mm7 # clear peak
|
||||
|
||||
1: subl $4,%ecx
|
||||
jl 2f
|
||||
movq (%esi),%mm0
|
||||
movq %mm0,%mm1
|
||||
psraw $15,%mm1 # mm1 = 1's if negative, 0's if positive
|
||||
pxor %mm1,%mm0 # complement negatives
|
||||
psubw %mm1,%mm0 # add 1 to negatives
|
||||
movq %mm7,%mm6 # copy previous peak
|
||||
pcmpgtw %mm0,%mm6 # ff == old peak greater
|
||||
pand %mm6,%mm7 # select old peaks that are greater
|
||||
pandn %mm0,%mm6 # select new values that are greater
|
||||
por %mm6,%mm7
|
||||
|
||||
addl $8,%esi
|
||||
jmp 1b
|
||||
|
||||
2: movd %mm7,%eax
|
||||
psrlq $16,%mm7
|
||||
andl $0xffff,%eax
|
||||
|
||||
movd %mm7,%edx
|
||||
psrlq $16,%mm7
|
||||
andl $0xffff,%edx
|
||||
cmpl %edx,%eax
|
||||
jnl 3f
|
||||
movl %edx,%eax
|
||||
3:
|
||||
movd %mm7,%edx
|
||||
psrlq $16,%mm7
|
||||
andl $0xffff,%edx
|
||||
cmpl %edx,%eax
|
||||
jnl 4f
|
||||
movl %edx,%eax
|
||||
4:
|
||||
movd %mm7,%edx
|
||||
andl $0xffff,%edx
|
||||
cmpl %edx,%eax
|
||||
jnl 5f
|
||||
movl %edx,%eax
|
||||
5:
|
||||
emms
|
||||
popl %ebx
|
||||
popl %ecx
|
||||
popl %esi
|
||||
popl %ebp
|
||||
ret
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/* Portable C version of peakval
|
||||
* Copyright 2004 Phil Karn, KA9Q
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include "fec.h"
|
||||
int peakval_port(signed short *b,int len){
|
||||
int peak = 0;
|
||||
int a,i;
|
||||
|
||||
for(i=0;i<len;i++){
|
||||
a = abs(b[i]);
|
||||
if(a > peak)
|
||||
peak = a;
|
||||
}
|
||||
return peak;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/* IA-32 SSE version of peakval
|
||||
* Copyright 2004 Phil Karn, KA9Q
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "fec.h"
|
||||
|
||||
int peakval_sse_assist(signed short *,int);
|
||||
|
||||
int peakval_sse(signed short *b,int cnt){
|
||||
int peak = 0;
|
||||
int a;
|
||||
|
||||
while(((int)b & 7) != 0 && cnt != 0){
|
||||
a = abs(*b);
|
||||
if(a > peak)
|
||||
peak = a;
|
||||
b++;
|
||||
cnt--;
|
||||
}
|
||||
a = peakval_sse_assist(b,cnt);
|
||||
if(a > peak)
|
||||
peak = a;
|
||||
b += cnt & ~3;
|
||||
cnt &= 3;
|
||||
|
||||
while(cnt != 0){
|
||||
a = abs(*b);
|
||||
if(a > peak)
|
||||
peak = a;
|
||||
b++;
|
||||
cnt--;
|
||||
}
|
||||
return peak;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/* Portable C version of peakval
|
||||
* Copyright 2004 Phil Karn, KA9Q
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include "fec.h"
|
||||
|
||||
int peakval_sse2_assist(signed short *,int);
|
||||
|
||||
int peakval_sse2(signed short *b,int cnt){
|
||||
int peak = 0;
|
||||
int a;
|
||||
|
||||
while(((int)b & 15) != 0 && cnt != 0){
|
||||
a = abs(*b);
|
||||
if(a > peak)
|
||||
peak = a;
|
||||
b++;
|
||||
cnt--;
|
||||
}
|
||||
a = peakval_sse2_assist(b,cnt);
|
||||
if(a > peak)
|
||||
peak = a;
|
||||
b += cnt & ~7;
|
||||
cnt &= 7;
|
||||
|
||||
while(cnt != 0){
|
||||
a = abs(*b);
|
||||
if(a > peak)
|
||||
peak = a;
|
||||
b++;
|
||||
cnt--;
|
||||
}
|
||||
return peak;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
# SSE2 assist routines for peakval
|
||||
# Copyright 2001 Phil Karn, KA9Q
|
||||
# May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
|
||||
.text
|
||||
|
||||
# Find peak absolute value in signed 16-bit input samples
|
||||
# int peakval_sse2_assist(signed short *in,int cnt);
|
||||
.global peakval_sse2_assist
|
||||
.type peakval_sse2_assist,@function
|
||||
.align 16
|
||||
peakval_sse2_assist:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
pushl %esi
|
||||
pushl %ecx
|
||||
|
||||
movl 8(%ebp),%esi
|
||||
movl 12(%ebp),%ecx
|
||||
|
||||
pxor %xmm7,%xmm7 # clear peak
|
||||
|
||||
1: subl $8,%ecx
|
||||
jl 2f
|
||||
movaps (%esi),%xmm0
|
||||
movaps %xmm0,%xmm1
|
||||
psraw $15,%xmm1 # xmm1 = 1's if negative, 0's if positive
|
||||
pxor %xmm1,%xmm0 # complement negatives
|
||||
psubw %xmm1,%xmm0 # add 1 to negatives
|
||||
pmaxsw %xmm0,%xmm7 # store peak
|
||||
|
||||
addl $16,%esi
|
||||
jmp 1b
|
||||
|
||||
2: movaps %xmm7,%xmm0
|
||||
psrldq $8,%xmm0
|
||||
pmaxsw %xmm0,%xmm7
|
||||
movaps %xmm7,%xmm0
|
||||
psrlq $32,%xmm0
|
||||
pmaxsw %xmm0,%xmm7
|
||||
movaps %xmm7,%xmm0
|
||||
psrlq $16,%xmm0
|
||||
pmaxsw %xmm0,%xmm7 # min value in low word of %xmm7
|
||||
|
||||
movd %xmm7,%eax
|
||||
andl $0xffff,%eax
|
||||
|
||||
popl %ecx
|
||||
popl %esi
|
||||
popl %ebp
|
||||
ret
|
||||
@@ -0,0 +1,49 @@
|
||||
# SSE assist routines for peakval
|
||||
# Copyright 2001 Phil Karn, KA9Q
|
||||
# May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
|
||||
.text
|
||||
|
||||
# Find peak absolute value in signed 16-bit input samples
|
||||
# int peakval_sse_assist(signed short *in,int cnt);
|
||||
.global peakval_sse_assist
|
||||
.type peakval_sse_assist,@function
|
||||
.align 16
|
||||
peakval_sse_assist:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
pushl %esi
|
||||
pushl %ecx
|
||||
|
||||
movl 8(%ebp),%esi
|
||||
movl 12(%ebp),%ecx
|
||||
|
||||
pxor %mm7,%mm7 # clear peak
|
||||
|
||||
1: subl $4,%ecx
|
||||
jl 2f
|
||||
movq (%esi),%mm0
|
||||
movq %mm0,%mm1
|
||||
psraw $15,%mm1 # mm1 = 1's if negative, 0's if positive
|
||||
pxor %mm1,%mm0 # complement negatives
|
||||
psubw %mm1,%mm0 # add 1 to negatives
|
||||
pmaxsw %mm0,%mm7 # store peak
|
||||
|
||||
addl $8,%esi
|
||||
jmp 1b
|
||||
|
||||
2: movq %mm7,%mm0
|
||||
psrlq $32,%mm0
|
||||
pmaxsw %mm0,%mm7
|
||||
movq %mm7,%mm0
|
||||
psrlq $16,%mm0
|
||||
pmaxsw %mm0,%mm7 # min value in low word of %mm7
|
||||
|
||||
movd %mm7,%eax
|
||||
andl $0xffff,%eax
|
||||
|
||||
emms
|
||||
popl %ecx
|
||||
popl %esi
|
||||
popl %ebp
|
||||
ret
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/* Stuff common to all the general-purpose Reed-Solomon codecs
|
||||
* Copyright 2004 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
|
||||
/* Reed-Solomon codec control block */
|
||||
struct rs {
|
||||
int mm; /* Bits per symbol */
|
||||
int nn; /* Symbols per block (= (1<<mm)-1) */
|
||||
data_t *alpha_to; /* log lookup table */
|
||||
data_t *index_of; /* Antilog lookup table */
|
||||
data_t *genpoly; /* Generator polynomial */
|
||||
int nroots; /* Number of generator roots = number of parity symbols */
|
||||
int fcr; /* First consecutive root, index form */
|
||||
int prim; /* Primitive element, index form */
|
||||
int iprim; /* prim-th root of 1, index form */
|
||||
int pad; /* Padding bytes in shortened block */
|
||||
};
|
||||
|
||||
static inline int modnn(struct rs *rs,int x){
|
||||
while (x >= rs->nn) {
|
||||
x -= rs->nn;
|
||||
x = (x >> rs->mm) + (x & rs->nn);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
.TH REED-SOLOMON 3
|
||||
.SH NAME
|
||||
init_rs_int, encode_rs_int, decode_rs_int, free_rs_int,
|
||||
init_rs_char, encode_rs_char, decode_rs_char, free_rs_char,
|
||||
encode_rs_8, decode_rs_8, encode_rs_ccsds, decode_rs_ccsds
|
||||
\- Reed-Solomon encoding/decoding
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
.ft B
|
||||
#include "fec.h"
|
||||
|
||||
void *init_rs_int(int symsize,int gfpoly,int fcr,int prim,
|
||||
int nroots,int pad);
|
||||
|
||||
void encode_rs_int(void *rs,int *data,int *parity);
|
||||
|
||||
int decode_rs_int(void *rs,int *data,int *eras_pos,int no_eras);
|
||||
|
||||
void free_rs_int(void *rs);
|
||||
|
||||
|
||||
void *init_rs_char(int symsize,int gfpoly,int fcr,int prim,
|
||||
int nroots,int pad);
|
||||
|
||||
void encode_rs_char(void *rs,unsigned char *data,
|
||||
unsigned char *parity);
|
||||
|
||||
int decode_rs_char(void *rs,unsigned char *data,int *eras_pos,
|
||||
int no_eras);
|
||||
|
||||
void free_rs_char(void *rs);
|
||||
|
||||
|
||||
void encode_rs_8(unsigned char *data,unsigned char *parity,
|
||||
int pad);
|
||||
|
||||
int decode_rs_8(unsigned char *data,int *eras_pos,int no_eras,
|
||||
int pad);
|
||||
|
||||
|
||||
void encode_rs_ccsds(unsigned char *data,unsigned char *parity,
|
||||
int pad);
|
||||
|
||||
int decode_rs_ccsds(unsigned char *data,int *eras_pos,int no_eras,
|
||||
int pad);
|
||||
|
||||
unsigned char Taltab[256];
|
||||
unsigned char Tal1tab[256];
|
||||
|
||||
.fi
|
||||
|
||||
.SH DESCRIPTION
|
||||
These functions implement Reed-Solomon error control encoding and
|
||||
decoding. For optimal performance in a variety of applications, three
|
||||
sets of functions are supplied. To access these functions, add "-lfec"
|
||||
to your linker command line.
|
||||
|
||||
The functions with names ending in \fB_int\fR handle data in integer arrays,
|
||||
permitting arbitrarily large codewords limited only by machine
|
||||
resources.
|
||||
|
||||
The functions with names ending in \fB_char\fR take unsigned char arrays and can
|
||||
handle codes with symbols of 8 bits or less (i.e., with codewords of
|
||||
255 symbols or less).
|
||||
|
||||
\fBencode_rs_8\fR and \fBdecode_rs_8\fR implement a specific
|
||||
(255,223) code with 8-bit symbols specified by the CCSDS:
|
||||
a field generator of 1 + X + X^2 + X^7 + X^8 and a code
|
||||
generator with first consecutive root = 112 and a primitive element of
|
||||
11. These functions use the conventional
|
||||
polynomial form, \fInot\fR the dual-basis specified in
|
||||
the CCSDS standard, to represent symbols. This code may be
|
||||
shortened by giving a non-zero \fBpad\fR value to produce a
|
||||
(255-\fBpad\fR,223-\fBpad\fR) code. The padding will consist of the
|
||||
specified number of zeroes at the front of the full codeword.
|
||||
|
||||
For full CCSDS compatibility, \fBencode_rs_ccsds\fR and
|
||||
\fBdecode_rs_ccsds\fR are provided. These functions use two lookup
|
||||
tables, \fBTaltab\fR to convert from conventional to dual-basis, and
|
||||
\fBTal1tab\fR to perform the inverse mapping from dual-basis to
|
||||
conventional form, before and after calls to \fBencode_rs_8\fR
|
||||
and \fBdecode_rs_8\fR.
|
||||
|
||||
The \fB_8\fR and \fB_ccsds\fR functions do not require initialization.
|
||||
|
||||
To use the general purpose RS encoder or decoder (i.e.,
|
||||
the \fB_char\fR or \fB_int\fR versions), the user must first
|
||||
call \fBinit_rs_int\fR or \fBinit_rs_char\fR as appropriate. The
|
||||
arguments are as follows:
|
||||
|
||||
\fBsymsize\fR gives the symbol size in bits, up to 8 for \fBinit_rs_char\fR
|
||||
or 32 for \fBinit_rs_int\fR on a machine with 32-bit ints (though such a
|
||||
huge code would exhaust memory limits on a 32-bit machine). The resulting
|
||||
Reed-Solomon code word will have 2^\fBsymsize\fR - 1 symbols,
|
||||
each containing \fBsymsize\fR bits. The codeword may be shortened with the
|
||||
\fBpad\fR parameter described below.
|
||||
|
||||
\fBgfpoly\fR gives the extended Galois field generator polynomial coefficients,
|
||||
with the 0th coefficient in the low order bit. The polynomial
|
||||
\fImust\fR be primitive; if not, the call will fail and NULL will be
|
||||
returned.
|
||||
|
||||
\fBfcr\fR gives, in index form, the first consecutive root of the
|
||||
Reed Solomon code generator polynomial.
|
||||
|
||||
\fBprim\fR gives, in index form, the primitive element in the Galois field
|
||||
used to generate the Reed Solomon code generator polynomial.
|
||||
|
||||
\fBnroots\fR gives the number of roots in the Reed Solomon code
|
||||
generator polynomial. This equals the number of parity symbols
|
||||
per code block.
|
||||
|
||||
\fBpad\fR gives the number of leading symbols in the codeword
|
||||
that are implicitly padded to zero in a shortened code block.
|
||||
|
||||
The resulting Reed-Solomon code has parameters (N,K), where
|
||||
N = 2^\fBsymsize\fR - \fBpad\fR - 1 and K = N-\fBnroots\fR.
|
||||
|
||||
The \fBencode_rs_char\fR and \fBencode_rs_int\fR functions accept
|
||||
the pointer returned by \fBinit_rs_char\fR or
|
||||
\fBinit_rs_int\fR, respectively, to
|
||||
encode a block of data using the specified code.
|
||||
The input data array is expected to
|
||||
contain K symbols (of \fBsymsize\fR bits each, right justified
|
||||
in each char or int) and \fBnroots\fR parity symbols will be placed
|
||||
into the \fBparity\fR array, right justified.
|
||||
|
||||
The \fBdecode_\fR functions correct
|
||||
the errors in a Reed-Solomon codeword of N symbols up to the capability of the code.
|
||||
An optional list of "erased" symbol indices may be given in the \fBeras_pos\fR
|
||||
array to assist the decoder; this parameter may be NULL if no erasures
|
||||
are given. The number of erased symbols must be given in the \fBno_eras\fR
|
||||
parameter.
|
||||
|
||||
To maximize performance, the encode and decode functions perform no
|
||||
"sanity checking" of their inputs. Decoder failure may result if
|
||||
\fBeras_pos\fR contains duplicate entries, and both encoder and
|
||||
decoder will fail if an input symbol exceeds its allowable range.
|
||||
(Symbol range overflow cannot occur with the \fB_8\fR or
|
||||
\fB_ccsds\fR functions,
|
||||
or with the \fB_char\fR functions when 8-bit symbols are specified.)
|
||||
|
||||
The decoder corrects the symbols "in place", returning the number
|
||||
of symbols in error. If the codeword is uncorrectable, -1 is returned
|
||||
and the data block is unchanged. If \fBeras_pos\fR is non-null, it is
|
||||
used to return a list of corrected symbol positions, in no particular
|
||||
order. This means that the
|
||||
array passed through this parameter \fImust\fR have at least \fBnroots\fR
|
||||
elements to prevent a possible buffer overflow.
|
||||
|
||||
The \fBfree_rs_int\fR and \fBfree_rs_char\fR functions free the internal
|
||||
space allocated by the \fBinit_rs_int\fR and \fBinit_rs_char\fR functions,
|
||||
respecitively.
|
||||
|
||||
The functions \fBencode_rs_8\fR and \fBdecode_rs_8\fR do not have
|
||||
corresponding \fBinit\fR and \fBfree\fR, nor do they take the
|
||||
\fBrs\fR argument accepted by the other functions as their parameters
|
||||
are statically compiled. These functions implement a code
|
||||
equivalent to calling
|
||||
|
||||
\fBinit_rs_char\fR(8,0x187,112,11,32,pad);
|
||||
|
||||
and using the resulting pointer with \fBencode_rs_char\fR and
|
||||
\fBdecode_rs_char\fR.
|
||||
|
||||
.SH RETURN VALUES
|
||||
\fBinit_rs_int\fR and \fBinit_rs_char\fR return a pointer to an internal
|
||||
control structure that must be passed to the corresponding encode, decode
|
||||
and free functions. These functions return NULL on error.
|
||||
|
||||
The \fBdecode_\fR functions return a count of corrected
|
||||
symbols, or -1 if the block was uncorrectible.
|
||||
|
||||
.SH AUTHOR
|
||||
Phil Karn, KA9Q (karn@ka9q.net), based heavily on earlier work by Robert
|
||||
Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari Thirumoorthy
|
||||
(harit@spectra.eng.hawaii.edu). Extra improvements suggested by Detmar
|
||||
Welz (dwelz@web.de).
|
||||
|
||||
.SH COPYRIGHT
|
||||
Copyright 2004, Phil Karn, KA9Q. May be used under the terms of the
|
||||
GNU Lesser General Public License (LGPL).
|
||||
|
||||
.SH SEE ALSO
|
||||
CCSDS 101.0-B-6: Telemetry Channel Coding.
|
||||
http://www.ccsds.org/documents/101x0b6.pdf
|
||||
|
||||
.SH NOTE
|
||||
CCSDS chose the "dual basis" symbol representation because it
|
||||
simplified the implementation of a Reed-Solomon encoder in dedicated
|
||||
hardware. However, this approach holds no advantages for a software
|
||||
implementation on a general purpose computer, so use of the dual basis
|
||||
is recommended only if compatibility with the CCSDS standard is needed,
|
||||
e.g., to decode data from an existing spacecraft using the CCSDS
|
||||
standard. If you just want a fast (255,223) RS codec without needing
|
||||
to interoperate with a CCSDS standard code, use \fBencode_rs_8\fR
|
||||
and \fBdecode_rs_8\fR.
|
||||
|
||||
@@ -0,0 +1,621 @@
|
||||
/*
|
||||
* Reed-Solomon coding and decoding
|
||||
* Phil Karn (karn@ka9q.ampr.org) September 1996
|
||||
*
|
||||
* This file is derived from the program "new_rs_erasures.c" by Robert
|
||||
* Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari Thirumoorthy
|
||||
* (harit@spectra.eng.hawaii.edu), Aug 1995
|
||||
*
|
||||
* I've made changes to improve performance, clean up the code and make it
|
||||
* easier to follow. Data is now passed to the encoding and decoding functions
|
||||
* through arguments rather than in global arrays. The decode function returns
|
||||
* the number of corrected symbols, or -1 if the word is uncorrectable.
|
||||
*
|
||||
* This code supports a symbol size from 2 bits up to 16 bits,
|
||||
* implying a block size of 3 2-bit symbols (6 bits) up to 65535
|
||||
* 16-bit symbols (1,048,560 bits). The code parameters are set in rs.h.
|
||||
*
|
||||
* Note that if symbols larger than 8 bits are used, the type of each
|
||||
* data array element switches from unsigned char to unsigned int. The
|
||||
* caller must ensure that elements larger than the symbol range are
|
||||
* not passed to the encoder or decoder.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "rs.h"
|
||||
|
||||
#if (KK >= NN)
|
||||
#error "KK must be less than 2**MM - 1"
|
||||
#endif
|
||||
|
||||
/* This defines the type used to store an element of the Galois Field
|
||||
* used by the code. Make sure this is something larger than a char if
|
||||
* if anything larger than GF(256) is used.
|
||||
*
|
||||
* Note: unsigned char will work up to GF(256) but int seems to run
|
||||
* faster on the Pentium.
|
||||
*/
|
||||
typedef int gf;
|
||||
|
||||
/* Primitive polynomials - see Lin & Costello, Appendix A,
|
||||
* and Lee & Messerschmitt, p. 453.
|
||||
*/
|
||||
#if(MM == 2)/* Admittedly silly */
|
||||
int Pp[MM+1] = { 1, 1, 1 };
|
||||
|
||||
#elif(MM == 3)
|
||||
/* 1 + x + x^3 */
|
||||
int Pp[MM+1] = { 1, 1, 0, 1 };
|
||||
|
||||
#elif(MM == 4)
|
||||
/* 1 + x + x^4 */
|
||||
int Pp[MM+1] = { 1, 1, 0, 0, 1 };
|
||||
|
||||
#elif(MM == 5)
|
||||
/* 1 + x^2 + x^5 */
|
||||
int Pp[MM+1] = { 1, 0, 1, 0, 0, 1 };
|
||||
|
||||
#elif(MM == 6)
|
||||
/* 1 + x + x^6 */
|
||||
int Pp[MM+1] = { 1, 1, 0, 0, 0, 0, 1 };
|
||||
|
||||
#elif(MM == 7)
|
||||
/* 1 + x^3 + x^7 */
|
||||
int Pp[MM+1] = { 1, 0, 0, 1, 0, 0, 0, 1 };
|
||||
|
||||
#elif(MM == 8)
|
||||
/* 1+x^2+x^3+x^4+x^8 */
|
||||
int Pp[MM+1] = { 1, 0, 1, 1, 1, 0, 0, 0, 1 };
|
||||
|
||||
#elif(MM == 9)
|
||||
/* 1+x^4+x^9 */
|
||||
int Pp[MM+1] = { 1, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
|
||||
|
||||
#elif(MM == 10)
|
||||
/* 1+x^3+x^10 */
|
||||
int Pp[MM+1] = { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1 };
|
||||
|
||||
#elif(MM == 11)
|
||||
/* 1+x^2+x^11 */
|
||||
int Pp[MM+1] = { 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
|
||||
|
||||
#elif(MM == 12)
|
||||
/* 1+x+x^4+x^6+x^12 */
|
||||
int Pp[MM+1] = { 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1 };
|
||||
|
||||
#elif(MM == 13)
|
||||
/* 1+x+x^3+x^4+x^13 */
|
||||
int Pp[MM+1] = { 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
|
||||
|
||||
#elif(MM == 14)
|
||||
/* 1+x+x^6+x^10+x^14 */
|
||||
int Pp[MM+1] = { 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 };
|
||||
|
||||
#elif(MM == 15)
|
||||
/* 1+x+x^15 */
|
||||
int Pp[MM+1] = { 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
|
||||
|
||||
#elif(MM == 16)
|
||||
/* 1+x+x^3+x^12+x^16 */
|
||||
int Pp[MM+1] = { 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1 };
|
||||
|
||||
#else
|
||||
#error "MM must be in range 2-16"
|
||||
#endif
|
||||
|
||||
/* Alpha exponent for the first root of the generator polynomial */
|
||||
#define B0 1
|
||||
|
||||
/* index->polynomial form conversion table */
|
||||
gf Alpha_to[NN + 1];
|
||||
|
||||
/* Polynomial->index form conversion table */
|
||||
gf Index_of[NN + 1];
|
||||
|
||||
/* No legal value in index form represents zero, so
|
||||
* we need a special value for this purpose
|
||||
*/
|
||||
#define A0 (NN)
|
||||
|
||||
/* Generator polynomial g(x)
|
||||
* Degree of g(x) = 2*TT
|
||||
* has roots @**B0, @**(B0+1), ... ,@^(B0+2*TT-1)
|
||||
*/
|
||||
gf Gg[NN - KK + 1];
|
||||
|
||||
/* Lookup tables for GF multiplication
|
||||
* Mtab[i][j] = j * alpha^i (note limited range of i)
|
||||
*/
|
||||
gf Mtab[NN-KK+1][NN+1];
|
||||
|
||||
/* Compute x % NN, where NN is 2**MM - 1,
|
||||
* without a slow divide
|
||||
*/
|
||||
static inline gf
|
||||
modnn(int x)
|
||||
{
|
||||
while (x >= NN) {
|
||||
x -= NN;
|
||||
x = (x >> MM) + (x & NN);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
#define min(a,b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
#define CLEAR(a,n) {\
|
||||
int ci;\
|
||||
for(ci=(n)-1;ci >=0;ci--)\
|
||||
(a)[ci] = 0;\
|
||||
}
|
||||
|
||||
#define COPY(a,b,n) {\
|
||||
int ci;\
|
||||
for(ci=(n)-1;ci >=0;ci--)\
|
||||
(a)[ci] = (b)[ci];\
|
||||
}
|
||||
#define COPYDOWN(a,b,n) {\
|
||||
int ci;\
|
||||
for(ci=(n)-1;ci >=0;ci--)\
|
||||
(a)[ci] = (b)[ci];\
|
||||
}
|
||||
|
||||
void init_rs(void)
|
||||
{
|
||||
void gen_mtab(void);
|
||||
generate_gf();
|
||||
gen_poly();
|
||||
gen_mtab();
|
||||
}
|
||||
|
||||
/* generate GF(2**m) from the irreducible polynomial p(X) in p[0]..p[m]
|
||||
lookup tables: index->polynomial form alpha_to[] contains j=alpha**i;
|
||||
polynomial form -> index form index_of[j=alpha**i] = i
|
||||
alpha=2 is the primitive element of GF(2**m)
|
||||
HARI's COMMENT: (4/13/94) alpha_to[] can be used as follows:
|
||||
Let @ represent the primitive element commonly called "alpha" that
|
||||
is the root of the primitive polynomial p(x). Then in GF(2^m), for any
|
||||
0 <= i <= 2^m-2,
|
||||
@^i = a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1)
|
||||
where the binary vector (a(0),a(1),a(2),...,a(m-1)) is the representation
|
||||
of the integer "alpha_to[i]" with a(0) being the LSB and a(m-1) the MSB. Thus for
|
||||
example the polynomial representation of @^5 would be given by the binary
|
||||
representation of the integer "alpha_to[5]".
|
||||
Similarily, index_of[] can be used as follows:
|
||||
As above, let @ represent the primitive element of GF(2^m) that is
|
||||
the root of the primitive polynomial p(x). In order to find the power
|
||||
of @ (alpha) that has the polynomial representation
|
||||
a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1)
|
||||
we consider the integer "i" whose binary representation with a(0) being LSB
|
||||
and a(m-1) MSB is (a(0),a(1),...,a(m-1)) and locate the entry
|
||||
"index_of[i]". Now, @^index_of[i] is that element whose polynomial
|
||||
representation is (a(0),a(1),a(2),...,a(m-1)).
|
||||
NOTE:
|
||||
The element alpha_to[2^m-1] = 0 always signifying that the
|
||||
representation of "@^infinity" = 0 is (0,0,0,...,0).
|
||||
Similarily, the element index_of[0] = A0 always signifying
|
||||
that the power of alpha which has the polynomial representation
|
||||
(0,0,...,0) is "infinity".
|
||||
|
||||
*/
|
||||
|
||||
void
|
||||
generate_gf(void)
|
||||
{
|
||||
register int i, mask;
|
||||
|
||||
mask = 1;
|
||||
Alpha_to[MM] = 0;
|
||||
for (i = 0; i < MM; i++) {
|
||||
Alpha_to[i] = mask;
|
||||
Index_of[Alpha_to[i]] = i;
|
||||
/* If Pp[i] == 1 then, term @^i occurs in poly-repr of @^MM */
|
||||
if (Pp[i] != 0)
|
||||
Alpha_to[MM] ^= mask; /* Bit-wise EXOR operation */
|
||||
mask <<= 1; /* single left-shift */
|
||||
}
|
||||
Index_of[Alpha_to[MM]] = MM;
|
||||
/*
|
||||
* Have obtained poly-repr of @^MM. Poly-repr of @^(i+1) is given by
|
||||
* poly-repr of @^i shifted left one-bit and accounting for any @^MM
|
||||
* term that may occur when poly-repr of @^i is shifted.
|
||||
*/
|
||||
mask >>= 1;
|
||||
for (i = MM + 1; i < NN; i++) {
|
||||
if (Alpha_to[i - 1] >= mask)
|
||||
Alpha_to[i] = Alpha_to[MM] ^ ((Alpha_to[i - 1] ^ mask) << 1);
|
||||
else
|
||||
Alpha_to[i] = Alpha_to[i - 1] << 1;
|
||||
Index_of[Alpha_to[i]] = i;
|
||||
}
|
||||
Index_of[0] = A0;
|
||||
Alpha_to[NN] = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Obtain the generator polynomial of the TT-error correcting, length
|
||||
* NN=(2**MM -1) Reed Solomon code from the product of (X+@**(B0+i)), i = 0,
|
||||
* ... ,(2*TT-1)
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* If B0 = 1, TT = 1. deg(g(x)) = 2*TT = 2.
|
||||
* g(x) = (x+@) (x+@**2)
|
||||
*
|
||||
* If B0 = 0, TT = 2. deg(g(x)) = 2*TT = 4.
|
||||
* g(x) = (x+1) (x+@) (x+@**2) (x+@**3)
|
||||
*/
|
||||
void
|
||||
gen_poly(void)
|
||||
{
|
||||
register int i, j;
|
||||
|
||||
Gg[0] = Alpha_to[B0];
|
||||
Gg[1] = 1; /* g(x) = (X+@**B0) initially */
|
||||
for (i = 2; i <= NN - KK; i++) {
|
||||
Gg[i] = 1;
|
||||
/*
|
||||
* Below multiply (Gg[0]+Gg[1]*x + ... +Gg[i]x^i) by
|
||||
* (@**(B0+i-1) + x)
|
||||
*/
|
||||
for (j = i - 1; j > 0; j--)
|
||||
if (Gg[j] != 0)
|
||||
Gg[j] = Gg[j - 1] ^ Alpha_to[modnn((Index_of[Gg[j]]) + B0 + i - 1)];
|
||||
else
|
||||
Gg[j] = Gg[j - 1];
|
||||
/* Gg[0] can never be zero */
|
||||
Gg[0] = Alpha_to[modnn((Index_of[Gg[0]]) + B0 + i - 1)];
|
||||
}
|
||||
/* convert Gg[] to index form for quicker encoding */
|
||||
for (i = 0; i <= NN - KK; i++)
|
||||
Gg[i] = Index_of[Gg[i]];
|
||||
}
|
||||
|
||||
void
|
||||
gen_mtab(void)
|
||||
{
|
||||
int i,j;
|
||||
|
||||
for(i=0;i<=NN-KK;i++){
|
||||
Mtab[i][0] = 0;
|
||||
for(j=1;j<=NN;j++){
|
||||
Mtab[i][j] = Alpha_to[modnn(Index_of[j] + i)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* take the string of symbols in data[i], i=0..(k-1) and encode
|
||||
* systematically to produce NN-KK parity symbols in bb[0]..bb[NN-KK-1] data[]
|
||||
* is input and bb[] is output in polynomial form. Encoding is done by using
|
||||
* a feedback shift register with appropriate connections specified by the
|
||||
* elements of Gg[], which was generated above. Codeword is c(X) =
|
||||
* data(X)*X**(NN-KK)+ b(X)
|
||||
*/
|
||||
int
|
||||
encode_rs(dtype data[KK], dtype bb[NN-KK])
|
||||
{
|
||||
register int i, j;
|
||||
gf feedback;
|
||||
|
||||
CLEAR(bb,NN-KK);
|
||||
for (i = KK - 1; i >= 0; i--) {
|
||||
#if (MM != 8)
|
||||
if(data[i] > NN)
|
||||
return -1; /* Illegal symbol */
|
||||
#endif
|
||||
feedback = Index_of[data[i] ^ bb[NN - KK - 1]];
|
||||
if (feedback != A0) { /* feedback term is non-zero */
|
||||
for (j = NN - KK - 1; j > 0; j--)
|
||||
if (Gg[j] != A0)
|
||||
bb[j] = bb[j - 1] ^ Alpha_to[modnn(Gg[j] + feedback)];
|
||||
else
|
||||
bb[j] = bb[j - 1];
|
||||
bb[0] = Alpha_to[modnn(Gg[0] + feedback)];
|
||||
} else { /* feedback term is zero. encoder becomes a
|
||||
* single-byte shifter */
|
||||
for (j = NN - KK - 1; j > 0; j--)
|
||||
bb[j] = bb[j - 1];
|
||||
bb[0] = 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Performs ERRORS+ERASURES decoding of RS codes. If decoding is successful,
|
||||
* writes the codeword into data[] itself. Otherwise data[] is unaltered.
|
||||
*
|
||||
* Return number of symbols corrected, or -1 if codeword is illegal
|
||||
* or uncorrectable.
|
||||
*
|
||||
* First "no_eras" erasures are declared by the calling program. Then, the
|
||||
* maximum # of errors correctable is t_after_eras = floor((NN-KK-no_eras)/2).
|
||||
* If the number of channel errors is not greater than "t_after_eras" the
|
||||
* transmitted codeword will be recovered. Details of algorithm can be found
|
||||
* in R. Blahut's "Theory ... of Error-Correcting Codes".
|
||||
*/
|
||||
int
|
||||
eras_dec_rs(dtype data[NN], int eras_pos[NN-KK], int no_eras)
|
||||
{
|
||||
int deg_lambda, el, deg_omega;
|
||||
int i, j, r;
|
||||
gf u,q,tmp,num1,num2,den,discr_r;
|
||||
gf lambda[NN-KK + 1], s[NN-KK + 1]; /* Err+Eras Locator poly
|
||||
* and syndrome poly */
|
||||
gf b[NN-KK + 1], t[NN-KK + 1], omega[NN-KK + 1];
|
||||
gf root[NN-KK], reg[NN-KK + 1], loc[NN-KK];
|
||||
int syn_error, count;
|
||||
|
||||
/*#define old */
|
||||
#ifdef old
|
||||
/* form the syndromes; i.e., evaluate data(x) at roots of g(x)
|
||||
* namely @**(B0+i), i = 0, ... ,(NN-KK-1)
|
||||
*/
|
||||
#if (MM != 8)
|
||||
if(data[0] > NN)
|
||||
return -1;
|
||||
#endif
|
||||
for(i=1;i<=NN-KK;i++)
|
||||
s[i] = data[0];
|
||||
for(j=1;j<NN;j++){
|
||||
#if (MM != 8)
|
||||
if(data[j] > NN)
|
||||
return -1; /* Illegal symbol */
|
||||
#endif
|
||||
if(data[j] == 0)
|
||||
continue;
|
||||
tmp = Index_of[data[j]];
|
||||
|
||||
/* The following code is equivalent to
|
||||
*
|
||||
* for(i=1;i<=NN-KK;i++)
|
||||
* s[i] ^= Alpha_to[modnn(tmp + (B0+i-1)*j)];
|
||||
*
|
||||
* but is faster.
|
||||
*/
|
||||
tmp += B0*j;
|
||||
for(i=1;i<=NN-KK;i++){
|
||||
if(tmp >= NN)
|
||||
tmp -= NN;
|
||||
s[i] ^= Alpha_to[tmp];
|
||||
tmp += j;
|
||||
}
|
||||
}
|
||||
#else
|
||||
for(i=1;i<=NN-KK;i++){
|
||||
tmp = data[NN-1];
|
||||
for(j=NN-2;j>=0;j--){
|
||||
tmp = data[j] ^ Mtab[i][tmp];
|
||||
}
|
||||
s[i] = tmp;
|
||||
}
|
||||
#endif
|
||||
/* Convert syndromes to index form, checking for nonzero condition */
|
||||
syn_error = 0;
|
||||
for(i=1;i<=NN-KK;i++){
|
||||
syn_error |= s[i];
|
||||
s[i] = Index_of[s[i]];
|
||||
}
|
||||
|
||||
if (!syn_error) {
|
||||
/*
|
||||
* if syndrome is zero, data[] is a codeword and there are no
|
||||
* errors to correct. So return data[] unmodified
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
#if (NN-KK > 2)
|
||||
/* Check for and quickly correct the special case of a single error.
|
||||
* This is indicated by
|
||||
* s[i] / s[i+1] = @^-k for all i and some constant k,
|
||||
* (s in polynomial form), or
|
||||
* s[i] - s[i+1] = -k,
|
||||
* (s in index form)
|
||||
*
|
||||
* See Clark & Cain, "Error Correction Coding for Digital
|
||||
* Communications", p209-210.
|
||||
*/
|
||||
if(s[2] != A0 && s[1] != A0){
|
||||
tmp = modnn(s[2] - s[1] + NN); /* s[] in index form */
|
||||
for(i=2;i<NN-KK;i++){
|
||||
if(s[i+1] == A0 || s[i] == A0
|
||||
|| modnn(s[i+1] - s[i] + NN) != tmp)
|
||||
break;
|
||||
}
|
||||
if(i == NN-KK){
|
||||
/* single error */
|
||||
data[tmp] ^= Alpha_to[modnn(s[1] - tmp + NN)];
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
CLEAR(&lambda[1],NN-KK);
|
||||
lambda[0] = 1;
|
||||
if (no_eras > 0) {
|
||||
/* Init lambda to be the erasure locator polynomial */
|
||||
lambda[1] = Alpha_to[eras_pos[0]];
|
||||
for (i = 1; i < no_eras; i++) {
|
||||
u = eras_pos[i];
|
||||
for (j = i+1; j > 0; j--) {
|
||||
tmp = Index_of[lambda[j - 1]];
|
||||
if(tmp != A0)
|
||||
lambda[j] ^= Alpha_to[modnn(u + tmp)];
|
||||
}
|
||||
}
|
||||
#ifdef ERASURE_DEBUG
|
||||
/* find roots of the erasure location polynomial */
|
||||
for(i=1;i<=no_eras;i++)
|
||||
reg[i] = Index_of[lambda[i]];
|
||||
count = 0;
|
||||
for (i = 1; i <= NN; i++) {
|
||||
q = 1;
|
||||
for (j = 1; j <= no_eras; j++)
|
||||
if (reg[j] != A0) {
|
||||
reg[j] = modnn(reg[j] + j);
|
||||
q ^= Alpha_to[reg[j]];
|
||||
}
|
||||
if (!q) {
|
||||
/* store root and error location
|
||||
* number indices
|
||||
*/
|
||||
root[count] = i;
|
||||
loc[count] = NN - i;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count != no_eras) {
|
||||
printf("\n lambda(x) is WRONG\n");
|
||||
return -1;
|
||||
}
|
||||
#ifndef NO_PRINT
|
||||
printf("\n Erasure positions as determined by roots of Eras Loc Poly:\n");
|
||||
for (i = 0; i < count; i++)
|
||||
printf("%d ", loc[i]);
|
||||
printf("\n");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
for(i=0;i<NN-KK+1;i++)
|
||||
b[i] = Index_of[lambda[i]];
|
||||
|
||||
/*
|
||||
* Begin Berlekamp-Massey algorithm to determine error+erasure
|
||||
* locator polynomial
|
||||
*/
|
||||
r = no_eras;
|
||||
el = no_eras;
|
||||
while (++r <= NN-KK) { /* r is the step number */
|
||||
/* Compute discrepancy at the r-th step in poly-form */
|
||||
discr_r = 0;
|
||||
for (i = 0; i < r; i++){
|
||||
if ((lambda[i] != 0) && (s[r - i] != A0)) {
|
||||
discr_r ^= Alpha_to[modnn(Index_of[lambda[i]] + s[r - i])];
|
||||
}
|
||||
}
|
||||
discr_r = Index_of[discr_r]; /* Index form */
|
||||
if (discr_r == A0) {
|
||||
/* 2 lines below: B(x) <-- x*B(x) */
|
||||
COPYDOWN(&b[1],b,NN-KK);
|
||||
b[0] = A0;
|
||||
} else {
|
||||
/* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */
|
||||
t[0] = lambda[0];
|
||||
for (i = 0 ; i < NN-KK; i++) {
|
||||
if(b[i] != A0)
|
||||
t[i+1] = lambda[i+1] ^ Alpha_to[modnn(discr_r + b[i])];
|
||||
else
|
||||
t[i+1] = lambda[i+1];
|
||||
}
|
||||
if (2 * el <= r + no_eras - 1) {
|
||||
el = r + no_eras - el;
|
||||
/*
|
||||
* 2 lines below: B(x) <-- inv(discr_r) *
|
||||
* lambda(x)
|
||||
*/
|
||||
for (i = 0; i <= NN-KK; i++)
|
||||
b[i] = (lambda[i] == 0) ? A0 : modnn(Index_of[lambda[i]] - discr_r + NN);
|
||||
} else {
|
||||
/* 2 lines below: B(x) <-- x*B(x) */
|
||||
COPYDOWN(&b[1],b,NN-KK);
|
||||
b[0] = A0;
|
||||
}
|
||||
COPY(lambda,t,NN-KK+1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert lambda to index form and compute deg(lambda(x)) */
|
||||
deg_lambda = 0;
|
||||
for(i=0;i<NN-KK+1;i++){
|
||||
lambda[i] = Index_of[lambda[i]];
|
||||
if(lambda[i] != A0)
|
||||
deg_lambda = i;
|
||||
}
|
||||
/*
|
||||
* Find roots of the error+erasure locator polynomial. By Chien
|
||||
* Search
|
||||
*/
|
||||
COPY(®[1],&lambda[1],NN-KK);
|
||||
count = 0; /* Number of roots of lambda(x) */
|
||||
for (i = 1; i <= NN; i++) {
|
||||
q = 1;
|
||||
for (j = deg_lambda; j > 0; j--)
|
||||
if (reg[j] != A0) {
|
||||
reg[j] = modnn(reg[j] + j);
|
||||
q ^= Alpha_to[reg[j]];
|
||||
}
|
||||
if (!q) {
|
||||
/* store root (index-form) and error location number */
|
||||
root[count] = i;
|
||||
loc[count] = NN - i;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("\n Final error positions:\t");
|
||||
for (i = 0; i < count; i++)
|
||||
printf("%d ", loc[i]);
|
||||
printf("\n");
|
||||
#endif
|
||||
if (deg_lambda != count) {
|
||||
/*
|
||||
* deg(lambda) unequal to number of roots => uncorrectable
|
||||
* error detected
|
||||
*/
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
|
||||
* x**(NN-KK)). in index form. Also find deg(omega).
|
||||
*/
|
||||
deg_omega = 0;
|
||||
for (i = 0; i < NN-KK;i++){
|
||||
tmp = 0;
|
||||
j = (deg_lambda < i) ? deg_lambda : i;
|
||||
for(;j >= 0; j--){
|
||||
if ((s[i + 1 - j] != A0) && (lambda[j] != A0))
|
||||
tmp ^= Alpha_to[modnn(s[i + 1 - j] + lambda[j])];
|
||||
}
|
||||
if(tmp != 0)
|
||||
deg_omega = i;
|
||||
omega[i] = Index_of[tmp];
|
||||
}
|
||||
omega[NN-KK] = A0;
|
||||
|
||||
/*
|
||||
* Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
|
||||
* inv(X(l))**(B0-1) and den = lambda_pr(inv(X(l))) all in poly-form
|
||||
*/
|
||||
for (j = count-1; j >=0; j--) {
|
||||
num1 = 0;
|
||||
for (i = deg_omega; i >= 0; i--) {
|
||||
if (omega[i] != A0)
|
||||
num1 ^= Alpha_to[modnn(omega[i] + i * root[j])];
|
||||
}
|
||||
num2 = Alpha_to[modnn(root[j] * (B0 - 1) + NN)];
|
||||
den = 0;
|
||||
|
||||
/* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
|
||||
for (i = min(deg_lambda,NN-KK-1) & ~1; i >= 0; i -=2) {
|
||||
if(lambda[i+1] != A0)
|
||||
den ^= Alpha_to[modnn(lambda[i+1] + i * root[j])];
|
||||
}
|
||||
if (den == 0) {
|
||||
#ifdef DEBUG
|
||||
printf("\n ERROR: denominator = 0\n");
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
/* Apply error to data */
|
||||
if (num1 != 0) {
|
||||
data[loc[j]] ^= Alpha_to[modnn(Index_of[num1] + Index_of[num2] + NN - Index_of[den])];
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/* Global definitions for Reed-Solomon encoder/decoder
|
||||
* Phil Karn KA9Q, September 1996
|
||||
*
|
||||
* The parameters MM and KK specify the Reed-Solomon code parameters.
|
||||
*
|
||||
* Set MM to be the size of each code symbol in bits. The Reed-Solomon
|
||||
* block size will then be NN = 2**M - 1 symbols. Supported values are
|
||||
* defined in rs.c.
|
||||
*
|
||||
* Set KK to be the number of data symbols in each block, which must be
|
||||
* less than the block size. The code will then be able to correct up
|
||||
* to NN-KK erasures or (NN-KK)/2 errors, or combinations thereof with
|
||||
* each error counting as two erasures.
|
||||
*/
|
||||
#define MM 8 /* RS code over GF(2**MM) - change to suit */
|
||||
#define KK 223 /* KK = number of information symbols */
|
||||
|
||||
#define NN ((1 << MM) - 1)
|
||||
|
||||
#if (MM <= 8)
|
||||
typedef unsigned char dtype;
|
||||
#else
|
||||
typedef unsigned int dtype;
|
||||
#endif
|
||||
|
||||
/* Initialization function */
|
||||
void init_rs(void);
|
||||
|
||||
/* These two functions *must* be called in this order (e.g.,
|
||||
* by init_rs()) before any encoding/decoding
|
||||
*/
|
||||
|
||||
void generate_gf(void); /* Generate Galois Field */
|
||||
void gen_poly(void); /* Generate generator polynomial */
|
||||
|
||||
/* Reed-Solomon encoding
|
||||
* data[] is the input block, parity symbols are placed in bb[]
|
||||
* bb[] may lie past the end of the data, e.g., for (255,223):
|
||||
* encode_rs(&data[0],&data[223]);
|
||||
*/
|
||||
int encode_rs(dtype data[], dtype bb[]);
|
||||
|
||||
/* Reed-Solomon erasures-and-errors decoding
|
||||
* The received block goes into data[], and a list of zero-origin
|
||||
* erasure positions, if any, goes in eras_pos[] with a count in no_eras.
|
||||
*
|
||||
* The decoder corrects the symbols in place, if possible and returns
|
||||
* the number of corrected symbols. If the codeword is illegal or
|
||||
* uncorrectible, the data array is unchanged and -1 is returned
|
||||
*/
|
||||
int eras_dec_rs(dtype data[], int eras_pos[], int no_eras);
|
||||
@@ -0,0 +1,54 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/times.h>
|
||||
#include "rs.h"
|
||||
|
||||
main(){
|
||||
char block[255];
|
||||
int i;
|
||||
void *rs;
|
||||
struct tms start,finish;
|
||||
double extime;
|
||||
int trials = 10000;
|
||||
|
||||
for(i=0;i<223;i++)
|
||||
block[i] = 0x01;
|
||||
|
||||
rs = init_rs_char(8,0x187,112,11,32,0);
|
||||
encode_rs_char(rs,block,&block[223]);
|
||||
|
||||
|
||||
times(&start);
|
||||
|
||||
for(i=0;i<trials;i++){
|
||||
#if 0
|
||||
block[0] ^= 0xff; /* Introduce an error */
|
||||
block[2] ^= 0xff; /* Introduce an error */
|
||||
#endif
|
||||
decode_rs_char(rs,block,NULL,0);
|
||||
}
|
||||
times(&finish);
|
||||
extime = ((double)(finish.tms_utime-start.tms_utime))/CLK_TCK;
|
||||
printf("Execution time for %d Reed-Solomon blocks using general decoder: %.2f sec\n",trials,extime);
|
||||
printf("decoder speed: %g bits/s\n",trials*223*8/extime);
|
||||
|
||||
|
||||
encode_rs_8(block,&block[223],0);
|
||||
times(&start);
|
||||
for(i=0;i<trials;i++){
|
||||
#if 0
|
||||
block[0] ^= 0xff; /* Introduce an error */
|
||||
block[2] ^= 0xff; /* Introduce an error */
|
||||
#endif
|
||||
decode_rs_8(block,NULL,0,0);
|
||||
}
|
||||
times(&finish);
|
||||
extime = ((double)(finish.tms_utime-start.tms_utime))/CLK_TCK;
|
||||
printf("Execution time for %d Reed-Solomon blocks using CCSDS decoder: %.2f sec\n",trials,extime);
|
||||
printf("decoder speed: %g bits/s\n",trials*223*8/extime);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,296 @@
|
||||
/* Test the Reed-Solomon codecs
|
||||
* for various block sizes and with random data and random error patterns
|
||||
*
|
||||
* Copyright 2002 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <time.h>
|
||||
#include "fec.h"
|
||||
|
||||
|
||||
struct etab {
|
||||
int symsize;
|
||||
int genpoly;
|
||||
int fcs;
|
||||
int prim;
|
||||
int nroots;
|
||||
int ntrials;
|
||||
} Tab[] = {
|
||||
{2, 0x7, 1, 1, 1, 10 },
|
||||
{3, 0xb, 1, 1, 2, 10 },
|
||||
{4, 0x13, 1, 1, 4, 10 },
|
||||
{5, 0x25, 1, 1, 6, 10 },
|
||||
{6, 0x43, 1, 1, 8, 10 },
|
||||
{7, 0x89, 1, 1, 10, 10 },
|
||||
{8, 0x11d, 1, 1, 32, 10 },
|
||||
{8, 0x187, 112,11, 32, 10 }, /* Duplicates CCSDS codec */
|
||||
{9, 0x211, 1, 1, 32, 10 },
|
||||
{10,0x409, 1, 1, 32, 10 },
|
||||
{11,0x805, 1, 1, 32, 10 },
|
||||
{12,0x1053, 1, 1, 32, 5 },
|
||||
{13,0x201b, 1, 1, 32, 2 },
|
||||
{14,0x4443, 1, 1, 32, 1 },
|
||||
{15,0x8003, 1, 1, 32, 1 },
|
||||
{16,0x1100b, 1, 1, 32, 1 },
|
||||
{0, 0, 0, 0, 0},
|
||||
};
|
||||
|
||||
int exercise_char(struct etab *e);
|
||||
int exercise_int(struct etab *e);
|
||||
int exercise_8(void);
|
||||
|
||||
int main(){
|
||||
int i;
|
||||
|
||||
srandom(time(NULL));
|
||||
|
||||
printf("Testing fixed CCSDS encoder...\n");
|
||||
exercise_8();
|
||||
for(i=0;Tab[i].symsize != 0;i++){
|
||||
int nn,kk;
|
||||
|
||||
nn = (1<<Tab[i].symsize) - 1;
|
||||
kk = nn - Tab[i].nroots;
|
||||
printf("Testing (%d,%d) code...\n",nn,kk);
|
||||
if(Tab[i].symsize <= 8)
|
||||
exercise_char(&Tab[i]);
|
||||
else
|
||||
exercise_int(&Tab[i]);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int exercise_8(void){
|
||||
int nn = 255;
|
||||
unsigned char block[nn],tblock[nn];
|
||||
int errlocs[nn],derrlocs[nn];
|
||||
int i;
|
||||
int errors;
|
||||
int derrors,kk;
|
||||
int errval,errloc;
|
||||
int erasures;
|
||||
int decoder_errors = 0;
|
||||
|
||||
/* Compute code parameters */
|
||||
kk = 223;
|
||||
|
||||
|
||||
/* Test up to the error correction capacity of the code */
|
||||
for(errors=0;errors<=(nn-kk)/2;errors++){
|
||||
|
||||
/* Load block with random data and encode */
|
||||
for(i=0;i<kk;i++)
|
||||
block[i] = random() & nn;
|
||||
memcpy(tblock,block,sizeof(block));
|
||||
encode_rs_8(block,&block[kk],0);
|
||||
|
||||
/* Make temp copy, seed with errors */
|
||||
memcpy(tblock,block,sizeof(block));
|
||||
memset(errlocs,0,sizeof(errlocs));
|
||||
memset(derrlocs,0,sizeof(derrlocs));
|
||||
erasures=0;
|
||||
for(i=0;i<errors;i++){
|
||||
do {
|
||||
errval = random() & nn;
|
||||
} while(errval == 0); /* Error value must be nonzero */
|
||||
|
||||
do {
|
||||
errloc = random() % nn;
|
||||
} while(errlocs[errloc] != 0); /* Must not choose the same location twice */
|
||||
|
||||
errlocs[errloc] = 1;
|
||||
|
||||
#if FLAG_ERASURE
|
||||
if(random() & 1) /* 50-50 chance */
|
||||
derrlocs[erasures++] = errloc;
|
||||
#endif
|
||||
tblock[errloc] ^= errval;
|
||||
}
|
||||
|
||||
/* Decode the errored block */
|
||||
derrors = decode_rs_8(tblock,derrlocs,erasures,0);
|
||||
|
||||
if(derrors != errors){
|
||||
printf("(%d,%d) decoder says %d errors, true number is %d\n",nn,kk,derrors,errors);
|
||||
decoder_errors++;
|
||||
}
|
||||
for(i=0;i<derrors;i++){
|
||||
if(errlocs[derrlocs[i]] == 0){
|
||||
printf("(%d,%d) decoder indicates error in location %d without error\n",nn,kk,derrlocs[i]);
|
||||
decoder_errors++;
|
||||
}
|
||||
}
|
||||
if(memcmp(tblock,block,sizeof(tblock)) != 0){
|
||||
printf("(%d,%d) decoder uncorrected errors! output ^ input:",nn,kk);
|
||||
decoder_errors++;
|
||||
for(i=0;i<nn;i++)
|
||||
printf(" %02x",tblock[i] ^ block[i]);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
return decoder_errors;
|
||||
}
|
||||
|
||||
|
||||
int exercise_char(struct etab *e){
|
||||
int nn = (1<<e->symsize) - 1;
|
||||
unsigned char block[nn],tblock[nn];
|
||||
int errlocs[nn],derrlocs[nn];
|
||||
int i;
|
||||
int errors;
|
||||
int derrors,kk;
|
||||
int errval,errloc;
|
||||
int erasures;
|
||||
int decoder_errors = 0;
|
||||
void *rs;
|
||||
|
||||
if(e->symsize > 8)
|
||||
return -1;
|
||||
|
||||
/* Compute code parameters */
|
||||
kk = nn - e->nroots;
|
||||
|
||||
rs = init_rs_char(e->symsize,e->genpoly,e->fcs,e->prim,e->nroots,0);
|
||||
if(rs == NULL){
|
||||
printf("init_rs_char failed!\n");
|
||||
return -1;
|
||||
}
|
||||
/* Test up to the error correction capacity of the code */
|
||||
for(errors=0;errors <= e->nroots/2;errors++){
|
||||
|
||||
/* Load block with random data and encode */
|
||||
for(i=0;i<kk;i++)
|
||||
block[i] = random() & nn;
|
||||
memcpy(tblock,block,sizeof(block));
|
||||
encode_rs_char(rs,block,&block[kk]);
|
||||
|
||||
/* Make temp copy, seed with errors */
|
||||
memcpy(tblock,block,sizeof(block));
|
||||
memset(errlocs,0,sizeof(errlocs));
|
||||
memset(derrlocs,0,sizeof(derrlocs));
|
||||
erasures=0;
|
||||
for(i=0;i<errors;i++){
|
||||
do {
|
||||
errval = random() & nn;
|
||||
} while(errval == 0); /* Error value must be nonzero */
|
||||
|
||||
do {
|
||||
errloc = random() % nn;
|
||||
} while(errlocs[errloc] != 0); /* Must not choose the same location twice */
|
||||
|
||||
errlocs[errloc] = 1;
|
||||
|
||||
#if FLAG_ERASURE
|
||||
if(random() & 1) /* 50-50 chance */
|
||||
derrlocs[erasures++] = errloc;
|
||||
#endif
|
||||
tblock[errloc] ^= errval;
|
||||
}
|
||||
|
||||
/* Decode the errored block */
|
||||
derrors = decode_rs_char(rs,tblock,derrlocs,erasures);
|
||||
|
||||
if(derrors != errors){
|
||||
printf("(%d,%d) decoder says %d errors, true number is %d\n",nn,kk,derrors,errors);
|
||||
decoder_errors++;
|
||||
}
|
||||
for(i=0;i<derrors;i++){
|
||||
if(errlocs[derrlocs[i]] == 0){
|
||||
printf("(%d,%d) decoder indicates error in location %d without error\n",nn,kk,derrlocs[i]);
|
||||
decoder_errors++;
|
||||
}
|
||||
}
|
||||
if(memcmp(tblock,block,sizeof(tblock)) != 0){
|
||||
printf("(%d,%d) decoder uncorrected errors! output ^ input:",nn,kk);
|
||||
decoder_errors++;
|
||||
for(i=0;i<nn;i++)
|
||||
printf(" %02x",tblock[i] ^ block[i]);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
free_rs_char(rs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exercise_int(struct etab *e){
|
||||
int nn = (1<<e->symsize) - 1;
|
||||
int block[nn],tblock[nn];
|
||||
int errlocs[nn],derrlocs[nn];
|
||||
int i;
|
||||
int errors;
|
||||
int derrors,kk;
|
||||
int errval,errloc;
|
||||
int erasures;
|
||||
int decoder_errors = 0;
|
||||
void *rs;
|
||||
|
||||
/* Compute code parameters */
|
||||
kk = nn - e->nroots;
|
||||
|
||||
rs = init_rs_int(e->symsize,e->genpoly,e->fcs,e->prim,e->nroots,0);
|
||||
if(rs == NULL){
|
||||
printf("init_rs_int failed!\n");
|
||||
return -1;
|
||||
}
|
||||
/* Test up to the error correction capacity of the code */
|
||||
for(errors=0;errors <= e->nroots/2;errors++){
|
||||
|
||||
/* Load block with random data and encode */
|
||||
for(i=0;i<kk;i++)
|
||||
block[i] = random() & nn;
|
||||
memcpy(tblock,block,sizeof(block));
|
||||
encode_rs_int(rs,block,&block[kk]);
|
||||
|
||||
/* Make temp copy, seed with errors */
|
||||
memcpy(tblock,block,sizeof(block));
|
||||
memset(errlocs,0,sizeof(errlocs));
|
||||
memset(derrlocs,0,sizeof(derrlocs));
|
||||
erasures=0;
|
||||
for(i=0;i<errors;i++){
|
||||
do {
|
||||
errval = random() & nn;
|
||||
} while(errval == 0); /* Error value must be nonzero */
|
||||
|
||||
do {
|
||||
errloc = random() % nn;
|
||||
} while(errlocs[errloc] != 0); /* Must not choose the same location twice */
|
||||
|
||||
errlocs[errloc] = 1;
|
||||
|
||||
#if FLAG_ERASURE
|
||||
if(random() & 1) /* 50-50 chance */
|
||||
derrlocs[erasures++] = errloc;
|
||||
#endif
|
||||
tblock[errloc] ^= errval;
|
||||
}
|
||||
|
||||
/* Decode the errored block */
|
||||
derrors = decode_rs_int(rs,tblock,derrlocs,erasures);
|
||||
|
||||
if(derrors != errors){
|
||||
printf("(%d,%d) decoder says %d errors, true number is %d\n",nn,kk,derrors,errors);
|
||||
decoder_errors++;
|
||||
}
|
||||
for(i=0;i<derrors;i++){
|
||||
if(errlocs[derrlocs[i]] == 0){
|
||||
printf("(%d,%d) decoder indicates error in location %d without error\n",nn,kk,derrlocs[i]);
|
||||
decoder_errors++;
|
||||
}
|
||||
}
|
||||
if(memcmp(tblock,block,sizeof(tblock)) != 0){
|
||||
printf("(%d,%d) decoder uncorrected errors! output ^ input:",nn,kk);
|
||||
decoder_errors++;
|
||||
for(i=0;i<nn;i++)
|
||||
printf(" %02x",tblock[i] ^ block[i]);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
free_rs_int(rs);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include "fec.h"
|
||||
|
||||
#define MAX_RANDOM 0x7fffffff
|
||||
|
||||
/* Generate gaussian random double with specified mean and std_dev */
|
||||
double normal_rand(double mean, double std_dev)
|
||||
{
|
||||
double fac,rsq,v1,v2;
|
||||
static double gset;
|
||||
static int iset;
|
||||
|
||||
if(iset){
|
||||
/* Already got one */
|
||||
iset = 0;
|
||||
return mean + std_dev*gset;
|
||||
}
|
||||
/* Generate two evenly distributed numbers between -1 and +1
|
||||
* that are inside the unit circle
|
||||
*/
|
||||
do {
|
||||
v1 = 2.0 * (double)random() / MAX_RANDOM - 1;
|
||||
v2 = 2.0 * (double)random() / MAX_RANDOM - 1;
|
||||
rsq = v1*v1 + v2*v2;
|
||||
} while(rsq >= 1.0 || rsq == 0.0);
|
||||
fac = sqrt(-2.0*log(rsq)/rsq);
|
||||
gset = v1*fac;
|
||||
iset++;
|
||||
return mean + std_dev*v2*fac;
|
||||
}
|
||||
|
||||
unsigned char addnoise(int sym,double amp,double gain,double offset,int clip){
|
||||
int sample;
|
||||
|
||||
sample = offset + gain*normal_rand(sym?amp:-amp,1.0);
|
||||
/* Clip to 8-bit offset range */
|
||||
if(sample < 0)
|
||||
sample = 0;
|
||||
else if(sample > clip)
|
||||
sample = clip;
|
||||
return sample;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// User include file for libfec
|
||||
// Copyright 2004-2014, Phil Karn, KA9Q
|
||||
// May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
|
||||
#ifndef _SIM_H_
|
||||
#define _SIM_H_
|
||||
|
||||
// Useful utilities for simulation
|
||||
double normal_rand(double mean, double std_dev);
|
||||
unsigned char addnoise(int sym, double signal, double noise);
|
||||
void setup_channel(double signal,double noise);
|
||||
unsigned char simulate(int data);
|
||||
|
||||
#endif /* _SIM_H_ */
|
||||
|
||||
|
||||
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
.TH SIMD-VITERBI 3
|
||||
.SH NAME
|
||||
create_viterbi27, init_viterbi27, update_viterbi27_blk,
|
||||
chainback_viterbi27,
|
||||
delete_viterbi27, create_viterbi29, init_viterbi29,
|
||||
update_viterbi29_blk,
|
||||
chainback_viterbi29, delete_viterbi29 -\ IA32 SIMD-assisted Viterbi decoders
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
.ft B
|
||||
#include "fec.h"
|
||||
void *create_viterbi27(int blocklen);
|
||||
int init_viterbi27(void *vp,int starting_state);
|
||||
void update_viterbi27_blk(void *vp,unsigned char syms[],int nbits);
|
||||
int chainback_viterbi27(void *vp, unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi27(void *vp);
|
||||
.fi
|
||||
.sp
|
||||
.nf
|
||||
.ft B
|
||||
void *create_viterbi29(int blocklen);
|
||||
int init_viterbi29(void *vp,int starting_state);
|
||||
void update_viterbi29_blk(void *vp,unsigned char syms[],int nbits);
|
||||
int chainback_viterbi29(void *vp, unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi29(void *vp);
|
||||
.fi
|
||||
.sp
|
||||
.nf
|
||||
.ft B
|
||||
void *create_viterbi615(int blocklen);
|
||||
int init_viterbi615(void *vp,int starting_state);
|
||||
void update_viterbi615_blk(void *vp,unsigned char syms[],int nbits);
|
||||
int chainback_viterbi615(void *vp, unsigned char *data,unsigned int nbits,unsigned int endstate);
|
||||
void delete_viterbi615(void *vp);
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
These functions implement high performance Viterbi decoders for three
|
||||
convolutional codes: a rate 1/2 constraint length 7 (k=7) code
|
||||
("viterbi27"), a rate 1/2 k=9 code ("viterbi29") and a rate 1/6 k=15 code ("viterbi615").
|
||||
The decoders use the Intel IA32 or PowerPC SIMD instruction sets, if available, to improve
|
||||
decoding speed.
|
||||
|
||||
On the IA32 there are three different SIMD instruction sets. The first
|
||||
and most common is MMX, introduced on later Intel Pentiums and then on
|
||||
the Intel Pentium II and most Intel clones (AMD K6, Transmeta Crusoe,
|
||||
etc). SSE was introduced on the Pentium III and later implemented in
|
||||
the AMD Athlon 4 (AMD calls it "3D Now! Professional"). Most
|
||||
recently, SSE2 was introduced in the Intel Pentium 4, and has been
|
||||
adopted by more recent AMD CPUs. The presence of SSE2 implies the
|
||||
existence of SSE, which in turn implies MMX.
|
||||
|
||||
Altivec is the PowerPC SIMD instruction set. It is roughly comparable
|
||||
to SSE2. Altivec was introduced to the general public in the Apple
|
||||
Macintosh G4; it is also present in the G5. Altivec is actually a
|
||||
Motorola trademark; Apple calls it "Velocity Engine" and IBM calls it
|
||||
"VMX". All refer to the same thing.
|
||||
|
||||
When built for the IA32 or PPC architectures, the functions
|
||||
automatically use the most powerful SIMD instruction set available. If
|
||||
no SIMD instructions are available, or if the library is built for
|
||||
non-IA32 and non-PPC machines, a portable C version is executed
|
||||
instead.
|
||||
|
||||
.SH USAGE
|
||||
Three versions of each function are provided, one for each code.
|
||||
In the following discussion the k=7 code
|
||||
will be assumed. To use the k=9 or k=15 code, simply change all references to
|
||||
"viterbi27" to "viterbi29" or "viterbi615" as appropriate.
|
||||
|
||||
Before Viterbi decoding can begin, an instance must first be created with
|
||||
\fBcreate_viterbi27()\fR. This function creates and returns a pointer to
|
||||
an internal control structure
|
||||
containing the path metrics and the branch
|
||||
decisions. \fBcreate_viterbi27()\fR takes one argument that gives the
|
||||
length of the data block in bits. You \fImust not\fR attempt to
|
||||
decode a block longer than the length given to \fBcreate_viterbi27()\fR.
|
||||
|
||||
Before decoding a new frame,
|
||||
\fBinit_viterbi27()\fR must be called to reset the decoder state.
|
||||
It accepts the instance pointer returned by
|
||||
\fBcreate_viterbi27()\fR and the initial starting state of the
|
||||
convolutional encoder (usually 0). If the initial starting state is unknown or
|
||||
incorrect, the decoder will still function but the decoded data may be
|
||||
incorrect at the start of the block.
|
||||
|
||||
Blocks of received symbols are processed with calls to
|
||||
\fBupdate_viterbi27_blk()\fR. The \fBnbits\fR parameter specifies the
|
||||
number of \fIdata bits\fR (not channel symbols) represented by the
|
||||
\fBsyms\fR buffer. (For rate 1/2 codes, the number of symbols in
|
||||
\fBsyms\fR is twice \fInbits\fR; for rate 1/6 codes, \fBsyms\fR is six times
|
||||
\fInbits\fR.) Each symbol is expected to range
|
||||
from 0 through 255, with 0 corresponding to a "strong 0" and 255
|
||||
corresponding to a "strong 1". The caller is responsible for
|
||||
determining the proper pairing of input symbols (commonly known as
|
||||
decoder symbol phasing).
|
||||
|
||||
At the end of the block, the data is recovered with a call to
|
||||
\fBchainback_viterbi27()\fR. The arguments are the pointer to the
|
||||
decoder instance, a pointer to a user-supplied buffer into which the
|
||||
decoded data is to be written, the number of data bits (not bytes)
|
||||
that are to be decoded, and the terminal state of the convolutional
|
||||
encoder at the end of the frame (usually 0). If the terminal state is
|
||||
incorrect or unknown, the decoded data bits at the end of the frame
|
||||
may be unreliable. The decoded data is written in big-endian order,
|
||||
i.e., the first bit in the frame is written into the high order bit of
|
||||
the first byte in the buffer. If the frame is not an integral number
|
||||
of bytes long, the low order bits of the last byte in the frame will
|
||||
be unused.
|
||||
|
||||
Note that the decoders assume the use of a tail, i.e., the encoding
|
||||
and transmission of a sufficient number of padding bits beyond the end
|
||||
of the user data to force the convolutional encoder into the known
|
||||
terminal state given to \fBchainback_viterbi27()\fR. The tail is
|
||||
always one bit less than the constraint length of the code, so the k=7
|
||||
code uses 6 tail bits (12 tail symbols), the k=9 code uses 8 tail bits
|
||||
(16 tail symbols) and the k=15 code uses 14 tail bits (84 tail
|
||||
symbols).
|
||||
|
||||
The tail bits are not included in the length arguments to
|
||||
\fBcreate_viterbi27()\fR and \fBchainback_viterbi27()\fR. For example, if
|
||||
the block contains 1000 user bits, then this would be the length
|
||||
parameter given to \fBcreate_viterbi27()\fR and
|
||||
\fBchainback_viterbi27()\fR, and \fBupdate_viterbi27()\fR would be called
|
||||
a total of 1006 times - the last 6 with the 12 encoded symbols
|
||||
representing the tail bits.
|
||||
|
||||
After the call to \fBchainback_viterbi27()\fR, the decoder may be reset
|
||||
with a call to \fBinit_viterbi27()\fR and another block can be decoded.
|
||||
Alternatively, \fBdelete_viterbi27()\fR can be called to free all resources
|
||||
used by the Viterbi decoder.
|
||||
|
||||
.SH ERROR PERFORMANCE
|
||||
These decoders have all been extensively tested and found to provide
|
||||
performance consistent with that expected for soft-decision Viterbi
|
||||
decoding with 8-bit symbols.
|
||||
|
||||
Due to internal differences, the various implementations of the k=7
|
||||
and k=9 Viterbi decoders vary slightly in error performance. In
|
||||
general, the portable C versions exhibit the best error performance
|
||||
because they use full-sized branch metrics, and the MMX versions
|
||||
exhibit the worst because they use 8-bit branch metrics with modulo
|
||||
comparisons. The SSE, SSE2 and Altivec implementations use unsigned
|
||||
8-bit branch metrics, and are almost as good as the C versions. The
|
||||
k=15 code is implemented with 16-bit path metrics in all SIMD
|
||||
versions.
|
||||
|
||||
.SH DIRECT ACCESS TO SPECIFIC FUNCTION VERSIONS
|
||||
Calling the functions listed above automatically calls the appropriate
|
||||
version of the function depending on the CPU type and available SIMD
|
||||
instructions. A particular version can also be called directly by
|
||||
appending the appropriate suffix to the function name. The available
|
||||
suffixes are "_mmx", "_sse", "_sse2", "_av" and "_port", for the MMX,
|
||||
SSE, SSE2, Altivec and portable versions, respectively. For example,
|
||||
the SSE2 version of the update_viterbi27_blk() function can be invoked
|
||||
as update_viterbi27_blk_sse2().
|
||||
|
||||
Naturally, the _av functions are only available on the PowerPC, the
|
||||
_mmx, _sse and _sse2 versions are only available on IA-32, and calling
|
||||
a SIMD-enabled function on a CPU that doesn't support the appropriate
|
||||
set of instructions will result in an illegal instruction exception.
|
||||
|
||||
.SH RETURN VALUES
|
||||
\fBcreate_viterbi27()\fR returns a pointer to the structure containing
|
||||
the decoder state. Depending on the specific version,
|
||||
\fBupdate_viterbi27()\fR returns either zero or the amount by which
|
||||
the decoder path metrics were normalized in the current step. This is
|
||||
an experimental feature that is not supported in all decoders and may
|
||||
be removed in future versions
|
||||
|
||||
.SH AUTHOR & COPYRIGHT
|
||||
Phil Karn, KA9Q (karn@ka9q.net)
|
||||
|
||||
.SH LICENSE
|
||||
This software may be used under the terms of the GNU Limited General Public License (LGPL).
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
void setup_channel(double signal, double noise);
|
||||
unsigned char simulate(int data);
|
||||
|
||||
double signal = 40;
|
||||
double esn0 = 3; // decibels
|
||||
|
||||
|
||||
int main(){
|
||||
double signal = 40.;
|
||||
double noise;
|
||||
int i;
|
||||
|
||||
noise = signal/sqrt(0.5 * pow(10.,esn0/10.));
|
||||
|
||||
setup_channel(signal,noise);
|
||||
|
||||
printf("simulated 0:\n");
|
||||
for(i=0;i<1000;i++){
|
||||
printf(" %d",simulate(0));
|
||||
}
|
||||
putchar('\n');
|
||||
|
||||
printf("simulated 1:\n");
|
||||
for(i=0;i<1000;i++){
|
||||
printf(" %d",simulate(1));
|
||||
}
|
||||
putchar('\n');
|
||||
|
||||
|
||||
|
||||
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/* Verify correctness of the sum-of-square routines */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
/* These values should trigger leading/trailing array fragment handling */
|
||||
#define NSAMP 200002
|
||||
#define OFFSET 1
|
||||
|
||||
long long sumsq_wq(signed short *in,int cnt);
|
||||
long long sumsq_wq_ref(signed short *in,int cnt);
|
||||
|
||||
int main(){
|
||||
int i;
|
||||
long long result,rresult;
|
||||
signed short samples[NSAMP];
|
||||
|
||||
srandom(time(NULL));
|
||||
|
||||
for(i=0;i<NSAMP;i++)
|
||||
samples[i] = random() & 0xffff;
|
||||
|
||||
rresult = sumsq_wq(&samples[OFFSET],NSAMP-OFFSET);
|
||||
result = sumsq_wq(&samples[OFFSET],NSAMP-OFFSET);
|
||||
if(result == rresult){
|
||||
printf("OK\n");
|
||||
} else {
|
||||
printf("sum mismatch: %lld != %lld\n",result,rresult);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
long long sumsq_wq_ref(signed short *in,int cnt){
|
||||
long long sum = 0;
|
||||
int i;
|
||||
|
||||
for(i=0;i<cnt;i++){
|
||||
sum += (long)in[i] * in[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
/* Intel SIMD (SSE2) implementations of Viterbi ACS butterflies
|
||||
for 64-state (k=7) convolutional code
|
||||
Copyright 2003 Phil Karn, KA9Q
|
||||
This code may be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
|
||||
void update_viterbi27_blk_sse2(struct v27 *vp,unsigned char syms[],int nbits) ;
|
||||
*/
|
||||
# SSE2 (128-bit integer SIMD) version
|
||||
# Requires Pentium 4 or better
|
||||
|
||||
# These are offsets into struct v27, defined in viterbi27.h
|
||||
.set DP,128
|
||||
.set OLDMETRICS,132
|
||||
.set NEWMETRICS,136
|
||||
.text
|
||||
.global update_viterbi27_blk_sse2,Branchtab27_sse2
|
||||
.type update_viterbi27_blk_sse2,@function
|
||||
.align 16
|
||||
|
||||
update_viterbi27_blk_sse2:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
pushl %edx
|
||||
pushl %ebx
|
||||
|
||||
movl 8(%ebp),%edx # edx = vp
|
||||
movl OLDMETRICS(%edx),%esi # esi -> old metrics
|
||||
movl NEWMETRICS(%edx),%edi # edi -> new metrics
|
||||
movl DP(%edx),%edx # edx -> decisions
|
||||
|
||||
1: movl 16(%ebp),%eax # eax = nbits
|
||||
decl %eax
|
||||
jl 2f # passed zero, we're done
|
||||
movl %eax,16(%ebp)
|
||||
|
||||
xorl %eax,%eax
|
||||
movl 12(%ebp),%ebx # ebx = syms
|
||||
movb (%ebx),%al
|
||||
movd %eax,%xmm6 # xmm6[0] = first symbol
|
||||
movb 1(%ebx),%al
|
||||
movd %eax,%xmm5 # xmm5[0] = second symbol
|
||||
addl $2,%ebx
|
||||
movl %ebx,12(%ebp)
|
||||
|
||||
punpcklbw %xmm6,%xmm6 # xmm6[1] = xmm6[0]
|
||||
punpcklbw %xmm5,%xmm5
|
||||
pshuflw $0,%xmm6,%xmm6 # copy low word to low 3
|
||||
pshuflw $0,%xmm5,%xmm5
|
||||
punpcklqdq %xmm6,%xmm6 # propagate to all 16
|
||||
punpcklqdq %xmm5,%xmm5
|
||||
# xmm6 now contains first symbol in each byte, xmm5 the second
|
||||
|
||||
movdqa thirtyones,%xmm7
|
||||
|
||||
# each invocation of this macro does 16 butterflies in parallel
|
||||
.MACRO butterfly GROUP
|
||||
# compute branch metrics
|
||||
movdqa Branchtab27_sse2+(16*\GROUP),%xmm4
|
||||
movdqa Branchtab27_sse2+32+(16*\GROUP),%xmm3
|
||||
pxor %xmm6,%xmm4
|
||||
pxor %xmm5,%xmm3
|
||||
|
||||
# compute 5-bit branch metric in xmm4 by adding the individual symbol metrics
|
||||
# This is okay for this
|
||||
# code because the worst-case metric spread (at high Eb/No) is only 120,
|
||||
# well within the range of our unsigned 8-bit path metrics, and even within
|
||||
# the range of signed 8-bit path metrics
|
||||
pavgb %xmm3,%xmm4
|
||||
psrlw $3,%xmm4
|
||||
|
||||
pand %xmm7,%xmm4
|
||||
|
||||
movdqa (16*\GROUP)(%esi),%xmm0 # Incoming path metric, high bit = 0
|
||||
movdqa ((16*\GROUP)+32)(%esi),%xmm3 # Incoming path metric, high bit = 1
|
||||
movdqa %xmm0,%xmm2
|
||||
movdqa %xmm3,%xmm1
|
||||
paddusb %xmm4,%xmm0 # note use of saturating arithmetic
|
||||
paddusb %xmm4,%xmm3 # this shouldn't be necessary, but why not?
|
||||
|
||||
# negate branch metrics
|
||||
pxor %xmm7,%xmm4
|
||||
paddusb %xmm4,%xmm1
|
||||
paddusb %xmm4,%xmm2
|
||||
|
||||
# Find survivors, leave in mm0,2
|
||||
pminub %xmm1,%xmm0
|
||||
pminub %xmm3,%xmm2
|
||||
# get decisions, leave in mm1,3
|
||||
pcmpeqb %xmm0,%xmm1
|
||||
pcmpeqb %xmm2,%xmm3
|
||||
|
||||
# interleave and store new branch metrics in mm0,2
|
||||
movdqa %xmm0,%xmm4
|
||||
punpckhbw %xmm2,%xmm0 # interleave second 16 new metrics
|
||||
punpcklbw %xmm2,%xmm4 # interleave first 16 new metrics
|
||||
movdqa %xmm0,(32*\GROUP+16)(%edi)
|
||||
movdqa %xmm4,(32*\GROUP)(%edi)
|
||||
|
||||
# interleave decisions & store
|
||||
movdqa %xmm1,%xmm4
|
||||
punpckhbw %xmm3,%xmm1
|
||||
punpcklbw %xmm3,%xmm4
|
||||
# work around bug in gas due to Intel doc error
|
||||
.byte 0x66,0x0f,0xd7,0xd9 # pmovmskb %xmm1,%ebx
|
||||
shll $16,%ebx
|
||||
.byte 0x66,0x0f,0xd7,0xc4 # pmovmskb %xmm4,%eax
|
||||
orl %eax,%ebx
|
||||
movl %ebx,(4*\GROUP)(%edx)
|
||||
.endm
|
||||
|
||||
# invoke macro 2 times for a total of 32 butterflies
|
||||
butterfly GROUP=0
|
||||
butterfly GROUP=1
|
||||
|
||||
addl $8,%edx # bump decision pointer
|
||||
|
||||
# See if we have to normalize. This requires an explanation. We don't want
|
||||
# our path metrics to exceed 255 on the *next* iteration. Since the
|
||||
# largest branch metric is 30, that means we don't want any to exceed 225
|
||||
# on *this* iteration. Rather than look them all, we just pick an arbitrary one
|
||||
# (the first) and see if it exceeds 225-120=105, where 120 is the experimentally-
|
||||
# determined worst-case metric spread for this code and branch metrics in the range 0-30.
|
||||
|
||||
# This is extremely conservative, and empirical testing at a variety of Eb/Nos might
|
||||
# show that a higher threshold could be used without affecting BER performance
|
||||
movl (%edi),%eax # extract first output metric
|
||||
andl $255,%eax
|
||||
cmp $105,%eax
|
||||
jle done # No, no need to normalize
|
||||
|
||||
# Normalize by finding smallest metric and subtracting it
|
||||
# from all metrics. We can't just pick an arbitrary small constant because
|
||||
# the minimum metric might be zero!
|
||||
movdqa (%edi),%xmm0
|
||||
movdqa %xmm0,%xmm4
|
||||
movdqa 16(%edi),%xmm1
|
||||
pminub %xmm1,%xmm4
|
||||
movdqa 32(%edi),%xmm2
|
||||
pminub %xmm2,%xmm4
|
||||
movdqa 48(%edi),%xmm3
|
||||
pminub %xmm3,%xmm4
|
||||
|
||||
# crunch down to single lowest metric
|
||||
movdqa %xmm4,%xmm5
|
||||
psrldq $8,%xmm5 # the count to psrldq is bytes, not bits!
|
||||
pminub %xmm5,%xmm4
|
||||
movdqa %xmm4,%xmm5
|
||||
psrlq $32,%xmm5
|
||||
pminub %xmm5,%xmm4
|
||||
movdqa %xmm4,%xmm5
|
||||
psrlq $16,%xmm5
|
||||
pminub %xmm5,%xmm4
|
||||
movdqa %xmm4,%xmm5
|
||||
psrlq $8,%xmm5
|
||||
pminub %xmm5,%xmm4 # now in lowest byte of %xmm4
|
||||
|
||||
punpcklbw %xmm4,%xmm4 # lowest 2 bytes
|
||||
pshuflw $0,%xmm4,%xmm4 # lowest 8 bytes
|
||||
punpcklqdq %xmm4,%xmm4 # all 16 bytes
|
||||
|
||||
# xmm4 now contains lowest metric in all 16 bytes
|
||||
# subtract it from every output metric
|
||||
psubusb %xmm4,%xmm0
|
||||
psubusb %xmm4,%xmm1
|
||||
psubusb %xmm4,%xmm2
|
||||
psubusb %xmm4,%xmm3
|
||||
movdqa %xmm0,(%edi)
|
||||
movdqa %xmm1,16(%edi)
|
||||
movdqa %xmm2,32(%edi)
|
||||
movdqa %xmm3,48(%edi)
|
||||
|
||||
done:
|
||||
# swap metrics
|
||||
movl %esi,%eax
|
||||
movl %edi,%esi
|
||||
movl %eax,%edi
|
||||
jmp 1b
|
||||
|
||||
2: movl 8(%ebp),%ebx # ebx = vp
|
||||
# stash metric pointers
|
||||
movl %esi,OLDMETRICS(%ebx)
|
||||
movl %edi,NEWMETRICS(%ebx)
|
||||
movl %edx,DP(%ebx) # stash incremented value of vp->dp
|
||||
popl %ebx
|
||||
popl %edx
|
||||
popl %edi
|
||||
popl %esi
|
||||
popl %ebp
|
||||
ret
|
||||
|
||||
.data
|
||||
.align 16
|
||||
|
||||
thirtyones:
|
||||
.byte 31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
/* Intel SIMD SSE2 implementation of Viterbi ACS butterflies
|
||||
for 256-state (k=9) convolutional code
|
||||
Copyright 2004 Phil Karn, KA9Q
|
||||
This code may be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
|
||||
void update_viterbi29_blk_sse2(struct v29 *vp,unsigned char *syms,int nbits) ;
|
||||
*/
|
||||
|
||||
# SSE2 (128-bit integer SIMD) version
|
||||
# Requires Pentium 4 or better
|
||||
# These are offsets into struct v29, defined in viterbi29.h
|
||||
.set DP,512
|
||||
.set OLDMETRICS,516
|
||||
.set NEWMETRICS,520
|
||||
|
||||
.text
|
||||
.global update_viterbi29_blk_sse2,Branchtab29_sse2
|
||||
.type update_viterbi29_blk_sse2,@function
|
||||
.align 16
|
||||
|
||||
update_viterbi29_blk_sse2:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
pushl %edx
|
||||
pushl %ebx
|
||||
|
||||
movl 8(%ebp),%edx # edx = vp
|
||||
movl OLDMETRICS(%edx),%esi # esi -> old metrics
|
||||
movl NEWMETRICS(%edx),%edi # edi -> new metrics
|
||||
movl DP(%edx),%edx # edx -> decisions
|
||||
|
||||
1: movl 16(%ebp),%eax # eax = nbits
|
||||
decl %eax
|
||||
jl 2f # passed zero, we're done
|
||||
movl %eax,16(%ebp)
|
||||
|
||||
xorl %eax,%eax
|
||||
movl 12(%ebp),%ebx # ebx = syms
|
||||
movb (%ebx),%al
|
||||
movd %eax,%xmm6 # xmm6[0] = first symbol
|
||||
movb 1(%ebx),%al
|
||||
movd %eax,%xmm5 # xmm5[0] = second symbol
|
||||
addl $2,%ebx
|
||||
movl %ebx,12(%ebp)
|
||||
|
||||
punpcklbw %xmm6,%xmm6 # xmm6[1] = xmm6[0]
|
||||
punpcklbw %xmm5,%xmm5
|
||||
movdqa thirtyones,%xmm7
|
||||
pshuflw $0,%xmm6,%xmm6 # copy low word to low 3
|
||||
pshuflw $0,%xmm5,%xmm5
|
||||
punpcklqdq %xmm6,%xmm6 # propagate to all 16
|
||||
punpcklqdq %xmm5,%xmm5
|
||||
# xmm6 now contains first symbol in each byte, xmm5 the second
|
||||
|
||||
movdqa thirtyones,%xmm7
|
||||
|
||||
# each invocation of this macro does 16 butterflies in parallel
|
||||
.MACRO butterfly GROUP
|
||||
# compute branch metrics
|
||||
movdqa Branchtab29_sse2+(16*\GROUP),%xmm4
|
||||
movdqa Branchtab29_sse2+128+(16*\GROUP),%xmm3
|
||||
pxor %xmm6,%xmm4
|
||||
pxor %xmm5,%xmm3
|
||||
pavgb %xmm3,%xmm4
|
||||
psrlw $3,%xmm4
|
||||
|
||||
pand %xmm7,%xmm4 # xmm4 contains branch metrics
|
||||
|
||||
movdqa (16*\GROUP)(%esi),%xmm0 # Incoming path metric, high bit = 0
|
||||
movdqa ((16*\GROUP)+128)(%esi),%xmm3 # Incoming path metric, high bit = 1
|
||||
movdqa %xmm0,%xmm2
|
||||
movdqa %xmm3,%xmm1
|
||||
paddusb %xmm4,%xmm0
|
||||
paddusb %xmm4,%xmm3
|
||||
|
||||
# invert branch metrics
|
||||
pxor %xmm7,%xmm4
|
||||
|
||||
paddusb %xmm4,%xmm1
|
||||
paddusb %xmm4,%xmm2
|
||||
|
||||
# Find survivors, leave in mm0,2
|
||||
pminub %xmm1,%xmm0
|
||||
pminub %xmm3,%xmm2
|
||||
# get decisions, leave in mm1,3
|
||||
pcmpeqb %xmm0,%xmm1
|
||||
pcmpeqb %xmm2,%xmm3
|
||||
|
||||
# interleave and store new branch metrics in mm0,2
|
||||
movdqa %xmm0,%xmm4
|
||||
punpckhbw %xmm2,%xmm0 # interleave second 16 new metrics
|
||||
punpcklbw %xmm2,%xmm4 # interleave first 16 new metrics
|
||||
movdqa %xmm0,(32*\GROUP+16)(%edi)
|
||||
movdqa %xmm4,(32*\GROUP)(%edi)
|
||||
|
||||
# interleave decisions & store
|
||||
movdqa %xmm1,%xmm4
|
||||
punpckhbw %xmm3,%xmm1
|
||||
punpcklbw %xmm3,%xmm4
|
||||
# work around bug in gas due to Intel doc error
|
||||
.byte 0x66,0x0f,0xd7,0xd9 # pmovmskb %xmm1,%ebx
|
||||
shll $16,%ebx
|
||||
.byte 0x66,0x0f,0xd7,0xc4 # pmovmskb %xmm4,%eax
|
||||
orl %eax,%ebx
|
||||
movl %ebx,(4*\GROUP)(%edx)
|
||||
.endm
|
||||
|
||||
# invoke macro 8 times for a total of 128 butterflies
|
||||
butterfly GROUP=0
|
||||
butterfly GROUP=1
|
||||
butterfly GROUP=2
|
||||
butterfly GROUP=3
|
||||
butterfly GROUP=4
|
||||
butterfly GROUP=5
|
||||
butterfly GROUP=6
|
||||
butterfly GROUP=7
|
||||
|
||||
addl $32,%edx # bump decision pointer
|
||||
|
||||
# see if we have to normalize
|
||||
movl (%edi),%eax # extract first output metric
|
||||
andl $255,%eax
|
||||
cmp $50,%eax # is it greater than 50?
|
||||
movl $0,%eax
|
||||
jle done # No, no need to normalize
|
||||
|
||||
# Normalize by finding smallest metric and subtracting it
|
||||
# from all metrics
|
||||
movdqa (%edi),%xmm0
|
||||
pminub 16(%edi),%xmm0
|
||||
pminub 32(%edi),%xmm0
|
||||
pminub 48(%edi),%xmm0
|
||||
pminub 64(%edi),%xmm0
|
||||
pminub 80(%edi),%xmm0
|
||||
pminub 96(%edi),%xmm0
|
||||
pminub 112(%edi),%xmm0
|
||||
pminub 128(%edi),%xmm0
|
||||
pminub 144(%edi),%xmm0
|
||||
pminub 160(%edi),%xmm0
|
||||
pminub 176(%edi),%xmm0
|
||||
pminub 192(%edi),%xmm0
|
||||
pminub 208(%edi),%xmm0
|
||||
pminub 224(%edi),%xmm0
|
||||
pminub 240(%edi),%xmm0
|
||||
|
||||
# crunch down to single lowest metric
|
||||
movdqa %xmm0,%xmm1
|
||||
psrldq $8,%xmm0 # the count to psrldq is bytes, not bits!
|
||||
pminub %xmm1,%xmm0
|
||||
movdqa %xmm0,%xmm1
|
||||
psrlq $32,%xmm0
|
||||
pminub %xmm1,%xmm0
|
||||
movdqa %xmm0,%xmm1
|
||||
psrlq $16,%xmm0
|
||||
pminub %xmm1,%xmm0
|
||||
movdqa %xmm0,%xmm1
|
||||
psrlq $8,%xmm0
|
||||
pminub %xmm1,%xmm0
|
||||
|
||||
punpcklbw %xmm0,%xmm0 # lowest 2 bytes
|
||||
pshuflw $0,%xmm0,%xmm0 # lowest 8 bytes
|
||||
punpcklqdq %xmm0,%xmm0 # all 16 bytes
|
||||
|
||||
# xmm0 now contains lowest metric in all 16 bytes
|
||||
# subtract it from every output metric
|
||||
movdqa (%edi),%xmm1
|
||||
psubusb %xmm0,%xmm1
|
||||
movdqa %xmm1,(%edi)
|
||||
movdqa 16(%edi),%xmm1
|
||||
psubusb %xmm0,%xmm1
|
||||
movdqa %xmm1,16(%edi)
|
||||
movdqa 32(%edi),%xmm1
|
||||
psubusb %xmm0,%xmm1
|
||||
movdqa %xmm1,32(%edi)
|
||||
movdqa 48(%edi),%xmm1
|
||||
psubusb %xmm0,%xmm1
|
||||
movdqa %xmm1,48(%edi)
|
||||
movdqa 64(%edi),%xmm1
|
||||
psubusb %xmm0,%xmm1
|
||||
movdqa %xmm1,64(%edi)
|
||||
movdqa 80(%edi),%xmm1
|
||||
psubusb %xmm0,%xmm1
|
||||
movdqa %xmm1,80(%edi)
|
||||
movdqa 96(%edi),%xmm1
|
||||
psubusb %xmm0,%xmm1
|
||||
movdqa %xmm1,96(%edi)
|
||||
movdqa 112(%edi),%xmm1
|
||||
psubusb %xmm0,%xmm1
|
||||
movdqa %xmm1,112(%edi)
|
||||
movdqa 128(%edi),%xmm1
|
||||
psubusb %xmm0,%xmm1
|
||||
movdqa %xmm1,128(%edi)
|
||||
movdqa 144(%edi),%xmm1
|
||||
psubusb %xmm0,%xmm1
|
||||
movdqa %xmm1,144(%edi)
|
||||
movdqa 160(%edi),%xmm1
|
||||
psubusb %xmm0,%xmm1
|
||||
movdqa %xmm1,160(%edi)
|
||||
movdqa 176(%edi),%xmm1
|
||||
psubusb %xmm0,%xmm1
|
||||
movdqa %xmm1,176(%edi)
|
||||
movdqa 192(%edi),%xmm1
|
||||
psubusb %xmm0,%xmm1
|
||||
movdqa %xmm1,192(%edi)
|
||||
movdqa 208(%edi),%xmm1
|
||||
psubusb %xmm0,%xmm1
|
||||
movdqa %xmm1,208(%edi)
|
||||
movdqa 224(%edi),%xmm1
|
||||
psubusb %xmm0,%xmm1
|
||||
movdqa %xmm1,224(%edi)
|
||||
movdqa 240(%edi),%xmm1
|
||||
psubusb %xmm0,%xmm1
|
||||
movdqa %xmm1,240(%edi)
|
||||
|
||||
done:
|
||||
# swap metrics
|
||||
movl %esi,%eax
|
||||
movl %edi,%esi
|
||||
movl %eax,%edi
|
||||
jmp 1b
|
||||
|
||||
2: movl 8(%ebp),%ebx # ebx = vp
|
||||
# stash metric pointers
|
||||
movl %esi,OLDMETRICS(%ebx)
|
||||
movl %edi,NEWMETRICS(%ebx)
|
||||
movl %edx,DP(%ebx) # stash incremented value of vp->dp
|
||||
popl %ebx
|
||||
popl %edx
|
||||
popl %edi
|
||||
popl %esi
|
||||
popl %ebp
|
||||
ret
|
||||
|
||||
.data
|
||||
.align 16
|
||||
thirtyones:
|
||||
.byte 31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31
|
||||
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
/* Intel SIMD (SSE) implementation of Viterbi ACS butterflies
|
||||
for 64-state (k=7) convolutional code
|
||||
Copyright 2001 Phil Karn, KA9Q
|
||||
This code may be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
|
||||
int update_viterbi27_blk_sse(struct v27 *vp,unsigned char syms[],int nbits) ;
|
||||
*/
|
||||
|
||||
# SSE (64-bit integer SIMD) version
|
||||
# Requires Pentium III or better
|
||||
|
||||
# These are offsets into struct v27, defined in viterbi27.h
|
||||
.set DP,128
|
||||
.set OLDMETRICS,132
|
||||
.set NEWMETRICS,136
|
||||
.text
|
||||
.global update_viterbi27_blk_sse,Branchtab27_sse
|
||||
.type update_viterbi27_blk_sse,@function
|
||||
.align 16
|
||||
|
||||
update_viterbi27_blk_sse:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
pushl %edx
|
||||
pushl %ebx
|
||||
|
||||
movl 8(%ebp),%edx # edx = vp
|
||||
movl OLDMETRICS(%edx),%esi # esi -> old metrics
|
||||
movl NEWMETRICS(%edx),%edi # edi -> new metrics
|
||||
movl DP(%edx),%edx # edx -> decisions
|
||||
|
||||
1: movl 16(%ebp),%eax # eax = nbits
|
||||
decl %eax
|
||||
jl 2f # passed zero, we're done
|
||||
movl %eax,16(%ebp)
|
||||
|
||||
xorl %eax,%eax
|
||||
movl 12(%ebp),%ebx # %ebx = syms
|
||||
movb (%ebx),%al
|
||||
movd %eax,%mm6 # mm6[0] = first symbol
|
||||
movb 1(%ebx),%al
|
||||
movd %eax,%mm5 # mm5[0] = second symbol
|
||||
addl $2,%ebx
|
||||
movl %ebx,12(%ebp)
|
||||
|
||||
punpcklbw %mm6,%mm6 # mm6[1] = mm6[0]
|
||||
punpcklbw %mm5,%mm5
|
||||
movq thirtyones,%mm7
|
||||
|
||||
pshufw $0,%mm6,%mm6 # copy low word to upper 3
|
||||
pshufw $0,%mm5,%mm5
|
||||
# mm6 now contains first symbol in each byte, mm5 the second
|
||||
|
||||
# each invocation of this macro does 8 butterflies in parallel
|
||||
.MACRO butterfly GROUP
|
||||
# compute branch metrics
|
||||
movq Branchtab27_sse+(8*\GROUP),%mm4
|
||||
movq Branchtab27_sse+32+(8*\GROUP),%mm3
|
||||
pxor %mm6,%mm4
|
||||
pxor %mm5,%mm3
|
||||
pavgb %mm3,%mm4 # mm4 contains branch metrics
|
||||
psrlw $3,%mm4
|
||||
pand %mm7,%mm4
|
||||
|
||||
movq (8*\GROUP)(%esi),%mm0 # Incoming path metric, high bit = 0
|
||||
movq ((8*\GROUP)+32)(%esi),%mm3 # Incoming path metric, high bit = 1
|
||||
movq %mm0,%mm2
|
||||
movq %mm3,%mm1
|
||||
paddusb %mm4,%mm0
|
||||
paddusb %mm4,%mm3
|
||||
|
||||
# invert branch metrics. This works only because they're 5 bits
|
||||
pxor %mm7,%mm4
|
||||
|
||||
paddusb %mm4,%mm1
|
||||
paddusb %mm4,%mm2
|
||||
|
||||
# Find survivors, leave in mm0,2
|
||||
pminub %mm1,%mm0
|
||||
pminub %mm3,%mm2
|
||||
# get decisions, leave in mm1,3
|
||||
pcmpeqb %mm0,%mm1
|
||||
pcmpeqb %mm2,%mm3
|
||||
|
||||
# interleave and store new branch metrics in mm0,2
|
||||
movq %mm0,%mm4
|
||||
punpckhbw %mm2,%mm0 # interleave second 8 new metrics
|
||||
punpcklbw %mm2,%mm4 # interleave first 8 new metrics
|
||||
movq %mm0,(16*\GROUP+8)(%edi)
|
||||
movq %mm4,(16*\GROUP)(%edi)
|
||||
|
||||
# interleave decisions, accumulate into %ebx
|
||||
movq %mm1,%mm4
|
||||
punpckhbw %mm3,%mm1
|
||||
punpcklbw %mm3,%mm4
|
||||
# Due to an error in the Intel instruction set ref (the register
|
||||
# fields are swapped), gas assembles pmovmskb incorrectly
|
||||
# See http://mail.gnu.org/pipermail/bug-gnu-utils/2000-August/002341.html
|
||||
.byte 0x0f,0xd7,0xc1 # pmovmskb %mm1,%eax
|
||||
shll $((16*\GROUP+8)&31),%eax
|
||||
orl %eax,%ebx
|
||||
.byte 0x0f,0xd7,0xc4 # pmovmskb %mm4,%eax
|
||||
shll $((16*\GROUP)&31),%eax
|
||||
orl %eax,%ebx
|
||||
.endm
|
||||
|
||||
# invoke macro 4 times for a total of 32 butterflies
|
||||
xorl %ebx,%ebx # clear decisions
|
||||
butterfly GROUP=0
|
||||
butterfly GROUP=1
|
||||
movl %ebx,(%edx) # stash first 32 decisions
|
||||
xorl %ebx,%ebx
|
||||
butterfly GROUP=2
|
||||
butterfly GROUP=3
|
||||
movl %ebx,4(%edx) # stash second 32 decisions
|
||||
|
||||
addl $8,%edx # bump decision pointer
|
||||
|
||||
# see if we have to normalize
|
||||
movl (%edi),%eax # extract first output metric
|
||||
andl $255,%eax
|
||||
cmpl $150,%eax # is it greater than 150?
|
||||
movl $0,%eax
|
||||
jle done # No, no need to normalize
|
||||
|
||||
# Normalize by finding smallest metric and subtracting it
|
||||
# from all metrics
|
||||
movq (%edi),%mm0
|
||||
pminub 8(%edi),%mm0
|
||||
pminub 16(%edi),%mm0
|
||||
pminub 24(%edi),%mm0
|
||||
pminub 32(%edi),%mm0
|
||||
pminub 40(%edi),%mm0
|
||||
pminub 48(%edi),%mm0
|
||||
pminub 56(%edi),%mm0
|
||||
# mm0 contains 8 smallest metrics
|
||||
# crunch down to single lowest metric
|
||||
movq %mm0,%mm1
|
||||
psrlq $32,%mm0
|
||||
pminub %mm1,%mm0
|
||||
movq %mm0,%mm1
|
||||
psrlq $16,%mm0
|
||||
pminub %mm1,%mm0
|
||||
movq %mm0,%mm1
|
||||
psrlq $8,%mm0
|
||||
pminub %mm1,%mm0
|
||||
punpcklbw %mm0,%mm0 # expand to all 8 bytes
|
||||
pshufw $0,%mm0,%mm0
|
||||
|
||||
# mm0 now contains lowest metric in all 8 bytes
|
||||
# subtract it from every output metric
|
||||
# Trashes %mm7
|
||||
.macro PSUBUSBM REG,MEM
|
||||
movq \MEM,%mm7
|
||||
psubusb \REG,%mm7
|
||||
movq %mm7,\MEM
|
||||
.endm
|
||||
|
||||
PSUBUSBM %mm0,(%edi)
|
||||
PSUBUSBM %mm0,8(%edi)
|
||||
PSUBUSBM %mm0,16(%edi)
|
||||
PSUBUSBM %mm0,24(%edi)
|
||||
PSUBUSBM %mm0,32(%edi)
|
||||
PSUBUSBM %mm0,40(%edi)
|
||||
PSUBUSBM %mm0,48(%edi)
|
||||
PSUBUSBM %mm0,56(%edi)
|
||||
|
||||
movd %mm0,%eax
|
||||
and $0xff,%eax
|
||||
|
||||
done: # swap metrics
|
||||
movl %esi,%eax
|
||||
movl %edi,%esi
|
||||
movl %eax,%edi
|
||||
jmp 1b
|
||||
|
||||
2: emms
|
||||
movl 8(%ebp),%ebx # ebx = vp
|
||||
# stash metric pointers
|
||||
movl %esi,OLDMETRICS(%ebx)
|
||||
movl %edi,NEWMETRICS(%ebx)
|
||||
movl %edx,DP(%ebx) # stash incremented value of vp->dp
|
||||
popl %ebx
|
||||
popl %edx
|
||||
popl %edi
|
||||
popl %esi
|
||||
popl %ebp
|
||||
|
||||
ret
|
||||
|
||||
.data
|
||||
|
||||
.align 16
|
||||
thirtyones:
|
||||
.byte 31,31,31,31,31,31,31,31
|
||||
|
||||
|
||||
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
/* Intel SIMD SSE implementation of Viterbi ACS butterflies
|
||||
for 256-state (k=9) convolutional code
|
||||
Copyright 2004 Phil Karn, KA9Q
|
||||
This code may be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
|
||||
void update_viterbi29_blk_sse(struct v29 *vp,unsigned char syms[],int nbits);
|
||||
*/
|
||||
# SSE (64-bit integer SIMD) version
|
||||
# Requires Pentium III or better
|
||||
# These are offsets into struct v29, defined in viterbi29.h
|
||||
.set DP,512
|
||||
.set OLDMETRICS,516
|
||||
.set NEWMETRICS,520
|
||||
.text
|
||||
.global update_viterbi29_blk_sse,Branchtab29_sse
|
||||
.type update_viterbi29_blk_sse,@function
|
||||
.align 16
|
||||
|
||||
update_viterbi29_blk_sse:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
pushl %edx
|
||||
pushl %ebx
|
||||
|
||||
movl 8(%ebp),%edx # edx = vp
|
||||
movl OLDMETRICS(%edx),%esi # esi -> old metrics
|
||||
movl NEWMETRICS(%edx),%edi # edi -> new metrics
|
||||
movl DP(%edx),%edx # edx -> decisions
|
||||
|
||||
1: movl 16(%ebp),%eax # eax = nbits
|
||||
decl %eax
|
||||
jl 2f # passed zero, we're done
|
||||
movl %eax,16(%ebp)
|
||||
|
||||
xorl %eax,%eax
|
||||
movl 12(%ebp),%ebx # ebx = syms
|
||||
movb (%ebx),%al
|
||||
movd %eax,%mm6 # mm6[0] = first symbol
|
||||
movb 1(%ebx),%al
|
||||
movd %eax,%mm5 # mm5[0] = second symbol
|
||||
addl $2,%ebx
|
||||
movl %ebx,12(%ebp)
|
||||
|
||||
punpcklbw %mm6,%mm6 # mm6[1] = mm6[0]
|
||||
punpcklbw %mm5,%mm5
|
||||
|
||||
movq thirtyones,%mm7
|
||||
pshufw $0,%mm6,%mm6 # copy low word to upper 3
|
||||
pshufw $0,%mm5,%mm5
|
||||
# mm6 now contains first symbol in each byte, mm5 the second
|
||||
|
||||
# each invocation of this macro does 8 butterflies in parallel
|
||||
.MACRO butterfly GROUP
|
||||
# compute branch metrics
|
||||
movq Branchtab29_sse+(8*\GROUP),%mm4
|
||||
movq Branchtab29_sse+128+(8*\GROUP),%mm3
|
||||
pxor %mm6,%mm4
|
||||
pxor %mm5,%mm3
|
||||
pavgb %mm3,%mm4 # mm4 contains branch metrics
|
||||
psrlw $3,%mm4
|
||||
pand %mm7,%mm4
|
||||
|
||||
movq (8*\GROUP)(%esi),%mm0 # Incoming path metric, high bit = 0
|
||||
movq ((8*\GROUP)+128)(%esi),%mm3 # Incoming path metric, high bit = 1
|
||||
movq %mm0,%mm2
|
||||
movq %mm3,%mm1
|
||||
paddusb %mm4,%mm0
|
||||
paddusb %mm4,%mm3
|
||||
|
||||
# invert branch metrics. This works only because they're 5 bits
|
||||
pxor %mm7,%mm4
|
||||
|
||||
paddusb %mm4,%mm1
|
||||
paddusb %mm4,%mm2
|
||||
|
||||
# Find survivors, leave in mm0,2
|
||||
pminub %mm1,%mm0
|
||||
pminub %mm3,%mm2
|
||||
# get decisions, leave in mm1,3
|
||||
pcmpeqb %mm0,%mm1
|
||||
pcmpeqb %mm2,%mm3
|
||||
|
||||
# interleave and store new branch metrics in mm0,2
|
||||
movq %mm0,%mm4
|
||||
punpckhbw %mm2,%mm0 # interleave second 8 new metrics
|
||||
punpcklbw %mm2,%mm4 # interleave first 8 new metrics
|
||||
movq %mm0,(16*\GROUP+8)(%edi)
|
||||
movq %mm4,(16*\GROUP)(%edi)
|
||||
|
||||
# interleave decisions, accumulate into %ebx
|
||||
movq %mm1,%mm4
|
||||
punpckhbw %mm3,%mm1
|
||||
punpcklbw %mm3,%mm4
|
||||
# Due to an error in the Intel instruction set ref (the register
|
||||
# fields are swapped), gas assembles pmovmskb incorrectly
|
||||
# See http://mail.gnu.org/pipermail/bug-gnu-utils/2000-August/002341.html
|
||||
.byte 0x0f,0xd7,0xc1 # pmovmskb %mm1,%eax
|
||||
shll $((16*\GROUP+8)&31),%eax
|
||||
orl %eax,%ebx
|
||||
.byte 0x0f,0xd7,0xc4 # pmovmskb %mm4,%eax
|
||||
shll $((16*\GROUP)&31),%eax
|
||||
orl %eax,%ebx
|
||||
.endm
|
||||
|
||||
# invoke macro 16 times for a total of 128 butterflies
|
||||
xorl %ebx,%ebx # clear decisions
|
||||
butterfly GROUP=0
|
||||
butterfly GROUP=1
|
||||
movl %ebx,(%edx) # stash first 32 decisions
|
||||
xorl %ebx,%ebx
|
||||
butterfly GROUP=2
|
||||
butterfly GROUP=3
|
||||
movl %ebx,4(%edx) # stash second 32 decisions
|
||||
xorl %ebx,%ebx # clear decisions
|
||||
butterfly GROUP=4
|
||||
butterfly GROUP=5
|
||||
movl %ebx,8(%edx) # stash first 32 decisions
|
||||
xorl %ebx,%ebx
|
||||
butterfly GROUP=6
|
||||
butterfly GROUP=7
|
||||
movl %ebx,12(%edx) # stash second 32 decisions
|
||||
xorl %ebx,%ebx # clear decisions
|
||||
butterfly GROUP=8
|
||||
butterfly GROUP=9
|
||||
movl %ebx,16(%edx) # stash first 32 decisions
|
||||
xorl %ebx,%ebx
|
||||
butterfly GROUP=10
|
||||
butterfly GROUP=11
|
||||
movl %ebx,20(%edx) # stash second 32 decisions
|
||||
xorl %ebx,%ebx # clear decisions
|
||||
butterfly GROUP=12
|
||||
butterfly GROUP=13
|
||||
movl %ebx,24(%edx) # stash first 32 decisions
|
||||
xorl %ebx,%ebx
|
||||
butterfly GROUP=14
|
||||
butterfly GROUP=15
|
||||
movl %ebx,28(%edx) # stash second 32 decisions
|
||||
|
||||
addl $32,%edx # bump decision pointer
|
||||
|
||||
# see if we have to normalize
|
||||
movl (%edi),%eax # extract first output metric
|
||||
andl $255,%eax
|
||||
cmp $50,%eax # is it greater than 50?
|
||||
movl $0,%eax
|
||||
jle done # No, no need to normalize
|
||||
|
||||
# Normalize by finding smallest metric and subtracting it
|
||||
# from all metrics
|
||||
movq (%edi),%mm0
|
||||
pminub 8(%edi),%mm0
|
||||
pminub 16(%edi),%mm0
|
||||
pminub 24(%edi),%mm0
|
||||
pminub 32(%edi),%mm0
|
||||
pminub 40(%edi),%mm0
|
||||
pminub 48(%edi),%mm0
|
||||
pminub 56(%edi),%mm0
|
||||
pminub 64(%edi),%mm0
|
||||
pminub 72(%edi),%mm0
|
||||
pminub 80(%edi),%mm0
|
||||
pminub 88(%edi),%mm0
|
||||
pminub 96(%edi),%mm0
|
||||
pminub 104(%edi),%mm0
|
||||
pminub 112(%edi),%mm0
|
||||
pminub 120(%edi),%mm0
|
||||
pminub 128(%edi),%mm0
|
||||
pminub 136(%edi),%mm0
|
||||
pminub 144(%edi),%mm0
|
||||
pminub 152(%edi),%mm0
|
||||
pminub 160(%edi),%mm0
|
||||
pminub 168(%edi),%mm0
|
||||
pminub 176(%edi),%mm0
|
||||
pminub 184(%edi),%mm0
|
||||
pminub 192(%edi),%mm0
|
||||
pminub 200(%edi),%mm0
|
||||
pminub 208(%edi),%mm0
|
||||
pminub 216(%edi),%mm0
|
||||
pminub 224(%edi),%mm0
|
||||
pminub 232(%edi),%mm0
|
||||
pminub 240(%edi),%mm0
|
||||
pminub 248(%edi),%mm0
|
||||
# mm0 contains 8 smallest metrics
|
||||
# crunch down to single lowest metric
|
||||
movq %mm0,%mm1
|
||||
psrlq $32,%mm0
|
||||
pminub %mm1,%mm0
|
||||
movq %mm0,%mm1
|
||||
psrlq $16,%mm0
|
||||
pminub %mm1,%mm0
|
||||
movq %mm0,%mm1
|
||||
psrlq $8,%mm0
|
||||
pminub %mm1,%mm0
|
||||
movq 8(%edi),%mm1 # reload
|
||||
punpcklbw %mm0,%mm0 # expand to all 8 bytes
|
||||
pshufw $0,%mm0,%mm0
|
||||
|
||||
# mm0 now contains lowest metric in all 8 bytes
|
||||
# subtract it from every output metric
|
||||
# Trashes %mm7
|
||||
.macro PSUBUSBM REG,MEM
|
||||
movq \MEM,%mm7
|
||||
psubusb \REG,%mm7
|
||||
movq %mm7,\MEM
|
||||
.endm
|
||||
|
||||
PSUBUSBM %mm0,(%edi)
|
||||
PSUBUSBM %mm0,8(%edi)
|
||||
PSUBUSBM %mm0,16(%edi)
|
||||
PSUBUSBM %mm0,24(%edi)
|
||||
PSUBUSBM %mm0,32(%edi)
|
||||
PSUBUSBM %mm0,40(%edi)
|
||||
PSUBUSBM %mm0,48(%edi)
|
||||
PSUBUSBM %mm0,56(%edi)
|
||||
PSUBUSBM %mm0,64(%edi)
|
||||
PSUBUSBM %mm0,72(%edi)
|
||||
PSUBUSBM %mm0,80(%edi)
|
||||
PSUBUSBM %mm0,88(%edi)
|
||||
PSUBUSBM %mm0,96(%edi)
|
||||
PSUBUSBM %mm0,104(%edi)
|
||||
PSUBUSBM %mm0,112(%edi)
|
||||
PSUBUSBM %mm0,120(%edi)
|
||||
PSUBUSBM %mm0,128(%edi)
|
||||
PSUBUSBM %mm0,136(%edi)
|
||||
PSUBUSBM %mm0,144(%edi)
|
||||
PSUBUSBM %mm0,152(%edi)
|
||||
PSUBUSBM %mm0,160(%edi)
|
||||
PSUBUSBM %mm0,168(%edi)
|
||||
PSUBUSBM %mm0,176(%edi)
|
||||
PSUBUSBM %mm0,184(%edi)
|
||||
PSUBUSBM %mm0,192(%edi)
|
||||
PSUBUSBM %mm0,200(%edi)
|
||||
PSUBUSBM %mm0,208(%edi)
|
||||
PSUBUSBM %mm0,216(%edi)
|
||||
PSUBUSBM %mm0,224(%edi)
|
||||
PSUBUSBM %mm0,232(%edi)
|
||||
PSUBUSBM %mm0,240(%edi)
|
||||
PSUBUSBM %mm0,248(%edi)
|
||||
|
||||
done:
|
||||
# swap metrics
|
||||
movl %esi,%eax
|
||||
movl %edi,%esi
|
||||
movl %eax,%edi
|
||||
jmp 1b
|
||||
|
||||
2: emms
|
||||
movl 8(%ebp),%ebx # ebx = vp
|
||||
# stash metric pointers
|
||||
movl %esi,OLDMETRICS(%ebx)
|
||||
movl %edi,NEWMETRICS(%ebx)
|
||||
movl %edx,DP(%ebx) # stash incremented value of vp->dp
|
||||
popl %ebx
|
||||
popl %edx
|
||||
popl %edi
|
||||
popl %esi
|
||||
popl %ebp
|
||||
ret
|
||||
|
||||
.data
|
||||
.align 8
|
||||
thirtyones:
|
||||
.byte 31,31,31,31,31,31,31,31
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/* Compute the sum of the squares of a vector of signed shorts
|
||||
|
||||
* Copyright 2004 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "fec.h"
|
||||
|
||||
unsigned long long sumsq_port(signed short *,int);
|
||||
|
||||
#ifdef __i386__
|
||||
unsigned long long sumsq_mmx(signed short *,int);
|
||||
unsigned long long sumsq_sse(signed short *,int);
|
||||
unsigned long long sumsq_sse2(signed short *,int);
|
||||
#endif
|
||||
|
||||
#ifdef __VEC__
|
||||
unsigned long long sumsq_av(signed short *,int);
|
||||
#endif
|
||||
|
||||
unsigned long long sumsq(signed short *in,int cnt){
|
||||
switch(Cpu_mode){
|
||||
case PORT:
|
||||
default:
|
||||
return sumsq_port(in,cnt);
|
||||
#ifdef __i386__
|
||||
case SSE:
|
||||
case MMX:
|
||||
return sumsq_mmx(in,cnt);
|
||||
case SSE2:
|
||||
return sumsq_sse2(in,cnt);
|
||||
#endif
|
||||
|
||||
#ifdef __VEC__
|
||||
case ALTIVEC:
|
||||
return sumsq_av(in,cnt);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/* Compute the sum of the squares of a vector of signed shorts
|
||||
|
||||
* This is the Altivec SIMD version. It's a little hairy because Altivec
|
||||
* does not do 64-bit operations directly, so we have to accumulate separate
|
||||
* 32-bit sums and carries
|
||||
|
||||
* Copyright 2004 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
|
||||
#include "fec.h"
|
||||
|
||||
unsigned long long sumsq_av(signed short *in,int cnt){
|
||||
long long sum;
|
||||
vector signed short x;
|
||||
vector unsigned int sums,carries,s1,s2;
|
||||
int pad;
|
||||
union { vector unsigned char cv; vector unsigned int iv; unsigned int w[4]; unsigned char c[16];} s;
|
||||
|
||||
carries = sums = (vector unsigned int)(0);
|
||||
if((pad = (int)in & 15)!=0){
|
||||
/* Load unaligned leading word */
|
||||
x = vec_perm(vec_ld(0,in),(vector signed short)(0),vec_lvsl(0,in));
|
||||
if(cnt < 8){ /* Shift right to chop stuff beyond end of short block */
|
||||
s.c[15] = (8-cnt)<<4;
|
||||
x = vec_sro(x,s.cv);
|
||||
}
|
||||
sums = (vector unsigned int)vec_msum(x,x,(vector signed int)(0));
|
||||
in += 8-pad/2;
|
||||
cnt -= 8-pad/2;
|
||||
}
|
||||
/* Everything is now aligned, rip through most of the block */
|
||||
while(cnt >= 8){
|
||||
x = vec_ld(0,in);
|
||||
/* A single vec_msum cannot overflow, but we have to sum it with
|
||||
* the earlier terms separately to handle the carries
|
||||
* The cast to unsigned is OK because squares are always positive
|
||||
*/
|
||||
s1 = (vector unsigned int)vec_msum(x,x,(vector signed int)(0));
|
||||
carries = vec_add(carries,vec_addc(sums,s1));
|
||||
sums = vec_add(sums,s1);
|
||||
in += 8;
|
||||
cnt -= 8;
|
||||
}
|
||||
/* Handle trailing fragment, if any */
|
||||
if(cnt > 0){
|
||||
x = vec_ld(0,in);
|
||||
s.c[15] = (8-cnt)<<4;
|
||||
x = vec_sro(x,s.cv);
|
||||
s1 = (vector unsigned int)vec_msum(x,x,(vector signed int)(0));
|
||||
carries = vec_add(carries,vec_addc(sums,s1));
|
||||
sums = vec_add(sums,s1);
|
||||
}
|
||||
/* Combine 4 sub-sums and carries */
|
||||
s.c[15] = 64; /* Shift right two 32-bit words */
|
||||
s1 = vec_sro(sums,s.cv);
|
||||
s2 = vec_sro(carries,s.cv);
|
||||
carries = vec_add(carries,vec_addc(sums,s1));
|
||||
sums = vec_add(sums,s1);
|
||||
carries = vec_add(carries,s2);
|
||||
|
||||
s.c[15] = 32; /* Shift right one 32-bit word */
|
||||
s1 = vec_sro(sums,s.cv);
|
||||
s2 = vec_sro(carries,s.cv);
|
||||
carries = vec_add(carries,vec_addc(sums,s1));
|
||||
sums = vec_add(sums,s1);
|
||||
carries = vec_add(carries,s2);
|
||||
|
||||
/* Extract sum and carries from right-hand words and combine into result */
|
||||
s.iv = sums;
|
||||
sum = s.w[3];
|
||||
|
||||
s.iv = carries;
|
||||
sum += (long long)s.w[3] << 32;
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/* Compute the sum of the squares of a vector of signed shorts
|
||||
|
||||
* MMX-assisted version (also used on SSE)
|
||||
|
||||
* The SSE2 and MMX assist routines both operate on multiples of
|
||||
* 8 words; they differ only in their alignment requirements (8 bytes
|
||||
* for MMX, 16 bytes for SSE2)
|
||||
|
||||
* Copyright 2004 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser Public License (LGPL)
|
||||
*/
|
||||
|
||||
long long sumsq_mmx_assist(signed short *,int);
|
||||
|
||||
long long sumsq_mmx(signed short *in,int cnt){
|
||||
long long sum = 0;
|
||||
|
||||
/* Handle stuff before the next 8-byte boundary */
|
||||
while(((int)in & 7) != 0 && cnt != 0){
|
||||
sum += (long)in[0] * in[0];
|
||||
in++;
|
||||
cnt--;
|
||||
}
|
||||
sum += sumsq_mmx_assist(in,cnt);
|
||||
in += cnt & ~7;
|
||||
cnt &= 7;
|
||||
|
||||
/* Handle up to 7 words at end */
|
||||
while(cnt != 0){
|
||||
sum += (long)in[0] * in[0];
|
||||
in++;
|
||||
cnt--;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
# MMX assist routines for sumsq
|
||||
# Copyright 2001 Phil Karn, KA9Q
|
||||
# May be used under the terms of the GNU Public License (GPL)
|
||||
|
||||
.text
|
||||
|
||||
# Evaluate sum of squares of signed 16-bit input samples
|
||||
# long long sumsq_mmx_assist(signed short *in,int cnt);
|
||||
.global sumsq_mmx_assist
|
||||
.type sumsq_mmx_assist,@function
|
||||
.align 16
|
||||
sumsq_mmx_assist:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
pushl %esi
|
||||
pushl %ecx
|
||||
pushl %ebx
|
||||
|
||||
movl 8(%ebp),%esi
|
||||
movl 12(%ebp),%ecx
|
||||
xor %eax,%eax
|
||||
xor %edx,%edx
|
||||
|
||||
# Since 4 * 32767**2 < 2**32, we can accumulate two at a time
|
||||
1: subl $8,%ecx
|
||||
jl 2f
|
||||
movq (%esi),%mm0 # S0 S1 S2 S3
|
||||
pmaddwd %mm0,%mm0 # (S0^2+S1^2) (S2^2+S3^2)
|
||||
movq 8(%esi),%mm6 # S4 S5 S6 S7
|
||||
pmaddwd %mm6,%mm6 # (S4^2+S5^2) (S6^2+S7^2)
|
||||
paddd %mm6,%mm0 # (S0^2+S1^2+S4^2+S5^2)(S2^2+S3^2+S6^2+S7^2)
|
||||
movd %mm0,%ebx
|
||||
addl %ebx,%eax
|
||||
adcl $0,%edx
|
||||
psrlq $32,%mm0
|
||||
movd %mm0,%ebx
|
||||
addl %ebx,%eax
|
||||
adcl $0,%edx
|
||||
addl $16,%esi
|
||||
jmp 1b
|
||||
|
||||
2: emms
|
||||
popl %ebx
|
||||
popl %ecx
|
||||
popl %esi
|
||||
popl %ebp
|
||||
ret
|
||||
|
||||
# Evaluate sum of squares of signed 16-bit input samples
|
||||
# long sumsq_wd_mmx_assist(signed short *in,int cnt);
|
||||
# Quick version, only safe for small numbers of small input values...
|
||||
.global sumsq_wd_mmx_assist
|
||||
.type sumsq_wd_mmx_assist,@function
|
||||
.align 16
|
||||
sumsq_wd_mmx_assist:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
pushl %esi
|
||||
|
||||
movl 8(%ebp),%esi
|
||||
movl 12(%ebp),%ecx
|
||||
pxor %mm2,%mm2 # zero sum
|
||||
|
||||
1: subl $8,%ecx
|
||||
jl 2f
|
||||
movq (%esi),%mm0 # S0 S1 S2 S3
|
||||
pmaddwd %mm0,%mm0 # (S0*S0+S1*S1) (S2*S2+S3*S3)
|
||||
movq 8(%esi),%mm1
|
||||
pmaddwd %mm1,%mm1
|
||||
paddd %mm1,%mm2
|
||||
paddd %mm0,%mm2 # accumulate
|
||||
|
||||
addl $16,%esi
|
||||
jmp 1b
|
||||
|
||||
2: movd %mm2,%eax # even sum
|
||||
psrlq $32,%mm2
|
||||
movd %mm2,%edx # odd sum
|
||||
addl %edx,%eax
|
||||
emms
|
||||
popl %esi
|
||||
popl %ebp
|
||||
ret
|
||||
@@ -0,0 +1,16 @@
|
||||
/* Compute the sum of the squares of a vector of signed shorts
|
||||
|
||||
* Portable C version
|
||||
* Copyright 2004 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser General Public License (LGPL)
|
||||
*/
|
||||
|
||||
unsigned long long sumsq_port(signed short *in,int cnt){
|
||||
long long sum = 0;
|
||||
int i;
|
||||
|
||||
for(i=0;i<cnt;i++){
|
||||
sum += (int)in[i] * (int)in[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/* Compute the sum of the squares of a vector of signed shorts
|
||||
|
||||
* The SSE2 and MMX assist routines both operate on multiples of
|
||||
* 8 words; they differ only in their alignment requirements (8 bytes
|
||||
* for MMX, 16 bytes for SSE2)
|
||||
|
||||
* Copyright 2004 Phil Karn, KA9Q
|
||||
* May be used under the terms of the GNU Lesser Public License (LGPL)
|
||||
*/
|
||||
|
||||
long long sumsq_sse2_assist(signed short *,int);
|
||||
|
||||
long long sumsq_sse2(signed short *in,int cnt){
|
||||
long long sum = 0;
|
||||
|
||||
/* Handle stuff before the next 8-byte boundary */
|
||||
while(((int)in & 15) != 0 && cnt != 0){
|
||||
sum += (long)in[0] * in[0];
|
||||
in++;
|
||||
cnt--;
|
||||
}
|
||||
sum += sumsq_sse2_assist(in,cnt);
|
||||
in += cnt & ~7;
|
||||
cnt &= 7;
|
||||
|
||||
/* Handle up to 7 trailing words */
|
||||
while(cnt != 0){
|
||||
sum += (long)in[0] * in[0];
|
||||
in++;
|
||||
cnt--;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
# SSE2 assist routines for sumsq
|
||||
# Copyright 2001 Phil Karn, KA9Q
|
||||
# May be used under the terms of the GNU Public License (GPL)
|
||||
|
||||
.text
|
||||
# Evaluate sum of squares of signed 16-bit input samples
|
||||
# long long sumsq_sse2_assist(signed short *in,int cnt);
|
||||
.global sumsq_sse2_assist
|
||||
.type sumsq_sse2_assist,@function
|
||||
.align 16
|
||||
sumsq_sse2_assist:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
pushl %esi
|
||||
pushl %ecx
|
||||
|
||||
movl 8(%ebp),%esi
|
||||
movl 12(%ebp),%ecx
|
||||
pxor %xmm2,%xmm2 # zero sum
|
||||
movaps low,%xmm3 # load mask
|
||||
|
||||
1: subl $8,%ecx
|
||||
jl 2f
|
||||
movaps (%esi),%xmm0 # S0 S1 S2 S3 S4 S5 S6 S7
|
||||
pmaddwd %xmm0,%xmm0 # (S0*S0+S1*S1) (S2*S2+S3*S3) (S4*S4+S5*S5) (S6*S6+S7*S7)
|
||||
movaps %xmm0,%xmm1
|
||||
pand %xmm3,%xmm1 # (S0*S0+S1*S1) 0 (S4*S4+S5*S5) 0
|
||||
paddq %xmm1,%xmm2 # sum even-numbered dwords
|
||||
psrlq $32,%xmm0 # (S2*S2+S3*S3) 0 (S6*S6+S7*S7) 0
|
||||
paddq %xmm0,%xmm2 # sum odd-numbered dwords
|
||||
addl $16,%esi
|
||||
jmp 1b
|
||||
|
||||
2: movaps %xmm2,%xmm0
|
||||
psrldq $8,%xmm0
|
||||
paddq %xmm2,%xmm0 # combine 64-bit sums
|
||||
|
||||
movd %xmm0,%eax # low 32 bits of sum
|
||||
psrldq $4,%xmm0
|
||||
movd %xmm0,%edx # high 32 bits of sum
|
||||
|
||||
popl %ecx
|
||||
popl %esi
|
||||
popl %ebp
|
||||
ret
|
||||
|
||||
.data
|
||||
.align 16
|
||||
low: .byte 255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user