From 0f56254df31aa4a06e78d003ec4130e6b18d5105 Mon Sep 17 00:00:00 2001 From: Gonzalo Arcos Date: Thu, 7 Apr 2016 17:20:12 -0300 Subject: [PATCH] - Added new Viterbi convolutional decoder to the OFDM Decode Mac block replacing the former IT++ version. This new decoder uses SSE2 and improves the performance significantly. The decoder was taken from the gr-dvbt module from GNU Radio. - Disabled some logging functions in the ofdm_mac and ofdm_decode_signal blocks that were degrading the performance significantly. [Gonzalo Arcos | Rodrigo Ferreri] --- examples/wifi_phy_hier.grc | 2 +- include/ieee802-11/ofdm_decode_mac.h | 7 + lib/ofdm_decode_mac.cc | 192 ++++++-- lib/ofdm_mac.cc | 2 +- lib/utils.cc | 694 +++++++++++++++++++++++++++ lib/utils.h | 65 +++ 6 files changed, 926 insertions(+), 36 deletions(-) diff --git a/examples/wifi_phy_hier.grc b/examples/wifi_phy_hier.grc index 26d0447..3aac1e9 100644 --- a/examples/wifi_phy_hier.grc +++ b/examples/wifi_phy_hier.grc @@ -1339,7 +1339,7 @@ log - True + False maxoutbuf diff --git a/include/ieee802-11/ofdm_decode_mac.h b/include/ieee802-11/ofdm_decode_mac.h index 8cb193c..91fd353 100644 --- a/include/ieee802-11/ofdm_decode_mac.h +++ b/include/ieee802-11/ofdm_decode_mac.h @@ -30,6 +30,13 @@ public: typedef boost::shared_ptr sptr; static sptr make(bool log = false, bool debug = false); +private: + + // Puncturing vectors + static const unsigned char d_puncture_1_2[]; + static const unsigned char d_puncture_2_3[]; + static const unsigned char d_puncture_3_4[]; + }; } // namespace ieee802_11 diff --git a/lib/ofdm_decode_mac.cc b/lib/ofdm_decode_mac.cc index 23d419a..8c7f987 100644 --- a/lib/ofdm_decode_mac.cc +++ b/lib/ofdm_decode_mac.cc @@ -23,6 +23,17 @@ #include #include + +static __m128i metric0[4] __attribute__ ((aligned(16))); +static __m128i metric1[4] __attribute__ ((aligned(16))); +static __m128i path0[4] __attribute__ ((aligned(16))); +static __m128i path1[4] __attribute__ ((aligned(16))); +struct viterbi_state state0[64]; +struct viterbi_state state1[64]; +static int d_init = 0; + +int decoded_count; + using namespace gr::ieee802_11; using namespace itpp; @@ -237,9 +248,6 @@ void demodulate() { bits = to_vec(demodulate_64qam(symbols)); break; } - - // I hate the guy who wrote itpp - bits = bits * (-2) + 1; } void deinterleave() { @@ -257,70 +265,173 @@ void deinterleave() { second[i] = 16 * i - (n_cbps - 1) * int(floor(16.0 * i / n_cbps)); } + int count = 0; for(int i = 0; i < d_tx.n_sym; i++) { for(int k = 0; k < n_cbps; k++) { - deinter[i * n_cbps + second[first[k]]] = bits[i * n_cbps + k]; + if (bits[i * n_cbps + k] > 0) + { + d_tmp_inbits[i * n_cbps + second[first[k]]] = 0x01; + } + else + { + d_tmp_inbits[i * n_cbps + second[first[k]]] = 0x00; + } } } + + // Set the appropiate coding rate for + // the convolutional decoder. + set_coding_rate(); + + if (d_ntraceback == 5) + { + count = d_tx.n_sym * n_cbps; + d_inbits = d_tmp_inbits; + } + else + { + d_inbits = d_inbits_punctured; + count = 0; + for(int i = 0; i < d_tx.n_sym; i++) { + for(int k = 0; k < n_cbps; k++) { + while (d_puncture[count % (2 * d_k)] == 0) + { + d_inbits[count] = 2; + count++; + } + + // Insert received bits + d_inbits[count] = d_tmp_inbits[i * n_cbps + k]; + count++; + + while (d_puncture[count % (2 * d_k)] == 0) + { + d_inbits[count] = 2; + count++; + } + } + } + } + + for (int i = count; i < NeededBits(); i++) + d_inbits[i] = 0x00; } -void decode_conv() { - Punctured_Convolutional_Code code; - ivec generator(2); - generator(0)=0133; - generator(1)=0171; - code.set_generator_polynomials(generator, 7); +int NeededBits() +{ + int return_value; + if (d_ntraceback == 5) + return_value = d_tx.n_encoded_bits / 2; + else if (d_ntraceback == 9) + { + return_value = (d_tx.n_encoded_bits / 3) * 2; + } + else // si d_ntraceback == 10 + { + return_value = (d_tx.n_encoded_bits / 4) * 3; + } + return return_value; +} - bmat puncture_matrix; +void decode_conv_viterbi() { + + d_viterbi_chunks_init(state0); + d_viterbi_chunks_init_sse2(metric0, path0); + + int out_count = 0; + decoded_count = 0; + + int in_count = 0; + int neededBits = NeededBits(); + while(decoded_count < neededBits) + { + if ((in_count % 4) == 0) //0 or 3 + { + d_viterbi_butterfly2_sse2(&d_inbits[in_count & 0xfffffffc], metric0, metric1, path0, path1); + + if ((in_count > 0) && (in_count % 16) == 8) // 8 or 11 + { + unsigned char c; + + d_viterbi_get_output_sse2(metric0, path0, d_ntraceback, &c); + + if (d_init == 0) + { + if (out_count >= d_ntraceback) + { + for (int i= 0; i < 8; i++) + { + out_bytes_viterbi[(out_count - d_ntraceback) * 8 + i] = (c >> (7 - i)) & 0x1; + decoded_count++; + } + } + } + else + { + out_bytes_viterbi[out_count] = c; + for (int i= 0; i < 8; i++) + { + out_bytes_viterbi[out_count * 8 + i] = (c >> (7 - i)) & 0x1; + decoded_count++; + } + } + out_count++; + } + } + in_count++; + } +} + +void set_coding_rate() +{ switch(d_ofdm.encoding) { case BPSK_1_2: case QPSK_1_2: case QAM16_1_2: - puncture_matrix = "1 1; 1 1"; + // Code rate 1/2 + d_ntraceback = 5; + d_puncture = d_puncture_1_2; + d_k = 1; + break; + case QAM64_2_3: + // Code rate 2/3 + d_ntraceback = 9; + d_puncture = d_puncture_2_3; + d_k = 2; break; case BPSK_3_4: case QPSK_3_4: case QAM16_3_4: case QAM64_3_4: - puncture_matrix = "1 1 0; 1 0 1;"; - break; - case QAM64_2_3: - puncture_matrix = "1 1 1 1 1 1; 1 0 1 0 1 0;"; + // Code rate 3/4 + d_ntraceback = 10; + d_puncture = d_puncture_3_4; + d_k = 3; break; } - code.set_puncture_matrix(puncture_matrix); - code.set_truncation_length(30); +} - dout << "coding rate " << code.get_rate() << std::endl; - dout << d_tx.n_encoded_bits << std::endl; - - vec rx_signal(deinter, d_tx.n_encoded_bits); - - code.reset(); - decoded_bits.set_length(d_tx.n_encoded_bits); - code.decode_tail(rx_signal, decoded_bits); - - //dout << "length decoded " << decoded_bits.size() << std::endl; - //std::cout << decoded_bits << std::endl; +void decode_conv() { + decode_conv_viterbi(); } void descramble () { int state = 0; for(int i = 0; i < 7; i++) { - if(decoded_bits(i)) { + if(out_bytes_viterbi[i]) { state |= 1 << (6 - i); } } int feedback; - for(int i = 7; i < decoded_bits.size(); i++) { + for(int i = 7; i < decoded_count; i++) { feedback = ((!!(state & 64))) ^ (!!(state & 8)); - out_bits[i] = feedback ^ (int)decoded_bits(i); + out_bits[i] = feedback ^ (out_bytes_viterbi[i] & 0x1); state = ((state << 1) & 0x7e) | feedback; } - for(int i = 0; i < decoded_bits.size(); i++) { + for(int i = 0; i < decoded_count; i++) { int bit = i % 8; int byte = i / 8; if(bit == 0) { @@ -356,11 +467,24 @@ void print_output() { private: gr_complex sym[1000 * 48 * 100]; vec bits; - double deinter[1000 * 48]; + unsigned char* d_inbits; + unsigned char d_inbits_punctured[1000 * 48]; + unsigned char d_tmp_inbits[1000 * 48]; char out_bits[40000]; char out_bytes[40000]; + char out_bytes_viterbi[40000 * 8]; bvec decoded_bits; + int d_ntraceback; + + // number of input bits in the convolutional encoder. + int d_k; + + const unsigned char * d_puncture; + const unsigned char d_puncture_1_2[2] = {1, 1}; + const unsigned char d_puncture_2_3[4] = {1, 1, 1, 0}; + const unsigned char d_puncture_3_4[6] = {1, 1, 1, 0, 0, 1}; + bool d_debug; bool d_log; tx_param d_tx; diff --git a/lib/ofdm_mac.cc b/lib/ofdm_mac.cc index 6f5ec7a..dec2262 100644 --- a/lib/ofdm_mac.cc +++ b/lib/ofdm_mac.cc @@ -73,7 +73,7 @@ void phy_in (pmt::pmt_t msg) { // TODO: check for frame type to determine header size pmt::pmt_t blob(pmt::cdr(msg)); const char *mpdu = reinterpret_cast(pmt::blob_data(blob)); - std::cout << "pdu len " << pmt::blob_length(blob) << std::endl; + //std::cout << "pdu len " << pmt::blob_length(blob) << std::endl; pmt::pmt_t msdu = pmt::make_blob(mpdu + 24, pmt::blob_length(blob) - 24); message_port_pub(pmt::mp("app out"), pmt::cons(pmt::car(msg), msdu)); diff --git a/lib/utils.cc b/lib/utils.cc index f476af8..9c84ba0 100644 --- a/lib/utils.cc +++ b/lib/utils.cc @@ -340,3 +340,697 @@ void generate_bits(const char *psdu, char *data_bits, tx_param &tx) { } } } +/* Here starts the SSE2 Viterbi convolutional decoder implementation */ +/* + * Copyright 1995 Phil Karn, KA9Q + * Copyright 2008 Free Software Foundation, Inc. + * 2014 Added SSE2 implementation Bogdan Diaconescu + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* + * Viterbi decoder for K=7 rate=1/2 convolutional code + * Some modifications from original Karn code by Matt Ettus + * Major modifications by adding SSE2 code by Bogdan Diaconescu + */ +#include + +/* The two generator polynomials for the NASA Standard K=7 code. + * Since these polynomials are known to be optimal for this constraint + * length there is not much point in changing them. But if you do, you + * will have to regenerate the BUTTERFLY macro calls in viterbi() + */ +#define POLYA 0x6d +#define POLYB 0x4f + +/* The basic Viterbi decoder operation, called a "butterfly" + * operation because of the way it looks on a trellis diagram. Each + * butterfly involves an Add-Compare-Select (ACS) operation on the two nodes + * where the 0 and 1 paths from the current node merge at the next step of + * the trellis. + * + * The code polynomials are assumed to have 1's on both ends. Given a + * function encode_state() that returns the two symbols for a given + * encoder state in the low two bits, such a code will have the following + * identities for even 'n' < 64: + * + * encode_state(n) = encode_state(n+65) + * encode_state(n+1) = encode_state(n+64) = (3 ^ encode_state(n)) + * + * Any convolutional code you would actually want to use will have + * these properties, so these assumptions aren't too limiting. + * + * Doing this as a macro lets the compiler evaluate at compile time the + * many expressions that depend on the loop index and encoder state and + * emit them as immediate arguments. + * This makes an enormous difference on register-starved machines such + * as the Intel x86 family where evaluating these expressions at runtime + * would spill over into memory. + */ + + +static union branchtab27 { unsigned char c[32]; __m128i v[2];} Branchtab27_sse2[2]; + +// Maximum number of traceback bytes +#define TRACEBACK_MAX 24 +// Metrics for each state +static unsigned char mmresult[64] __attribute__((aligned(16))) = {0}; +// Paths for each state +static unsigned char ppresult[TRACEBACK_MAX][64] __attribute__((aligned(16))) = {{0,0}}; +// Position in circular buffer where the current decoded byte is stored +static int store_pos = 0; + +#define BUTTERFLY(i,sym) { \ + int m0,m1,m2,m3;\ + /* ACS for 0 branch */\ + m0 = state[i].metric + mets[sym]; /* 2*i */\ + m1 = state[i+32].metric + mets[3 ^ sym]; /* 2*i + 64 */\ + if(m0 > m1){\ + next[2*i].metric = m0;\ + next[2*i].path = state[i].path << 1;\ + } else {\ + next[2*i].metric = m1;\ + next[2*i].path = (state[i+32].path << 1)|1;\ + }\ + /* ACS for 1 branch */\ + m2 = state[i].metric + mets[3 ^ sym]; /* 2*i + 1 */\ + m3 = state[i+32].metric + mets[sym]; /* 2*i + 65 */\ + if(m2 > m3){\ + next[2*i+1].metric = m2;\ + next[2*i+1].path = state[i].path << 1;\ + } else {\ + next[2*i+1].metric = m3;\ + next[2*i+1].path = (state[i+32].path << 1)|1;\ + }\ +} + +unsigned char d_Partab[] = { + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, +};/* Parity lookup table */ + +/* Convolutionally encode data into binary symbols */ +unsigned char +d_encode(unsigned char *symbols, + unsigned char *data, + unsigned int nbytes, + unsigned char encstate) +{ + int i; + + while(nbytes-- != 0){ + for(i=7;i>=0;i--){ + encstate = (encstate << 1) | ((*data >> i) & 1); + *symbols++ = d_Partab[encstate & POLYA]; + *symbols++ = d_Partab[encstate & POLYB]; + } + data++; + } + + return encstate; +} + +/* Viterbi decoder */ +int +d_viterbi(unsigned long *metric, /* Final path metric (returned value) */ + unsigned char *data, /* Decoded output data */ + unsigned char *symbols, /* Raw deinterleaved input symbols */ + unsigned int nbits, /* Number of output bits */ + int mettab[2][256] /* Metric table, [sent sym][rx symbol] */ + ){ + unsigned int bitcnt = 0; + int mets[4]; + long bestmetric; + int beststate,i; + struct viterbi_state state0[64],state1[64],*state,*next; + + state = state0; + next = state1; + + /* Initialize starting metrics to prefer 0 state */ + state[0].metric = 0; + for(i=1;i<64;i++) + state[i].metric = -999999; + state[0].path = 0; + + for(bitcnt = 0;bitcnt < nbits;bitcnt++){ + /* Read input symbol pair and compute all possible branch + * metrics + */ + mets[0] = mettab[0][symbols[0]] + mettab[0][symbols[1]]; + mets[1] = mettab[0][symbols[0]] + mettab[1][symbols[1]]; + mets[2] = mettab[1][symbols[0]] + mettab[0][symbols[1]]; + mets[3] = mettab[1][symbols[0]] + mettab[1][symbols[1]]; + symbols += 2; + +BUTTERFLY(0,0); BUTTERFLY(1,1); BUTTERFLY(2,3); BUTTERFLY(3,2); +BUTTERFLY(4,3); BUTTERFLY(5,2); BUTTERFLY(6,0); BUTTERFLY(7,1); +BUTTERFLY(8,0); BUTTERFLY(9,1); BUTTERFLY(10,3); BUTTERFLY(11,2); +BUTTERFLY(12,3); BUTTERFLY(13,2); BUTTERFLY(14,0); BUTTERFLY(15,1); +BUTTERFLY(16,2); BUTTERFLY(17,3); BUTTERFLY(18,1); BUTTERFLY(19,0); +BUTTERFLY(20,1); BUTTERFLY(21,0); BUTTERFLY(22,2); BUTTERFLY(23,3); +BUTTERFLY(24,2); BUTTERFLY(25,3); BUTTERFLY(26,1); BUTTERFLY(27,0); +BUTTERFLY(28,1); BUTTERFLY(29,0); BUTTERFLY(30,2); BUTTERFLY(31,3); + + /* Swap current and next states */ + if(bitcnt & 1){ + state = state0; + next = state1; + } else { + state = state1; + next = state0; + } + // ETTUS + //if(bitcnt > nbits-7){ + /* In tail, poison non-zero nodes */ + //for(i=1;i<64;i += 2) + // state[i].metric = -9999999; + //} + /* Produce output every 8 bits once path memory is full */ + if((bitcnt % 8) == 5 && bitcnt > 32){ + /* Find current best path */ + bestmetric = state[0].metric; + beststate = 0; + for(i=1;i<64;i++){ + if(state[i].metric > bestmetric){ + bestmetric = state[i].metric; + beststate = i; + } + } +#ifdef notdef + printf("metrics[%d] = %d state = %lx\n",beststate, + state[beststate].metric,state[beststate].path); +#endif + *data++ = state[beststate].path >> 24; + } + + } + /* Output remaining bits from 0 state */ + // ETTUS Find best state instead + bestmetric = state[0].metric; + beststate = 0; + for(i=1;i<64;i++){ + if(state[i].metric > bestmetric){ + bestmetric = state[i].metric; + beststate = i; + } + } + if((i = bitcnt % 8) != 6) + state[beststate].path <<= 6-i; + + *data++ = state[beststate].path >> 24; + *data++ = state[beststate].path >> 16; + *data++ = state[beststate].path >> 8; + *data = state[beststate].path; + //printf ("BS = %d\tBSM = %d\tM0 = %d\n",beststate,state[beststate].metric,state[0].metric); + *metric = state[beststate].metric; + return 0; +} + +void +d_viterbi_chunks_init(struct viterbi_state* state) { + // Initialize starting metrics to prefer 0 state + int i; + state[0].metric = 0; + state[0].path = 0; + for(i=1;i<64;i++) + { + state[i].metric = 0;//-999999; + state[i].path = 0; + } +} + +void +d_viterbi_chunks_init_sse2(__m128i *mm0, __m128i *pp0) { + // Initialize starting metrics to prefer 0 state + int i, j; + + for (i = 0; i < 4; i++) + { + mm0[i] = _mm_setzero_si128(); + pp0[i] = _mm_setzero_si128(); + } + + int polys[2] = { POLYA, POLYB }; + for(i=0; i < 32; i++) + { + Branchtab27_sse2[0].c[i] = (polys[0] < 0) ^ d_Partab[(2*i) & abs(polys[0])] ? 1 : 0; + Branchtab27_sse2[1].c[i] = (polys[1] < 0) ^ d_Partab[(2*i) & abs(polys[1])] ? 1 : 0; + } + + for (i = 0; i < 64; i++) + { + mmresult[i] = 0; + for (j = 0; j < TRACEBACK_MAX; j++) + ppresult[j][i] = 0; + } +} + + +void +d_viterbi_butterfly8(unsigned char *symbols, int mettab[2][256], struct viterbi_state *state0, struct viterbi_state *state1) +{ + unsigned int bitcnt; + int mets[4]; + + struct viterbi_state *state, *next; + state = state0; + next = state1; + // Operate on 16 symbols (8 bits) at a time + for(bitcnt = 0;bitcnt < 8;bitcnt++){ + // Read input symbol pair and compute all possible branch metrics + mets[0] = mettab[0][symbols[0]] + mettab[0][symbols[1]]; + mets[1] = mettab[0][symbols[0]] + mettab[1][symbols[1]]; + mets[2] = mettab[1][symbols[0]] + mettab[0][symbols[1]]; + mets[3] = mettab[1][symbols[0]] + mettab[1][symbols[1]]; + symbols += 2; + + // These macro calls were generated by genbut.c + BUTTERFLY(0,0); BUTTERFLY(1,1); BUTTERFLY(2,3); BUTTERFLY(3,2); +BUTTERFLY(4,3); BUTTERFLY(5,2); BUTTERFLY(6,0); BUTTERFLY(7,1); +BUTTERFLY(8,0); BUTTERFLY(9,1); BUTTERFLY(10,3); BUTTERFLY(11,2); +BUTTERFLY(12,3); BUTTERFLY(13,2); BUTTERFLY(14,0); BUTTERFLY(15,1); +BUTTERFLY(16,2); BUTTERFLY(17,3); BUTTERFLY(18,1); BUTTERFLY(19,0); +BUTTERFLY(20,1); BUTTERFLY(21,0); BUTTERFLY(22,2); BUTTERFLY(23,3); +BUTTERFLY(24,2); BUTTERFLY(25,3); BUTTERFLY(26,1); BUTTERFLY(27,0); +BUTTERFLY(28,1); BUTTERFLY(29,0); BUTTERFLY(30,2); BUTTERFLY(31,3); + + // Swap current and next states + if(bitcnt & 1){ + state = state0; + next = state1; + } else { + state = state1; + next = state0; + } + } +} + +void +d_viterbi_butterfly2(unsigned char *symbols, int mettab[2][256], struct viterbi_state *state0, struct viterbi_state *state1) +{ + int mets[4]; + + struct viterbi_state *state, *next; + state = state0; + next = state1; + + // Operate on 4 symbols (2 bits) at a time + + // Read input symbol pair and compute all possible branch metrics +#if 0 + if (symbols[0] == 2) + { + mets[0] = mettab[0][symbols[1]]; + mets[1] = mettab[1][symbols[1]]; + mets[2] = mettab[0][symbols[1]]; + mets[3] = mettab[1][symbols[1]]; + } + else if (symbols[1] == 2) + { + mets[0] = mettab[0][symbols[0]]; + mets[1] = mettab[1][symbols[0]]; + mets[2] = mettab[0][symbols[0]]; + mets[3] = mettab[1][symbols[0]]; + } + else +#endif + { + mets[0] = mettab[0][symbols[0]] + mettab[0][symbols[1]]; + mets[1] = mettab[0][symbols[0]] + mettab[1][symbols[1]]; + mets[2] = mettab[1][symbols[0]] + mettab[0][symbols[1]]; + mets[3] = mettab[1][symbols[0]] + mettab[1][symbols[1]]; + } + + // These macro calls were generated by genbut.c +BUTTERFLY(0,0); BUTTERFLY(1,1); BUTTERFLY(2,3); BUTTERFLY(3,2); +BUTTERFLY(4,3); BUTTERFLY(5,2); BUTTERFLY(6,0); BUTTERFLY(7,1); +BUTTERFLY(8,0); BUTTERFLY(9,1); BUTTERFLY(10,3); BUTTERFLY(11,2); +BUTTERFLY(12,3); BUTTERFLY(13,2); BUTTERFLY(14,0); BUTTERFLY(15,1); +BUTTERFLY(16,2); BUTTERFLY(17,3); BUTTERFLY(18,1); BUTTERFLY(19,0); +BUTTERFLY(20,1); BUTTERFLY(21,0); BUTTERFLY(22,2); BUTTERFLY(23,3); +BUTTERFLY(24,2); BUTTERFLY(25,3); BUTTERFLY(26,1); BUTTERFLY(27,0); +BUTTERFLY(28,1); BUTTERFLY(29,0); BUTTERFLY(30,2); BUTTERFLY(31,3); + + state = state1; + next = state0; + + // Read input symbol pair and compute all possible branch metrics + +#if 0 + if (symbols[2] == 2) + { + mets[0] = mettab[0][symbols[3]]; + mets[1] = mettab[1][symbols[3]]; + mets[2] = mettab[0][symbols[3]]; + mets[3] = mettab[1][symbols[3]]; + } + else if (symbols[3] == 2) + { + mets[0] = mettab[0][symbols[2]]; + mets[1] = mettab[1][symbols[2]]; + mets[2] = mettab[0][symbols[2]]; + mets[3] = mettab[1][symbols[2]]; + } + else +#endif + { + mets[0] = mettab[0][symbols[2]] + mettab[0][symbols[3]]; + mets[1] = mettab[0][symbols[2]] + mettab[1][symbols[3]]; + mets[2] = mettab[1][symbols[2]] + mettab[0][symbols[3]]; + mets[3] = mettab[1][symbols[2]] + mettab[1][symbols[3]]; + } + + // These macro calls were generated by genbut.c + BUTTERFLY(0,0); BUTTERFLY(1,1); BUTTERFLY(2,3); BUTTERFLY(3,2); +BUTTERFLY(4,3); BUTTERFLY(5,2); BUTTERFLY(6,0); BUTTERFLY(7,1); +BUTTERFLY(8,0); BUTTERFLY(9,1); BUTTERFLY(10,3); BUTTERFLY(11,2); +BUTTERFLY(12,3); BUTTERFLY(13,2); BUTTERFLY(14,0); BUTTERFLY(15,1); +BUTTERFLY(16,2); BUTTERFLY(17,3); BUTTERFLY(18,1); BUTTERFLY(19,0); +BUTTERFLY(20,1); BUTTERFLY(21,0); BUTTERFLY(22,2); BUTTERFLY(23,3); +BUTTERFLY(24,2); BUTTERFLY(25,3); BUTTERFLY(26,1); BUTTERFLY(27,0); +BUTTERFLY(28,1); BUTTERFLY(29,0); BUTTERFLY(30,2); BUTTERFLY(31,3); +} + +void +d_viterbi_butterfly2_sse2(unsigned char *symbols, __m128i *mm0, __m128i *mm1, __m128i *pp0, __m128i *pp1) +{ + int i; + + __m128i *metric0, *metric1; + __m128i *path0, *path1; + + metric0 = mm0; + path0 = pp0; + metric1 = mm1; + path1 = pp1; + + // Operate on 4 symbols (2 bits) at a time + + __m128i m0, m1, m2, m3, decision0, decision1, survivor0, survivor1; + __m128i metsv, metsvm; + __m128i shift0, shift1; + __m128i tmp0, tmp1; + __m128i sym0v, sym1v; + + sym0v = _mm_set1_epi8(symbols[0]); + sym1v = _mm_set1_epi8(symbols[1]); + + for (i = 0; i < 2; i++) + { + if (symbols[0] == 2) + { + metsvm = _mm_xor_si128(Branchtab27_sse2[1].v[i],sym1v); + metsv = _mm_sub_epi8(_mm_set1_epi8(1),metsvm); + } + else if (symbols[1] == 2) + { + metsvm = _mm_xor_si128(Branchtab27_sse2[0].v[i],sym0v); + metsv = _mm_sub_epi8(_mm_set1_epi8(1),metsvm); + } + else + { + metsvm = _mm_add_epi8(_mm_xor_si128(Branchtab27_sse2[0].v[i],sym0v),_mm_xor_si128(Branchtab27_sse2[1].v[i],sym1v)); + metsv = _mm_sub_epi8(_mm_set1_epi8(2),metsvm); + } + + m0 = _mm_add_epi8(metric0[i], metsv); + m1 = _mm_add_epi8(metric0[i+2], metsvm); + m2 = _mm_add_epi8(metric0[i], metsvm); + m3 = _mm_add_epi8(metric0[i+2], metsv); + + 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,m0),_mm_andnot_si128(decision0,m1)); + survivor1 = _mm_or_si128(_mm_and_si128(decision1,m2),_mm_andnot_si128(decision1,m3)); + + shift0 = _mm_slli_epi16(path0[i], 1); + shift1 = _mm_slli_epi16(path0[2+i], 1); + shift1 = _mm_add_epi8(shift1, _mm_set1_epi8(1)); + + metric1[2*i] = _mm_unpacklo_epi8(survivor0,survivor1); + tmp0 = _mm_or_si128(_mm_and_si128(decision0,shift0),_mm_andnot_si128(decision0,shift1)); + + metric1[2*i+1] = _mm_unpackhi_epi8(survivor0,survivor1); + tmp1 = _mm_or_si128(_mm_and_si128(decision1,shift0),_mm_andnot_si128(decision1,shift1)); + + path1[2*i] = _mm_unpacklo_epi8(tmp0, tmp1); + path1[2*i+1] = _mm_unpackhi_epi8(tmp0, tmp1); + } + + metric0 = mm1; + path0 = pp1; + metric1 = mm0; + path1 = pp0; + + sym0v = _mm_set1_epi8(symbols[2]); + sym1v = _mm_set1_epi8(symbols[3]); + + for (i = 0; i < 2; i++) + { + if (symbols[2] == 2) + { + metsvm = _mm_xor_si128(Branchtab27_sse2[1].v[i],sym1v); + metsv = _mm_sub_epi8(_mm_set1_epi8(1),metsvm); + } + else if (symbols[3] == 2) + { + metsvm = _mm_xor_si128(Branchtab27_sse2[0].v[i],sym0v); + metsv = _mm_sub_epi8(_mm_set1_epi8(1),metsvm); + } + else + { + metsvm = _mm_add_epi8(_mm_xor_si128(Branchtab27_sse2[0].v[i],sym0v),_mm_xor_si128(Branchtab27_sse2[1].v[i],sym1v)); + metsv = _mm_sub_epi8(_mm_set1_epi8(2),metsvm); + } + + m0 = _mm_add_epi8(metric0[i], metsv); + m1 = _mm_add_epi8(metric0[i+2], metsvm); + m2 = _mm_add_epi8(metric0[i], metsvm); + m3 = _mm_add_epi8(metric0[i+2], metsv); + + 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,m0),_mm_andnot_si128(decision0,m1)); + survivor1 = _mm_or_si128(_mm_and_si128(decision1,m2),_mm_andnot_si128(decision1,m3)); + + shift0 = _mm_slli_epi16(path0[i], 1); + shift1 = _mm_slli_epi16(path0[2+i], 1); + shift1 = _mm_add_epi8(shift1, _mm_set1_epi8(1)); + + metric1[2*i] = _mm_unpacklo_epi8(survivor0,survivor1); + tmp0 = _mm_or_si128(_mm_and_si128(decision0,shift0),_mm_andnot_si128(decision0,shift1)); + + metric1[2*i+1] = _mm_unpackhi_epi8(survivor0,survivor1); + tmp1 = _mm_or_si128(_mm_and_si128(decision1,shift0),_mm_andnot_si128(decision1,shift1)); + + path1[2*i] = _mm_unpacklo_epi8(tmp0, tmp1); + path1[2*i+1] = _mm_unpackhi_epi8(tmp0, tmp1); + } +} + +void +d_viterbi_butterfly_sse2(unsigned char *symbols, __m128i *mm0, __m128i *mm1, __m128i *pp0, __m128i *pp1) +{ + int i; + + __m128i *metric0, *metric1; + __m128i *path0, *path1; + + metric0 = mm0; + path0 = pp0; + metric1 = mm1; + path1 = pp1; + + for (i = 0; i < 2; i++) + printf("symbols[%i]: %i\n", i, symbols[i]); + + + // Operate on 4 symbols (2 bits) at a time + + __m128i m0, m1, m2, m3, decision0, decision1, survivor0, survivor1; + __m128i metsv, metsvm; + __m128i shift0, shift1; + __m128i tmp0, tmp1; + __m128i sym0v, sym1v; + + sym0v = _mm_set1_epi8(symbols[0]); + sym1v = _mm_set1_epi8(symbols[1]); + + for (i = 0; i < 2; i++) + { +#if 1 + if (symbols[0] == 2) + { + metsvm = _mm_xor_si128(Branchtab27_sse2[1].v[i],sym1v); + } + else if (symbols[1] == 0) + { + metsvm = _mm_xor_si128(Branchtab27_sse2[0].v[i],sym0v); + } + else +#endif + metsvm = _mm_add_epi8(_mm_xor_si128(Branchtab27_sse2[0].v[i],sym0v),_mm_xor_si128(Branchtab27_sse2[1].v[i],sym1v)); + + metsv = _mm_sub_epi8(_mm_set1_epi8(2),metsvm); + + m0 = _mm_add_epi8(metric0[i], metsv); + m1 = _mm_add_epi8(metric0[i+2], metsvm); + m2 = _mm_add_epi8(metric0[i], metsvm); + m3 = _mm_add_epi8(metric0[i+2], metsv); + + 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,m0),_mm_andnot_si128(decision0,m1)); + survivor1 = _mm_or_si128(_mm_and_si128(decision1,m2),_mm_andnot_si128(decision1,m3)); + + shift0 = _mm_slli_epi16(path0[i], 1); + shift1 = _mm_slli_epi16(path0[2+i], 1); + shift1 = _mm_add_epi8(shift1, _mm_set1_epi8(1)); + + metric1[2*i] = _mm_unpacklo_epi8(survivor0,survivor1); + tmp0 = _mm_or_si128(_mm_and_si128(decision0,shift0),_mm_andnot_si128(decision0,shift1)); + + metric1[2*i+1] = _mm_unpackhi_epi8(survivor0,survivor1); + tmp1 = _mm_or_si128(_mm_and_si128(decision1,shift0),_mm_andnot_si128(decision1,shift1)); + + path1[2*i] = _mm_unpacklo_epi8(tmp0, tmp1); + path1[2*i+1] = _mm_unpackhi_epi8(tmp0, tmp1); + } + + for (i = 0; i < 4; i++) + { + mm0[i] = metric1[i]; + pp0[i] = path1[i]; + } + +} + + +unsigned char +d_viterbi_get_output(struct viterbi_state *state, unsigned char *outbuf) { + // Produce output every 8 bits once path memory is full + // if((bitcnt % 8) == 5 && bitcnt > 32) { + + // Find current best path + unsigned int i, beststate = 0; + int bestmetric; + + bestmetric = state[0].metric; + beststate = 0; + for(i=1;i<64;i++) + { + if(state[i].metric > bestmetric) { + bestmetric = state[i].metric; + beststate = i; + } + } + + *outbuf = state[beststate].path >> 24; + + return bestmetric; +} + +unsigned char +d_viterbi_get_output_sse2(__m128i *mm0, __m128i *pp0, int ntraceback, unsigned char *outbuf) { + // Find current best path + int i; + int bestmetric, minmetric; + int beststate = 0; + int pos = 0; + + // Implement a circular buffer with the last ntraceback paths + store_pos = (store_pos + 1) % ntraceback; + + // TODO - find another way to extract the value + for (i = 0; i < 4; i++) + { + _mm_store_si128((__m128i *) &mmresult[i*16], mm0[i]); + _mm_store_si128((__m128i *) &ppresult[store_pos][i*16], pp0[i]); + } + + // Find out the best final state + bestmetric = mmresult[beststate]; + minmetric = mmresult[beststate]; + + for (i = 1; i < 64; i++) + { + if (mmresult[i] > bestmetric) + { + bestmetric = mmresult[i]; + beststate = i; + } + if (mmresult[i] < minmetric) + minmetric = mmresult[i]; + } + + // Trace back + for (i = 0, pos = store_pos; i < (ntraceback - 1); i++) + { + // Obtain the state from the output bits + // by clocking in the output bits in reverse order. + // The state has only 6 bits + beststate = ppresult[pos][beststate] >> 2; + pos = (pos - 1 + ntraceback) % ntraceback; + } + + // Store output byte + *outbuf = ppresult[pos][beststate]; + + // Zero out the path variable + // and prevent metric overflow + for (i = 0; i < 4; i++) + { + pp0[i] = _mm_setzero_si128(); + mm0[i] = _mm_sub_epi8(mm0[i], _mm_set1_epi8(minmetric)); + } + + return bestmetric; +} +/* end of new functions */ diff --git a/lib/utils.h b/lib/utils.h index ac3400d..c04c7ea 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -23,6 +23,9 @@ #include #include +// Needed for the SSE2 Viterbi decoder +#include + const int MAX_SYM = 520; // enough for 1500 byte BPSK 1/2 const int MAX_BITS = 1550 * 8 * 2; @@ -120,4 +123,66 @@ void split_symbols(const char *input, char *out, tx_param &tx, ofdm_param &ofdm) void generate_bits(const char *psdu, char *data_bits, tx_param &tx); +/* New functions for the SSE2 Convolutional Decoder */ + +/* This Viterbi decoder was taken from the gr-dvbt module of + * GNU Radio. It is an SSE2 version of the Viterbi Decoder + * created by Phil Karn. The SSE2 version was made by Bogdan + * Diaconescu. For more info see: gr-dvbt/lib/d_viterbi.h + */ + + +struct viterbi_state { + unsigned long path; /* Decoded path to this state */ + long metric; /* Cumulative metric to this state */ + + /* vector implementation */ + __m128i pp; /* Decoded path to this state */ + __m128i mm; /* Cumulative metric to this state */ +}; + +int d_gen_met(int mettab[2][256], /* Metric table */ + int amp, /* Signal amplitude */ + double esn0, /* Es/N0 ratio in dB */ + double bias, /* Metric bias */ + int scale); /* Scale factor */ + +unsigned char +d_encode(unsigned char *symbols, unsigned char *data, + unsigned int nbytes,unsigned char encstate); + +void +d_viterbi_chunks_init(struct viterbi_state* state); + +void +d_viterbi_chunks_init_sse2(__m128i *mm0, __m128i *pp0); + +void +d_viterbi_butterfly2(unsigned char *symbols, int mettab[2][256], struct viterbi_state *state0, struct viterbi_state *state1); + +void +d_viterbi_butterfly2_sse2(unsigned char *symbols, __m128i m0[], __m128i m1[], __m128i p0[], __m128i p1[]); + +void +d_viterbi_butterfly_sse2(unsigned char *symbols, __m128i m0[], __m128i m1[], __m128i p0[], __m128i p1[]); + +unsigned char +d_viterbi_get_output(struct viterbi_state *state, unsigned char *outbuf); + +unsigned char +d_viterbi_get_output_sse2(__m128i *mm0, __m128i *pp0, int ntraceback, unsigned char *outbuf); + + +int +d_viterbi(unsigned long *metric, /* Final path metric (returned value) */ + unsigned char *data, /* Decoded output data */ + unsigned char *symbols, /* Raw deinterleaved input symbols */ + unsigned int nbits, /* Number of output bits */ + int mettab[2][256] /* Metric table, [sent sym][rx symbol] */ +); + + +/* End of added functions */ + + #endif /* INCLUDED_IEEE802_11_OFDM_UTILS_H */