viterbi decoder: separate viterbi implementations
reorganizes viterbi decoder source so that each implementation resides in a separate file.
This commit is contained in:
+11
-1
@@ -120,9 +120,19 @@ list(APPEND gr_ieee802_11_sources
|
|||||||
sync_long.cc
|
sync_long.cc
|
||||||
sync_short.cc
|
sync_short.cc
|
||||||
utils.cc
|
utils.cc
|
||||||
viterbi_decoder.cc
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# use SSE2 optimized viterbi implementation if SSE2 is enabled
|
||||||
|
if(SSE2_SUPPORTED)
|
||||||
|
list(APPEND gr_ieee802_11_sources
|
||||||
|
viterbi_decoder/viterbi_decoder_x86.cc
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
list(APPEND gr_ieee802_11_sources
|
||||||
|
viterbi_decoder/viterbi_decoder_generic.cc
|
||||||
|
)
|
||||||
|
endif(SSE2_SUPPORTED)
|
||||||
|
|
||||||
list(APPEND gr_ieee802_11_libs
|
list(APPEND gr_ieee802_11_libs
|
||||||
${Boost_LIBRARIES}
|
${Boost_LIBRARIES}
|
||||||
${GNURADIO_ALL_LIBRARIES}
|
${GNURADIO_ALL_LIBRARIES}
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@
|
|||||||
#include <ieee802-11/decode_mac.h>
|
#include <ieee802-11/decode_mac.h>
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "viterbi_decoder.h"
|
#include "viterbi_decoder/viterbi_decoder.h"
|
||||||
|
|
||||||
#include <boost/crc.hpp>
|
#include <boost/crc.hpp>
|
||||||
#include <gnuradio/io_signature.h>
|
#include <gnuradio/io_signature.h>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
#include <ieee802-11/frame_equalizer.h>
|
#include <ieee802-11/frame_equalizer.h>
|
||||||
#include <ieee802-11/constellations.h>
|
#include <ieee802-11/constellations.h>
|
||||||
#include "equalizer/base.h"
|
#include "equalizer/base.h"
|
||||||
#include "viterbi_decoder.h"
|
#include "viterbi_decoder/viterbi_decoder.h"
|
||||||
|
|
||||||
namespace gr {
|
namespace gr {
|
||||||
namespace ieee802_11 {
|
namespace ieee802_11 {
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Bastian Bloessl <bloessl@ccs-labs.org>
|
||||||
|
*
|
||||||
|
* This program 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 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#ifndef INCLUDED_IEEE802_11_VITERBI_DECODER_H
|
||||||
|
#define INCLUDED_IEEE802_11_VITERBI_DECODER_H
|
||||||
|
|
||||||
|
#ifdef IEEE80211_MSSE2
|
||||||
|
#include "viterbi_decoder_x86.h"
|
||||||
|
#else
|
||||||
|
#include "viterbi_decoder_generic.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* INCLUDED_IEEE802_11_VITERBI_DECODER_H */
|
||||||
@@ -90,114 +90,6 @@ viterbi_decoder::viterbi_decoder() :
|
|||||||
viterbi_decoder::~viterbi_decoder() {
|
viterbi_decoder::~viterbi_decoder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef IEEE80211_MSSE2
|
|
||||||
void
|
|
||||||
viterbi_decoder::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(d_branchtab27_sse2[1].v[i],sym1v);
|
|
||||||
metsv = _mm_sub_epi8(_mm_set1_epi8(1),metsvm);
|
|
||||||
} else if (symbols[1] == 2) {
|
|
||||||
metsvm = _mm_xor_si128(d_branchtab27_sse2[0].v[i],sym0v);
|
|
||||||
metsv = _mm_sub_epi8(_mm_set1_epi8(1),metsvm);
|
|
||||||
} else {
|
|
||||||
metsvm = _mm_add_epi8(_mm_xor_si128(d_branchtab27_sse2[0].v[i],sym0v),_mm_xor_si128(d_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(d_branchtab27_sse2[1].v[i],sym1v);
|
|
||||||
metsv = _mm_sub_epi8(_mm_set1_epi8(1),metsvm);
|
|
||||||
|
|
||||||
} else if (symbols[3] == 2) {
|
|
||||||
metsvm = _mm_xor_si128(d_branchtab27_sse2[0].v[i],sym0v);
|
|
||||||
metsv = _mm_sub_epi8(_mm_set1_epi8(1),metsvm);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
metsvm = _mm_add_epi8(_mm_xor_si128(d_branchtab27_sse2[0].v[i],sym0v),_mm_xor_si128(d_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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
void
|
void
|
||||||
viterbi_decoder::viterbi_butterfly2_generic(unsigned char *symbols,
|
viterbi_decoder::viterbi_butterfly2_generic(unsigned char *symbols,
|
||||||
unsigned char *mm0, unsigned char *mm1, unsigned char *pp0,
|
unsigned char *mm0, unsigned char *mm1, unsigned char *pp0,
|
||||||
@@ -391,18 +283,11 @@ viterbi_decoder::viterbi_butterfly2_generic(unsigned char *symbols,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Find current best path
|
// Find current best path
|
||||||
#ifdef IEEE80211_MSSE2
|
|
||||||
unsigned char
|
|
||||||
viterbi_decoder::viterbi_get_output_sse2(__m128i *mm0, __m128i *pp0,
|
|
||||||
int ntraceback, unsigned char *outbuf) {
|
|
||||||
#else
|
|
||||||
unsigned char
|
unsigned char
|
||||||
viterbi_decoder::viterbi_get_output_generic(unsigned char *mm0,
|
viterbi_decoder::viterbi_get_output_generic(unsigned char *mm0,
|
||||||
unsigned char *pp0, int ntraceback, unsigned char *outbuf) {
|
unsigned char *pp0, int ntraceback, unsigned char *outbuf) {
|
||||||
#endif
|
|
||||||
int i;
|
int i;
|
||||||
int bestmetric, minmetric;
|
int bestmetric, minmetric;
|
||||||
int beststate = 0;
|
int beststate = 0;
|
||||||
@@ -414,19 +299,12 @@ viterbi_decoder::viterbi_get_output_generic(unsigned char *mm0,
|
|||||||
// circular buffer with the last ntraceback paths
|
// circular buffer with the last ntraceback paths
|
||||||
d_store_pos = (d_store_pos + 1) % ntraceback;
|
d_store_pos = (d_store_pos + 1) % ntraceback;
|
||||||
|
|
||||||
#ifdef IEEE80211_MSSE2
|
|
||||||
for (i = 0; i < 4; i++) {
|
|
||||||
_mm_store_si128((__m128i *) &d_mmresult[i*16], mm0[i]);
|
|
||||||
_mm_store_si128((__m128i *) &d_ppresult[d_store_pos][i*16], pp0[i]);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
for (j = 0; j < 16; j++) {
|
for (j = 0; j < 16; j++) {
|
||||||
d_mmresult[(i*16) + j] = mm0[(i*16) + j];
|
d_mmresult[(i*16) + j] = mm0[(i*16) + j];
|
||||||
d_ppresult[d_store_pos][(i*16) + j] = pp0[(i*16) + j];
|
d_ppresult[d_store_pos][(i*16) + j] = pp0[(i*16) + j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Find out the best final state
|
// Find out the best final state
|
||||||
bestmetric = d_mmresult[beststate];
|
bestmetric = d_mmresult[beststate];
|
||||||
@@ -454,21 +332,12 @@ viterbi_decoder::viterbi_get_output_generic(unsigned char *mm0,
|
|||||||
// Store output byte
|
// Store output byte
|
||||||
*outbuf = d_ppresult[pos][beststate];
|
*outbuf = d_ppresult[pos][beststate];
|
||||||
|
|
||||||
#ifdef IEEE80211_MSSE2
|
|
||||||
// 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));
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
for (j = 0; j < 16; j++) {
|
for (j = 0; j < 16; j++) {
|
||||||
pp0[(i*16) + j] = 0;
|
pp0[(i*16) + j] = 0;
|
||||||
mm0[(i*16) + j] = mm0[(i*16) + j] - minmetric;
|
mm0[(i*16) + j] = mm0[(i*16) + j] - minmetric;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
return bestmetric;
|
return bestmetric;
|
||||||
}
|
}
|
||||||
@@ -525,21 +394,13 @@ viterbi_decoder::decode(ofdm_param *ofdm, frame_param *frame, uint8_t *in) {
|
|||||||
while(n_decoded < d_frame->n_data_bits) {
|
while(n_decoded < d_frame->n_data_bits) {
|
||||||
|
|
||||||
if ((in_count % 4) == 0) { //0 or 3
|
if ((in_count % 4) == 0) { //0 or 3
|
||||||
#ifdef IEEE80211_MSSE2
|
|
||||||
viterbi_butterfly2_sse2(&depunctured[in_count & 0xfffffffc], d_metric0, d_metric1, d_path0, d_path1);
|
|
||||||
#else
|
|
||||||
viterbi_butterfly2_generic(&depunctured[in_count & 0xfffffffc], d_metric0_generic, d_metric1_generic,
|
viterbi_butterfly2_generic(&depunctured[in_count & 0xfffffffc], d_metric0_generic, d_metric1_generic,
|
||||||
d_path0_generic, d_path1_generic);
|
d_path0_generic, d_path1_generic);
|
||||||
#endif
|
|
||||||
|
|
||||||
if ((in_count > 0) && (in_count % 16) == 8) { // 8 or 11
|
if ((in_count > 0) && (in_count % 16) == 8) { // 8 or 11
|
||||||
unsigned char c;
|
unsigned char c;
|
||||||
|
|
||||||
#ifdef IEEE80211_MSSE2
|
|
||||||
viterbi_get_output_sse2(d_metric0, d_path0, d_ntraceback, &c);
|
|
||||||
#else
|
|
||||||
viterbi_get_output_generic(d_metric0_generic, d_path0_generic, d_ntraceback, &c);
|
viterbi_get_output_generic(d_metric0_generic, d_path0_generic, d_ntraceback, &c);
|
||||||
#endif
|
|
||||||
|
|
||||||
if (out_count >= d_ntraceback) {
|
if (out_count >= d_ntraceback) {
|
||||||
for (int i= 0; i < 8; i++) {
|
for (int i= 0; i < 8; i++) {
|
||||||
@@ -559,11 +420,7 @@ viterbi_decoder::decode(ofdm_param *ofdm, frame_param *frame, uint8_t *in) {
|
|||||||
void
|
void
|
||||||
viterbi_decoder::reset() {
|
viterbi_decoder::reset() {
|
||||||
|
|
||||||
#ifdef IEEE80211_MSSE2
|
|
||||||
viterbi_chunks_init_sse2();
|
|
||||||
#else
|
|
||||||
viterbi_chunks_init_generic();
|
viterbi_chunks_init_generic();
|
||||||
#endif
|
|
||||||
|
|
||||||
switch(d_ofdm->encoding) {
|
switch(d_ofdm->encoding) {
|
||||||
case BPSK_1_2:
|
case BPSK_1_2:
|
||||||
@@ -590,27 +447,10 @@ viterbi_decoder::reset() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize starting metrics to prefer 0 state
|
// Initialize starting metrics to prefer 0 state
|
||||||
#ifdef IEEE80211_MSSE2
|
|
||||||
void
|
|
||||||
viterbi_decoder::viterbi_chunks_init_sse2() {
|
|
||||||
#else
|
|
||||||
void
|
void
|
||||||
viterbi_decoder::viterbi_chunks_init_generic() {
|
viterbi_decoder::viterbi_chunks_init_generic() {
|
||||||
#endif
|
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
#ifdef IEEE80211_MSSE2
|
|
||||||
for (i = 0; i < 4; i++) {
|
|
||||||
d_metric0[i] = _mm_setzero_si128();
|
|
||||||
d_path0[i] = _mm_setzero_si128();
|
|
||||||
}
|
|
||||||
|
|
||||||
int polys[2] = { 0x6d, 0x4f };
|
|
||||||
for(i=0; i < 32; i++) {
|
|
||||||
d_branchtab27_sse2[0].c[i] = (polys[0] < 0) ^ PARTAB[(2*i) & abs(polys[0])] ? 1 : 0;
|
|
||||||
d_branchtab27_sse2[1].c[i] = (polys[1] < 0) ^ PARTAB[(2*i) & abs(polys[1])] ? 1 : 0;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
d_metric0_generic[i] = 0;
|
d_metric0_generic[i] = 0;
|
||||||
d_path0_generic[i] = 0;
|
d_path0_generic[i] = 0;
|
||||||
@@ -621,7 +461,6 @@ viterbi_decoder::viterbi_chunks_init_generic() {
|
|||||||
d_branchtab27_generic[0].c[i] = (polys[0] < 0) ^ PARTAB[(2*i) & abs(polys[0])] ? 1 : 0;
|
d_branchtab27_generic[0].c[i] = (polys[0] < 0) ^ PARTAB[(2*i) & abs(polys[0])] ? 1 : 0;
|
||||||
d_branchtab27_generic[1].c[i] = (polys[1] < 0) ^ PARTAB[(2*i) & abs(polys[1])] ? 1 : 0;
|
d_branchtab27_generic[1].c[i] = (polys[1] < 0) ^ PARTAB[(2*i) & abs(polys[1])] ? 1 : 0;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
for (i = 0; i < 64; i++) {
|
for (i = 0; i < 64; i++) {
|
||||||
d_mmresult[i] = 0;
|
d_mmresult[i] = 0;
|
||||||
@@ -14,12 +14,9 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#ifndef INCLUDED_IEEE802_11_VITERBI_DECODER_H
|
#ifndef INCLUDED_IEEE802_11_VITERBI_DECODER_GENERIC_H
|
||||||
#define INCLUDED_IEEE802_11_VITERBI_DECODER_H
|
#define INCLUDED_IEEE802_11_VITERBI_DECODER_GENERIC_H
|
||||||
|
|
||||||
#ifdef IEEE80211_MSSE2
|
|
||||||
#include <xmmintrin.h>
|
|
||||||
#endif
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
namespace gr {
|
namespace gr {
|
||||||
@@ -52,28 +49,14 @@ private:
|
|||||||
unsigned char d_ppresult[TRACEBACK_MAX][64] __attribute__((aligned(16)));
|
unsigned char d_ppresult[TRACEBACK_MAX][64] __attribute__((aligned(16)));
|
||||||
|
|
||||||
|
|
||||||
#ifdef IEEE80211_MSSE2
|
|
||||||
union branchtab27 {
|
|
||||||
unsigned char c[32];
|
|
||||||
__m128i v[2];
|
|
||||||
} d_branchtab27_sse2[2];
|
|
||||||
#else
|
|
||||||
union branchtab27 {
|
union branchtab27 {
|
||||||
unsigned char c[32];
|
unsigned char c[32];
|
||||||
} d_branchtab27_generic[2];
|
} d_branchtab27_generic[2];
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef IEEE80211_MSSE2
|
|
||||||
__m128i d_metric0[4] __attribute__ ((aligned(16)));
|
|
||||||
__m128i d_metric1[4] __attribute__ ((aligned(16)));
|
|
||||||
__m128i d_path0[4] __attribute__ ((aligned(16)));
|
|
||||||
__m128i d_path1[4] __attribute__ ((aligned(16)));
|
|
||||||
#else
|
|
||||||
unsigned char d_metric0_generic[64] __attribute__ ((aligned(16)));
|
unsigned char d_metric0_generic[64] __attribute__ ((aligned(16)));
|
||||||
unsigned char d_metric1_generic[64] __attribute__ ((aligned(16)));
|
unsigned char d_metric1_generic[64] __attribute__ ((aligned(16)));
|
||||||
unsigned char d_path0_generic[64] __attribute__ ((aligned(16)));
|
unsigned char d_path0_generic[64] __attribute__ ((aligned(16)));
|
||||||
unsigned char d_path1_generic[64] __attribute__ ((aligned(16)));
|
unsigned char d_path1_generic[64] __attribute__ ((aligned(16)));
|
||||||
#endif
|
|
||||||
|
|
||||||
int d_ntraceback;
|
int d_ntraceback;
|
||||||
int d_k;
|
int d_k;
|
||||||
@@ -92,23 +75,15 @@ private:
|
|||||||
void reset();
|
void reset();
|
||||||
uint8_t* depuncture(uint8_t *in);
|
uint8_t* depuncture(uint8_t *in);
|
||||||
|
|
||||||
#ifdef IEEE80211_MSSE2
|
|
||||||
void viterbi_chunks_init_sse2();
|
|
||||||
void viterbi_butterfly2_sse2(unsigned char *symbols,
|
|
||||||
__m128i m0[], __m128i m1[], __m128i p0[], __m128i p1[]);
|
|
||||||
unsigned char viterbi_get_output_sse2(__m128i *mm0,
|
|
||||||
__m128i *pp0, int ntraceback, unsigned char *outbuf);
|
|
||||||
#else
|
|
||||||
void viterbi_chunks_init_generic();
|
void viterbi_chunks_init_generic();
|
||||||
void viterbi_butterfly2_generic(unsigned char *symbols,
|
void viterbi_butterfly2_generic(unsigned char *symbols,
|
||||||
unsigned char m0[], unsigned char m1[], unsigned char p0[],
|
unsigned char m0[], unsigned char m1[], unsigned char p0[],
|
||||||
unsigned char p1[]);
|
unsigned char p1[]);
|
||||||
unsigned char viterbi_get_output_generic(unsigned char *mm0,
|
unsigned char viterbi_get_output_generic(unsigned char *mm0,
|
||||||
unsigned char *pp0, int ntraceback, unsigned char *outbuf);
|
unsigned char *pp0, int ntraceback, unsigned char *outbuf);
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ieee802_11
|
} // namespace ieee802_11
|
||||||
} // namespace gr
|
} // namespace gr
|
||||||
|
|
||||||
#endif /* INCLUDED_IEEE802_11_VITERBI_DECODER_H */
|
#endif /* INCLUDED_IEEE802_11_VITERBI_DECODER_GENERIC_H */
|
||||||
@@ -0,0 +1,422 @@
|
|||||||
|
/*
|
||||||
|
* 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 "viterbi_decoder.h"
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace gr::ieee802_11;
|
||||||
|
|
||||||
|
|
||||||
|
viterbi_decoder::viterbi_decoder() :
|
||||||
|
d_store_pos(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
viterbi_decoder::~viterbi_decoder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
viterbi_decoder::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(d_branchtab27_sse2[1].v[i],sym1v);
|
||||||
|
metsv = _mm_sub_epi8(_mm_set1_epi8(1),metsvm);
|
||||||
|
} else if (symbols[1] == 2) {
|
||||||
|
metsvm = _mm_xor_si128(d_branchtab27_sse2[0].v[i],sym0v);
|
||||||
|
metsv = _mm_sub_epi8(_mm_set1_epi8(1),metsvm);
|
||||||
|
} else {
|
||||||
|
metsvm = _mm_add_epi8(_mm_xor_si128(d_branchtab27_sse2[0].v[i],sym0v),_mm_xor_si128(d_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(d_branchtab27_sse2[1].v[i],sym1v);
|
||||||
|
metsv = _mm_sub_epi8(_mm_set1_epi8(1),metsvm);
|
||||||
|
|
||||||
|
} else if (symbols[3] == 2) {
|
||||||
|
metsvm = _mm_xor_si128(d_branchtab27_sse2[0].v[i],sym0v);
|
||||||
|
metsv = _mm_sub_epi8(_mm_set1_epi8(1),metsvm);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
metsvm = _mm_add_epi8(_mm_xor_si128(d_branchtab27_sse2[0].v[i],sym0v),_mm_xor_si128(d_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find current best path
|
||||||
|
unsigned char
|
||||||
|
viterbi_decoder::viterbi_get_output_sse2(__m128i *mm0, __m128i *pp0,
|
||||||
|
int ntraceback, unsigned char *outbuf) {
|
||||||
|
int i;
|
||||||
|
int bestmetric, minmetric;
|
||||||
|
int beststate = 0;
|
||||||
|
int pos = 0;
|
||||||
|
#ifndef IEEE80211_MSSE2
|
||||||
|
int j;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// circular buffer with the last ntraceback paths
|
||||||
|
d_store_pos = (d_store_pos + 1) % ntraceback;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
_mm_store_si128((__m128i *) &d_mmresult[i*16], mm0[i]);
|
||||||
|
_mm_store_si128((__m128i *) &d_ppresult[d_store_pos][i*16], pp0[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find out the best final state
|
||||||
|
bestmetric = d_mmresult[beststate];
|
||||||
|
minmetric = d_mmresult[beststate];
|
||||||
|
|
||||||
|
for (i = 1; i < 64; i++) {
|
||||||
|
if (d_mmresult[i] > bestmetric) {
|
||||||
|
bestmetric = d_mmresult[i];
|
||||||
|
beststate = i;
|
||||||
|
}
|
||||||
|
if (d_mmresult[i] < minmetric) {
|
||||||
|
minmetric = d_mmresult[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trace back
|
||||||
|
for (i = 0, pos = d_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 = d_ppresult[pos][beststate] >> 2;
|
||||||
|
pos = (pos - 1 + ntraceback) % ntraceback;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store output byte
|
||||||
|
*outbuf = d_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t*
|
||||||
|
viterbi_decoder::depuncture(uint8_t *in) {
|
||||||
|
|
||||||
|
int count;
|
||||||
|
int n_cbps = d_ofdm->n_cbps;
|
||||||
|
uint8_t *depunctured;
|
||||||
|
|
||||||
|
if (d_ntraceback == 5) {
|
||||||
|
count = d_frame->n_sym * n_cbps;
|
||||||
|
depunctured = in;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
depunctured = d_depunctured;
|
||||||
|
count = 0;
|
||||||
|
for(int i = 0; i < d_frame->n_sym; i++) {
|
||||||
|
for(int k = 0; k < n_cbps; k++) {
|
||||||
|
while (d_depuncture_pattern[count % (2 * d_k)] == 0) {
|
||||||
|
depunctured[count] = 2;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert received bits
|
||||||
|
depunctured[count] = in[i * n_cbps + k];
|
||||||
|
count++;
|
||||||
|
|
||||||
|
while (d_depuncture_pattern[count % (2 * d_k)] == 0) {
|
||||||
|
depunctured[count] = 2;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return depunctured;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t*
|
||||||
|
viterbi_decoder::decode(ofdm_param *ofdm, frame_param *frame, uint8_t *in) {
|
||||||
|
|
||||||
|
d_ofdm = ofdm;
|
||||||
|
d_frame = frame;
|
||||||
|
|
||||||
|
reset();
|
||||||
|
uint8_t *depunctured = depuncture(in);
|
||||||
|
|
||||||
|
int in_count = 0;
|
||||||
|
int out_count = 0;
|
||||||
|
int n_decoded = 0;
|
||||||
|
|
||||||
|
while(n_decoded < d_frame->n_data_bits) {
|
||||||
|
|
||||||
|
if ((in_count % 4) == 0) { //0 or 3
|
||||||
|
viterbi_butterfly2_sse2(&depunctured[in_count & 0xfffffffc], d_metric0, d_metric1, d_path0, d_path1);
|
||||||
|
|
||||||
|
if ((in_count > 0) && (in_count % 16) == 8) { // 8 or 11
|
||||||
|
unsigned char c;
|
||||||
|
|
||||||
|
viterbi_get_output_sse2(d_metric0, d_path0, d_ntraceback, &c);
|
||||||
|
|
||||||
|
if (out_count >= d_ntraceback) {
|
||||||
|
for (int i= 0; i < 8; i++) {
|
||||||
|
d_decoded[(out_count - d_ntraceback) * 8 + i] = (c >> (7 - i)) & 0x1;
|
||||||
|
n_decoded++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return d_decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
viterbi_decoder::reset() {
|
||||||
|
|
||||||
|
viterbi_chunks_init_sse2();
|
||||||
|
|
||||||
|
switch(d_ofdm->encoding) {
|
||||||
|
case BPSK_1_2:
|
||||||
|
case QPSK_1_2:
|
||||||
|
case QAM16_1_2:
|
||||||
|
d_ntraceback = 5;
|
||||||
|
d_depuncture_pattern = PUNCTURE_1_2;
|
||||||
|
d_k = 1;
|
||||||
|
break;
|
||||||
|
case QAM64_2_3:
|
||||||
|
d_ntraceback = 9;
|
||||||
|
d_depuncture_pattern = PUNCTURE_2_3;
|
||||||
|
d_k = 2;
|
||||||
|
break;
|
||||||
|
case BPSK_3_4:
|
||||||
|
case QPSK_3_4:
|
||||||
|
case QAM16_3_4:
|
||||||
|
case QAM64_3_4:
|
||||||
|
d_ntraceback = 10;
|
||||||
|
d_depuncture_pattern = PUNCTURE_3_4;
|
||||||
|
d_k = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize starting metrics to prefer 0 state
|
||||||
|
void
|
||||||
|
viterbi_decoder::viterbi_chunks_init_sse2() {
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
d_metric0[i] = _mm_setzero_si128();
|
||||||
|
d_path0[i] = _mm_setzero_si128();
|
||||||
|
}
|
||||||
|
|
||||||
|
int polys[2] = { 0x6d, 0x4f };
|
||||||
|
for(i=0; i < 32; i++) {
|
||||||
|
d_branchtab27_sse2[0].c[i] = (polys[0] < 0) ^ PARTAB[(2*i) & abs(polys[0])] ? 1 : 0;
|
||||||
|
d_branchtab27_sse2[1].c[i] = (polys[1] < 0) ^ PARTAB[(2*i) & abs(polys[1])] ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 64; i++) {
|
||||||
|
d_mmresult[i] = 0;
|
||||||
|
for (j = 0; j < TRACEBACK_MAX; j++) {
|
||||||
|
d_ppresult[j][i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Parity lookup table */
|
||||||
|
const unsigned char viterbi_decoder::PARTAB[256] = {
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsigned char viterbi_decoder::PUNCTURE_1_2[2] = {1, 1};
|
||||||
|
const unsigned char viterbi_decoder::PUNCTURE_2_3[4] = {1, 1, 1, 0};
|
||||||
|
const unsigned char viterbi_decoder::PUNCTURE_3_4[6] = {1, 1, 1, 0, 0, 1};
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Bastian Bloessl <bloessl@ccs-labs.org>
|
||||||
|
*
|
||||||
|
* This program 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 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#ifndef INCLUDED_IEEE802_11_VITERBI_DECODER_X86_H
|
||||||
|
#define INCLUDED_IEEE802_11_VITERBI_DECODER_X86_H
|
||||||
|
|
||||||
|
#include <xmmintrin.h>
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
namespace gr {
|
||||||
|
namespace ieee802_11 {
|
||||||
|
|
||||||
|
// Maximum number of traceback bytes
|
||||||
|
#define TRACEBACK_MAX 24
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
|
class viterbi_decoder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
viterbi_decoder();
|
||||||
|
virtual ~viterbi_decoder();
|
||||||
|
|
||||||
|
uint8_t* decode(ofdm_param *ofdm, frame_param *frame, uint8_t *in);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// Position in circular buffer where the current decoded byte is stored
|
||||||
|
int d_store_pos;
|
||||||
|
// Metrics for each state
|
||||||
|
unsigned char d_mmresult[64] __attribute__((aligned(16)));
|
||||||
|
// Paths for each state
|
||||||
|
unsigned char d_ppresult[TRACEBACK_MAX][64] __attribute__((aligned(16)));
|
||||||
|
|
||||||
|
|
||||||
|
union branchtab27 {
|
||||||
|
unsigned char c[32];
|
||||||
|
__m128i v[2];
|
||||||
|
} d_branchtab27_sse2[2];
|
||||||
|
|
||||||
|
__m128i d_metric0[4] __attribute__ ((aligned(16)));
|
||||||
|
__m128i d_metric1[4] __attribute__ ((aligned(16)));
|
||||||
|
__m128i d_path0[4] __attribute__ ((aligned(16)));
|
||||||
|
__m128i d_path1[4] __attribute__ ((aligned(16)));
|
||||||
|
|
||||||
|
int d_ntraceback;
|
||||||
|
int d_k;
|
||||||
|
ofdm_param *d_ofdm;
|
||||||
|
frame_param *d_frame;
|
||||||
|
const unsigned char *d_depuncture_pattern;
|
||||||
|
|
||||||
|
uint8_t d_depunctured[MAX_ENCODED_BITS];
|
||||||
|
uint8_t d_decoded[MAX_ENCODED_BITS * 3 / 4];
|
||||||
|
|
||||||
|
static const unsigned char PARTAB[256];
|
||||||
|
static const unsigned char PUNCTURE_1_2[2];
|
||||||
|
static const unsigned char PUNCTURE_2_3[4];
|
||||||
|
static const unsigned char PUNCTURE_3_4[6];
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
uint8_t* depuncture(uint8_t *in);
|
||||||
|
|
||||||
|
void viterbi_chunks_init_sse2();
|
||||||
|
void viterbi_butterfly2_sse2(unsigned char *symbols,
|
||||||
|
__m128i m0[], __m128i m1[], __m128i p0[], __m128i p1[]);
|
||||||
|
unsigned char viterbi_get_output_sse2(__m128i *mm0,
|
||||||
|
__m128i *pp0, int ntraceback, unsigned char *outbuf);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ieee802_11
|
||||||
|
} // namespace gr
|
||||||
|
|
||||||
|
#endif /* INCLUDED_IEEE802_11_VITERBI_DECODER_X86_H */
|
||||||
Reference in New Issue
Block a user