- 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]
This commit is contained in:
committed by
Bastian Bloessl
parent
74ae445143
commit
0f56254df3
@@ -1339,7 +1339,7 @@
|
|||||||
</param>
|
</param>
|
||||||
<param>
|
<param>
|
||||||
<key>log</key>
|
<key>log</key>
|
||||||
<value>True</value>
|
<value>False</value>
|
||||||
</param>
|
</param>
|
||||||
<param>
|
<param>
|
||||||
<key>maxoutbuf</key>
|
<key>maxoutbuf</key>
|
||||||
|
|||||||
@@ -30,6 +30,13 @@ public:
|
|||||||
typedef boost::shared_ptr<ofdm_decode_mac> sptr;
|
typedef boost::shared_ptr<ofdm_decode_mac> sptr;
|
||||||
static sptr make(bool log = false, bool debug = false);
|
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
|
} // namespace ieee802_11
|
||||||
|
|||||||
+158
-34
@@ -23,6 +23,17 @@
|
|||||||
#include <itpp/itcomm.h>
|
#include <itpp/itcomm.h>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
||||||
|
|
||||||
|
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 gr::ieee802_11;
|
||||||
using namespace itpp;
|
using namespace itpp;
|
||||||
|
|
||||||
@@ -237,9 +248,6 @@ void demodulate() {
|
|||||||
bits = to_vec(demodulate_64qam(symbols));
|
bits = to_vec(demodulate_64qam(symbols));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// I hate the guy who wrote itpp
|
|
||||||
bits = bits * (-2) + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void deinterleave() {
|
void deinterleave() {
|
||||||
@@ -257,70 +265,173 @@ void deinterleave() {
|
|||||||
second[i] = 16 * i - (n_cbps - 1) * int(floor(16.0 * i / n_cbps));
|
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 i = 0; i < d_tx.n_sym; i++) {
|
||||||
for(int k = 0; k < n_cbps; k++) {
|
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() {
|
int NeededBits()
|
||||||
Punctured_Convolutional_Code code;
|
{
|
||||||
ivec generator(2);
|
int return_value;
|
||||||
generator(0)=0133;
|
if (d_ntraceback == 5)
|
||||||
generator(1)=0171;
|
return_value = d_tx.n_encoded_bits / 2;
|
||||||
code.set_generator_polynomials(generator, 7);
|
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) {
|
switch(d_ofdm.encoding) {
|
||||||
case BPSK_1_2:
|
case BPSK_1_2:
|
||||||
case QPSK_1_2:
|
case QPSK_1_2:
|
||||||
case QAM16_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;
|
break;
|
||||||
case BPSK_3_4:
|
case BPSK_3_4:
|
||||||
case QPSK_3_4:
|
case QPSK_3_4:
|
||||||
case QAM16_3_4:
|
case QAM16_3_4:
|
||||||
case QAM64_3_4:
|
case QAM64_3_4:
|
||||||
puncture_matrix = "1 1 0; 1 0 1;";
|
// Code rate 3/4
|
||||||
break;
|
d_ntraceback = 10;
|
||||||
case QAM64_2_3:
|
d_puncture = d_puncture_3_4;
|
||||||
puncture_matrix = "1 1 1 1 1 1; 1 0 1 0 1 0;";
|
d_k = 3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
code.set_puncture_matrix(puncture_matrix);
|
}
|
||||||
code.set_truncation_length(30);
|
|
||||||
|
|
||||||
dout << "coding rate " << code.get_rate() << std::endl;
|
void decode_conv() {
|
||||||
dout << d_tx.n_encoded_bits << std::endl;
|
decode_conv_viterbi();
|
||||||
|
|
||||||
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 descramble () {
|
void descramble () {
|
||||||
int state = 0;
|
int state = 0;
|
||||||
for(int i = 0; i < 7; i++) {
|
for(int i = 0; i < 7; i++) {
|
||||||
if(decoded_bits(i)) {
|
if(out_bytes_viterbi[i]) {
|
||||||
state |= 1 << (6 - i);
|
state |= 1 << (6 - i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int feedback;
|
int feedback;
|
||||||
|
|
||||||
for(int i = 7; i < decoded_bits.size(); i++) {
|
for(int i = 7; i < decoded_count; i++) {
|
||||||
feedback = ((!!(state & 64))) ^ (!!(state & 8));
|
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;
|
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 bit = i % 8;
|
||||||
int byte = i / 8;
|
int byte = i / 8;
|
||||||
if(bit == 0) {
|
if(bit == 0) {
|
||||||
@@ -356,11 +467,24 @@ void print_output() {
|
|||||||
private:
|
private:
|
||||||
gr_complex sym[1000 * 48 * 100];
|
gr_complex sym[1000 * 48 * 100];
|
||||||
vec bits;
|
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_bits[40000];
|
||||||
char out_bytes[40000];
|
char out_bytes[40000];
|
||||||
|
char out_bytes_viterbi[40000 * 8];
|
||||||
bvec decoded_bits;
|
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_debug;
|
||||||
bool d_log;
|
bool d_log;
|
||||||
tx_param d_tx;
|
tx_param d_tx;
|
||||||
|
|||||||
+1
-1
@@ -73,7 +73,7 @@ void phy_in (pmt::pmt_t msg) {
|
|||||||
// TODO: check for frame type to determine header size
|
// TODO: check for frame type to determine header size
|
||||||
pmt::pmt_t blob(pmt::cdr(msg));
|
pmt::pmt_t blob(pmt::cdr(msg));
|
||||||
const char *mpdu = reinterpret_cast<const char *>(pmt::blob_data(blob));
|
const char *mpdu = reinterpret_cast<const char *>(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);
|
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));
|
message_port_pub(pmt::mp("app out"), pmt::cons(pmt::car(msg), msdu));
|
||||||
|
|||||||
+694
@@ -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 <xmmintrin.h>
|
||||||
|
|
||||||
|
/* 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 */
|
||||||
|
|||||||
+65
@@ -23,6 +23,9 @@
|
|||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
// Needed for the SSE2 Viterbi decoder
|
||||||
|
#include <xmmintrin.h>
|
||||||
|
|
||||||
const int MAX_SYM = 520; // enough for 1500 byte BPSK 1/2
|
const int MAX_SYM = 520; // enough for 1500 byte BPSK 1/2
|
||||||
const int MAX_BITS = 1550 * 8 * 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);
|
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 */
|
#endif /* INCLUDED_IEEE802_11_OFDM_UTILS_H */
|
||||||
|
|||||||
Reference in New Issue
Block a user