commit ba6f1bde2285e3f2bfbcdb8e35cbf37fb0fcf6e3 Author: Uladzimir Karpenka Date: Mon Jun 22 11:39:50 2026 +0300 init commit diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..e9e5baf --- /dev/null +++ b/INSTALL @@ -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 + + + diff --git a/README b/README new file mode 100644 index 0000000..d14edf6 --- /dev/null +++ b/README @@ -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) diff --git a/ccsds.h b/ccsds.h new file mode 100644 index 0000000..ae65468 --- /dev/null +++ b/ccsds.h @@ -0,0 +1,5 @@ +typedef unsigned char data_t; +extern unsigned char Taltab[],Tal1tab[]; +#define NN 255 +#define NROOTS 32 + diff --git a/char.h b/char.h new file mode 100644 index 0000000..25efd65 --- /dev/null +++ b/char.h @@ -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) + + + + diff --git a/code.h b/code.h new file mode 100644 index 0000000..f1cc3c7 --- /dev/null +++ b/code.h @@ -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 diff --git a/config.cache b/config.cache new file mode 100644 index 0000000..1a007aa --- /dev/null +++ b/config.cache @@ -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} diff --git a/config.guess b/config.guess new file mode 100755 index 0000000..ba66165 --- /dev/null +++ b/config.guess @@ -0,0 +1,1371 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. + +timestamp='2001-04-20' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Written by Per Bothner . +# Please send patches to . +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 93, 94, 95, 96, 97, 98, 99, 2000 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + + +dummy=dummy-$$ +trap 'rm -f $dummy.c $dummy.o $dummy.rel $dummy; exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int dummy(){}" > $dummy.c + for c in cc gcc c89 ; do + ($c $dummy.c -c -o $dummy.o) >/dev/null 2>&1 + if test $? = 0 ; then + CC_FOR_BUILD="$c"; break + fi + done + rm -f $dummy.c $dummy.o $dummy.rel + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 8/24/94.) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # Netbsd (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # Determine the machine/vendor (is the vendor relevant). + case "${UNAME_MACHINE}" in + amiga) machine=m68k-unknown ;; + arm32) machine=arm-unknown ;; + atari*) machine=m68k-atari ;; + sun3*) machine=m68k-sun ;; + mac68k) machine=m68k-apple ;; + macppc) machine=powerpc-apple ;; + hp3[0-9][05]) machine=m68k-hp ;; + ibmrt|romp-ibm) machine=romp-ibm ;; + *) machine=${UNAME_MACHINE}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE}" in + i386|sparc|amiga|arm*|hp300|mvme68k|vax|atari|luna68k|mac68k|news68k|next68k|pc532|sun3*|x68k) + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit 0 ;; + alpha:OSF1:*:*) + if test $UNAME_RELEASE = "V4.0"; then + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + fi + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + cat <$dummy.s + .data +\$Lformat: + .byte 37,100,45,37,120,10,0 # "%d-%x\n" + + .text + .globl main + .align 4 + .ent main +main: + .frame \$30,16,\$26,0 + ldgp \$29,0(\$27) + .prologue 1 + .long 0x47e03d80 # implver \$0 + lda \$2,-1 + .long 0x47e20c21 # amask \$2,\$1 + lda \$16,\$Lformat + mov \$0,\$17 + not \$1,\$18 + jsr \$26,printf + ldgp \$29,0(\$26) + mov 0,\$16 + jsr \$26,exit + .end main +EOF + $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null + if test "$?" = 0 ; then + case `./$dummy` in + 0-0) + UNAME_MACHINE="alpha" + ;; + 1-0) + UNAME_MACHINE="alphaev5" + ;; + 1-1) + UNAME_MACHINE="alphaev56" + ;; + 1-101) + UNAME_MACHINE="alphapca56" + ;; + 2-303) + UNAME_MACHINE="alphaev6" + ;; + 2-307) + UNAME_MACHINE="alphaev67" + ;; + esac + fi + rm -f $dummy.s $dummy + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit 0 ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit 0;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit 0 ;; + arc64:OpenBSD:*:*) + echo mips64el-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + arc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + hkmips:OpenBSD:*:*) + echo mips-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + pmax:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sgi:OpenBSD:*:*) + echo mips-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + wgrisc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit 0;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + atari*:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit 0 ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit 0 ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit 0 ;; + sun3*:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit 0 ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD $dummy.c -o $dummy \ + && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + case "${HPUX_REV}" in + 11.[0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + esac ;; + esac + fi ;; + esac + if [ "${HP_ARCH}" = "" ]; then + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy` + if test -z "$HP_ARCH"; then HP_ARCH=hppa; fi + rm -f $dummy.c $dummy + fi ;; + esac + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + *9??*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + hppa*:OpenBSD:*:*) + echo hppa-unknown-openbsd + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*X-MP:*:*:*) + echo xmp-cray-unicos + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} + exit 0 ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ + exit 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3D:*:*:*) + echo alpha-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY-2:*:*:*) + echo cray2-cray-unicos + exit 0 ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + exit 0 ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit 0 ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit 0 ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit 0 ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i386-pc-interix + exit 0 ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit 0 ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux + exit 0 ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + mips:Linux:*:*) + cat >$dummy.c < /* for printf() prototype */ +int main (int argc, char *argv[]) { +#else +int main (argc, argv) int argc; char *argv[]; { +#endif +#ifdef __MIPSEB__ + printf ("%s-unknown-linux-gnu\n", argv[1]); +#endif +#ifdef __MIPSEL__ + printf ("%sel-unknown-linux-gnu\n", argv[1]); +#endif + return 0; +} +EOF + $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + ;; + ppc:Linux:*:*) + # Determine Lib Version + cat >$dummy.c < +#if defined(__GLIBC__) +extern char __libc_version[]; +extern char __libc_release[]; +#endif +main(argc, argv) + int argc; + char *argv[]; +{ +#if defined(__GLIBC__) + printf("%s %s\n", __libc_version, __libc_release); +#else + printf("unknown\n"); +#endif + return 0; +} +EOF + LIBC="" + $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null + if test "$?" = 0 ; then + ./$dummy | grep 1\.99 > /dev/null + if test "$?" = 0 ; then LIBC="libc1" ; fi + fi + rm -f $dummy.c $dummy + echo powerpc-unknown-linux-gnu${LIBC} + exit 0 ;; + alpha:Linux:*:*) + cat <$dummy.s + .data + \$Lformat: + .byte 37,100,45,37,120,10,0 # "%d-%x\n" + .text + .globl main + .align 4 + .ent main + main: + .frame \$30,16,\$26,0 + ldgp \$29,0(\$27) + .prologue 1 + .long 0x47e03d80 # implver \$0 + lda \$2,-1 + .long 0x47e20c21 # amask \$2,\$1 + lda \$16,\$Lformat + mov \$0,\$17 + not \$1,\$18 + jsr \$26,printf + ldgp \$29,0(\$26) + mov 0,\$16 + jsr \$26,exit + .end main +EOF + LIBC="" + $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null + if test "$?" = 0 ; then + case `./$dummy` in + 0-0) UNAME_MACHINE="alpha" ;; + 1-0) UNAME_MACHINE="alphaev5" ;; + 1-1) UNAME_MACHINE="alphaev56" ;; + 1-101) UNAME_MACHINE="alphapca56" ;; + 2-303) UNAME_MACHINE="alphaev6" ;; + 2-307) UNAME_MACHINE="alphaev67" ;; + esac + objdump --private-headers $dummy | \ + grep ld.so.1 > /dev/null + if test "$?" = 0 ; then + LIBC="libc1" + fi + fi + rm -f $dummy.s $dummy + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit 0 ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit 0 ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit 0 ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit 0 ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit 0 ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + ld_supported_emulations=`cd /; ld --help 2>&1 \ + | sed -ne '/supported emulations:/!d + s/[ ][ ]*/ /g + s/.*supported emulations: *// + s/ .*// + p'` + case "$ld_supported_emulations" in + i*86linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit 0 + ;; + elf_i*86) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + i*86coff) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit 0 + ;; + esac + # Either a pre-BFD a.out linker (linux-gnuoldld) + # or one that does not give us useful --help. + # GCC wants to distinguish between linux-gnuoldld and linux-gnuaout. + # If ld does not provide *any* "supported emulations:" + # that means it is gnuoldld. + test -z "$ld_supported_emulations" && echo "${UNAME_MACHINE}-pc-linux-gnuoldld" && exit 0 + case "${UNAME_MACHINE}" in + i*86) + VENDOR=pc; + ;; + *) + VENDOR=unknown; + ;; + esac + # Determine whether the default compiler is a.out or elf + cat >$dummy.c < +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif +#ifdef __ELF__ +# ifdef __GLIBC__ +# if __GLIBC__ >= 2 + printf ("%s-${VENDOR}-linux-gnu\n", argv[1]); +# else + printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]); +# endif +# else + printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]); +# endif +#else + printf ("%s-${VENDOR}-linux-gnuaout\n", argv[1]); +#endif + return 0; +} +EOF + $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 + ;; +# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. earlier versions +# are messed up and put the nodename in both sysname and nodename. + i*86:DYNIX/ptx:4*:*) + echo i386-sequent-sysv4 + exit 0 ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit 0 ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit 0 ;; + i*86:*:5:7*) + # Fixed at (any) Pentium or better + UNAME_MACHINE=i586 + if [ ${UNAME_SYSTEM} = "UnixWare" ] ; then + echo ${UNAME_MACHINE}-sco-sysv${UNAME_RELEASE}uw${UNAME_VERSION} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE} + fi + exit 0 ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')` + (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit 0 ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + M68*:*:R3V[567]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3${OS_REL} && exit 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + PENTIUM:CPunix:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit 0 ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit 0 ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit 0 ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit 0 ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit 0 ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Darwin:*:*) + echo `uname -p`-apple-darwin${UNAME_RELEASE} + exit 0 ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + if test "${UNAME_MACHINE}" = "x86pc"; then + UNAME_MACHINE=pc + fi + echo `uname -p`-${UNAME_MACHINE}-nto-qnx + exit 0 ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit 0 ;; + NSR-[KW]:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit 0 ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit 0 ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit 0 ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit 0 ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit 0 ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit 0 ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit 0 ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit 0 ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit 0 ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit 0 ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit 0 ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm -f $dummy.c $dummy && exit 0 +rm -f $dummy.c $dummy + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config.h b/config.h new file mode 100644 index 0000000..210ac56 --- /dev/null +++ b/config.h @@ -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 header file. */ +#define HAVE_GETOPT_H 1 + +/* Define if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define if you have the c library (-lc). */ +#define HAVE_LIBC 1 diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..f0a5c51 --- /dev/null +++ b/config.h.in @@ -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 header file. */ +#undef HAVE_GETOPT_H + +/* Define if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define if you have the header file. */ +#undef HAVE_STDIO_H + +/* Define if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define if you have the c library (-lc). */ +#undef HAVE_LIBC diff --git a/config.log b/config.log new file mode 100644 index 0000000..793b61e --- /dev/null +++ b/config.log @@ -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 '' + 766 | #include "confdefs.h" + +++ |+#include + 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 '' + 1101 | #include + +++ |+#include + 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 '' + 1101 | #include + +++ |+#include + 1102 | /* Override any gcc2 internal prototype to avoid an error. */ diff --git a/config.status b/config.status new file mode 100755 index 0000000..f339152 --- /dev/null +++ b/config.status @@ -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 < conftest.out + rm -f conftest.in + mv conftest.out conftest.in + + cat > conftest.frag < conftest.out + rm -f conftest.in + mv conftest.out conftest.in + + cat > conftest.frag < 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 diff --git a/config.sub b/config.sub new file mode 100755 index 0000000..a06a480 --- /dev/null +++ b/config.sub @@ -0,0 +1,1362 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. + +timestamp='2001-04-20' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Please send patches to . +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit 0;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | storm-chaos* | os2-emx*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + tahoe | i860 | ia64 | m32r | m68k | m68000 | m88k | ns32k | arc \ + | arm | arme[lb] | arm[bl]e | armv[2345] | armv[345][lb] | strongarm | xscale \ + | pyramid | mn10200 | mn10300 | tron | a29k \ + | 580 | i960 | h8300 \ + | x86 | ppcbe | mipsbe | mipsle | shbe | shle \ + | hppa | hppa1.0 | hppa1.1 | hppa2.0 | hppa2.0w | hppa2.0n \ + | hppa64 \ + | alpha | alphaev[4-8] | alphaev56 | alphapca5[67] \ + | alphaev6[78] \ + | we32k | ns16k | clipper | i370 | sh | sh[34] \ + | powerpc | powerpcle \ + | 1750a | dsp16xx | pdp10 | pdp11 \ + | mips16 | mips64 | mipsel | mips64el \ + | mips64orion | mips64orionel | mipstx39 | mipstx39el \ + | mips64vr4300 | mips64vr4300el | mips64vr4100 | mips64vr4100el \ + | mips64vr5000 | miprs64vr5000el | mcore | s390 | s390x \ + | sparc | sparclet | sparclite | sparc64 | sparcv9 | sparcv9b \ + | v850 | c4x \ + | thumb | d10v | d30v | fr30 | avr | openrisc | tic80 \ + | pj | pjl | h8500) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | z8k | v70 | w65) + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + # FIXME: clean up the formatting here. + vax-* | tahoe-* | i*86-* | i860-* | ia64-* | m32r-* | m68k-* | m68000-* \ + | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | c[123]* \ + | arm-* | armbe-* | armle-* | armv*-* | strongarm-* | xscale-* \ + | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \ + | power-* | none-* | 580-* | cray2-* | h8300-* | h8500-* | i960-* \ + | xmp-* | ymp-* \ + | x86-* | ppcbe-* | mipsbe-* | mipsle-* | shbe-* | shle-* \ + | hppa-* | hppa1.0-* | hppa1.1-* | hppa2.0-* | hppa2.0w-* \ + | hppa2.0n-* | hppa64-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphapca5[67]-* \ + | alphaev6[78]-* \ + | we32k-* | cydra-* | ns16k-* | pn-* | np1-* | xps100-* \ + | clipper-* | orion-* \ + | sparclite-* | pdp10-* | pdp11-* | sh-* | powerpc-* | powerpcle-* \ + | sparc64-* | sparcv9-* | sparcv9b-* | sparc86x-* \ + | mips16-* | mips64-* | mipsel-* \ + | mips64el-* | mips64orion-* | mips64orionel-* \ + | mips64vr4100-* | mips64vr4100el-* | mips64vr4300-* | mips64vr4300el-* \ + | mipstx39-* | mipstx39el-* | mcore-* \ + | f30[01]-* | f700-* | s390-* | s390x-* | sv1-* | t3e-* \ + | [cjt]90-* \ + | m88110-* | m680[01234]0-* | m683?2-* | m68360-* | z8k-* | d10v-* \ + | thumb-* | v850-* | d30v-* | tic30-* | tic80-* | c30-* | fr30-* \ + | bs2000-* | tic54x-* | c54x-* | x86_64-* | pj-* | pjl-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | ymp) + basic_machine=ymp-cray + os=-unicos + ;; + cray2) + basic_machine=cray2-cray + os=-unicos + ;; + [cjt]90) + basic_machine=${basic_machine}-cray + os=-unicos + ;; + crds | unos) + basic_machine=m68k-crds + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mipsel*-linux*) + basic_machine=mipsel-unknown + os=-linux-gnu + ;; + mips*-linux*) + basic_machine=mips-unknown + os=-linux-gnu + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + mmix*) + basic_machine=mmix-knuth + os=-mmixware + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon) + basic_machine=i686-pc + ;; + pentiumii | pentium2) + basic_machine=i686-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sparclite-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=t3e-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xmp) + basic_machine=xmp-cray + os=-unicos + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + mips) + if [ x$os = x-linux-gnu ]; then + basic_machine=mips-unknown + else + basic_machine=mips-mips + fi + ;; + romp) + basic_machine=romp-ibm + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh3 | sh4) + basic_machine=sh-unknown + ;; + sparc | sparcv9 | sparcv9b) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + c4x*) + basic_machine=c4x-none + os=-coff + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \ + | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* | -os2*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto*) + os=-nto-qnx + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -vxsim* | -vxworks*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/configure b/configure new file mode 100755 index 0000000..370324c --- /dev/null +++ b/configure @@ -0,0 +1,1513 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.13 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.13" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=viterbi27.c + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +ac_exeext= +ac_objext=o +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + +SO_NAME=3 +VERSION=3.0.0 + + + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:534: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:564: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_prog_rejected=no + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test -z "$CC"; then + case "`uname -s`" in + *win32* | *WIN32*) + # Extract the first word of "cl", so it can be a program name with args. +set dummy cl; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:615: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="cl" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + ;; + esac + fi + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:647: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext << EOF + +#line 658 "configure" +#include "confdefs.h" + +main(){return(0);} +EOF +if { (eval echo configure:663: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:689: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:694: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes +else + GCC= +fi + +ac_test_CFLAGS="${CFLAGS+set}" +ac_save_CFLAGS="$CFLAGS" +CFLAGS= +echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:722: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi + +if test $GCC != "yes" +then + { echo "configure: error: Need GNU C compiler" 1>&2; exit 1; } +fi +echo $ac_n "checking for malloc in -lc""... $ac_c" 1>&6 +echo "configure:758: checking for malloc in -lc" >&5 +ac_lib_var=`echo c'_'malloc | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lc $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo c | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + + +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:806: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:827: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:844: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -nologo -E" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:861: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + +for ac_hdr in getopt.h stdio.h stdlib.h memory.h string.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:889: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:899: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + +if test -z "$HAVE_stdio.h" +then + { echo "configure: error: Need stdio.h!" 1>&2; exit 1; } +fi +if test -z "$HAVE_stdlib.h" +then + { echo "configure: error: Need stdlib.h!" 1>&2; exit 1; } +fi +if test -z "$HAVE_stdlib.h" +then + { echo "configure: error: Need memory.h!" 1>&2; exit 1; } +fi +if test -z "$HAVE_string.h" +then + { echo "configure: error: Need string.h" 1>&2; exit 1; } +fi + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + + +# Do some error checking and defaulting for the host and target type. +# The inputs are: +# configure --host=HOST --target=TARGET --build=BUILD NONOPT +# +# The rules are: +# 1. You are not allowed to specify --host, --target, and nonopt at the +# same time. +# 2. Host defaults to nonopt. +# 3. If nonopt is not specified, then host defaults to the current host, +# as determined by config.guess. +# 4. Target and build default to nonopt. +# 5. If nonopt is not specified, then target and build default to host. + +# The aliases save the names the user supplied, while $host etc. +# will get canonicalized. +case $host---$target---$nonopt in +NONE---*---* | *---NONE---* | *---*---NONE) ;; +*) { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } ;; +esac + + +# Make sure we can run config.sub. +if ${CONFIG_SHELL-/bin/sh} $ac_config_sub sun4 >/dev/null 2>&1; then : +else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; } +fi + +echo $ac_n "checking host system type""... $ac_c" 1>&6 +echo "configure:989: checking host system type" >&5 + +host_alias=$host +case "$host_alias" in +NONE) + case $nonopt in + NONE) + if host_alias=`${CONFIG_SHELL-/bin/sh} $ac_config_guess`; then : + else { echo "configure: error: can not guess host type; you must specify one" 1>&2; exit 1; } + fi ;; + *) host_alias=$nonopt ;; + esac ;; +esac + +host=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $host_alias` +host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` +echo "$ac_t""$host" 1>&6 + +echo $ac_n "checking target system type""... $ac_c" 1>&6 +echo "configure:1010: checking target system type" >&5 + +target_alias=$target +case "$target_alias" in +NONE) + case $nonopt in + NONE) target_alias=$host_alias ;; + *) target_alias=$nonopt ;; + esac ;; +esac + +target=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $target_alias` +target_cpu=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +target_vendor=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +target_os=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` +echo "$ac_t""$target" 1>&6 + +echo $ac_n "checking build system type""... $ac_c" 1>&6 +echo "configure:1028: checking build system type" >&5 + +build_alias=$build +case "$build_alias" in +NONE) + case $nonopt in + NONE) build_alias=$host_alias ;; + *) build_alias=$nonopt ;; + esac ;; +esac + +build=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $build_alias` +build_cpu=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +build_vendor=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +build_os=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` +echo "$ac_t""$build" 1>&6 + +test "$host_alias" != "$target_alias" && + test "$program_prefix$program_suffix$program_transform_name" = \ + NONENONEs,x,x, && + program_prefix=${target_alias}- + +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 + + + + + + +for ac_func in getopt_long memset memmove +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1093: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1121: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +fi +done + + +trap '' 1 2 15 +cat > confcache <<\EOF +# 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. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +DEFS=-DHAVE_CONFIG_H + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS </dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# 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} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --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=$srcdir + +trap 'rm -fr `echo "makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@SHELL@%$SHELL%g +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@FFLAGS@%$FFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@SO_NAME@%$SO_NAME%g +s%@VERSION@%$VERSION%g +s%@CC@%$CC%g +s%@CPP@%$CPP%g +s%@host@%$host%g +s%@host_alias@%$host_alias%g +s%@host_cpu@%$host_cpu%g +s%@host_vendor@%$host_vendor%g +s%@host_os@%$host_os%g +s%@target@%$target%g +s%@target_alias@%$target_alias%g +s%@target_cpu@%$target_cpu%g +s%@target_vendor@%$target_vendor%g +s%@target_os@%$target_os%g +s%@build@%$build%g +s%@build_alias@%$build_alias%g +s%@build_cpu@%$build_cpu%g +s%@build_vendor@%$build_vendor%g +s%@build_os@%$build_os%g +s%@SH_LIB@%$SH_LIB%g +s%@REBIND@%$REBIND%g +s%@MLIBS@%$MLIBS%g +s%@ARCH_OPTION@%$ARCH_OPTION%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# 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 +EOF + +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +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 +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +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 + +EOF + +# Transform confdefs.h into a sed script conftest.vals that substitutes +# the proper values into config.h.in to produce config.h. And first: +# Protect against being on the right side of a sed subst in config.status. +# Protect against being in an unquoted here document in config.status. +rm -f conftest.vals +cat > conftest.hdr <<\EOF +s/[\\&%]/\\&/g +s%[\\$`]%\\&%g +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp +s%ac_d%ac_u%gp +s%ac_u%ac_e%gp +EOF +sed -n -f conftest.hdr confdefs.h > conftest.vals +rm -f conftest.hdr + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >> conftest.vals <<\EOF +s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */% +EOF + +# Break up conftest.vals because some shells have a limit on +# the size of here documents, and old seds have small limits too. + +rm -f conftest.tail +while : +do + ac_lines=`grep -c . conftest.vals` + # grep -c gives empty output for an empty file on some AIX systems. + if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi + # Write a limited-size here document to conftest.frag. + echo ' cat > conftest.frag <> $CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS + echo 'CEOF + sed -f conftest.frag conftest.in > conftest.out + rm -f conftest.in + mv conftest.out conftest.in +' >> $CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail + rm -f conftest.vals + mv conftest.tail conftest.vals +done +rm -f conftest.vals + +cat >> $CONFIG_STATUS <<\EOF + 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 + +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..bcd062e --- /dev/null +++ b/configure.in @@ -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) diff --git a/cpu_features.s b/cpu_features.s new file mode 100644 index 0000000..ef4ba4e --- /dev/null +++ b/cpu_features.s @@ -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 + \ No newline at end of file diff --git a/cpu_mode_generic.c b/cpu_mode_generic.c new file mode 100644 index 0000000..7771924 --- /dev/null +++ b/cpu_mode_generic.c @@ -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; +} diff --git a/cpu_mode_ppc.c b/cpu_mode_ppc.c new file mode 100644 index 0000000..0071558 --- /dev/null +++ b/cpu_mode_ppc.c @@ -0,0 +1,40 @@ +/* Determine CPU support for SIMD on Power PC + * Copyright 2004 Phil Karn, KA9Q + */ +#include +#include "fec.h" +#ifdef __VEC__ +#include +#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]); +} diff --git a/cpu_mode_x86.c b/cpu_mode_x86.c new file mode 100644 index 0000000..d040416 --- /dev/null +++ b/cpu_mode_x86.c @@ -0,0 +1,35 @@ +/* Determine CPU support for SIMD + * Copyright 2004 Phil Karn, KA9Q + */ +#include +#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]); +} diff --git a/decode_rs.c b/decode_rs.c new file mode 100644 index 0000000..d7f97b3 --- /dev/null +++ b/decode_rs.c @@ -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 +#endif + +#include + +#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 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 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) 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 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 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 +#endif + +#include + +#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; +} diff --git a/decode_rs_ccsds.c b/decode_rs_ccsds.c new file mode 100644 index 0000000..0e246b4 --- /dev/null +++ b/decode_rs_ccsds.c @@ -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 0){ + /* Convert from conventional to dual basis */ + for(i=0;i +#endif + +#include + +#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; +} diff --git a/decode_rs_int.c b/decode_rs_int.c new file mode 100644 index 0000000..1ef1a1f --- /dev/null +++ b/decode_rs_int.c @@ -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 +#endif + +#include + +#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; +} diff --git a/dotprod.c b/dotprod.c new file mode 100644 index 0000000..b3be913 --- /dev/null +++ b/dotprod.c @@ -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 +#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 + } +} + + diff --git a/dotprod.h b/dotprod.h new file mode 100644 index 0000000..6b62b70 --- /dev/null +++ b/dotprod.h @@ -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]; +}; diff --git a/dotprod_av.c b/dotprod_av.c new file mode 100644 index 0000000..1f70471 --- /dev/null +++ b/dotprod_av.c @@ -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 +#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;jcoeffs[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]; +} + + diff --git a/dotprod_mmx.c b/dotprod_mmx.c new file mode 100644 index 0000000..c516afe --- /dev/null +++ b/dotprod_mmx.c @@ -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 +#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;jcoeffs[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); +} + diff --git a/dotprod_mmx_assist.s b/dotprod_mmx_assist.s new file mode 100644 index 0000000..25deffd --- /dev/null +++ b/dotprod_mmx_assist.s @@ -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 diff --git a/dotprod_port.c b/dotprod_port.c new file mode 100644 index 0000000..ef635ec --- /dev/null +++ b/dotprod_port.c @@ -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 +#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;jcoeffs[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;ilen;i++){ + corr += (long)a[i] * dp->coeffs[i]; + } + return corr; +} + + diff --git a/dotprod_sse2.c b/dotprod_sse2.c new file mode 100644 index 0000000..1fddd18 --- /dev/null +++ b/dotprod_sse2.c @@ -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 +#include +#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;jcoeffs[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); +} diff --git a/dotprod_sse2_assist.s b/dotprod_sse2_assist.s new file mode 100644 index 0000000..47348fa --- /dev/null +++ b/dotprod_sse2_assist.s @@ -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 diff --git a/dsp.3 b/dsp.3 new file mode 100644 index 0000000..e9794da --- /dev/null +++ b/dsp.3 @@ -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) diff --git a/dtest.c b/dtest.c new file mode 100644 index 0000000..394cb03 --- /dev/null +++ b/dtest.c @@ -0,0 +1,99 @@ +/* Test dot-product function */ + +#include +#include +#include +#include +#include "config.h" +#ifdef HAVE_GETOPT_H +#include +#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); +} diff --git a/encode.c b/encode.c new file mode 100644 index 0000000..98424c0 --- /dev/null +++ b/encode.c @@ -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; +} diff --git a/encode_rs.c b/encode_rs.c new file mode 100644 index 0000000..0649094 --- /dev/null +++ b/encode_rs.c @@ -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 + +#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) 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 +#include "fixed.h" +#ifdef __VEC__ +#include +#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 +#include +#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 + +#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" + +} diff --git a/encode_rs_int.c b/encode_rs_int.c new file mode 100644 index 0000000..3c9ce78 --- /dev/null +++ b/encode_rs_int.c @@ -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 + +#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" + +} diff --git a/exercise.c b/exercise.c new file mode 100644 index 0000000..8ae008c --- /dev/null +++ b/exercise.c @@ -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 +#include +#include + +#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 +#include +#include +#include +#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<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 +} diff --git a/fano.h b/fano.h new file mode 100644 index 0000000..9148ddf --- /dev/null +++ b/fano.h @@ -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); + + diff --git a/fanotest.c b/fanotest.c new file mode 100644 index 0000000..a5d1d06 --- /dev/null +++ b/fanotest.c @@ -0,0 +1,178 @@ +#include +#include +#include +#include +#include +#include +#include +#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 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 +#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, +}; + diff --git a/fec.h b/fec.h new file mode 100644 index 0000000..fae198a --- /dev/null +++ b/fec.h @@ -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_ */ + + + diff --git a/fec.h~ b/fec.h~ new file mode 100644 index 0000000..fb11b4c --- /dev/null +++ b/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_ */ + + + diff --git a/fixed.h b/fixed.h new file mode 100644 index 0000000..0ff27b2 --- /dev/null +++ b/fixed.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 + diff --git a/gen_ccsds.c b/gen_ccsds.c new file mode 100644 index 0000000..e1e2e26 --- /dev/null +++ b/gen_ccsds.c @@ -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 +#include +#include +#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); +} diff --git a/gen_ccsds_tal.c b/gen_ccsds_tal.c new file mode 100644 index 0000000..fc75503 --- /dev/null +++ b/gen_ccsds_tal.c @@ -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 +#include + +#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< +#include +#include +#include +#include +#include +#include +#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 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 1){ + for(i=0;i 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 1){ + for(i=0;i +#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; +} diff --git a/init_rs.h b/init_rs.h new file mode 100644 index 0000000..2b2ae98 --- /dev/null +++ b/init_rs.h @@ -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<= (1<= (1<= ((1<mm = symsize; + rs->nn = (1<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;inn;i++){ + rs->index_of[sr] = i; + rs->alpha_to[i] = sr; + sr <<= 1; + if(sr & (1<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:; + +} diff --git a/init_rs_char.c b/init_rs_char.c new file mode 100644 index 0000000..a51099a --- /dev/null +++ b/init_rs_char.c @@ -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 + +#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; +} diff --git a/init_rs_int.c b/init_rs_int.c new file mode 100644 index 0000000..a6036c2 --- /dev/null +++ b/init_rs_int.c @@ -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 + +#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; +} diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..e9de238 --- /dev/null +++ b/install-sh @@ -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 diff --git a/int.h b/int.h new file mode 100644 index 0000000..46e865d --- /dev/null +++ b/int.h @@ -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) + + diff --git a/lesser.txt b/lesser.txt new file mode 100644 index 0000000..b1e3f5a --- /dev/null +++ b/lesser.txt @@ -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. + + + Copyright (C) + + 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/makefile b/makefile new file mode 100644 index 0000000..a7cc2f8 --- /dev/null +++ b/makefile @@ -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 + diff --git a/makefile.in b/makefile.in new file mode 100644 index 0000000..521f55a --- /dev/null +++ b/makefile.in @@ -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 + diff --git a/metrics.c b/metrics.c new file mode 100644 index 0000000..e2c808c --- /dev/null +++ b/metrics.c @@ -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 +#include +#include +#include + +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]); + } + } +} diff --git a/mmxbfly27.s b/mmxbfly27.s new file mode 100644 index 0000000..87d92c6 --- /dev/null +++ b/mmxbfly27.s @@ -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 diff --git a/mmxbfly29.s b/mmxbfly29.s new file mode 100644 index 0000000..0db215e --- /dev/null +++ b/mmxbfly29.s @@ -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 diff --git a/peak_mmx_assist.s b/peak_mmx_assist.s new file mode 100644 index 0000000..dae831f --- /dev/null +++ b/peak_mmx_assist.s @@ -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 + diff --git a/peak_sse2_assist.s b/peak_sse2_assist.s new file mode 100644 index 0000000..1dee3a8 --- /dev/null +++ b/peak_sse2_assist.s @@ -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 diff --git a/peak_sse_assist.s b/peak_sse_assist.s new file mode 100644 index 0000000..ea6fce8 --- /dev/null +++ b/peak_sse_assist.s @@ -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 diff --git a/peaktest.c b/peaktest.c new file mode 100644 index 0000000..fa4b280 --- /dev/null +++ b/peaktest.c @@ -0,0 +1,38 @@ +/* Verify correctness of the peak routine + * Copyright 2004 Phil Karn, KA9Q + */ +#include +#include +#include + +/* 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 +#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 + } +} diff --git a/peakval_av.c b/peakval_av.c new file mode 100644 index 0000000..ae54c10 --- /dev/null +++ b/peakval_av.c @@ -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]; +} diff --git a/peakval_mmx.c b/peakval_mmx.c new file mode 100644 index 0000000..436fe88 --- /dev/null +++ b/peakval_mmx.c @@ -0,0 +1,34 @@ +/* Wrapper for the MMX version of peakval + * Copyright 2004 Phil Karn, KA9Q + */ + +#include + +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; +} diff --git a/peakval_mmx_assist.s b/peakval_mmx_assist.s new file mode 100644 index 0000000..553cb79 --- /dev/null +++ b/peakval_mmx_assist.s @@ -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 + diff --git a/peakval_port.c b/peakval_port.c new file mode 100644 index 0000000..07ab316 --- /dev/null +++ b/peakval_port.c @@ -0,0 +1,16 @@ +/* Portable C version of peakval + * Copyright 2004 Phil Karn, KA9Q + */ +#include +#include "fec.h" +int peakval_port(signed short *b,int len){ + int peak = 0; + int a,i; + + for(i=0;i peak) + peak = a; + } + return peak; +} diff --git a/peakval_sse.c b/peakval_sse.c new file mode 100644 index 0000000..9868b7f --- /dev/null +++ b/peakval_sse.c @@ -0,0 +1,35 @@ +/* IA-32 SSE version of peakval + * Copyright 2004 Phil Karn, KA9Q + */ + +#include +#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; +} diff --git a/peakval_sse2.c b/peakval_sse2.c new file mode 100644 index 0000000..79d9059 --- /dev/null +++ b/peakval_sse2.c @@ -0,0 +1,34 @@ +/* Portable C version of peakval + * Copyright 2004 Phil Karn, KA9Q + */ +#include +#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; +} diff --git a/peakval_sse2_assist.s b/peakval_sse2_assist.s new file mode 100644 index 0000000..c7a58e7 --- /dev/null +++ b/peakval_sse2_assist.s @@ -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 diff --git a/peakval_sse_assist.s b/peakval_sse_assist.s new file mode 100644 index 0000000..827c800 --- /dev/null +++ b/peakval_sse_assist.s @@ -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 diff --git a/rs-common.h b/rs-common.h new file mode 100644 index 0000000..e64eb39 --- /dev/null +++ b/rs-common.h @@ -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<= rs->nn) { + x -= rs->nn; + x = (x >> rs->mm) + (x & rs->nn); + } + return x; +} diff --git a/rs.3 b/rs.3 new file mode 100644 index 0000000..5d71503 --- /dev/null +++ b/rs.3 @@ -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. + diff --git a/rs.c b/rs.c new file mode 100644 index 0000000..400b421 --- /dev/null +++ b/rs.c @@ -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 +#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) + 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 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 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; +} diff --git a/rs.h b/rs.h new file mode 100644 index 0000000..dfebb11 --- /dev/null +++ b/rs.h @@ -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); diff --git a/rs_speedtest.c b/rs_speedtest.c new file mode 100644 index 0000000..858258d --- /dev/null +++ b/rs_speedtest.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#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<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;isymsize) - 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 +#include +#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; +} diff --git a/sim.h b/sim.h new file mode 100644 index 0000000..91f2dc8 --- /dev/null +++ b/sim.h @@ -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_ */ + + + diff --git a/simd-viterbi.3 b/simd-viterbi.3 new file mode 100644 index 0000000..171600a --- /dev/null +++ b/simd-viterbi.3 @@ -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). + + diff --git a/simtest.c b/simtest.c new file mode 100644 index 0000000..1cdf9ae --- /dev/null +++ b/simtest.c @@ -0,0 +1,37 @@ +#include +#include +#include + +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); +} diff --git a/sqtest.c b/sqtest.c new file mode 100644 index 0000000..b2abb09 --- /dev/null +++ b/sqtest.c @@ -0,0 +1,42 @@ +/* Verify correctness of the sum-of-square routines */ +#include +#include +#include + +/* 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 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 diff --git a/sse2bfly29.s b/sse2bfly29.s new file mode 100644 index 0000000..ca659d2 --- /dev/null +++ b/sse2bfly29.s @@ -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 + diff --git a/ssebfly27.s b/ssebfly27.s new file mode 100644 index 0000000..32a57fb --- /dev/null +++ b/ssebfly27.s @@ -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 + + + diff --git a/ssebfly29.s b/ssebfly29.s new file mode 100644 index 0000000..a6d26cb --- /dev/null +++ b/ssebfly29.s @@ -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 + + diff --git a/sumsq.c b/sumsq.c new file mode 100644 index 0000000..9ed6a39 --- /dev/null +++ b/sumsq.c @@ -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 +#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 + } +} diff --git a/sumsq_av.c b/sumsq_av.c new file mode 100644 index 0000000..53c6acf --- /dev/null +++ b/sumsq_av.c @@ -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; +} + diff --git a/sumsq_mmx.c b/sumsq_mmx.c new file mode 100644 index 0000000..e766831 --- /dev/null +++ b/sumsq_mmx.c @@ -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; +} diff --git a/sumsq_mmx_assist.s b/sumsq_mmx_assist.s new file mode 100644 index 0000000..b3bac66 --- /dev/null +++ b/sumsq_mmx_assist.s @@ -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 diff --git a/sumsq_port.c b/sumsq_port.c new file mode 100644 index 0000000..6d0b4c1 --- /dev/null +++ b/sumsq_port.c @@ -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 +#include +#include +#include +#include "config.h" +#ifdef HAVE_GETOPT_H +#include +#endif +#include "fec.h" + +#if HAVE_GETOPT_LONG +struct option Options[] = { + {"frame-length",1,NULL,'l'}, + {"frame-count",1,NULL,'n'}, + {"verbose",0,NULL,'v'}, + {"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'}, + {NULL}, +}; +#endif + +int Verbose = 0; + +int main(int argc,char *argv[]){ + signed short *buf; + int i,d,trial,trials=10000; + int bufsize = 2048; + long long port_sum,simd_sum; + time_t t; + int timetrials=0; + + find_cpu_mode(); + time(&t); + srandom(t); + +#if HAVE_GETOPT_LONG + while((d = getopt_long(argc,argv,"vapmstl:n:T",Options,NULL)) != EOF){ +#else + while((d = getopt(argc,argv,"vapmstl:n:T")) != 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 'l': + bufsize = atoi(optarg); + break; + case 'n': + trials = atoi(optarg); + break; + case 'v': + Verbose++; + break; + case 'T': + timetrials++; + break; + } + } + + buf = (signed short *)calloc(bufsize,sizeof(signed short)); + if(timetrials){ + for(trial=0;trial +#include +#include +#include +#include +#include "fec.h" +#include "code.h" + +// Portable version of 32-bit parity +static inline int parity(int x){ + x ^= x >> 16; + x ^= x >> 8; + x ^= x >> 4; + x ^= x >> 2; + return (x ^ (x >> 1)) & 1; +} + + + +typedef union { uint32_t w[1<<(K-6)]; uint8_t c[1<<(K-4)];} decision_t; +typedef union { uint32_t w[1<<(K-1)]; } metric_t; + +static union branchtab224 { uint32_t w[1<<(K-2)]; } Branchtab224[2] __attribute__ ((aligned(16))); + +/* State info for instance of Viterbi decoder */ +struct v224 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + decision_t *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + decision_t *decisions; /* Beginning of decisions for block */ +}; + + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi224(void *p,int starting_state){ + struct v224 *vp = p; + int i; + + if(p == NULL) + return -1; + for(i=0;i<(1<<(K-1));i++) + vp->metrics1.w[i] = 1000; + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + vp->old_metrics->w[starting_state & ((1<<(K-1))-1)] = 0; /* Bias known start state */ + return 0; +} + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi224(int len){ + struct v224 *vp; + int state; + + if((vp = (struct v224 *)malloc(sizeof(struct v224))) == NULL) + return NULL; + if((vp->decisions = malloc(len*sizeof(decision_t))) == NULL){ + free(vp); + return NULL; + } + for(state=0;state < (1<<(K-2));state++){ + Branchtab224[0].w[state] = G1FLIP ^ parity((2*state) & POLY1) ? 255 : 0; + Branchtab224[1].w[state] = G2FLIP ^ parity((2*state) & POLY2) ? 255 : 0; + } + init_viterbi224(vp,0); + return vp; +} + + +/* Viterbi chainback */ +int chainback_viterbi224( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + struct v224 *vp = p; + decision_t *d; + + if(p == NULL) + return -1; + d = (decision_t *)vp->decisions; + endstate &= (1<<(K-1))-1; + + unsigned char dbyte = 0; + while(nbits-- != 0){ + int bit; + + // Accumulate decoded data bits as they fall off the right end of endstate + dbyte = ((endstate & 1) << 7) | (dbyte >> 1); + if((nbits & 7) == 0) + data[nbits>>3] = dbyte; + + // Shift new bit into left end of endstate + bit = (d[nbits].c[endstate >> 3] >> (endstate & 7)) & 1; + endstate = (bit << (K-2)) | (endstate >> 1); + } + return 0; +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi224(void *p){ + struct v224 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} + +/* Update decoder with a block of demodulated symbols + * Note that nbits is the number of decoded data bits, not the number + * of symbols! + */ + +int update_viterbi224_blk(void *p,const unsigned char *syms,int nbits){ + struct v224 *vp = p; + void *tmp; + decision_t *d; + int i; + + if(p == NULL) + return -1; + d = (decision_t *)vp->dp; + while(nbits--){ + memset(d,0,sizeof(decision_t)); + for(i=0;i<(1<<(K-2));i++){ + unsigned long metric,m0,m1,m2,m3,decision0,decision1; + + metric = ((Branchtab224[0].w[i] ^ syms[0]) + (Branchtab224[1].w[i] ^ syms[1])); + m0 = vp->old_metrics->w[i] + metric; + m1 = vp->old_metrics->w[i+(1<<(K-2))] + (510 - metric); + m2 = vp->old_metrics->w[i] + (510-metric); + m3 = vp->old_metrics->w[i+(1<<(K-2))] + metric; + decision0 = (signed long)(m0-m1) >= 0; + decision1 = (signed long)(m2-m3) >= 0; + vp->new_metrics->w[2*i] = decision0 ? m1 : m0; + vp->new_metrics->w[2*i+1] = decision1 ? m3 : m2; + d->c[i/4] |= ((decision0|(decision1<<1)) << ((2*i)&7)); + } + syms += 2; + d++; + /* Swap pointers to old and new metrics */ + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + } + vp->dp = d; + return 0; +} + diff --git a/viterbi224_sse2.c b/viterbi224_sse2.c new file mode 100644 index 0000000..2f8c8fd --- /dev/null +++ b/viterbi224_sse2.c @@ -0,0 +1,261 @@ +// K=24 r=1/2 Viterbi decoder for ICE +// Copyright April 2014, Phil Karn, KA9Q +#include +#include +#include +#include +#include +#include +#include + +#include "viterbi224.h" +#include "code.h" + +static inline int parity(int x){ + return __builtin_parity(x); +} + + +typedef union { uint32_t w[1<<18]; uint16_t s[1<<19];} decision_t; +typedef union { int16_t s[1<<23]; __m128i v[1<<20];} metric_t; + +static union branchtab224 { uint16_t s[1<<22]; __m128i v[1<<19];} Branchtab224[2]; + +// State info for instance of Viterbi decoder +struct v224 { + metric_t metrics1; // path metric buffer 1 + metric_t metrics2; // path metric buffer 2 + void *dp; // Pointer to current decision + metric_t *old_metrics,*new_metrics; // Pointers to path metrics, swapped on every bit + void *decisions; // Beginning of decisions for block +}; + +// Initialize Viterbi decoder for start of new frame +int init_viterbi224(void *p,int starting_state){ + struct v224 *vp = p; + int i; + + if(p == NULL) + return -1; + + for(i=0;i<(1<<(K-1));i++) + vp->metrics1.s[i] = (SHRT_MIN+5000); + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + vp->old_metrics->s[starting_state & ((1<<(K-1))-1)] = SHRT_MIN; // Bias known start state + return 0; +} + +// Create a new instance of a Viterbi decoder +void *create_viterbi224(int len){ + void *p; + struct v224 *vp; + int i; + int state; + + // Ordinary malloc() only returns 8-byte alignment, we need 16 + i = posix_memalign(&p, sizeof(__m128i),sizeof(struct v224)); + assert(i == 0); + + vp = (struct v224 *)p; + p = malloc(len*sizeof(decision_t)); + assert(p != NULL); + // free(vp); + // return NULL; + + vp->decisions = (decision_t *)p; + + for(state=0;state < (1<<(K-2));state++){ + Branchtab224[0].s[state] = G1FLIP ^ parity((2*state) & POLY1) ? 255 : 0; + Branchtab224[1].s[state] = G2FLIP ^ parity((2*state) & POLY2) ? 255 : 0; + } + init_viterbi224(vp,0); + return vp; +} + +// Viterbi chainback +int chainback_viterbi224( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + struct v224 *vp = p; + decision_t *d = (decision_t *)vp->decisions; + + if(d == NULL) + return -1; + + endstate &= (1<<(K-1))-1; + +#if 1 + { + unsigned char dbyte = 0; + while(nbits-- > 0){ + int bit; + + // Accumulate decoded data bits as they fall off the right end of endstate + dbyte = ((endstate & 1) << 7) | (dbyte >> 1); + if((nbits & 7) == 0) + data[nbits>>3] = dbyte; + bit = (d[nbits].w[endstate>>5] >> (endstate & 31)) & 1; // these constants do NOT change with K + endstate = (bit << (K-2)) | (endstate >> 1); + } + } +#else + // alternative that accumulates the data byte in the low 8 bits of endstate + // This requires endstate to be at least K+8 bits wide + endstate <<= 8; + while(nbits-- > 0){ + int bit; + + bit = (d[nbits].w[endstate>>13] >> ((endstate >> 8)& 31)) & 1; // Constants do NOT change with K + endstate = (bit << (K+6)) | (endstate >> 1); + if((nbits & 7) == 0) + data[nbits>>3] = endstate & 0xff; + } +#endif + return 0; + +} + +// Delete instance of a Viterbi decoder +void delete_viterbi224(void *p){ + struct v224 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} + + +// Process received symbols +int update_viterbi224_blk(void *p,const unsigned char *syms,int nbits){ + struct v224 *vp = p; + decision_t *d = (decision_t *)vp->dp; + + while(nbits--){ + __m128i sym0v,sym1v; + void *tmp; + int i; + + // Splat the 0th symbol across sym0v, the 1st symbol across sym1v, etc + sym0v = _mm_set1_epi16(syms[0]); + sym1v = _mm_set1_epi16(syms[1]); + syms += 2; + + // SSE2 doesn't support minimum of unsigned words but does minimum of signed words. + // So we use signed shorts. SSE 4.1 does do minimum of unsigned words (PMINUW) + // but there doesn't seem to be any particular advantage to it + for(i=0; i < 1<<(K-5); i++){ + __m128i decision0,decision1,metric,m_metric,m0,m1,m2,m3,survivor0,survivor1; + + // Form branch metrics + // Because Branchtab takes on values 0 and 255, and the values of sym?v are offset binary in the range 0-255, + // the XOR operations constitute conditional negation. + // metric and m_metric (-metric) are in the range 0-510 + metric = _mm_add_epi16(_mm_xor_si128(Branchtab224[0].v[i],sym0v),_mm_xor_si128(Branchtab224[1].v[i],sym1v)); + m_metric = _mm_sub_epi16(_mm_set1_epi16(510),metric); + + // Add branch metrics to path metrics using saturating signed addition + m0 = _mm_adds_epi16(vp->old_metrics->v[i],metric); + m3 = _mm_adds_epi16(vp->old_metrics->v[(1<<(K-5))+i],metric); + m1 = _mm_adds_epi16(vp->old_metrics->v[(1<<(K-5))+i],m_metric); + m2 = _mm_adds_epi16(vp->old_metrics->v[i],m_metric); + +#if 0 + _mm_prefetch((void *)&Branchtab224[0].v[i+1],3); + _mm_prefetch((void *)&Branchtab224[1].v[i+1],3); + _mm_prefetch((void *)&vp->old_metrics->v[i+1],0); + _mm_prefetch((void *)&vp->old_metrics->v[(1<<(K-5))+i],0); +#endif + + // Determine winners in one of two ways: + // Direct comparison ought to be faster by not having to wait for the minimum operation, + // but it doesn't seem to make any difference on the machines I've tried (Core i7-3612QM; Xeon E5620), + // perhaps because memory bandwidth is the bottleneck anyway. + // One minor difference is that the first method breaks ties in favor of the 1-branch + // while the second breaks ties in favor of the 0-branch. That probably doesn't matter in practice. +#if 0 + // Find winning metrics. Note: signed, there is an unsigned version on SSE 4.1 + survivor0 = _mm_min_epi16(m0,m1); + survivor1 = _mm_min_epi16(m2,m3); + // Compare survivors with metrics to see who won + decision0 = _mm_cmpeq_epi16(survivor0,m1); + decision1 = _mm_cmpeq_epi16(survivor1,m3); +#else + // Do this before computing the surviving metrics + decision0 = _mm_cmpgt_epi16(m0,m1); + decision1 = _mm_cmpgt_epi16(m2,m3); + // Find winning metrics. Note: signed, there is an unsigned version on SSE 4.1 + survivor0 = _mm_min_epi16(m0,m1); + survivor1 = _mm_min_epi16(m2,m3); +#endif + + // Pack each set of decisions into 8 8-bit bytes, then interleave and compress into 16 bits + d->s[i] = _mm_movemask_epi8(_mm_unpacklo_epi8(_mm_packs_epi16(decision0,_mm_setzero_si128()),_mm_packs_epi16(decision1,_mm_setzero_si128()))); + + // Store surviving metrics + vp->new_metrics->v[2*i] = _mm_unpacklo_epi16(survivor0,survivor1); + vp->new_metrics->v[2*i+1] = _mm_unpackhi_epi16(survivor0,survivor1); + } + + // Eyeball experiments show the metric spread for this code to start at 5000-6000 or so due to the large + // starting bias for state 0. After 1 constraint length it falls to 1000-2000 +#if 0 + { + int minmetric,maxmetric; + + minmetric = 9999999; + maxmetric = -9999999; + for(i=0;i<(1<<(K-1));i++){ + if(minmetric > vp->new_metrics->s[i]) + minmetric = vp->new_metrics->s[i]; + if(maxmetric < vp->new_metrics->s[i]) + maxmetric = vp->new_metrics->s[i]; + } + printf("metric range %d %d spread %d\n",minmetric,maxmetric,maxmetric-minmetric); + } +#endif + // See if we need to renormalize + // The comparison constant is chosen empirically; a higher value causes renormalization + // to take place less often, but it risks some other metric saturating positive + // This number should be 32767 minus the maximum observed metric spread (see above) minus a margin + if(vp->new_metrics->s[0] >= 25000){ + int i,adjust; + __m128i adjustv; + union { __m128i v; uint16_t w[8]; } t; + + // Find smallest metric and set adjustv to bring it down to SHRT_MIN + adjustv = vp->new_metrics->v[0]; + for(i=1;i<(1<<(K-4));i++) + adjustv = _mm_min_epi16(adjustv,vp->new_metrics->v[i]); + + adjustv = _mm_min_epi16(adjustv,_mm_srli_si128(adjustv,8)); + adjustv = _mm_min_epi16(adjustv,_mm_srli_si128(adjustv,4)); + adjustv = _mm_min_epi16(adjustv,_mm_srli_si128(adjustv,2)); + t.v = adjustv; + adjust = t.w[0] - SHRT_MIN; + adjustv = _mm_set1_epi16(adjust); + + // We cannot use a saturated subtract, because we often have to adjust by more than SHRT_MAX + // This is okay since it can't overflow anyway + for(i=0;i < 1<<(K-4);i++) + vp->new_metrics->v[i] = _mm_sub_epi16(vp->new_metrics->v[i],adjustv); +#if 0 + printf("Adjust metrics by %d\n",adjust); +#endif + } + d++; + // Swap pointers to old and new metrics + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + } + vp->dp = d; + return 0; +} + + diff --git a/viterbi27.c b/viterbi27.c new file mode 100644 index 0000000..6e87a50 --- /dev/null +++ b/viterbi27.c @@ -0,0 +1,127 @@ +/* K=7 r=1/2 Viterbi decoder with optional Intel or PowerPC SIMD + * Copyright Feb 2004, Phil Karn, KA9Q + */ +#include +#include +#include +#include "fec.h" + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi27(int len){ + find_cpu_mode(); + + switch(Cpu_mode){ + case PORT: + default: + return create_viterbi27_port(len); +#ifdef __VEC__ + case ALTIVEC: + return create_viterbi27_av(len); +#endif +#ifdef __i386__ + case MMX: + return create_viterbi27_mmx(len); + case SSE: + return create_viterbi27_sse(len); + case SSE2: + return create_viterbi27_sse2(len); +#endif + } +} + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi27(void *p,int starting_state){ + switch(Cpu_mode){ + case PORT: + default: + return init_viterbi27_port(p,starting_state); +#ifdef __VEC__ + case ALTIVEC: + return init_viterbi27_av(p,starting_state); +#endif +#ifdef __i386__ + case MMX: + return init_viterbi27_mmx(p,starting_state); + case SSE: + return init_viterbi27_sse(p,starting_state); + case SSE2: + return init_viterbi27_sse2(p,starting_state); +#endif + } +} + +/* Viterbi chainback */ +int chainback_viterbi27( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + + switch(Cpu_mode){ + case PORT: + default: + return chainback_viterbi27_port(p,data,nbits,endstate); +#ifdef __VEC__ + case ALTIVEC: + return chainback_viterbi27_av(p,data,nbits,endstate); +#endif +#ifdef __i386__ + case MMX: + return chainback_viterbi27_mmx(p,data,nbits,endstate); + case SSE: + return chainback_viterbi27_sse(p,data,nbits,endstate); + case SSE2: + return chainback_viterbi27_sse2(p,data,nbits,endstate); +#endif + } +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi27(void *p){ + switch(Cpu_mode){ + case PORT: + default: + delete_viterbi27_port(p); + break; +#ifdef __VEC__ + case ALTIVEC: + delete_viterbi27_av(p); + break; +#endif +#ifdef __i386__ + case MMX: + delete_viterbi27_mmx(p); + break; + case SSE: + delete_viterbi27_sse(p); + break; + case SSE2: + delete_viterbi27_sse2(p); + break; +#endif + } +} + +/* Update decoder with a block of demodulated symbols + * Note that nbits is the number of decoded data bits, not the number + * of symbols! + */ +int update_viterbi27_blk(void *p,unsigned char syms[],int nbits){ + switch(Cpu_mode){ + case PORT: + default: + return update_viterbi27_blk_port(p,syms,nbits); +#ifdef __VEC__ + case ALTIVEC: + return update_viterbi27_blk_av(p,syms,nbits); +#endif +#ifdef __i386__ + case MMX: + return update_viterbi27_blk_mmx(p,syms,nbits); + case SSE: + return update_viterbi27_blk_sse(p,syms,nbits); + case SSE2: + return update_viterbi27_blk_sse2(p,syms,nbits); +#endif + } +} diff --git a/viterbi27_av.c b/viterbi27_av.c new file mode 100644 index 0000000..3ffb782 --- /dev/null +++ b/viterbi27_av.c @@ -0,0 +1,206 @@ +/* K=7 r=1/2 Viterbi decoder for PowerPC G4/G5 Altivec instructions + * Feb 2004, Phil Karn, KA9Q + */ +#include +#include +#include +#include "fec.h" + +typedef union { long long p; unsigned char c[64]; vector bool char v[4]; } decision_t; +typedef union { long long p; unsigned char c[64]; vector unsigned char v[4]; } metric_t; + +static union branchtab27 { unsigned char c[32]; vector unsigned char v[2];} Branchtab27[2]; +static int Init = 0; + +/* State info for instance of Viterbi decoder + * Don't change this without also changing references in [mmx|sse|sse2]bfly29.s! + */ +struct v27 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + decision_t *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + decision_t *decisions; /* Beginning of decisions for block */ +}; + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi27_av(void *p,int starting_state){ + struct v27 *vp = p; + int i; + + for(i=0;i<4;i++) + vp->metrics1.v[i] = (vector unsigned char)(63); + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + vp->old_metrics->c[starting_state & 63] = 0; /* Bias known start state */ + return 0; +} + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi27_av(int len){ + struct v27 *vp; + int state; + + if(!Init){ + /* Initialize branch tables */ + for(state=0;state < 32;state++){ + Branchtab27[0].c[state] = parity((2*state) & V27POLYA) ? 255:0; + Branchtab27[1].c[state] = parity((2*state) & V27POLYB) ? 255:0; + } + Init++; + } + vp = (struct v27 *)malloc(sizeof(struct v27)); + vp->decisions = (decision_t *)malloc((len+6)*sizeof(decision_t)); + init_viterbi27_av(vp,0); + return vp; +} + +/* Viterbi chainback */ +int chainback_viterbi27_av( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + struct v27 *vp = p; + decision_t *d = (decision_t *)vp->decisions; + + /* Make room beyond the end of the encoder register so we can + * accumulate a full byte of decoded data + */ + endstate %= 64; + endstate <<= 2; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 6; /* Look past tail */ + while(nbits-- != 0){ + int k; + + k = d[nbits].c[endstate>>2] & 1; + data[nbits>>3] = endstate = (endstate >> 1) | (k << 7); + } + return 0; +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi27_av(void *p){ + struct v27 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} + +/* Process received symbols */ +int update_viterbi27_blk_av(void *p,unsigned char *syms,int nbits){ + struct v27 *vp = p; + decision_t *d = (decision_t *)vp->dp; + + while(nbits--){ + vector unsigned char survivor0,survivor1,sym1v,sym2v; + vector bool char decision0,decision1; + vector unsigned char metric,m_metric,m0,m1,m2,m3; + void *tmp; + + /* sym1v.0 = syms[0]; sym1v.1 = syms[1] */ + sym1v = vec_perm(vec_ld(0,syms),vec_ld(1,syms),vec_lvsl(0,syms)); + + sym2v = vec_splat(sym1v,1); /* Splat syms[1] across sym2v */ + sym1v = vec_splat(sym1v,0); /* Splat syms[0] across sym1v */ + syms += 2; + + /* Do the 32 butterflies as two interleaved groups of 16 each to keep the pipes full */ + + /* Form first set of 16 branch metrics */ + metric = vec_avg(vec_xor(Branchtab27[0].v[0],sym1v),vec_xor(Branchtab27[1].v[0],sym2v)); + metric = vec_sr(metric,(vector unsigned char)(3)); + m_metric = vec_sub((vector unsigned char)(31),metric); + + /* Form first set of path metrics */ + m0 = vec_adds(vp->old_metrics->v[0],metric); + m3 = vec_adds(vp->old_metrics->v[2],metric); + m1 = vec_adds(vp->old_metrics->v[2],m_metric); + m2 = vec_adds(vp->old_metrics->v[0],m_metric); + + /* Form second set of 16 branch metrics */ + metric = vec_avg(vec_xor(Branchtab27[0].v[1],sym1v),vec_xor(Branchtab27[1].v[1],sym2v)); + metric = vec_sr(metric,(vector unsigned char)(3)); + m_metric = vec_sub((vector unsigned char)(31),metric); + + /* Compare and select first set */ + decision0 = vec_cmpgt(m0,m1); + decision1 = vec_cmpgt(m2,m3); + survivor0 = vec_min(m0,m1); + survivor1 = vec_min(m2,m3); + + /* Compute second set of path metrics */ + m0 = vec_adds(vp->old_metrics->v[1],metric); + m3 = vec_adds(vp->old_metrics->v[3],metric); + m1 = vec_adds(vp->old_metrics->v[3],m_metric); + m2 = vec_adds(vp->old_metrics->v[1],m_metric); + + /* Interleave and store first decisions and survivors */ + d->v[0] = vec_mergeh(decision0,decision1); + d->v[1] = vec_mergel(decision0,decision1); + vp->new_metrics->v[0] = vec_mergeh(survivor0,survivor1); + vp->new_metrics->v[1] = vec_mergel(survivor0,survivor1); + + /* Compare and select second set */ + decision0 = vec_cmpgt(m0,m1); + decision1 = vec_cmpgt(m2,m3); + survivor0 = vec_min(m0,m1); + survivor1 = vec_min(m2,m3); + + /* Interleave and store second set of decisions and survivors */ + d->v[2] = vec_mergeh(decision0,decision1); + d->v[3] = vec_mergel(decision0,decision1); + vp->new_metrics->v[2] = vec_mergeh(survivor0,survivor1); + vp->new_metrics->v[3] = vec_mergel(survivor0,survivor1); + + /* renormalize if necessary */ + if(vp->new_metrics->c[0] >= 105){ + vector unsigned char scale0,scale1; + + /* Find smallest metric and splat */ + scale0 = vec_min(vp->new_metrics->v[0],vp->new_metrics->v[1]); + scale1 = vec_min(vp->new_metrics->v[2],vp->new_metrics->v[3]); + scale0 = vec_min(scale0,scale1); + scale0 = vec_min(scale0,vec_sld(scale0,scale0,8)); + scale0 = vec_min(scale0,vec_sld(scale0,scale0,4)); + scale0 = vec_min(scale0,vec_sld(scale0,scale0,2)); + scale0 = vec_min(scale0,vec_sld(scale0,scale0,1)); + + /* Now subtract from all metrics */ + vp->new_metrics->v[0] = vec_subs(vp->new_metrics->v[0],scale0); + vp->new_metrics->v[1] = vec_subs(vp->new_metrics->v[1],scale0); + vp->new_metrics->v[2] = vec_subs(vp->new_metrics->v[2],scale0); + vp->new_metrics->v[3] = vec_subs(vp->new_metrics->v[3],scale0); + } +#if 0 + { + int i; + fprintf(stderr,"metrics:"); + for(i=0;i<64;i++) + fprintf(stderr," %d",vp->new_metrics->c[i]); + fprintf(stderr,"\n"); + fprintf(stderr,"decisions: "); + for(i=0;i<64;i++) + fprintf(stderr,"%d",d->c[i]&1); + fprintf(stderr,"\n"); + } +#endif + d++; + /* Swap pointers to old and new metrics */ + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + } + vp->dp = d; + + return 0; +} + diff --git a/viterbi27_mmx.c b/viterbi27_mmx.c new file mode 100644 index 0000000..22c7c73 --- /dev/null +++ b/viterbi27_mmx.c @@ -0,0 +1,96 @@ +/* K=7 r=1/2 Viterbi decoder for MMX + * Copyright Feb 2004, Phil Karn, KA9Q + */ +#include +#include +#include +#include +#include "fec.h" + +typedef union { char c[64]; __m64 v[8];} decision_t; +typedef union { unsigned char c[64]; __m64 v[8];} metric_t; + +unsigned char Mettab27_1[256][32] __attribute__ ((aligned(16))); +unsigned char Mettab27_2[256][32] __attribute__ ((aligned(16))); +static int Init = 0; + +/* State info for instance of Viterbi decoder + * Don't change this without also changing references in mmxbfly27.s! + */ +struct v27 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + decision_t *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + decision_t *decisions; /* Beginning of decisions for block */ +}; + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi27_mmx(void *p,int starting_state){ + struct v27 *vp = (struct v27 *)p; + int i; + + for(i=0;i<64;i++) + vp->metrics1.c[i] = 63; + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + vp->old_metrics->c[starting_state & 63] = 0; /* Bias known start state */ + return 0; +} + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi27_mmx(int len){ + int state; + struct v27 *vp; + + if(Init == 0){ + /* Initialize branch tables */ + for(state=0;state < 32;state++){ + int symbol,bit1,bit2; + bit1 = parity((2*state) & V27POLYA); + bit2 = parity((2*state) & V27POLYB); + for(symbol = 0;symbol < 256;symbol++){ + Mettab27_1[symbol][state] = (bit1 ? (255-symbol):symbol) / 16; + Mettab27_2[symbol][state] = (bit2 ? (255-symbol):symbol) / 16; + } + } + Init++; + } + vp = (struct v27 *)malloc(sizeof(struct v27)); + vp->decisions = (decision_t *)malloc((len+6)*sizeof(decision_t)); + init_viterbi27_mmx(vp,0); + return vp; +} + +/* Viterbi chainback */ +int chainback_viterbi27_mmx( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + + struct v27 *vp = (struct v27 *)p; + decision_t *d = (decision_t *)vp->decisions; + + endstate &= 63; + d += 6; /* Look past tail */ + while(nbits-- != 0){ + int k; + + k = d[nbits].c[endstate>>2] & 1; + data[nbits>>3] = endstate = (endstate >> 1) | (k << 7); + } + return 0; +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi27_mmx(void *p){ + struct v27 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} diff --git a/viterbi27_port.c b/viterbi27_port.c new file mode 100644 index 0000000..74a0a21 --- /dev/null +++ b/viterbi27_port.c @@ -0,0 +1,174 @@ +/* K=7 r=1/2 Viterbi decoder in portable C + * Copyright Feb 2004, Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ +#include +#include +#include +#include +#include "fec.h" + + +typedef union { unsigned int w[64]; } metric_t; +typedef union { unsigned long w[2];} decision_t; +static union branchtab27 { unsigned char c[32]; } Branchtab27[2] __attribute__ ((aligned(16))); +static int Init = 0; + +/* State info for instance of Viterbi decoder + * Don't change this without also changing references in [mmx|sse|sse2]bfly29.s! + */ +struct v27 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + decision_t *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + decision_t *decisions; /* Beginning of decisions for block */ +}; + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi27_port(void *p,int starting_state){ + struct v27 *vp = p; + int i; + + for(i=0;i<64;i++) + vp->metrics1.w[i] = 63; + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + vp->old_metrics->w[starting_state & 63] = 0; /* Bias known start state */ + return 0; +} + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi27_port(int len){ + struct v27 *vp; + int state; + + if(!Init){ + /* Initialize branch tables */ + for(state=0;state < 32;state++){ + Branchtab27[0].c[state] = parity((2*state) & V27POLYA) ? 255:0; + Branchtab27[1].c[state] = parity((2*state) & V27POLYB) ? 255:0; + } + Init++; + } + vp = malloc(sizeof(struct v27)); + vp->decisions = malloc((len+6)*sizeof(decision_t)); + init_viterbi27_port(vp,0); + + return vp; +} + +/* Viterbi chainback */ +int chainback_viterbi27_port( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + struct v27 *vp = p; + decision_t *d = vp->decisions; + + /* Make room beyond the end of the encoder register so we can + * accumulate a full byte of decoded data + */ + endstate %= 64; + endstate <<= 2; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 6; /* Look past tail */ + while(nbits-- != 0){ + int k; + + k = (d[nbits].w[(endstate>>2)/32] >> ((endstate>>2)%32)) & 1; + data[nbits>>3] = endstate = (endstate >> 1) | (k << 7); + } + return 0; +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi27_port(void *p){ + struct v27 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} + +/* C-language butterfly */ +#define BFLY(i) {\ +unsigned int metric,m0,m1,decision;\ + metric = (Branchtab27[0].c[i] ^ sym0) + (Branchtab27[1].c[i] ^ sym1);\ + m0 = vp->old_metrics->w[i] + metric;\ + m1 = vp->old_metrics->w[i+32] + (510 - metric);\ + decision = (signed int)(m0-m1) > 0;\ + vp->new_metrics->w[2*i] = decision ? m1 : m0;\ + d->w[i/16] |= decision << ((2*i)&31);\ + m0 -= (metric+metric-510);\ + m1 += (metric+metric-510);\ + decision = (signed int)(m0-m1) > 0;\ + vp->new_metrics->w[2*i+1] = decision ? m1 : m0;\ + d->w[i/16] |= decision << ((2*i+1)&31);\ +} + +/* Update decoder with a block of demodulated symbols + * Note that nbits is the number of decoded data bits, not the number + * of symbols! + */ +int update_viterbi27_blk_port(void *p,unsigned char *syms,int nbits){ + struct v27 *vp = p; + void *tmp; + decision_t *d = (decision_t *)vp->dp; + + while(nbits--){ + unsigned char sym0,sym1; + + d->w[0] = d->w[1] = 0; + sym0 = *syms++; + sym1 = *syms++; + + BFLY(0); + BFLY(1); + BFLY(2); + BFLY(3); + BFLY(4); + BFLY(5); + BFLY(6); + BFLY(7); + BFLY(8); + BFLY(9); + BFLY(10); + BFLY(11); + BFLY(12); + BFLY(13); + BFLY(14); + BFLY(15); + BFLY(16); + BFLY(17); + BFLY(18); + BFLY(19); + BFLY(20); + BFLY(21); + BFLY(22); + BFLY(23); + BFLY(24); + BFLY(25); + BFLY(26); + BFLY(27); + BFLY(28); + BFLY(29); + BFLY(30); + BFLY(31); + d++; + /* Swap pointers to old and new metrics */ + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + } + vp->dp = d; + return 0; +} diff --git a/viterbi27_sse.c b/viterbi27_sse.c new file mode 100644 index 0000000..191316a --- /dev/null +++ b/viterbi27_sse.c @@ -0,0 +1,97 @@ +/* K=7 r=1/2 Viterbi decoder for SSE + * Feb 2004, Phil Karn, KA9Q + */ +#include +#include +#include +#include +#include "fec.h" + +typedef union { unsigned char c[64]; } metric_t; +typedef union { unsigned long w[2]; unsigned char c[8]; __m64 v[1];} decision_t; +union branchtab27 { unsigned char c[32]; __m64 v[4];} Branchtab27_sse[2]; +static int Init = 0; + +/* State info for instance of Viterbi decoder + * Don't change this without also changing references in ssebfly27.s! + */ +struct v27 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + decision_t *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + decision_t *decisions; /* Beginning of decisions for block */ +}; + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi27_sse(int len){ + struct v27 *vp; + int state; + + if(!Init){ + /* Initialize branch tables */ + for(state=0;state < 32;state++){ + Branchtab27_sse[0].c[state] = parity((2*state) & V27POLYA) ? 255:0; + Branchtab27_sse[1].c[state] = parity((2*state) & V27POLYB) ? 255:0; + } + Init++; + } + vp = malloc(sizeof(struct v27)); + vp->decisions = malloc((len+6)*sizeof(decision_t)); + init_viterbi27(vp,0); + return vp; +} + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi27_sse(void *p,int starting_state){ + struct v27 *vp = p; + int i; + + for(i=0;i<64;i++) + vp->metrics1.c[i] = 63; + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + vp->old_metrics->c[starting_state & 63] = 0; /* Bias known start state */ + return 0; +} + +/* Viterbi chainback */ +int chainback_viterbi27_sse( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + struct v27 *vp = p; + decision_t *d = vp->decisions; + + /* Make room beyond the end of the encoder register so we can + * accumulate a full byte of decoded data + */ + endstate %= 64; + endstate <<= 2; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 6; /* Look past tail */ + while(nbits-- != 0){ + int k; + + k = (d[nbits].c[(endstate>>2)/8] >> ((endstate>>2)%8)) & 1; + data[nbits>>3] = endstate = (endstate >> 1) | (k << 7); + } + return 0; +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi27_sse(void *p){ + struct v27 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} diff --git a/viterbi27_sse2.c b/viterbi27_sse2.c new file mode 100644 index 0000000..7f97b8a --- /dev/null +++ b/viterbi27_sse2.c @@ -0,0 +1,157 @@ +/* K=7 r=1/2 Viterbi decoder for SSE2 + * Feb 2004, Phil Karn, KA9Q + */ +#include +#include +#include +#include +#include "fec.h" + +typedef union { unsigned char c[64]; __m128i v[4]; } metric_t; +typedef union { unsigned long w[2]; unsigned char c[8]; unsigned short s[4]; __m64 v[1];} decision_t; +union branchtab27 { unsigned char c[32]; __m128i v[2];} Branchtab27_sse2[2]; +static int Init = 0; + +/* State info for instance of Viterbi decoder + * Don't change this without also changing references in sse2bfly27.s! + */ +struct v27 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + decision_t *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + decision_t *decisions; /* Beginning of decisions for block */ +}; + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi27_sse2(void *p,int starting_state){ + struct v27 *vp = p; + int i; + + for(i=0;i<64;i++) + vp->metrics1.c[i] = 63; + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + vp->old_metrics->c[starting_state & 63] = 0; /* Bias known start state */ + return 0; +} + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi27_sse2(int len){ + struct v27 *vp; + int state; + + if(!Init){ + /* Initialize branch tables */ + for(state=0;state < 32;state++){ + Branchtab27_sse2[0].c[state] = parity((2*state) & V27POLYA) ? 255:0; + Branchtab27_sse2[1].c[state] = parity((2*state) & V27POLYB) ? 255:0; + } + Init++; + } + vp = (struct v27 *)malloc(sizeof(struct v27)); + vp->decisions = (decision_t *)malloc((len+6)*sizeof(decision_t)); + init_viterbi27_sse2(vp,0); + + return vp; +} + +/* Viterbi chainback */ +int chainback_viterbi27_sse2( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + struct v27 *vp = p; + decision_t *d = vp->decisions; + + /* Make room beyond the end of the encoder register so we can + * accumulate a full byte of decoded data + */ + endstate %= 64; + endstate <<= 2; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 6; /* Look past tail */ + while(nbits-- != 0){ + int k; + + k = (d[nbits].c[(endstate>>2)/8] >> ((endstate>>2)%8)) & 1; + data[nbits>>3] = endstate = (endstate >> 1) | (k << 7); + } + return 0; +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi27_sse2(void *p){ + struct v27 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} + + +#if 0 +/* This code is turned off because it's slower than my hand-crafted assembler in sse2bfly27.s. But it does work. */ +void update_viterbi27_blk_sse2(void *p,unsigned char *syms,int nbits){ + struct v27 *vp = p; + decision_t *d = (decision_t *)vp->dp; + + while(nbits--){ + __m128i sym0v,sym1v; + void *tmp; + int i; + + /* Splat the 0th symbol across sym0v, the 1st symbol across sym1v, etc */ + sym0v = _mm_set1_epi8(syms[0]); + sym1v = _mm_set1_epi8(syms[1]); + syms += 2; + + for(i=0;i<2;i++){ + __m128i decision0,decision1,metric,m_metric,m0,m1,m2,m3,survivor0,survivor1; + + /* Form branch metrics */ + metric = _mm_avg_epu8(_mm_xor_si128(Branchtab27_sse2[0].v[i],sym0v),_mm_xor_si128(Branchtab27_sse2[1].v[i],sym1v)); + /* There's no packed bytes right shift in SSE2, so we use the word version and mask + * (I'm *really* starting to like Altivec...) + */ + metric = _mm_srli_epi16(metric,3); + metric = _mm_and_si128(metric,_mm_set1_epi8(31)); + m_metric = _mm_sub_epi8(_mm_set1_epi8(31),metric); + + /* Add branch metrics to path metrics */ + m0 = _mm_add_epi8(vp->old_metrics->v[i],metric); + m3 = _mm_add_epi8(vp->old_metrics->v[2+i],metric); + m1 = _mm_add_epi8(vp->old_metrics->v[2+i],m_metric); + m2 = _mm_add_epi8(vp->old_metrics->v[i],m_metric); + + /* Compare and select, using modulo arithmetic */ + decision0 = _mm_cmpgt_epi8(_mm_sub_epi8(m0,m1),_mm_setzero_si128()); + decision1 = _mm_cmpgt_epi8(_mm_sub_epi8(m2,m3),_mm_setzero_si128()); + survivor0 = _mm_or_si128(_mm_and_si128(decision0,m1),_mm_andnot_si128(decision0,m0)); + survivor1 = _mm_or_si128(_mm_and_si128(decision1,m3),_mm_andnot_si128(decision1,m2)); + + /* Pack each set of decisions into 16 bits */ + d->s[2*i] = _mm_movemask_epi8(_mm_unpacklo_epi8(decision0,decision1)); + d->s[2*i+1] = _mm_movemask_epi8(_mm_unpackhi_epi8(decision0,decision1)); + + /* Store surviving metrics */ + vp->new_metrics->v[2*i] = _mm_unpacklo_epi8(survivor0,survivor1); + vp->new_metrics->v[2*i+1] = _mm_unpackhi_epi8(survivor0,survivor1); + } + d++; + /* Swap pointers to old and new metrics */ + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + } + vp->dp = d; +} +#endif diff --git a/viterbi27_sse2.c-saved b/viterbi27_sse2.c-saved new file mode 100644 index 0000000..4859d07 --- /dev/null +++ b/viterbi27_sse2.c-saved @@ -0,0 +1,259 @@ +// K=7 r=1/2 Viterbi decoder for SSE2 +// Feb 2004, Phil Karn, KA9Q +// Updated April 2014 + +#include +#include +#include +#include +#include +#include "fec.h" + +static inline int parity(int x){ + return __builtin_parity(x); +} + +int encode_27( + unsigned char *symbols, // Output buffer, 2*8*nbytes + const unsigned char *data, // Input buffer, nbytes + unsigned int nbytes) // Number of bytes in data +{ + unsigned int encstate; + int i; + + encstate = 0; + while(nbytes-- != 0){ + encstate |= *data++; + for(i=0;i<8;i++){ // Transmit 8 bits, MSB first + encstate <<= 1; + *symbols++ = parity(encstate & (V27POLYB << 8)); + *symbols++ = !parity(encstate & (V27POLYA << 8)); + } + } + if((encstate & (63 << 8)) != 0) + return -1; // Warn if frame wasn't tailed to 0 + return 0; +} + +typedef union { uint8_t c[64]; __m128i v[4]; } metric_t; +typedef union { uint32_t w[2]; uint8_t c[8]; uint16_t s[4]; __m64 v[1];} decision_t; +union branchtab27 { uint8_t c[32]; __m128i v[2];} Branchtab27_sse2[2]; + +// State info for instance of Viterbi decoder +// Don't change this without also changing references in sse2bfly27.s! +struct v27 { + metric_t metrics1; // path metric buffer 1 + metric_t metrics2; // path metric buffer 2 + decision_t *dp; // Pointer to current decision + metric_t *old_metrics,*new_metrics; // Pointers to path metrics, swapped on every bit + decision_t *decisions; // Beginning of decisions for block +}; + +// Initialize Viterbi decoder for start of new frame +int init_viterbi27(void *p,int starting_state){ + struct v27 *vp = p; + int i; + + if(p == NULL) + return -1; + for(i=0;i<64;i++) + vp->metrics1.c[i] = 63; + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + vp->old_metrics->c[starting_state & 63] = 0; // Bias known start state + return 0; +} + +// Create a new instance of a Viterbi decoder +void *create_viterbi27(int len){ + void *p; + struct v27 *vp; + int state; + + for(state=0;state < 32;state++){ + Branchtab27_sse2[0].c[state] = parity((2*state) & V27POLYB) ? 255 : 0; + Branchtab27_sse2[1].c[state] = !parity((2*state) & V27POLYA) ? 255 : 0; + } + + // Ordinary malloc() only returns 8-byte alignment, we need 16 + if(posix_memalign(&p, sizeof(__m128i),sizeof(struct v27))) + return NULL; + vp = (struct v27 *)p; + + if((p = malloc(len*sizeof(decision_t))) == NULL){ + free(vp); + return NULL; + } + vp->decisions = (decision_t *)p; + init_viterbi27(vp,0); + + return vp; +} + +// Viterbi chainback +int chainback_viterbi27( + void *p, + unsigned char *data, // Decoded output data + unsigned int nbits, // Number of data bits + unsigned int endstate){ // Terminal encoder state + struct v27 *vp = p; + decision_t *d; + unsigned char dbyte = 0; + + if(p == NULL) + return -1; + + d = vp->decisions; + endstate &= 63; + while(nbits-- != 0){ + int bit; + + // Accumulate decoded data bits as they fall off the right end of endstate + dbyte = ((endstate & 1) << 7) | (dbyte >> 1); + if((nbits % 8) == 0) + data[nbits/8] = dbyte; + + // Extract decision and push onto left (tail) end of encoder + bit = (d[nbits].w[endstate/32] >> (endstate%32)) & 1; + endstate = (bit << 5) | (endstate >> 1); + } + return 0; +} + +// Delete instance of a Viterbi decoder +void delete_viterbi27(void *p){ + struct v27 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} + + +int update_viterbi27_blk(void *p,unsigned char *syms,int nbits){ + struct v27 *vp = p; + decision_t *d; + + if(p == NULL) + return 0; + d = (decision_t *)vp->dp; + while(nbits--){ + __m128i sym0v,sym1v; + void *tmp; + int i; + + // Splat the 0th symbol across sym0v, the 1st symbol across sym1v, etc + sym0v = _mm_set1_epi8(syms[0]); + sym1v = _mm_set1_epi8(syms[1]); + syms += 2; + +#if 0 + for(i=0;i<2;i++){ + __m128i decision0,decision1,metric,m_metric,m0,m1,m2,m3,survivor0,survivor1; + + /* Form branch metrics */ + metric = _mm_avg_epu8(_mm_xor_si128(Branchtab27_sse2[0].v[i],sym0v),_mm_xor_si128(Branchtab27_sse2[1].v[i],sym1v)); + // There's no packed bytes right shift in SSE2, so we use the word version and mask + // (I'm *really* starting to like Altivec...) + metric = _mm_srli_epi16(metric,3); + metric = _mm_and_si128(metric,_mm_set1_epi8(31)); + m_metric = _mm_sub_epi8(_mm_set1_epi8(31),metric); + + // Add branch metrics to path metrics + m0 = _mm_add_epi8(vp->old_metrics->v[i],metric); + m3 = _mm_add_epi8(vp->old_metrics->v[2+i],metric); + m1 = _mm_add_epi8(vp->old_metrics->v[2+i],m_metric); + m2 = _mm_add_epi8(vp->old_metrics->v[i],m_metric); + + // Compare and select, using modulo arithmetic + decision0 = _mm_cmpgt_epi8(_mm_sub_epi8(m0,m1),_mm_setzero_si128()); + decision1 = _mm_cmpgt_epi8(_mm_sub_epi8(m2,m3),_mm_setzero_si128()); + survivor0 = _mm_or_si128(_mm_and_si128(decision0,m1),_mm_andnot_si128(decision0,m0)); + survivor1 = _mm_or_si128(_mm_and_si128(decision1,m3),_mm_andnot_si128(decision1,m2)); + + // Pack each set of decisions into 16 bits + d->s[2*i] = _mm_movemask_epi8(_mm_unpacklo_epi8(decision0,decision1)); + d->s[2*i+1] = _mm_movemask_epi8(_mm_unpackhi_epi8(decision0,decision1)); + + // Store surviving metrics + vp->new_metrics->v[2*i] = _mm_unpacklo_epi8(survivor0,survivor1); + vp->new_metrics->v[2*i+1] = _mm_unpackhi_epi8(survivor0,survivor1); + } +#else + { + // Unwound version + __m128i decision0,decision1,metric,m_metric,m0,m1,m2,m3,survivor0,survivor1,survivor2,survivor3; + + // First + // Form branch metrics + metric = _mm_avg_epu8(_mm_xor_si128(Branchtab27_sse2[0].v[0],sym0v),_mm_xor_si128(Branchtab27_sse2[1].v[0],sym1v)); + // There's no packed bytes right shift in SSE2, so we use the word version and mask + // (I'm *really* starting to like Altivec...) + metric = _mm_srli_epi16(metric,3); + metric = _mm_and_si128(metric,_mm_set1_epi8(31)); + m_metric = _mm_sub_epi8(_mm_set1_epi8(31),metric); + + // Add branch metrics to path metrics + m0 = _mm_add_epi8(vp->old_metrics->v[0],metric); + m3 = _mm_add_epi8(vp->old_metrics->v[2],metric); + m1 = _mm_add_epi8(vp->old_metrics->v[2],m_metric); + m2 = _mm_add_epi8(vp->old_metrics->v[0],m_metric); + + // Compare and select, using modulo arithmetic + decision0 = _mm_cmpgt_epi8(_mm_sub_epi8(m0,m1),_mm_setzero_si128()); + decision1 = _mm_cmpgt_epi8(_mm_sub_epi8(m2,m3),_mm_setzero_si128()); + survivor0 = _mm_or_si128(_mm_and_si128(decision0,m1),_mm_andnot_si128(decision0,m0)); + survivor1 = _mm_or_si128(_mm_and_si128(decision1,m3),_mm_andnot_si128(decision1,m2)); + + // Pack each set of decisions into 16 bits + d->s[0] = _mm_movemask_epi8(_mm_unpacklo_epi8(decision0,decision1)); + d->s[1] = _mm_movemask_epi8(_mm_unpackhi_epi8(decision0,decision1)); + + // Store surviving metrics + vp->new_metrics->v[0] = _mm_unpacklo_epi8(survivor0,survivor1); + vp->new_metrics->v[1] = _mm_unpackhi_epi8(survivor0,survivor1); + + // Second + // Form branch metrics + metric = _mm_avg_epu8(_mm_xor_si128(Branchtab27_sse2[0].v[1],sym0v),_mm_xor_si128(Branchtab27_sse2[1].v[1],sym1v)); + // There's no packed bytes right shift in SSE2, so we use the word version and mask + // (I'm *really* starting to like Altivec...) + metric = _mm_srli_epi16(metric,3); + metric = _mm_and_si128(metric,_mm_set1_epi8(31)); + m_metric = _mm_sub_epi8(_mm_set1_epi8(31),metric); + + // Add branch metrics to path metrics + m0 = _mm_add_epi8(vp->old_metrics->v[1],metric); + m3 = _mm_add_epi8(vp->old_metrics->v[3],metric); + m1 = _mm_add_epi8(vp->old_metrics->v[3],m_metric); + m2 = _mm_add_epi8(vp->old_metrics->v[1],m_metric); + + // Compare and select, using modulo arithmetic + decision0 = _mm_cmpgt_epi8(_mm_sub_epi8(m0,m1),_mm_setzero_si128()); + decision1 = _mm_cmpgt_epi8(_mm_sub_epi8(m2,m3),_mm_setzero_si128()); + survivor0 = _mm_or_si128(_mm_and_si128(decision0,m1),_mm_andnot_si128(decision0,m0)); + survivor1 = _mm_or_si128(_mm_and_si128(decision1,m3),_mm_andnot_si128(decision1,m2)); + + // Pack each set of decisions into 16 bits + d->s[2] = _mm_movemask_epi8(_mm_unpacklo_epi8(decision0,decision1)); + d->s[3] = _mm_movemask_epi8(_mm_unpackhi_epi8(decision0,decision1)); + + // Store surviving metrics + vp->new_metrics->v[2] = _mm_unpacklo_epi8(survivor0,survivor1); + vp->new_metrics->v[3] = _mm_unpackhi_epi8(survivor0,survivor1); + + + } +#endif + d++; + // Swap pointers to old and new metrics + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + } + vp->dp = d; + return 0; +} diff --git a/viterbi29.c b/viterbi29.c new file mode 100644 index 0000000..52c8a47 --- /dev/null +++ b/viterbi29.c @@ -0,0 +1,127 @@ +/* Switch to K=9 r=1/2 Viterbi decoder with optional Intel or PowerPC SIMD + * Copyright Feb 2004, Phil Karn, KA9Q + */ +#include +#include +#include +#include "fec.h" + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi29(int len){ + find_cpu_mode(); + + switch(Cpu_mode){ + case PORT: + default: + return create_viterbi29_port(len); +#ifdef __VEC__ + case ALTIVEC: + return create_viterbi29_av(len); +#endif +#ifdef __i386__ + case MMX: + return create_viterbi29_mmx(len); + case SSE: + return create_viterbi29_sse(len); + case SSE2: + return create_viterbi29_sse2(len); +#endif + } +} + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi29(void *p,int starting_state){ + switch(Cpu_mode){ + case PORT: + default: + return init_viterbi29_port(p,starting_state); +#ifdef __VEC__ + case ALTIVEC: + return init_viterbi29_av(p,starting_state); +#endif +#ifdef __i386__ + case MMX: + return init_viterbi29_mmx(p,starting_state); + case SSE: + return init_viterbi29_sse(p,starting_state); + case SSE2: + return init_viterbi29_sse2(p,starting_state); +#endif + } +} + +/* Viterbi chainback */ +int chainback_viterbi29( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + + switch(Cpu_mode){ + case PORT: + default: + return chainback_viterbi29_port(p,data,nbits,endstate); +#ifdef __VEC__ + case ALTIVEC: + return chainback_viterbi29_av(p,data,nbits,endstate); +#endif +#ifdef __i386__ + case MMX: + return chainback_viterbi29_mmx(p,data,nbits,endstate); + case SSE: + return chainback_viterbi29_sse(p,data,nbits,endstate); + case SSE2: + return chainback_viterbi29_sse2(p,data,nbits,endstate); +#endif + } +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi29(void *p){ + switch(Cpu_mode){ + case PORT: + default: + delete_viterbi29_port(p); + break; +#ifdef __VEC__ + case ALTIVEC: + delete_viterbi29_av(p); + break; +#endif +#ifdef __i386__ + case MMX: + delete_viterbi29_mmx(p); + break; + case SSE: + delete_viterbi29_sse(p); + break; + case SSE2: + delete_viterbi29_sse2(p); + break; +#endif + } +} + +/* Update decoder with a block of demodulated symbols + * Note that nbits is the number of decoded data bits, not the number + * of symbols! + */ +int update_viterbi29_blk(void *p,unsigned char syms[],int nbits){ + switch(Cpu_mode){ + case PORT: + default: + return update_viterbi29_blk_port(p,syms,nbits); +#ifdef __VEC__ + case ALTIVEC: + return update_viterbi29_blk_av(p,syms,nbits); +#endif +#ifdef __i386__ + case MMX: + return update_viterbi29_blk_mmx(p,syms,nbits); + case SSE: + return update_viterbi29_blk_sse(p,syms,nbits); + case SSE2: + return update_viterbi29_blk_sse2(p,syms,nbits); +#endif + } +} diff --git a/viterbi29_av.c b/viterbi29_av.c new file mode 100644 index 0000000..ecd1c19 --- /dev/null +++ b/viterbi29_av.c @@ -0,0 +1,195 @@ +/* K=9 r=1/2 Viterbi decoder for PowerPC G4/G5 Altivec + * Copyright Feb 2004, Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ +#include +#include +#include +#include +#include "fec.h" + +typedef union { unsigned char c[256]; vector bool char v[16]; } decision_t; +typedef union { unsigned char c[256]; vector unsigned char v[16]; } metric_t; + +static union branchtab29 { unsigned char c[128]; vector unsigned char v[8]; } Branchtab29[2]; +static int Init = 0; + +/* State info for instance of Viterbi decoder */ +struct v29 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + decision_t *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + decision_t *decisions; /* Beginning of decisions for block */ +}; + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi29_av(void *p,int starting_state){ + struct v29 *vp = p; + int i; + + for(i=0;i<16;i++) + vp->metrics1.v[i] = (vector unsigned char)(63); + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + vp->old_metrics->c[starting_state & 255] = 0; /* Bias known start state */ + return 0; +} + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi29_av(int len){ + struct v29 *vp; + int state; + + if(!Init){ + /* Initialize branch tables */ + for(state=0;state < 128;state++){ + Branchtab29[0].c[state] = parity((2*state) & V29POLYA) ? 255:0; + Branchtab29[1].c[state] = parity((2*state) & V29POLYB) ? 255:0; + } + Init++; + } + vp = (struct v29 *)malloc(sizeof(struct v29)); + vp->decisions = (decision_t *)malloc((len+8)*sizeof(decision_t)); + init_viterbi29_av(vp,0); + return vp; +} + +/* Viterbi chainback */ +int chainback_viterbi29_av( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + struct v29 *vp = p; + decision_t *d = (decision_t *)vp->decisions; + + /* Make room beyond the end of the encoder register so we can + * accumulate a full byte of decoded data + */ + endstate %= 256; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 8; /* Look past tail */ + while(nbits-- != 0){ + int k; + + k = d[nbits].c[endstate] & 1; + data[nbits>>3] = endstate = (endstate >> 1) | (k << 7); + } + return 0; +} + + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi29_av(void *p){ + struct v29 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} + + +int update_viterbi29_blk_av(void *p,unsigned char *syms,int nbits){ + struct v29 *vp = p; + decision_t *d = (decision_t *)vp->dp; + int i; + + while(nbits--){ + vector unsigned char sym1v,sym2v; + void *tmp; + + /* All this seems necessary just to load a byte into all elements of a vector! */ + sym1v = vec_perm(vec_ld(0,syms),vec_ld(1,syms),vec_lvsl(0,syms)); /* sym1v.0 = syms[0]; sym1v.1 = syms[1] */ + sym2v = vec_splat(sym1v,1); /* Splat syms[1] across sym2v */ + sym1v = vec_splat(sym1v,0); /* Splat syms[0] across sym1v */ + syms += 2; + + for(i=0;i<8;i+=2){ + vector bool char decision0,decision1; + vector unsigned char metric,m_metric,m0,m1,m2,m3,survivor0,survivor1; + + /* Form branch metrics */ + metric = vec_avg(vec_xor(Branchtab29[0].v[i],sym1v),vec_xor(Branchtab29[1].v[i],sym2v)); + metric = vec_sr(metric,(vector unsigned char)(3)); + m_metric = (vector unsigned char)(31) - metric; + + /* Add branch metrics to path metrics */ + m0 = vec_adds(vp->old_metrics->v[i],metric); + m3 = vec_adds(vp->old_metrics->v[8+i],metric); + m1 = vec_adds(vp->old_metrics->v[8+i],m_metric); + m2 = vec_adds(vp->old_metrics->v[i],m_metric); + + /* Compare and select first set */ + decision0 = vec_cmpgt(m0,m1); + decision1 = vec_cmpgt(m2,m3); + survivor0 = vec_min(m0,m1); + survivor1 = vec_min(m2,m3); + + /* Interleave and store decisions and survivors */ + d->v[2*i] = vec_mergeh(decision0,decision1); + d->v[2*i+1] = vec_mergel(decision0,decision1); + vp->new_metrics->v[2*i] = vec_mergeh(survivor0,survivor1); + vp->new_metrics->v[2*i+1] = vec_mergel(survivor0,survivor1); + + /* Form branch metrics */ + metric = vec_avg(vec_xor(Branchtab29[0].v[i+1],sym1v),vec_xor(Branchtab29[1].v[i+1],sym2v)); + metric = vec_sr(metric,(vector unsigned char)(3)); + m_metric = (vector unsigned char)(31) - metric; + + /* Add branch metrics to path metrics */ + m0 = vec_adds(vp->old_metrics->v[i+1],metric); + m3 = vec_adds(vp->old_metrics->v[9+i],metric); + m1 = vec_adds(vp->old_metrics->v[9+i],m_metric); + m2 = vec_adds(vp->old_metrics->v[i+1],m_metric); + + /* Compare and select second set */ + decision0 = vec_cmpgt(m0,m1); + decision1 = vec_cmpgt(m2,m3); + survivor0 = vec_min(m0,m1); + survivor1 = vec_min(m2,m3); + + /* Interleave and store decisions and survivors */ + d->v[2*i+2] = vec_mergeh(decision0,decision1); + d->v[2*i+3] = vec_mergel(decision0,decision1); + vp->new_metrics->v[2*i+2] = vec_mergeh(survivor0,survivor1); + vp->new_metrics->v[2*i+3] = vec_mergel(survivor0,survivor1); + } + d++; + /* renormalize if necessary */ + if(vp->new_metrics->c[0] >= 50){ + int i; + vector unsigned char scale0,scale1; + + /* Find smallest metric and splat */ + scale0 = vp->new_metrics->v[0]; + scale1 = vp->new_metrics->v[1]; + for(i=2;i<16;i+=2){ + scale0 = vec_min(scale0,vp->new_metrics->v[i]); + scale1 = vec_min(scale1,vp->new_metrics->v[i+1]); + } + scale0 = vec_min(scale0,scale1); + scale0 = vec_min(scale0,vec_sld(scale0,scale0,8)); + scale0 = vec_min(scale0,vec_sld(scale0,scale0,4)); + scale0 = vec_min(scale0,vec_sld(scale0,scale0,2)); + scale0 = vec_min(scale0,vec_sld(scale0,scale0,1)); + + /* Now subtract from all metrics */ + for(i=0;i<16;i++) + vp->new_metrics->v[i] = vec_subs(vp->new_metrics->v[i],scale0); + } + /* Swap pointers to old and new metrics */ + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + } + vp->dp = d; + return 0; +} diff --git a/viterbi29_mmx.c b/viterbi29_mmx.c new file mode 100644 index 0000000..e14e72b --- /dev/null +++ b/viterbi29_mmx.c @@ -0,0 +1,95 @@ +/* K=9 r=1/2 Viterbi decoder for MMX + * Copyright Feb 2004, Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ +#include +#include +#include +#include +#include "fec.h" + +typedef union { char c[256]; __m64 v[32];} decision_t; +typedef union { unsigned char c[256]; __m64 v[32];} metric_t; + +unsigned char Mettab29_1[256][128] __attribute__ ((aligned(8))); +unsigned char Mettab29_2[256][128] __attribute__ ((aligned(8))); +static int Init = 0; + +/* State info for instance of Viterbi decoder + * Don't change this without also changing references in mmxbfly29.s! + */ +struct v29 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + decision_t *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + decision_t *decisions; /* Beginning of decisions for block */ +}; + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi29_mmx(int len){ + struct v29 *vp; + int state; + + if(Init == 0){ + /* Initialize branch tables */ + for(state=0;state < 128;state++){ + int symbol; + for(symbol = 0;symbol < 256;symbol++){ + Mettab29_1[symbol][state] = (parity((2*state) & V29POLYA) ? (255-symbol):symbol)/16; + Mettab29_2[symbol][state] = (parity((2*state) & V29POLYB) ? (255-symbol):symbol)/16; + } + } + Init++; + } + vp = (struct v29 *)malloc(sizeof(struct v29)); + vp->decisions = (decision_t *)malloc((len+8)*sizeof(decision_t)); + init_viterbi29(vp,0); + return vp; +} + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi29_mmx(void *p,int starting_state){ + struct v29 *vp = p; + int i; + + for(i=0;i<256;i++) + vp->metrics1.c[i] = 63; + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + vp->old_metrics->c[starting_state & 255] = 0; /* Bias known start state */ + return 0; +} + +/* Viterbi chainback */ +int chainback_viterbi29_mmx( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + + struct v29 *vp = (struct v29 *)p; + decision_t *d = (decision_t *)vp->decisions; + + endstate &= 255; + d += 8; /* Look past tail */ + while(nbits-- != 0){ + int k; + + k = d[nbits].c[endstate] & 1; + data[nbits>>3] = endstate = (endstate >> 1) | (k << 7); + } + return 0; +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi29_mmx(void *p){ + struct v29 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} diff --git a/viterbi29_port.c b/viterbi29_port.c new file mode 100644 index 0000000..33fdfd3 --- /dev/null +++ b/viterbi29_port.c @@ -0,0 +1,144 @@ +/* K=9 r=1/2 Viterbi decoder in portable C + * Copyright Feb 2004, Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ +#include +#include +#include +#include "fec.h" + +typedef union { unsigned int w[256]; } metric_t; +typedef union { unsigned long w[8];} decision_t; + +static union { unsigned char c[128]; } Branchtab29[2]; +static int Init = 0; + +/* State info for instance of Viterbi decoder */ +struct v29 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + decision_t *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + decision_t *decisions; /* Beginning of decisions for block */ +}; + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi29_port(void *p,int starting_state){ + struct v29 *vp = p; + int i; + + for(i=0;i<256;i++) + vp->metrics1.w[i] = 63; + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + vp->old_metrics->w[starting_state & 255] = 0; /* Bias known start state */ + return 0; +} + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi29_port(int len){ + struct v29 *vp; + int state; + + if(!Init){ + /* Initialize branch tables */ + for(state=0;state < 128;state++){ + Branchtab29[0].c[state] = parity((2*state) & V29POLYA) ? 255:0; + Branchtab29[1].c[state] = parity((2*state) & V29POLYB) ? 255:0; + } + Init++; + } + vp = (struct v29 *)malloc(sizeof(struct v29)); + vp->decisions = (decision_t *)malloc((len+8)*sizeof(decision_t)); + init_viterbi29_port(vp,0); + + return vp; +} + + +/* Viterbi chainback */ +int chainback_viterbi29_port( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + struct v29 *vp = p; + decision_t *d = vp->decisions; + + /* Make room beyond the end of the encoder register so we can + * accumulate a full byte of decoded data + */ + endstate %= 256; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 8; /* Look past tail */ + while(nbits-- != 0){ + int k; + + k = (d[nbits].w[(endstate)/32] >> (endstate%32)) & 1; + data[nbits>>3] = endstate = (endstate >> 1) | (k << 7); + } + return 0; +} + + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi29_port(void *p){ + struct v29 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} + +/* C-language butterfly */ +#define BFLY(i) {\ +unsigned int metric,m0,m1,decision;\ + metric = (Branchtab29[0].c[i] ^ sym0) + (Branchtab29[1].c[i] ^ sym1);\ + m0 = vp->old_metrics->w[i] + metric;\ + m1 = vp->old_metrics->w[i+128] + (510 - metric);\ + decision = (signed int)(m0-m1) > 0;\ + vp->new_metrics->w[2*i] = decision ? m1 : m0;\ + d->w[i/16] |= decision << ((2*i)&31);\ + m0 -= (metric+metric-510);\ + m1 += (metric+metric-510);\ + decision = (signed int)(m0-m1) > 0;\ + vp->new_metrics->w[2*i+1] = decision ? m1 : m0;\ + d->w[i/16] |= decision << ((2*i+1)&31);\ +} + +/* Update decoder with a block of demodulated symbols + * Note that nbits is the number of decoded data bits, not the number + * of symbols! + */ + +int update_viterbi29_blk_port(void *p,unsigned char *syms,int nbits){ + struct v29 *vp = p; + void *tmp; + decision_t *d = (decision_t *)vp->dp; + while(nbits--){ + unsigned char sym0,sym1; + int i; + + for(i=0;i<8;i++) + d->w[i] = 0; + sym0 = *syms++; + sym1 = *syms++; + + for(i=0;i<128;i++) + BFLY(i); + + d++; + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + } + vp->dp = d; + return 0; +} diff --git a/viterbi29_sse.c b/viterbi29_sse.c new file mode 100644 index 0000000..d53c32a --- /dev/null +++ b/viterbi29_sse.c @@ -0,0 +1,99 @@ +/* K=9 r=1/2 Viterbi decoder for SSE + * Copyright Feb 2004, Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ +#include +#include +#include +#include +#include "fec.h" + +typedef union { unsigned char w[256]; __m64 v[32];} metric_t; +typedef union { unsigned long w[8]; unsigned char c[32]; __m64 v[4];} decision_t; + +union branchtab29 { unsigned char c[128]; } Branchtab29_sse[2]; +static int Init = 0; + +/* State info for instance of Viterbi decoder + * Don't change this without also changing references in [mmx|sse|sse2]bfly29.s! + */ +struct v29 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + decision_t *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + decision_t *decisions; /* Beginning of decisions for block */ +}; + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi29_sse(int len){ + struct v29 *vp; + int state; + + if(!Init){ + /* Initialize branch tables */ + for(state=0;state < 128;state++){ + Branchtab29_sse[0].c[state] = parity((2*state) & V29POLYA) ? 255:0; + Branchtab29_sse[1].c[state] = parity((2*state) & V29POLYB) ? 255:0; + } + Init++; + } + vp = (struct v29 *)malloc(sizeof(struct v29)); + vp->decisions = (decision_t *)malloc((len+8)*sizeof(decision_t)); + init_viterbi29(vp,0); + return vp; +} + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi29_sse(void *p,int starting_state){ + struct v29 *vp = p; + int i; + + for(i=0;i<256;i++) + vp->metrics1.w[i] = 200; + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + vp->old_metrics->w[starting_state & 255] = 0; /* Bias known start state */ + return 0; +} + +/* Viterbi chainback */ +int chainback_viterbi29_sse( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + struct v29 *vp = p; + decision_t *d = vp->decisions; + + /* Make room beyond the end of the encoder register so we can + * accumulate a full byte of decoded data + */ + endstate %= 256; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 8; /* Look past tail */ + while(nbits-- != 0){ + int k; + + k = (d[nbits].c[endstate/8] >> (endstate%8)) & 1; + data[nbits>>3] = endstate = (endstate >> 1) | (k << 7); + } + return 0; +} + + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi29_sse(void *p){ + struct v29 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} diff --git a/viterbi29_sse2.c b/viterbi29_sse2.c new file mode 100644 index 0000000..2c08e62 --- /dev/null +++ b/viterbi29_sse2.c @@ -0,0 +1,100 @@ +/* K=9 r=1/2 Viterbi decoder for SSE2 + * Copyright Feb 2004, Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ +#include +#include +#include +#include +#include "fec.h" + +typedef union { unsigned char c[256]; __m128i v[16];} metric_t; +typedef union { unsigned long w[8]; unsigned char c[32];} decision_t; + +union branchtab29 { unsigned char c[128]; } Branchtab29_sse2[2]; +static int Init = 0; + +/* State info for instance of Viterbi decoder + * Don't change this without also changing references in sse2bfly29.s! + */ +struct v29 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + decision_t *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + decision_t *decisions; /* Beginning of decisions for block */ +}; + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi29_sse2(void *p,int starting_state){ + struct v29 *vp = p; + int i; + + for(i=0;i<256;i++) + vp->metrics1.c[i] = 63; + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + vp->old_metrics->c[starting_state & 255] = 0; /* Bias known start state */ + return 0; +} + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi29_sse2(int len){ + struct v29 *vp; + int state; + + if(!Init){ + /* Initialize branch tables */ + for(state=0;state < 128;state++){ + Branchtab29_sse2[0].c[state] = parity((2*state) & V29POLYA) ? 255:0; + Branchtab29_sse2[1].c[state] = parity((2*state) & V29POLYB) ? 255:0; + } + Init++; + } + vp = (struct v29 *)malloc(sizeof(struct v29)); + vp->decisions = (decision_t *)malloc((len+8)*sizeof(decision_t)); + init_viterbi29_sse2(vp,0); + return vp; +} + + +/* Viterbi chainback */ +int chainback_viterbi29_sse2( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + struct v29 *vp = p; + decision_t *d = vp->decisions; + + /* Make room beyond the end of the encoder register so we can + * accumulate a full byte of decoded data + */ + endstate %= 256; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 8; /* Look past tail */ + while(nbits-- != 0){ + int k; + + k = (d[nbits].c[endstate/8] >> (endstate%8)) & 1; + data[nbits>>3] = endstate = (endstate >> 1) | (k << 7); + } + return 0; +} + + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi29_sse2(void *p){ + struct v29 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} diff --git a/viterbi615.c b/viterbi615.c new file mode 100644 index 0000000..32dc724 --- /dev/null +++ b/viterbi615.c @@ -0,0 +1,129 @@ +/* K=15 r=1/6 Viterbi decoder with optional Intel or PowerPC SIMD + * Copyright Feb 2004, Phil Karn, KA9Q + */ +#include +#include +#include +#include "fec.h" + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi615(int len){ + + find_cpu_mode(); + + switch(Cpu_mode){ + case PORT: + default: + return create_viterbi615_port(len); +#ifdef __VEC__ + case ALTIVEC: + return create_viterbi615_av(len); +#endif +#ifdef __i386__ + case MMX: + return create_viterbi615_mmx(len); + case SSE: + return create_viterbi615_sse(len); + case SSE2: + return create_viterbi615_sse2(len); +#endif + } +} + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi615(void *p,int starting_state){ + switch(Cpu_mode){ + case PORT: + default: + return init_viterbi615_port(p,starting_state); +#ifdef __VEC__ + case ALTIVEC: + return init_viterbi615_av(p,starting_state); +#endif +#ifdef __i386__ + case MMX: + return init_viterbi615_mmx(p,starting_state); + case SSE: + return init_viterbi615_sse(p,starting_state); + case SSE2: + return init_viterbi615_sse2(p,starting_state); +#endif + } +} + +/* Viterbi chainback */ +int chainback_viterbi615( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + + switch(Cpu_mode){ + case PORT: + default: + return chainback_viterbi615_port(p,data,nbits,endstate); +#ifdef __VEC__ + case ALTIVEC: + return chainback_viterbi615_av(p,data,nbits,endstate); +#endif +#ifdef __i386__ + case MMX: + return chainback_viterbi615_mmx(p,data,nbits,endstate); + case SSE: + return chainback_viterbi615_sse(p,data,nbits,endstate); + case SSE2: + return chainback_viterbi615_sse2(p,data,nbits,endstate); +#endif + } +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi615(void *p){ + switch(Cpu_mode){ + case PORT: + default: + delete_viterbi615_port(p); + break; +#ifdef __VEC__ + case ALTIVEC: + delete_viterbi615_av(p); + break; +#endif +#ifdef __i386__ + case MMX: + delete_viterbi615_mmx(p); + break; + case SSE: + delete_viterbi615_sse(p); + break; + case SSE2: + delete_viterbi615_sse2(p); + break; +#endif + } +} + +/* Update decoder with a block of demodulated symbols + * Note that nbits is the number of decoded data bits, not the number + * of symbols! + */ +int update_viterbi615_blk(void *p,unsigned char syms[],int nbits){ + switch(Cpu_mode){ + case PORT: + default: + return update_viterbi615_blk_port(p,syms,nbits); +#ifdef __VEC__ + case ALTIVEC: + return update_viterbi615_blk_av(p,syms,nbits); +#endif +#ifdef __i386__ + case MMX: + return update_viterbi615_blk_mmx(p,syms,nbits); + case SSE: + return update_viterbi615_blk_sse(p,syms,nbits); + case SSE2: + return update_viterbi615_blk_sse2(p,syms,nbits); +#endif + } +} + diff --git a/viterbi615_av.c b/viterbi615_av.c new file mode 100644 index 0000000..8581f52 --- /dev/null +++ b/viterbi615_av.c @@ -0,0 +1,255 @@ +/* K=15 r=1/6 Viterbi decoder for PowerPC G4/G5 Altivec vector instructions + * 8-bit offset-binary soft decision samples + * Copyright Mar 2004, Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ +#include +#include +#include +#include +#include "fec.h" + +typedef union { unsigned char c[128][16]; vector unsigned char v[128]; } decision_t; +typedef union { unsigned short s[16384]; vector unsigned short v[2048]; } metric_t; + +static union branchtab615 { unsigned short s[8192]; vector unsigned short v[512];} Branchtab615[6]; +static int Init = 0; + +/* State info for instance of Viterbi decoder */ +struct v615 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + void *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + void *decisions; /* Beginning of decisions for block */ +}; + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi615_av(void *p,int starting_state){ + struct v615 *vp = p; + int i; + + for(i=0;i<2048;i++) + vp->metrics1.v[i] = (vector unsigned short)(1000); + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + vp->old_metrics->s[starting_state & 16383] = 0; /* Bias known start state */ + return 0; +} + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi615_av(int len){ + struct v615 *vp; + int state; + + if(!Init){ + /* Initialize branch tables */ + for(state=0;state < 8192;state++){ + Branchtab615[0].s[state] = parity((2*state) & V615POLYA) ? 255:0; + Branchtab615[1].s[state] = parity((2*state) & V615POLYB) ? 255:0; + Branchtab615[2].s[state] = parity((2*state) & V615POLYC) ? 255:0; + Branchtab615[3].s[state] = parity((2*state) & V615POLYD) ? 255:0; + Branchtab615[4].s[state] = parity((2*state) & V615POLYE) ? 255:0; + Branchtab615[5].s[state] = parity((2*state) & V615POLYF) ? 255:0; + } + Init++; + } + vp = (struct v615 *)malloc(sizeof(struct v615)); + vp->decisions = malloc(sizeof(decision_t)*(len+14)); + init_viterbi615_av(vp,0); + return vp; +} + + +/* Viterbi chainback */ +int chainback_viterbi615_av( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + struct v615 *vp = p; + decision_t *d = (decision_t *)vp->decisions; + int path_metric; + + /* Make room beyond the end of the encoder register so we can + * accumulate a full byte of decoded data + */ + endstate %= 16384; + + path_metric = vp->old_metrics->s[endstate]; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 14; /* Look past tail */ + while(nbits-- != 0){ + int k; + + k = (d[nbits].c[endstate >> 7][endstate & 15] & (0x80 >> ((endstate>>4)&7)) ) ? 1 : 0; + endstate = (k << 13) | (endstate >> 1); + data[nbits>>3] = endstate >> 6; + } + return path_metric; +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi615_av(void *p){ + struct v615 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} + +int update_viterbi615_blk_av(void *p,unsigned char *syms,int nbits){ + struct v615 *vp = p; + decision_t *d = (decision_t *)vp->dp; + int path_metric = 0; + vector unsigned char decisions = (vector unsigned char)(0); + + while(nbits--){ + vector unsigned short symv,sym0v,sym1v,sym2v,sym3v,sym4v,sym5v; + vector unsigned char s; + void *tmp; + int i; + + /* Splat the 0th symbol across sym0v, the 1st symbol across sym1v, etc */ + s = (vector unsigned char)vec_perm(vec_ld(0,syms),vec_ld(5,syms),vec_lvsl(0,syms)); + + symv = (vector unsigned short)vec_mergeh((vector unsigned char)(0),s); /* Unsigned byte->word unpack */ + sym0v = vec_splat(symv,0); + sym1v = vec_splat(symv,1); + sym2v = vec_splat(symv,2); + sym3v = vec_splat(symv,3); + sym4v = vec_splat(symv,4); + sym5v = vec_splat(symv,5); + syms += 6; + + for(i=0;i<1024;i++){ + vector bool short decision0,decision1; + vector unsigned short metric,m_metric,m0,m1,m2,m3,survivor0,survivor1; + + /* Form branch metrics + * Because Branchtab takes on values 0 and 255, and the values of sym?v are offset binary in the range 0-255, + * the XOR operations constitute conditional negation. + * metric and m_metric (-metric) are in the range 0-1530 + */ + m0 = vec_add(vec_xor(Branchtab615[0].v[i],sym0v),vec_xor(Branchtab615[1].v[i],sym1v)); + m1 = vec_add(vec_xor(Branchtab615[2].v[i],sym2v),vec_xor(Branchtab615[3].v[i],sym3v)); + m2 = vec_add(vec_xor(Branchtab615[4].v[i],sym4v),vec_xor(Branchtab615[5].v[i],sym5v)); + metric = vec_add(m0,m1); + metric = vec_add(metric,m2); + m_metric = vec_sub((vector unsigned short)(1530),metric); + + /* Add branch metrics to path metrics */ + m0 = vec_adds(vp->old_metrics->v[i],metric); + m3 = vec_adds(vp->old_metrics->v[1024+i],metric); + m1 = vec_adds(vp->old_metrics->v[1024+i],m_metric); + m2 = vec_adds(vp->old_metrics->v[i],m_metric); + + /* Compare and select */ + decision0 = vec_cmpgt(m0,m1); + decision1 = vec_cmpgt(m2,m3); + survivor0 = vec_min(m0,m1); + survivor1 = vec_min(m2,m3); + + /* Store decisions and survivors. + * To save space without SSE2's handy PMOVMSKB instruction, we pack and store them in + * a funny interleaved fashion that we undo in the chainback function. + */ + decisions = vec_add(decisions,decisions); /* Shift each byte 1 bit to the left */ + + /* Booleans are either 0xff or 0x00. Subtracting 0x00 leaves the lsb zero; subtracting + * 0xff is equivalent to adding 1, which sets the lsb. + */ + decisions = vec_sub(decisions,(vector unsigned char)vec_pack(vec_mergeh(decision0,decision1),vec_mergel(decision0,decision1))); + + vp->new_metrics->v[2*i] = vec_mergeh(survivor0,survivor1); + vp->new_metrics->v[2*i+1] = vec_mergel(survivor0,survivor1); + + if((i % 8) == 7){ + /* We've accumulated a total of 128 decisions, stash and start again */ + d->v[i>>3] = decisions; /* No need to clear, the new bits will replace the old */ + } + } +#if 0 + /* Experimentally determine metric spread + * The results are fixed for a given code and input symbol size + */ + { + int i; + vector unsigned short min_metric; + vector unsigned short max_metric; + union { vector unsigned short v; unsigned short s[8];} t; + int minimum,maximum; + static int max_spread = 0; + + min_metric = max_metric = vp->new_metrics->v[0]; + for(i=1;i<2048;i++){ + min_metric = vec_min(min_metric,vp->new_metrics->v[i]); + max_metric = vec_max(max_metric,vp->new_metrics->v[i]); + } + min_metric = vec_min(min_metric,vec_sld(min_metric,min_metric,8)); + max_metric = vec_max(max_metric,vec_sld(max_metric,max_metric,8)); + min_metric = vec_min(min_metric,vec_sld(min_metric,min_metric,4)); + max_metric = vec_max(max_metric,vec_sld(max_metric,max_metric,4)); + min_metric = vec_min(min_metric,vec_sld(min_metric,min_metric,2)); + max_metric = vec_max(max_metric,vec_sld(max_metric,max_metric,2)); + + t.v = min_metric; + minimum = t.s[0]; + t.v = max_metric; + maximum = t.s[0]; + if(maximum-minimum > max_spread){ + max_spread = maximum-minimum; + printf("metric spread = %d\n",max_spread); + } + } +#endif + + /* Renormalize if necessary. This deserves some explanation. + + * The maximum possible spread, found by experiment, for 4-bit symbols is 405; for 8 bit symbols, it's 12750. + * So by looking at one arbitrary metric we can tell if any of them have possibly saturated. + * However, this is very conservative. Large spreads occur only at very high Eb/No, where + * saturating a bad path metric doesn't do much to increase its chances of being erroneously chosen as a survivor. + + * At more interesting (low) Eb/No ratios, the spreads are much smaller so our chances of saturating a metric + * by not not normalizing when we should are extremely low. So either way, the risk to performance is small. + + * All this is borne out by experiment. + */ + if(vp->new_metrics->s[0] >= USHRT_MAX-12750){ + vector unsigned short scale; + union { vector unsigned short v; unsigned short s[8];} t; + + /* Find smallest metric and splat */ + scale = vp->new_metrics->v[0]; + for(i=1;i<2048;i++) + scale = vec_min(scale,vp->new_metrics->v[i]); + + scale = vec_min(scale,vec_sld(scale,scale,8)); + scale = vec_min(scale,vec_sld(scale,scale,4)); + scale = vec_min(scale,vec_sld(scale,scale,2)); + + /* Subtract it from all metrics + * Work backwards to try to improve the cache hit ratio, assuming LRU + */ + for(i=2047;i>=0;i--) + vp->new_metrics->v[i] = vec_subs(vp->new_metrics->v[i],scale); + t.v = scale; + path_metric += t.s[0]; + } + d++; + /* Swap pointers to old and new metrics */ + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + } + vp->dp = d; + return path_metric; +} diff --git a/viterbi615_mmx.c b/viterbi615_mmx.c new file mode 100644 index 0000000..cb78690 --- /dev/null +++ b/viterbi615_mmx.c @@ -0,0 +1,170 @@ +/* K=15 r=1/6 Viterbi decoder for x86 MMX + * Mar 2004, Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ +#include +#include +#include +#include +#include "fec.h" + +typedef union { unsigned char c[16384]; __m64 v[2048];} decision_t; +typedef union { unsigned short s[16384]; __m64 v[4096];} metric_t; + +static union branchtab615 { unsigned short s[8192]; __m64 v[2048];} Branchtab615[6]; +static int Init = 0; + +/* State info for instance of Viterbi decoder */ +struct v615 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + void *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + void *decisions; /* Beginning of decisions for block */ +}; + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi615_mmx(void *p,int starting_state){ + struct v615 *vp = p; + int i; + + for(i=0;i<16384;i++) + vp->metrics1.s[i] = 1000; + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + vp->old_metrics->s[starting_state & 16383] = 0; /* Bias known start state */ + return 0; +} + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi615_mmx(int len){ + struct v615 *vp; + int state; + + if(!Init){ + /* Initialize branch tables */ + for(state=0;state < 8192;state++){ + Branchtab615[0].s[state] = parity((2*state) & V615POLYA) ? 255:0; + Branchtab615[1].s[state] = parity((2*state) & V615POLYB) ? 255:0; + Branchtab615[2].s[state] = parity((2*state) & V615POLYC) ? 255:0; + Branchtab615[3].s[state] = parity((2*state) & V615POLYD) ? 255:0; + Branchtab615[4].s[state] = parity((2*state) & V615POLYE) ? 255:0; + Branchtab615[5].s[state] = parity((2*state) & V615POLYF) ? 255:0; + } + Init++; + } + vp = (struct v615 *)malloc(sizeof(struct v615)); + vp->decisions = malloc((len+14)*sizeof(decision_t)); + init_viterbi615_mmx(vp,0); + return vp; +} + +/* Viterbi chainback */ +int chainback_viterbi615_mmx( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + struct v615 *vp = p; + decision_t *d = (decision_t *)vp->decisions; + int path_metric; + + endstate %= 16384; + + path_metric = vp->old_metrics->s[endstate]; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 14; /* Look past tail */ + while(nbits-- != 0){ + int k; + + k = d[nbits].c[endstate] & 1; + endstate = (k << 13) | (endstate >> 1); + data[nbits>>3] = endstate >> 6; + } + return path_metric; +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi615_mmx(void *p){ + struct v615 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} + + +int update_viterbi615_blk_mmx(void *p,unsigned char *syms,int nbits){ + struct v615 *vp = p; + decision_t *d = (decision_t *)vp->dp; + int path_metric = 0; + + while(nbits--){ + __m64 sym0v,sym1v,sym2v,sym3v,sym4v,sym5v; + void *tmp; + int i; + + /* Splat the 0th symbol across sym0v, the 1st symbol across sym1v, etc */ + sym0v = _mm_set1_pi16(syms[0]); + sym1v = _mm_set1_pi16(syms[1]); + sym2v = _mm_set1_pi16(syms[2]); + sym3v = _mm_set1_pi16(syms[3]); + sym4v = _mm_set1_pi16(syms[4]); + sym5v = _mm_set1_pi16(syms[5]); + syms += 6; + + for(i=0;i<2048;i++){ + __m64 decision0,decision1,metric,m_metric,m0,m1,m2,m3,survivor0,survivor1; + + /* Form branch metrics + * Because Branchtab takes on values 0 and 255, and the values of sym?v are offset binary in the range 0-255, + * the XOR operations constitute conditional negation. + * metric and m_metric (-metric) are in the range 0-1530 + */ + m0 = _mm_add_pi16(_mm_xor_si64(Branchtab615[0].v[i],sym0v),_mm_xor_si64(Branchtab615[1].v[i],sym1v)); + m1 = _mm_add_pi16(_mm_xor_si64(Branchtab615[2].v[i],sym2v),_mm_xor_si64(Branchtab615[3].v[i],sym3v)); + m2 = _mm_add_pi16(_mm_xor_si64(Branchtab615[4].v[i],sym4v),_mm_xor_si64(Branchtab615[5].v[i],sym5v)); + metric = _mm_add_pi16(m0,_mm_add_pi16(m1,m2)); + m_metric = _mm_sub_pi16(_mm_set1_pi16(1530),metric); + + /* Add branch metrics to path metrics */ + m0 = _mm_add_pi16(vp->old_metrics->v[i],metric); + m3 = _mm_add_pi16(vp->old_metrics->v[2048+i],metric); + m1 = _mm_add_pi16(vp->old_metrics->v[2048+i],m_metric); + m2 = _mm_add_pi16(vp->old_metrics->v[i],m_metric); + + /* Compare and select + * There's no packed min instruction in MMX, so we use modulo arithmetic + * to form the decisions and then do the select the hard way + */ + decision0 = _mm_cmpgt_pi16(_mm_sub_pi16(m0,m1),_mm_setzero_si64()); + decision1 = _mm_cmpgt_pi16(_mm_sub_pi16(m2,m3),_mm_setzero_si64()); + survivor0 = _mm_or_si64(_mm_and_si64(decision0,m1),_mm_andnot_si64(decision0,m0)); + survivor1 = _mm_or_si64(_mm_and_si64(decision1,m3),_mm_andnot_si64(decision1,m2)); + + /* Merge decisions and store as bytes */ + d->v[i] = _mm_unpacklo_pi8(_mm_packs_pi16(decision0,_mm_setzero_si64()),_mm_packs_pi16(decision1,_mm_setzero_si64())); + + /* Store surviving metrics */ + vp->new_metrics->v[2*i] = _mm_unpacklo_pi16(survivor0,survivor1); + vp->new_metrics->v[2*i+1] = _mm_unpackhi_pi16(survivor0,survivor1); + } + if(vp->new_metrics->s[0] < vp->old_metrics->s[0]) + path_metric += 65536; /* Hack: wraparound probably occured */ + d++; + /* Swap pointers to old and new metrics */ + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + } + vp->dp = d; + _mm_empty(); + return path_metric; +} diff --git a/viterbi615_port.c b/viterbi615_port.c new file mode 100644 index 0000000..a3d0213 --- /dev/null +++ b/viterbi615_port.c @@ -0,0 +1,143 @@ +/* K=15 r=1/6 Viterbi decoder in portable C + * Copyright Mar 2004, Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ +#include +#include +#include +#include +#include "fec.h" + +typedef union { unsigned long w[512]; unsigned char c[2048];} decision_t; +typedef union { unsigned long w[16384]; } metric_t; + +static union branchtab615 { unsigned long w[8192]; } Branchtab615[6] __attribute__ ((aligned(16))); +static int Init = 0; + +/* State info for instance of Viterbi decoder */ +struct v615 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + decision_t *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + decision_t *decisions; /* Beginning of decisions for block */ +}; + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi615_port(int len){ + struct v615 *vp; + int state; + + if(!Init){ + /* Initialize branch tables */ + for(state=0;state < 8192;state++){ + Branchtab615[0].w[state] = parity((2*state) & V615POLYA) ? 255:0; + Branchtab615[1].w[state] = parity((2*state) & V615POLYB) ? 255:0; + Branchtab615[2].w[state] = parity((2*state) & V615POLYC) ? 255:0; + Branchtab615[3].w[state] = parity((2*state) & V615POLYD) ? 255:0; + Branchtab615[4].w[state] = parity((2*state) & V615POLYE) ? 255:0; + Branchtab615[5].w[state] = parity((2*state) & V615POLYF) ? 255:0; + } + Init++; + } + vp = (struct v615 *)malloc(sizeof(struct v615)); + vp->decisions = malloc((len+14)*sizeof(decision_t)); + init_viterbi615(vp,0); + return vp; +} + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi615_port(void *p,int starting_state){ + struct v615 *vp = p; + int i; + + for(i=0;i<16384;i++) + vp->metrics1.w[i] = 1000; + + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + vp->old_metrics->w[starting_state & 16383] = 0; /* Bias known start state */ + return 0; +} + +/* Viterbi chainback */ +int chainback_viterbi615_port( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + struct v615 *vp = p; + decision_t *d = (decision_t *)vp->decisions; + + endstate %= 16384; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 14; /* Look past tail */ + while(nbits-- != 0){ + int k; + + k = (d[nbits].c[endstate/8] >> (endstate%8)) & 1; + endstate = (k << 13) | (endstate >> 1); + data[nbits>>3] = endstate >> 6; + } + return 0; +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi615_port(void *p){ + struct v615 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} + +/* C-language butterfly */ +#define BFLY(i) {\ +unsigned long metric,m0,m1,m2,m3,decision0,decision1;\ + metric = ((Branchtab615[0].w[i] ^ syms[0]) + (Branchtab615[1].w[i] ^ syms[1])\ + +(Branchtab615[2].w[i] ^ syms[2]) + (Branchtab615[3].w[i] ^ syms[3])\ + +(Branchtab615[4].w[i] ^ syms[4]) + (Branchtab615[5].w[i] ^ syms[5]));\ + m0 = vp->old_metrics->w[i] + metric;\ + m1 = vp->old_metrics->w[i+8192] + (1530 - metric);\ + m2 = vp->old_metrics->w[i] + (1530-metric);\ + m3 = vp->old_metrics->w[i+8192] + metric;\ + decision0 = (signed long)(m0-m1) >= 0;\ + decision1 = (signed long)(m2-m3) >= 0;\ + vp->new_metrics->w[2*i] = decision0 ? m1 : m0;\ + vp->new_metrics->w[2*i+1] = decision1 ? m3 : m2;\ + d->c[i/4] |= ((decision0|(decision1<<1)) << ((2*i)&7));\ +} +/* Update decoder with a block of demodulated symbols + * Note that nbits is the number of decoded data bits, not the number + * of symbols! + */ + +int update_viterbi615_blk_port(void *p,unsigned char *syms,int nbits){ + struct v615 *vp = p; + void *tmp; + decision_t *d = (decision_t *)vp->dp; + int i; + + while(nbits--){ + memset(d,0,sizeof(decision_t)); + for(i=0;i<8192;i++) + BFLY(i); + + syms += 6; + d++; + /* Swap pointers to old and new metrics */ + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + } + vp->dp = d; + return 0; +} + diff --git a/viterbi615_sse.c b/viterbi615_sse.c new file mode 100644 index 0000000..e593390 --- /dev/null +++ b/viterbi615_sse.c @@ -0,0 +1,190 @@ +/* K=15 r=1/6 Viterbi decoder for x86 SSE + * Copyright Mar 2004, Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ +#include +#include +#include +#include +#include +#include "fec.h" + +typedef union { unsigned long w[512]; unsigned char c[2048];} decision_t; +typedef union { signed short s[16384]; __m64 v[4096];} metric_t; + +static union branchtab615 { unsigned short s[8192]; __m64 v[2048];} Branchtab615[6]; +static int Init = 0; + +/* State info for instance of Viterbi decoder */ +struct v615 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + void *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + void *decisions; /* Beginning of decisions for block */ +}; + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi615_sse(void *p,int starting_state){ + struct v615 *vp = p; + int i; + + for(i=0;i<16384;i++) + vp->metrics1.s[i] = (SHRT_MIN+1000); + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + vp->old_metrics->s[starting_state & 16383] = SHRT_MIN; /* Bias known start state */ + return 0; +} + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi615_sse(int len){ + struct v615 *vp; + int state; + + if(!Init){ + /* Initialize branch tables */ + for(state=0;state < 8192;state++){ + Branchtab615[0].s[state] = parity((2*state) & V615POLYA) ? 255:0; + Branchtab615[1].s[state] = parity((2*state) & V615POLYB) ? 255:0; + Branchtab615[2].s[state] = parity((2*state) & V615POLYC) ? 255:0; + Branchtab615[3].s[state] = parity((2*state) & V615POLYD) ? 255:0; + Branchtab615[4].s[state] = parity((2*state) & V615POLYE) ? 255:0; + Branchtab615[5].s[state] = parity((2*state) & V615POLYF) ? 255:0; + } + Init++; + } + vp = (struct v615 *)malloc(sizeof(struct v615)); + vp->decisions = malloc((len+14)*sizeof(decision_t)); + init_viterbi615_sse(vp,0); + return vp; +} + +/* Viterbi chainback */ +int chainback_viterbi615_sse( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + struct v615 *vp = p; + decision_t *d = (decision_t *)vp->decisions; + int path_metric; + + endstate %= 16384; + + path_metric = vp->old_metrics->s[endstate]; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 14; /* Look past tail */ + while(nbits-- != 0){ + int k; + + /* k = (d[nbits].w[endstate/32] >> (endstate%32)) & 1;*/ + k = (d[nbits].c[endstate/8] >> (endstate%8)) & 1; + endstate = (k << 13) | (endstate >> 1); + data[nbits>>3] = endstate >> 6; + } + return path_metric - SHRT_MIN; +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi615_sse(void *p){ + struct v615 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} + + +int update_viterbi615_blk_sse(void *p,unsigned char *syms,int nbits){ + struct v615 *vp = p; + decision_t *d = (decision_t *)vp->dp; + int path_metric = 0; + + while(nbits--){ + __m64 sym0v,sym1v,sym2v,sym3v,sym4v,sym5v; + void *tmp; + int i; + + /* Splat the 0th symbol across sym0v, the 1st symbol across sym1v, etc */ + sym0v = _mm_set1_pi16(syms[0]); + sym1v = _mm_set1_pi16(syms[1]); + sym2v = _mm_set1_pi16(syms[2]); + sym3v = _mm_set1_pi16(syms[3]); + sym4v = _mm_set1_pi16(syms[4]); + sym5v = _mm_set1_pi16(syms[5]); + syms += 6; + + for(i=0;i<2048;i++){ + __m64 decision0,decision1,metric,m_metric,m0,m1,m2,m3,survivor0,survivor1; + + /* Form branch metrics + * Because Branchtab takes on values 0 and 255, and the values of sym?v are offset binary in the range 0-255, + * the XOR operations constitute conditional negation. + * metric and m_metric (-metric) are in the range 0-1530 + */ + m0 = _mm_add_pi16(_mm_xor_si64(Branchtab615[0].v[i],sym0v),_mm_xor_si64(Branchtab615[1].v[i],sym1v)); + m1 = _mm_add_pi16(_mm_xor_si64(Branchtab615[2].v[i],sym2v),_mm_xor_si64(Branchtab615[3].v[i],sym3v)); + m2 = _mm_add_pi16(_mm_xor_si64(Branchtab615[4].v[i],sym4v),_mm_xor_si64(Branchtab615[5].v[i],sym5v)); + metric = _mm_add_pi16(m0,_mm_add_pi16(m1,m2)); + m_metric = _mm_sub_pi16(_mm_set1_pi16(1530),metric); + + /* Add branch metrics to path metrics */ + m0 = _mm_adds_pi16(vp->old_metrics->v[i],metric); + m3 = _mm_adds_pi16(vp->old_metrics->v[2048+i],metric); + m1 = _mm_adds_pi16(vp->old_metrics->v[2048+i],m_metric); + m2 = _mm_adds_pi16(vp->old_metrics->v[i],m_metric); + + /* Compare and select */ + survivor0 = _mm_min_pi16(m0,m1); + survivor1 = _mm_min_pi16(m2,m3); + decision0 = _mm_cmpeq_pi16(survivor0,m1); + decision1 = _mm_cmpeq_pi16(survivor1,m3); + + /* Pack decisions into 8 bits and store */ + d->c[i] = _mm_movemask_pi8(_mm_unpacklo_pi8(_mm_packs_pi16(decision0,_mm_setzero_si64()),_mm_packs_pi16(decision1,_mm_setzero_si64()))); + + /* Store surviving metrics */ + vp->new_metrics->v[2*i] = _mm_unpacklo_pi16(survivor0,survivor1); + vp->new_metrics->v[2*i+1] = _mm_unpackhi_pi16(survivor0,survivor1); + } + /* See if we need to renormalize + * Max metric spread for this code with 0-255 branch metrics is 12750 + */ + if(vp->new_metrics->s[0] >= SHRT_MAX-12750){ + int i,adjust; + __m64 adjustv; + union { __m64 v; signed short w[4]; } t; + + /* Find smallest metric and set adjustv to bring it down to SHRT_MIN */ + adjustv = vp->new_metrics->v[0]; + for(i=1;i<4096;i++) + adjustv = _mm_min_pi16(adjustv,vp->new_metrics->v[i]); + + adjustv = _mm_min_pi16(adjustv,_mm_srli_si64(adjustv,32)); + adjustv = _mm_min_pi16(adjustv,_mm_srli_si64(adjustv,16)); + t.v = adjustv; + adjust = t.w[0] - SHRT_MIN; + path_metric += adjust; + adjustv = _mm_set1_pi16(adjust); + + for(i=0;i<4096;i++) + vp->new_metrics->v[i] = _mm_sub_pi16(vp->new_metrics->v[i],adjustv); + } + d++; + /* Swap pointers to old and new metrics */ + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + } + vp->dp = d; + _mm_empty(); + return path_metric; +} diff --git a/viterbi615_sse2.c b/viterbi615_sse2.c new file mode 100644 index 0000000..c41be85 --- /dev/null +++ b/viterbi615_sse2.c @@ -0,0 +1,195 @@ +/* K=15 r=1/6 Viterbi decoder for x86 SSE2 + * Copyright Mar 2004, Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ +#include +#include +#include +#include +#include +#include "fec.h" + +typedef union { unsigned long w[512]; unsigned short s[1024];} decision_t; +typedef union { signed short s[16384]; __m128i v[2048];} metric_t; + +static union branchtab615 { unsigned short s[8192]; __m128i v[1024];} Branchtab615[6]; +static int Init = 0; + +/* State info for instance of Viterbi decoder */ +struct v615 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + void *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + void *decisions; /* Beginning of decisions for block */ +}; + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi615_sse2(void *p,int starting_state){ + struct v615 *vp = p; + int i; + + for(i=0;i<16384;i++) + vp->metrics1.s[i] = (SHRT_MIN+1000); + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + vp->old_metrics->s[starting_state & 16383] = SHRT_MIN; /* Bias known start state */ + return 0; +} + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi615_sse2(int len){ + struct v615 *vp; + int state; + + if(!Init){ + /* Initialize branch tables */ + for(state=0;state < 8192;state++){ + Branchtab615[0].s[state] = parity((2*state) & V615POLYA) ? 255:0; + Branchtab615[1].s[state] = parity((2*state) & V615POLYB) ? 255:0; + Branchtab615[2].s[state] = parity((2*state) & V615POLYC) ? 255:0; + Branchtab615[3].s[state] = parity((2*state) & V615POLYD) ? 255:0; + Branchtab615[4].s[state] = parity((2*state) & V615POLYE) ? 255:0; + Branchtab615[5].s[state] = parity((2*state) & V615POLYF) ? 255:0; + } + Init++; + } + vp = (struct v615 *)malloc(sizeof(struct v615)); + vp->decisions = malloc((len+14)*sizeof(decision_t)); + init_viterbi615_sse2(vp,0); + return vp; +} + +/* Viterbi chainback */ +int chainback_viterbi615_sse2( + void *p, + unsigned char *data, /* Decoded output data */ + unsigned int nbits, /* Number of data bits */ + unsigned int endstate){ /* Terminal encoder state */ + struct v615 *vp = p; + decision_t *d = (decision_t *)vp->decisions; + int path_metric; + + endstate %= 16384; + + path_metric = vp->old_metrics->s[endstate]; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 14; /* Look past tail */ + while(nbits-- != 0){ + int k; + + k = (d[nbits].w[endstate/32] >> (endstate%32)) & 1; + endstate = (k << 13) | (endstate >> 1); + data[nbits>>3] = endstate >> 6; + } + return path_metric; +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi615_sse2(void *p){ + struct v615 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} + + +int update_viterbi615_blk_sse2(void *p,unsigned char *syms,int nbits){ + struct v615 *vp = p; + decision_t *d = (decision_t *)vp->dp; + int path_metric = 0; + + while(nbits--){ + __m128i sym0v,sym1v,sym2v,sym3v,sym4v,sym5v; + void *tmp; + int i; + + /* Splat the 0th symbol across sym0v, the 1st symbol across sym1v, etc */ + sym0v = _mm_set1_epi16(syms[0]); + sym1v = _mm_set1_epi16(syms[1]); + sym2v = _mm_set1_epi16(syms[2]); + sym3v = _mm_set1_epi16(syms[3]); + sym4v = _mm_set1_epi16(syms[4]); + sym5v = _mm_set1_epi16(syms[5]); + syms += 6; + + /* SSE2 doesn't support saturated adds on unsigned shorts, so we have to use signed shorts */ + for(i=0;i<1024;i++){ + __m128i decision0,decision1,metric,m_metric,m0,m1,m2,m3,survivor0,survivor1; + + /* Form branch metrics + * Because Branchtab takes on values 0 and 255, and the values of sym?v are offset binary in the range 0-255, + * the XOR operations constitute conditional negation. + * metric and m_metric (-metric) are in the range 0-1530 + */ + m0 = _mm_add_epi16(_mm_xor_si128(Branchtab615[0].v[i],sym0v),_mm_xor_si128(Branchtab615[1].v[i],sym1v)); + m1 = _mm_add_epi16(_mm_xor_si128(Branchtab615[2].v[i],sym2v),_mm_xor_si128(Branchtab615[3].v[i],sym3v)); + m2 = _mm_add_epi16(_mm_xor_si128(Branchtab615[4].v[i],sym4v),_mm_xor_si128(Branchtab615[5].v[i],sym5v)); + metric = _mm_add_epi16(m0,_mm_add_epi16(m1,m2)); + m_metric = _mm_sub_epi16(_mm_set1_epi16(1530),metric); + + /* Add branch metrics to path metrics */ + m0 = _mm_adds_epi16(vp->old_metrics->v[i],metric); + m3 = _mm_adds_epi16(vp->old_metrics->v[1024+i],metric); + m1 = _mm_adds_epi16(vp->old_metrics->v[1024+i],m_metric); + m2 = _mm_adds_epi16(vp->old_metrics->v[i],m_metric); + + /* Compare and select */ + survivor0 = _mm_min_epi16(m0,m1); + survivor1 = _mm_min_epi16(m2,m3); + decision0 = _mm_cmpeq_epi16(survivor0,m1); + decision1 = _mm_cmpeq_epi16(survivor1,m3); + + /* Pack each set of decisions into 8 8-bit bytes, then interleave them and compress into 16 bits */ + d->s[i] = _mm_movemask_epi8(_mm_unpacklo_epi8(_mm_packs_epi16(decision0,_mm_setzero_si128()),_mm_packs_epi16(decision1,_mm_setzero_si128()))); + + /* Store surviving metrics */ + vp->new_metrics->v[2*i] = _mm_unpacklo_epi16(survivor0,survivor1); + vp->new_metrics->v[2*i+1] = _mm_unpackhi_epi16(survivor0,survivor1); + } + /* See if we need to renormalize + * Max metric spread for this code with 0-90 branch metrics is 405 + */ + if(vp->new_metrics->s[0] >= SHRT_MAX-12750){ + int i,adjust; + __m128i adjustv; + union { __m128i v; signed short w[8]; } t; + + /* Find smallest metric and set adjustv to bring it down to SHRT_MIN */ + adjustv = vp->new_metrics->v[0]; + for(i=1;i<2048;i++) + adjustv = _mm_min_epi16(adjustv,vp->new_metrics->v[i]); + + adjustv = _mm_min_epi16(adjustv,_mm_srli_si128(adjustv,8)); + adjustv = _mm_min_epi16(adjustv,_mm_srli_si128(adjustv,4)); + adjustv = _mm_min_epi16(adjustv,_mm_srli_si128(adjustv,2)); + t.v = adjustv; + adjust = t.w[0] - SHRT_MIN; + path_metric += adjust; + adjustv = _mm_set1_epi16(adjust); + + /* We cannot use a saturated subtract, because we often have to adjust by more than SHRT_MAX + * This is okay since it can't overflow anyway + */ + for(i=0;i<2048;i++) + vp->new_metrics->v[i] = _mm_sub_epi16(vp->new_metrics->v[i],adjustv); + } + d++; + /* Swap pointers to old and new metrics */ + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + } + vp->dp = d; + return path_metric; +} + + diff --git a/vtest224.c b/vtest224.c new file mode 100644 index 0000000..4f7c330 --- /dev/null +++ b/vtest224.c @@ -0,0 +1,185 @@ +/* Test viterbi decoder speeds */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_GETOPT_H +#include +#endif +#include "viterbi224.h" +#include "code.h" +#include "sim.h" + +#if HAVE_GETOPT_LONG +struct option Options[] = { + {"frame-length",1,NULL,'l'}, + {"frame-count",1,NULL,'n'}, + {"ebn0",1,NULL,'e'}, + {"gain",1,NULL,'g'}, + {"verbose",0,NULL,'v'}, + {NULL}, +}; +#endif + +#define RATE (0.5) +#define MAXBYTES 10000 + + +double Gain = 24.0; +int Verbose = 0; + +static inline int popcount(int x){ + return __builtin_popcount(x); +} + + +int main(int argc,char *argv[]){ + int i,d,tr; + int trials = 10,errcnt,framebits=1024; + int tot_errs=0; + unsigned char data[MAXBYTES]; + unsigned char decoded_data[MAXBYTES]; + unsigned char xordata[MAXBYTES]; + unsigned char symbols[8*2*MAXBYTES]; + void *vp; + extern char *optarg; + struct rusage start,finish; + double extime; + double noise,esn0,ebn0; + time_t t; + int badframes=0; + + time(&t); + srandom(t); + ebn0 = -100; +#if HAVE_GETOPT_LONG + while((d = getopt_long(argc,argv,"l:n:e:g:v",Options,NULL)) != EOF){ +#else + while((d = getopt(argc,argv,"l:n:e:g:v")) != EOF){ +#endif + switch(d){ + case 'l': + framebits = atoi(optarg); + break; + case 'n': + trials = atoi(optarg); + break; + case 'e': + ebn0 = atof(optarg); + break; + case 'g': + Gain = atof(optarg); + break; + case 'v': + Verbose++; + break; + } + } + if(framebits > 8*MAXBYTES){ + fprintf(stderr,"Frame limited to %d bits\n",MAXBYTES*8); + framebits = MAXBYTES*8; + } + if((vp = create_viterbi224(framebits)) == NULL){ + printf("create_viterbi224 failed\n"); + exit(1); + } + if(ebn0 != -100){ + esn0 = ebn0 + 10*log10((double)RATE); /* Es/No in dB */ + // Compute noise voltage; the sqrt(1/2) term is for BPSK + noise = Gain * M_SQRT1_2 / pow(10.,0.05 * esn0); + setup_channel(Gain,noise); + + printf("nframes = %d framesize = %d ebn0 = %.2f dB Gain = %g noise = %g\n",trials,framebits,ebn0,Gain,noise); + + for(tr=0;tr 1 && errcnt != 0){ + printf("frame %d, %d errors: ",tr,errcnt); + for(i=0;i 1) + printf("nframes = %d framesize = %d ebn0 = %.2f dB Gain = %g\n",trials,framebits,ebn0,Gain); + else if(Verbose == 0) + printf("BER %d/%d (%.3g) FER %d/%d (%.3g)\n", + tot_errs,framebits*trials,tot_errs/((double)framebits*trials), + badframes,trials,(double)badframes/trials); + else + printf("\n"); + } else { + /* Do time trials */ + memset(symbols,128,sizeof(symbols)); + printf("Starting time trials\n"); + getrusage(RUSAGE_SELF,&start); + for(tr=0;tr < trials;tr++){ + /* Initialize Viterbi decoder */ + init_viterbi224(vp,0); + + /* Decode block */ + update_viterbi224_blk(vp,symbols,framebits); + + /* Do Viterbi chainback */ + chainback_viterbi224(vp,decoded_data,framebits,0); + } + getrusage(RUSAGE_SELF,&finish); + extime = finish.ru_utime.tv_sec - start.ru_utime.tv_sec + 1e-6*(finish.ru_utime.tv_usec - start.ru_utime.tv_usec); + printf("Execution time for %d %d-bit frames: %.2f sec\n",trials, + framebits,extime); + printf("decoder speed: %g bits/s\n",trials*framebits/extime); + } + delete_viterbi224(vp); + exit(0); +} diff --git a/vtest27.c b/vtest27.c new file mode 100644 index 0000000..acc131d --- /dev/null +++ b/vtest27.c @@ -0,0 +1,183 @@ +/* Test viterbi decoder speeds */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_GETOPT_H +#include +#endif +#include "fec.h" + +#if HAVE_GETOPT_LONG +struct option Options[] = { + {"frame-length",1,NULL,'l'}, + {"frame-count",1,NULL,'n'}, + {"ebn0",1,NULL,'e'}, + {"gain",1,NULL,'g'}, + {"verbose",0,NULL,'v'}, + {"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'}, + {NULL}, +}; +#endif + +#define RATE (1./2.) +#define MAXBYTES 10000 + +double Gain = 32.0; +int Verbose = 0; + +int main(int argc,char *argv[]){ + int i,d,tr; + int sr=0,trials = 10000,errcnt,framebits=2048; + long long int tot_errs=0; + unsigned char bits[MAXBYTES]; + unsigned char data[MAXBYTES]; + unsigned char xordata[MAXBYTES]; + unsigned char symbols[8*2*(MAXBYTES+6)]; + void *vp; + extern char *optarg; + struct tms start,finish; + double extime; + double gain,esn0,ebn0; + time_t t; + int badframes=0; + + time(&t); + srandom(t); + ebn0 = -100; +#if HAVE_GETOPT_LONG + while((d = getopt_long(argc,argv,"l:n:te:g:vapmst",Options,NULL)) != EOF){ +#else + while((d = getopt(argc,argv,"l:n:te:g:vapmst")) != 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 'l': + framebits = atoi(optarg); + break; + case 'n': + trials = atoi(optarg); + break; + case 'e': + ebn0 = atof(optarg); + break; + case 'g': + Gain = atof(optarg); + break; + case 'v': + Verbose++; + break; + } + } + if(framebits > 8*MAXBYTES){ + fprintf(stderr,"Frame limited to %d bits\n",MAXBYTES*8); + framebits = MAXBYTES*8; + } + if((vp = create_viterbi27(framebits)) == NULL){ + printf("create_viterbi27 failed\n"); + exit(1); + } + if(ebn0 != -100){ + esn0 = ebn0 + 10*log10((double)RATE); /* Es/No in dB */ + /* Compute noise voltage. The 0.5 factor accounts for BPSK seeing + * only half the noise power, and the sqrt() converts power to + * voltage. + */ + gain = 1./sqrt(0.5/pow(10.,esn0/10.)); + + printf("nframes = %d framesize = %d ebn0 = %.2f dB gain = %g\n",trials,framebits,ebn0,Gain); + + for(tr=0;tr 1 && errcnt != 0){ + printf("frame %d, %d errors: ",tr,errcnt); + for(i=0;i 1) + printf("nframes = %d framesize = %d ebn0 = %.2f dB gain = %g\n",trials,framebits,ebn0,Gain); + else if(Verbose == 0) + printf("BER %lld/%lld (%.3g) FER %d/%d (%.3g)\n", + tot_errs,(long long)framebits*trials,tot_errs/((double)framebits*trials), + badframes,tr+1,(double)badframes/(tr+1)); + else + printf("\n"); + + } else { + /* Do time trials */ + memset(symbols,127,sizeof(symbols)); + printf("Starting time trials\n"); + times(&start); + for(tr=0;tr < trials;tr++){ + /* Initialize Viterbi decoder */ + init_viterbi27(vp,0); + + /* Decode block */ + update_viterbi27_blk(vp,symbols,framebits); + + /* Do Viterbi chainback */ + chainback_viterbi27(vp,data,framebits,0); + } + times(&finish); + extime = ((double)(finish.tms_utime-start.tms_utime))/CLOCKS_PER_SEC; + printf("Execution time for %d %d-bit frames: %.2f sec\n",trials, + framebits,extime); + printf("decoder speed: %g bits/s\n",trials*framebits/extime); + } + exit(0); +} diff --git a/vtest29.c b/vtest29.c new file mode 100644 index 0000000..e04a97c --- /dev/null +++ b/vtest29.c @@ -0,0 +1,184 @@ +/* Test viterbi decoder speeds */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_GETOPT_H +#include +#endif +#include "fec.h" + +#if HAVE_GETOPT_LONG +struct option Options[] = { + {"frame-length",1,NULL,'l'}, + {"frame-count",1,NULL,'n'}, + {"ebn0",1,NULL,'e'}, + {"gain",1,NULL,'g'}, + {"verbose",0,NULL,'v'}, + {"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'}, + {NULL}, +}; +#endif + +#define RATE (1./2.) +#define MAXBYTES 10000 + +double Gain = 32.0; +int Verbose = 0; + +int main(int argc,char *argv[]){ + int i,d,tr; + int sr=0,trials = 10000,errcnt,framebits=2048; + long long tot_errs=0; + unsigned char bits[MAXBYTES]; + unsigned char data[MAXBYTES]; + unsigned char xordata[MAXBYTES]; + unsigned char symbols[8*2*(MAXBYTES+8)]; + void *vp; + extern char *optarg; + struct tms start,finish; + double extime; + double gain,esn0,ebn0; + time_t t; + int badframes=0; + + time(&t); + srandom(t); + ebn0 = -100; +#if HAVE_GETOPT_LONG + while((d = getopt_long(argc,argv,"l:n:te:g:vapmst",Options,NULL)) != EOF){ +#else + while((d = getopt(argc,argv,"l:n:te:g:vapmst")) != 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 'l': + framebits = atoi(optarg); + break; + case 'n': + trials = atoi(optarg); + break; + case 'e': + ebn0 = atof(optarg); + break; + case 'g': + Gain = atof(optarg); + break; + case 'v': + Verbose++; + break; + } + } + if(framebits > 8*MAXBYTES){ + fprintf(stderr,"Frame limited to %d bits\n",MAXBYTES*8); + framebits = MAXBYTES*8; + } + if((vp = create_viterbi29(framebits)) == NULL){ + printf("create_viterbi29 failed\n"); + exit(1); + } + if(ebn0 != -100){ + esn0 = ebn0 + 10*log10((double)RATE); /* Es/No in dB */ + /* Compute noise voltage. The 0.5 factor accounts for BPSK seeing + * only half the noise power, and the sqrt() converts power to + * voltage. + */ + gain = 1./sqrt(0.5/pow(10.,esn0/10.)); + + printf("nframes = %d framesize = %d ebn0 = %.2f dB gain = %g\n",trials,framebits,ebn0,Gain); + + for(tr=0;tr 1 && errcnt != 0){ + printf("frame %d, %d errors: ",tr,errcnt); + for(i=0;i 1) + printf("nframes = %d framesize = %d ebn0 = %.2f dB gain = %g\n",trials,framebits,ebn0,Gain); + else if(Verbose == 0) + printf("BER %lld/%lld (%.3g) FER %d/%d (%.3g)\n", + tot_errs,(long long)framebits*trials,tot_errs/((double)framebits*trials), + badframes,tr+1,(double)badframes/(tr+1)); + else + printf("\n"); + } else { + /* Do time trials */ + memset(symbols,127,sizeof(symbols)); + printf("Starting time trials\n"); + times(&start); + for(tr=0;tr < trials;tr++){ + /* Initialize Viterbi decoder */ + init_viterbi29(vp,0); + + /* Decode block */ + update_viterbi29_blk(vp,symbols,framebits); + + /* Do Viterbi chainback */ + chainback_viterbi29(vp,data,framebits,0); + } + times(&finish); + extime = ((double)(finish.tms_utime-start.tms_utime))/CLOCKS_PER_SEC; + printf("Execution time for %d %d-bit frames: %.2f sec\n",trials, + framebits,extime); + printf("decoder speed: %g bits/s\n",trials*framebits/extime); + } + exit(0); +} + + diff --git a/vtest615.c b/vtest615.c new file mode 100644 index 0000000..0030d87 --- /dev/null +++ b/vtest615.c @@ -0,0 +1,193 @@ +/* Test viterbi decoder speeds */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_GETOPT_H +#include +#endif +#include "fec.h" + +#if HAVE_GETOPT_LONG +struct option Options[] = { + {"frame-length",1,NULL,'l'}, + {"frame-count",1,NULL,'n'}, + {"ebn0",1,NULL,'e'}, + {"gain",1,NULL,'g'}, + {"verbose",0,NULL,'v'}, + {"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'}, + {NULL}, +}; +#endif + +#define RATE (1./6.) +#define MAXBYTES 10000 +#define OFFSET (127.5) +#define CLIP 255 + +double Gain = 24.0; +int Verbose = 0; + +int main(int argc,char *argv[]){ + int i,d,tr; + int sr=0,trials = 10,errcnt,framebits=2048; + int tot_errs=0; + unsigned char bits[MAXBYTES]; + unsigned char data[MAXBYTES]; + unsigned char xordata[MAXBYTES]; + unsigned char symbols[8*6*(MAXBYTES+14)]; + void *vp; + extern char *optarg; + struct tms start,finish; + double extime; + double gain,esn0,ebn0; + time_t t; + long long metrics = 0; + int badframes=0; + + time(&t); + srandom(t); + ebn0 = -100; +#if HAVE_GETOPT_LONG + while((d = getopt_long(argc,argv,"l:n:te:g:vapmst",Options,NULL)) != EOF){ +#else + while((d = getopt(argc,argv,"l:n:te:g:vapmst")) != 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 'l': + framebits = atoi(optarg); + break; + case 'n': + trials = atoi(optarg); + break; + case 'e': + ebn0 = atof(optarg); + break; + case 'g': + Gain = atof(optarg); + break; + case 'v': + Verbose++; + break; + } + } + if(framebits > 8*MAXBYTES){ + fprintf(stderr,"Frame limited to %d bits\n",MAXBYTES*8); + framebits = MAXBYTES*8; + } + if((vp = create_viterbi615(framebits)) == NULL){ + printf("create_viterbi615 failed\n"); + exit(1); + } + if(ebn0 != -100){ + esn0 = ebn0 + 10*log10((double)RATE); /* Es/No in dB */ + /* Compute noise voltage. The 0.5 factor accounts for BPSK seeing + * only half the noise power, and the sqrt() converts power to + * voltage. + */ + gain = 1./sqrt(0.5/pow(10.,esn0/10.)); + + printf("nframes = %d framesize = %d ebn0 = %.2f dB gain = %g\n",trials,framebits,ebn0,Gain); + + for(tr=0;tr 1 && errcnt != 0){ + printf("frame %d, %d errors: ",tr,errcnt); + for(i=0;i 1) + printf("nframes = %d framesize = %d ebn0 = %.2f dB gain = %g\n",trials,framebits,ebn0,Gain); + else if(Verbose == 0) + printf("BER %d/%d (%.3g) FER %d/%d (%.3g) avg path metric %.2f\n", + tot_errs,framebits*(tr+1),tot_errs/((double)framebits*(tr+1)), + badframes,(tr+1),(double)badframes/(tr+1), + (double)metrics/(tr+1)); + else + printf("\n"); + } else { + /* Do time trials */ + memset(symbols,127,sizeof(symbols)); + printf("Starting time trials\n"); + times(&start); + for(tr=0;tr < trials;tr++){ + /* Initialize Viterbi decoder */ + init_viterbi615(vp,0); + + /* Decode block */ + update_viterbi615_blk(vp,symbols,framebits+14); + + /* Do Viterbi chainback */ + chainback_viterbi615(vp,data,framebits,0); + } + times(&finish); + extime = ((double)(finish.tms_utime-start.tms_utime))/CLOCKS_PER_SEC; + printf("Execution time for %d %d-bit frames: %.2f sec\n",trials, + framebits,extime); + printf("decoder speed: %g bits/s\n",trials*framebits/extime); + } + exit(0); +}