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