mirror of https://github.com/pvnis/srsRAN_4G.git
Merge branch 'next' into next_novolk
commit
7aa33c1f27
@ -0,0 +1,115 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2015 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the srsLTE library.
|
||||||
|
*
|
||||||
|
* srsLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* srsLTE 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Affero General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRSLTE_MAT_H
|
||||||
|
#define SRSLTE_MAT_H
|
||||||
|
|
||||||
|
#include "srslte/phy/utils/simd.h"
|
||||||
|
#include "srslte/config.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generic Macros
|
||||||
|
*/
|
||||||
|
#define RANDOM_CF() (((float)rand())/((float)RAND_MAX) + _Complex_I*((float)rand())/((float)RAND_MAX))
|
||||||
|
|
||||||
|
/* Generic implementation for complex reciprocal */
|
||||||
|
SRSLTE_API cf_t srslte_mat_cf_recip_gen(cf_t a);
|
||||||
|
|
||||||
|
/* Generic implementation for 2x2 determinant */
|
||||||
|
SRSLTE_API cf_t srslte_mat_2x2_det_gen(cf_t a00, cf_t a01, cf_t a10, cf_t a11);
|
||||||
|
|
||||||
|
/* Generic implementation for 2x2 Matrix Inversion */
|
||||||
|
SRSLTE_API void srslte_mat_2x2_inv_gen(cf_t a00, cf_t a01, cf_t a10, cf_t a11,
|
||||||
|
cf_t *r00, cf_t *r01, cf_t *r10, cf_t *r11);
|
||||||
|
|
||||||
|
/* Generic implementation for Zero Forcing (ZF) solver */
|
||||||
|
SRSLTE_API void srslte_mat_2x2_zf_gen(cf_t y0, cf_t y1,
|
||||||
|
cf_t h00, cf_t h01, cf_t h10, cf_t h11,
|
||||||
|
cf_t *x0, cf_t *x1,
|
||||||
|
float norm);
|
||||||
|
|
||||||
|
/* Generic implementation for Minimum Mean Squared Error (MMSE) solver */
|
||||||
|
SRSLTE_API void srslte_mat_2x2_mmse_gen(cf_t y0, cf_t y1,
|
||||||
|
cf_t h00, cf_t h01, cf_t h10, cf_t h11,
|
||||||
|
cf_t *x0, cf_t *x1,
|
||||||
|
float noise_estimate,
|
||||||
|
float norm);
|
||||||
|
|
||||||
|
SRSLTE_API float srslte_mat_2x2_cn(cf_t h00,
|
||||||
|
cf_t h01,
|
||||||
|
cf_t h10,
|
||||||
|
cf_t h11);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef LV_HAVE_SSE
|
||||||
|
#include <smmintrin.h>
|
||||||
|
|
||||||
|
/* SSE implementation for complex reciprocal */
|
||||||
|
SRSLTE_API __m128 srslte_mat_cf_recip_sse(__m128 a);
|
||||||
|
|
||||||
|
/* SSE implementation for 2x2 determinant */
|
||||||
|
SRSLTE_API __m128 srslte_mat_2x2_det_sse(__m128 a00, __m128 a01, __m128 a10, __m128 a11);
|
||||||
|
|
||||||
|
/* SSE implementation for Zero Forcing (ZF) solver */
|
||||||
|
SRSLTE_API void srslte_mat_2x2_zf_sse(__m128 y0, __m128 y1,
|
||||||
|
__m128 h00, __m128 h01, __m128 h10, __m128 h11,
|
||||||
|
__m128 *x0, __m128 *x1,
|
||||||
|
float norm);
|
||||||
|
|
||||||
|
/* SSE implementation for Minimum Mean Squared Error (MMSE) solver */
|
||||||
|
SRSLTE_API void srslte_mat_2x2_mmse_sse(__m128 y0, __m128 y1,
|
||||||
|
__m128 h00, __m128 h01, __m128 h10, __m128 h11,
|
||||||
|
__m128 *x0, __m128 *x1,
|
||||||
|
float noise_estimate, float norm);
|
||||||
|
|
||||||
|
#endif /* LV_HAVE_SSE */
|
||||||
|
|
||||||
|
#ifdef LV_HAVE_AVX
|
||||||
|
|
||||||
|
#include <immintrin.h>
|
||||||
|
|
||||||
|
/* AVX implementation for complex reciprocal */
|
||||||
|
SRSLTE_API __m256 srslte_mat_cf_recip_avx(__m256 a);
|
||||||
|
|
||||||
|
/* AVX implementation for 2x2 determinant */
|
||||||
|
SRSLTE_API __m256 srslte_mat_2x2_det_avx(__m256 a00, __m256 a01, __m256 a10, __m256 a11);
|
||||||
|
|
||||||
|
/* AVX implementation for Zero Forcing (ZF) solver */
|
||||||
|
SRSLTE_API void srslte_mat_2x2_zf_avx(__m256 y0, __m256 y1,
|
||||||
|
__m256 h00, __m256 h01, __m256 h10, __m256 h11,
|
||||||
|
__m256 *x0, __m256 *x1,
|
||||||
|
float norm);
|
||||||
|
|
||||||
|
/* AVX implementation for Minimum Mean Squared Error (MMSE) solver */
|
||||||
|
SRSLTE_API void srslte_mat_2x2_mmse_avx(__m256 y0, __m256 y1,
|
||||||
|
__m256 h00, __m256 h01, __m256 h10, __m256 h11,
|
||||||
|
__m256 *x0, __m256 *x1,
|
||||||
|
float noise_estimate, float norm);
|
||||||
|
|
||||||
|
#endif /* LV_HAVE_AVX */
|
||||||
|
|
||||||
|
#endif /* SRSLTE_MAT_H */
|
@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2015 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the srsLTE library.
|
||||||
|
*
|
||||||
|
* srsLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* srsLTE 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Affero General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRSLTE_SIMD_H_H
|
||||||
|
#define SRSLTE_SIMD_H_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SSE Macros
|
||||||
|
*/
|
||||||
|
#ifdef LV_HAVE_SSE
|
||||||
|
#define _MM_SWAP(X) ((__m128)_mm_shuffle_ps(X, X, _MM_SHUFFLE(2,3,0,1)))
|
||||||
|
#define _MM_PERM(X) ((__m128)_mm_shuffle_ps(X, X, _MM_SHUFFLE(2,1,3,0)))
|
||||||
|
#define _MM_MULJ_PS(X) _MM_SWAP(_MM_CONJ_PS(X))
|
||||||
|
#define _MM_CONJ_PS(X) (_mm_xor_ps(X, _mm_set_ps(-0.0f, 0.0f, -0.0f, 0.0f)))
|
||||||
|
#define _MM_SQMOD_PS(X) _MM_PERM(_mm_hadd_ps(_mm_mul_ps(X,X), _mm_set_ps(0.0f, 0.0f, 0.0f, 0.0f)))
|
||||||
|
#define _MM_PROD_PS(a, b) _mm_addsub_ps(_mm_mul_ps(a,_mm_moveldup_ps(b)),_mm_mul_ps(\
|
||||||
|
_mm_shuffle_ps(a,a,0xB1),_mm_movehdup_ps(b)))
|
||||||
|
|
||||||
|
#endif /* LV_HAVE_SSE */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVX Macros
|
||||||
|
*/
|
||||||
|
#ifdef LV_HAVE_AVX
|
||||||
|
|
||||||
|
#define _MM256_MULJ_PS(X) _mm256_permute_ps(_MM256_CONJ_PS(X), 0b10110001)
|
||||||
|
#define _MM256_CONJ_PS(X) (_mm256_xor_ps(X, _mm256_set_ps(-0.0f, 0.0f, -0.0f, 0.0f, -0.0f, 0.0f, -0.0f, 0.0f)))
|
||||||
|
|
||||||
|
#ifdef LV_HAVE_FMA
|
||||||
|
#define _MM256_SQMOD_PS(A, B) _mm256_permute_ps(_mm256_hadd_ps(_mm256_fmadd_ps(A, A, _mm256_mul_ps(B,B)), \
|
||||||
|
_mm256_set_ps(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)), 0b11011100)
|
||||||
|
#define _MM256_PROD_PS(a, b) _mm256_fmaddsub_ps(a,_mm256_moveldup_ps(b),\
|
||||||
|
_mm256_mul_ps(_mm256_shuffle_ps(a,a,0xB1),_mm256_movehdup_ps(b)))
|
||||||
|
#else
|
||||||
|
#define _MM256_SQMOD_PS(A, B) _mm256_permute_ps(_mm256_hadd_ps(_mm256_add_ps(_mm256_mul_ps(A,A), _mm256_mul_ps(B,B)), \
|
||||||
|
_mm256_set_ps(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)), 0b11011100)
|
||||||
|
#define _MM256_PROD_PS(a, b) _mm256_addsub_ps(_mm256_mul_ps(a,_mm256_moveldup_ps(b)),\
|
||||||
|
_mm256_mul_ps(_mm256_shuffle_ps(a,a,0xB1),_mm256_movehdup_ps(b)))
|
||||||
|
#endif /* LV_HAVE_FMA */
|
||||||
|
#endif /* LV_HAVE_AVX */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVX extension with FMA Macros
|
||||||
|
*/
|
||||||
|
#ifdef LV_HAVE_FMA
|
||||||
|
|
||||||
|
#define _MM256_SQMOD_ADD_PS(A, B, C) _mm256_permute_ps(_mm256_hadd_ps(_mm256_fmadd_ps(A, A, _mm256_fmadd_ps(B, B, C)),\
|
||||||
|
_mm256_set_ps(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)), 0b11011100)
|
||||||
|
|
||||||
|
#define _MM256_PROD_ADD_PS(A, B, C) _mm256_fmaddsub_ps(A,_mm256_moveldup_ps(B),\
|
||||||
|
_mm256_fmaddsub_ps(_mm256_shuffle_ps(A,A,0xB1),_mm256_movehdup_ps(B), C))
|
||||||
|
|
||||||
|
#define _MM256_PROD_SUB_PS(A, B, C) _mm256_fmaddsub_ps(A,_mm256_moveldup_ps(B),\
|
||||||
|
_mm256_fmsubadd_ps(_mm256_shuffle_ps(A,A,0xB1),_mm256_movehdup_ps(B), C))
|
||||||
|
#endif /* LV_HAVE_FMA */
|
||||||
|
|
||||||
|
#endif //SRSLTE_SIMD_H_H
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,158 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2015 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the srsLTE library.
|
||||||
|
*
|
||||||
|
* srsLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* srsLTE 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Affero General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <complex.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "srslte/phy/utils/vector.h"
|
||||||
|
#include "srslte/phy/mimo/precoding.h"
|
||||||
|
#include "pmi_select_test.h"
|
||||||
|
#include "srslte/phy/utils/debug.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS];
|
||||||
|
float noise_estimate;
|
||||||
|
float sinr_1l[SRSLTE_MAX_CODEBOOKS];
|
||||||
|
float sinr_2l[SRSLTE_MAX_CODEBOOKS];
|
||||||
|
float cn;
|
||||||
|
uint32_t pmi[2];
|
||||||
|
uint32_t nof_symbols = (uint32_t) SRSLTE_SF_LEN_RE(6, SRSLTE_CP_NORM);
|
||||||
|
int ret = SRSLTE_ERROR;
|
||||||
|
|
||||||
|
/* Allocate channels */
|
||||||
|
for (int i = 0; i < SRSLTE_MAX_PORTS; i++) {
|
||||||
|
for (int j = 0; j < SRSLTE_MAX_PORTS; j++) {
|
||||||
|
h[i][j] = srslte_vec_malloc(sizeof(cf_t) * nof_symbols);
|
||||||
|
if (!h[i][j]) {
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
bzero(h[i][j], sizeof(cf_t) * nof_symbols);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int c = 0; c < PMI_SELECT_TEST_NOF_CASES; c++) {
|
||||||
|
pmi_select_test_case_gold_t *gold = &pmi_select_test_case_gold[c];
|
||||||
|
|
||||||
|
/* Set channel */
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
for (int j = 0; j < 2; j++) {
|
||||||
|
cf_t hij = gold->h[i][j];
|
||||||
|
|
||||||
|
for (int k = 0; k < nof_symbols; k++) {
|
||||||
|
h[i][j][k] = hij;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set noise estimate */
|
||||||
|
noise_estimate = gold->n;
|
||||||
|
|
||||||
|
/* PMI select for 1 layer */
|
||||||
|
ret = srslte_precoding_pmi_select(h, nof_symbols, noise_estimate, 1, &pmi[0], sinr_1l);
|
||||||
|
if (ret < 0) {
|
||||||
|
ERROR("During PMI selection for 1 layer");
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check SINR for 1 layer */
|
||||||
|
for (int i = 0; i < ret; i++) {
|
||||||
|
if (fabsf(gold->snri_1l[i] - sinr_1l[i]) > 0.1) {
|
||||||
|
ERROR("Test case %d failed computing 1 layer SINR for codebook %d (test=%.2f; gold=%.2f)\n",
|
||||||
|
c + 1, i, sinr_1l[i], gold->snri_1l[i]);
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check PMI select for 1 layer*/
|
||||||
|
if (pmi[0] != gold->pmi[0]) {
|
||||||
|
ERROR("Test case %d failed computing 1 layer PMI (test=%d; gold=%d)\n", c + 1, pmi[0], gold->pmi[0]);
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PMI select for 2 layer */
|
||||||
|
ret = srslte_precoding_pmi_select(h, nof_symbols, noise_estimate, 2, &pmi[1], sinr_2l);
|
||||||
|
if (ret < 0) {
|
||||||
|
ERROR("During PMI selection for 2 layer");
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check SINR for 2 layer */
|
||||||
|
for (int i = 0; i < ret; i++) {
|
||||||
|
if (fabsf(gold->snri_2l[i] - sinr_2l[i]) > 0.1) {
|
||||||
|
ERROR("Test case %d failed computing 2 layer SINR for codebook %d (test=%.2f; gold=%.2f)\n",
|
||||||
|
c + 1, i, sinr_2l[i], gold->snri_2l[i]);
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check PMI select for 2 layer*/
|
||||||
|
if (pmi[1] != gold->pmi[1]) {
|
||||||
|
ERROR("Test case %d failed computing 2 layer PMI (test=%d; gold=%d)\n", c + 1, pmi[1], gold->pmi[1]);
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Condition number */
|
||||||
|
if (srslte_precoding_cn(h, 2, 2, nof_symbols, &cn)) {
|
||||||
|
ERROR("Test case %d condition number returned error\n");
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check condition number */
|
||||||
|
if (fabsf(gold->k - cn) > 0.1) {
|
||||||
|
ERROR("Test case %d failed computing condition number (test=%.2f; gold=%.2f)\n",
|
||||||
|
c + 1, cn, gold->k);
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test passed */
|
||||||
|
ret = SRSLTE_SUCCESS;
|
||||||
|
|
||||||
|
clean:
|
||||||
|
for (int i = 0; i < SRSLTE_MAX_PORTS; i++) {
|
||||||
|
for (int j = 0; j < SRSLTE_MAX_PORTS; j++) {
|
||||||
|
if (h[i][j]) {
|
||||||
|
free(h[i][j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
printf("Failed!\n");
|
||||||
|
} else {
|
||||||
|
printf("Passed!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
@ -0,0 +1,237 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2015 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the srsLTE library.
|
||||||
|
*
|
||||||
|
* srsLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* srsLTE 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Affero General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PMI_SELECT_TEST_H
|
||||||
|
#define PMI_SELECT_TEST_H
|
||||||
|
|
||||||
|
#define PMI_SELECT_TEST_NOF_CASES 16
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
cf_t h[2][2]; /* Channel estimate */
|
||||||
|
float n; /* Noise estimation */
|
||||||
|
float snri_1l[4]; /* SINR Approximation for 1 layer (linear) */
|
||||||
|
float snri_2l[2]; /* SINR Approximation for 2 layers (linear) */
|
||||||
|
uint32_t pmi[2]; /* Precoding Matrix Indicator for 1 and 2 layers */
|
||||||
|
uint32_t ri; /* Rank indicator */
|
||||||
|
float k; /* Condition number (κ) in dB */
|
||||||
|
} pmi_select_test_case_gold_t;
|
||||||
|
|
||||||
|
static pmi_select_test_case_gold_t pmi_select_test_case_gold [PMI_SELECT_TEST_NOF_CASES] = {
|
||||||
|
{ /* Test case 1 */
|
||||||
|
.h = {
|
||||||
|
{+0.626226f+0.060103f*_Complex_I, -0.233387f-0.449860f*_Complex_I},
|
||||||
|
{+0.234558f-0.502742f*_Complex_I, +0.150990f-0.096722f*_Complex_I}
|
||||||
|
},
|
||||||
|
.n = 0.227713,
|
||||||
|
.snri_1l = {2.728043f, 1.630673f, 3.226421f, 1.132295f},
|
||||||
|
.snri_2l = {1.797660f, 1.982149f},
|
||||||
|
.pmi = {2, 1},
|
||||||
|
.ri = 1,
|
||||||
|
.k = 6.4007,
|
||||||
|
},
|
||||||
|
{ /* Test case 2 */
|
||||||
|
.h = {
|
||||||
|
{+0.608899f-0.825846f*_Complex_I, +0.972208f+0.604183f*_Complex_I},
|
||||||
|
{-0.940016f+0.978290f*_Complex_I, +0.071328f-0.866107f*_Complex_I}
|
||||||
|
},
|
||||||
|
.n = 0.939398,
|
||||||
|
.snri_1l = {0.686850f, 4.591972f, 3.773925f, 1.504897f},
|
||||||
|
.snri_2l = {2.298235f, 1.761859f},
|
||||||
|
.pmi = {1, 0},
|
||||||
|
.ri = 1,
|
||||||
|
.k = 11.1305,
|
||||||
|
},
|
||||||
|
{ /* Test case 3 */
|
||||||
|
.h = {
|
||||||
|
{-0.963645f+0.770719f*_Complex_I, +0.367677f+0.798010f*_Complex_I},
|
||||||
|
{+0.567473f+0.251875f*_Complex_I, +0.068275f-0.724262f*_Complex_I}
|
||||||
|
},
|
||||||
|
.n = 0.217802,
|
||||||
|
.snri_1l = {3.209674f, 11.525338f, 11.962786f, 2.772226f},
|
||||||
|
.snri_2l = {3.226053f, 3.526363f},
|
||||||
|
.pmi = {2, 1},
|
||||||
|
.ri = 1,
|
||||||
|
.k = 15.4589,
|
||||||
|
},
|
||||||
|
{ /* Test case 4 */
|
||||||
|
.h = {
|
||||||
|
{-0.635718f+0.879322f*_Complex_I, -0.916360f-0.291089f*_Complex_I},
|
||||||
|
{-0.786117f-0.178742f*_Complex_I, +0.232887f+0.968699f*_Complex_I}
|
||||||
|
},
|
||||||
|
.n = 0.945579,
|
||||||
|
.snri_1l = {1.818313f, 2.141519f, 1.995787f, 1.964045f},
|
||||||
|
.snri_2l = {1.965011f, 1.958537f},
|
||||||
|
.pmi = {1, 0},
|
||||||
|
.ri = 2,
|
||||||
|
.k = 1.2910,
|
||||||
|
},
|
||||||
|
{ /* Test case 5 */
|
||||||
|
.h = {
|
||||||
|
{+0.353289f+0.324764f*_Complex_I, +0.976605f-0.511669f*_Complex_I},
|
||||||
|
{+0.533663f-0.408985f*_Complex_I, -0.326601f+0.360357f*_Complex_I}
|
||||||
|
},
|
||||||
|
.n = 0.527847,
|
||||||
|
.snri_1l = {1.173803f, 2.869865f, 2.273783f, 1.769885f},
|
||||||
|
.snri_2l = {1.871430f, 1.713879f},
|
||||||
|
.pmi = {1, 0},
|
||||||
|
.ri = 2,
|
||||||
|
.k = 5.5388,
|
||||||
|
},
|
||||||
|
{ /* Test case 6 */
|
||||||
|
.h = {
|
||||||
|
{-0.176813f+0.103585f*_Complex_I, +0.205276f+0.167141f*_Complex_I},
|
||||||
|
{+0.501040f+0.023640f*_Complex_I, +0.167066f-0.834815f*_Complex_I}
|
||||||
|
},
|
||||||
|
.n = 0.719570,
|
||||||
|
.snri_1l = {0.490387f, 1.022313f, 1.111245f, 0.401456f},
|
||||||
|
.snri_2l = {0.578124f, 0.597176f},
|
||||||
|
.pmi = {2, 1},
|
||||||
|
.ri = 1,
|
||||||
|
.k = 21.8808,
|
||||||
|
},
|
||||||
|
{ /* Test case 7 */
|
||||||
|
.h = {
|
||||||
|
{+0.992312f+0.773088f*_Complex_I, -0.290931f-0.090610f*_Complex_I},
|
||||||
|
{+0.942518f-0.173145f*_Complex_I, -0.307102f-0.564536f*_Complex_I}
|
||||||
|
},
|
||||||
|
.n = 0.125655,
|
||||||
|
.snri_1l = {19.459529f, 4.467420f, 18.044021f, 5.882928f},
|
||||||
|
.snri_2l = {8.055238f, 6.832247f},
|
||||||
|
.pmi = {0, 0},
|
||||||
|
.ri = 1,
|
||||||
|
.k = 9.9136,
|
||||||
|
},
|
||||||
|
{ /* Test case 8 */
|
||||||
|
.h = {
|
||||||
|
{-0.382171f-0.980395f*_Complex_I, +0.452209f+0.686427f*_Complex_I},
|
||||||
|
{+0.565744f+0.844664f*_Complex_I, +0.387575f+0.541908f*_Complex_I}
|
||||||
|
},
|
||||||
|
.n = 0.042660,
|
||||||
|
.snri_1l = {26.560881f, 49.864772f, 33.269985f, 43.155668f},
|
||||||
|
.snri_2l = {37.201526f, 34.461078f},
|
||||||
|
.pmi = {1, 0},
|
||||||
|
.ri = 2,
|
||||||
|
.k = 3.1172,
|
||||||
|
},
|
||||||
|
{ /* Test case 9 */
|
||||||
|
.h = {
|
||||||
|
{-0.243628f-0.461891f*_Complex_I, +0.408679f+0.346062f*_Complex_I},
|
||||||
|
{+0.459026f-0.045016f*_Complex_I, -0.551446f+0.247433f*_Complex_I}
|
||||||
|
},
|
||||||
|
.n = 0.236445,
|
||||||
|
.snri_1l = {1.429443f, 3.381496f, 0.227617f, 4.583322f},
|
||||||
|
.snri_2l = {1.272903f, 2.118832f},
|
||||||
|
.pmi = {3, 1},
|
||||||
|
.ri = 1,
|
||||||
|
.k = 24.1136,
|
||||||
|
},
|
||||||
|
{ /* Test case 10 */
|
||||||
|
.h = {
|
||||||
|
{-0.645752f-0.784222f*_Complex_I, +0.659287f-0.635545f*_Complex_I},
|
||||||
|
{+0.533843f-0.801809f*_Complex_I, +0.868957f-0.020472f*_Complex_I}
|
||||||
|
},
|
||||||
|
.n = 0.193245,
|
||||||
|
.snri_1l = {13.697372f, 4.693597f, 1.561737f, 16.829232f},
|
||||||
|
.snri_2l = {2.961344f, 5.773049f},
|
||||||
|
.pmi = {3, 1},
|
||||||
|
.ri = 1,
|
||||||
|
.k = 17.5194,
|
||||||
|
},
|
||||||
|
{ /* Test case 11 */
|
||||||
|
.h = {
|
||||||
|
{+0.791783f+0.544990f*_Complex_I, -0.801821f-0.376120f*_Complex_I},
|
||||||
|
{-0.911669f-0.642035f*_Complex_I, +0.114590f-0.322089f*_Complex_I}
|
||||||
|
},
|
||||||
|
.n = 0.210146,
|
||||||
|
.snri_1l = {2.340213f, 12.261749f, 5.921675f, 8.680286f},
|
||||||
|
.snri_2l = {6.912040f, 4.520201f},
|
||||||
|
.pmi = {1, 0},
|
||||||
|
.ri = 2,
|
||||||
|
.k = 7.7819,
|
||||||
|
},
|
||||||
|
{ /* Test case 12 */
|
||||||
|
.h = {
|
||||||
|
{+0.020305f-0.218290f*_Complex_I, +0.812729f-0.890767f*_Complex_I},
|
||||||
|
{+0.257848f+0.002566f*_Complex_I, -0.796932f-0.136558f*_Complex_I}
|
||||||
|
},
|
||||||
|
.n = 0.997560,
|
||||||
|
.snri_1l = {0.591218f, 1.636514f, 1.880263f, 0.347469f},
|
||||||
|
.snri_2l = {0.869026f, 0.967991f},
|
||||||
|
.pmi = {2, 1},
|
||||||
|
.ri = 1,
|
||||||
|
.k = 12.9774,
|
||||||
|
},
|
||||||
|
{ /* Test case 13 */
|
||||||
|
.h = {
|
||||||
|
{+0.623205f-0.219990f*_Complex_I, -0.028697f+0.854712f*_Complex_I},
|
||||||
|
{+0.788896f+0.834988f*_Complex_I, -0.724907f+0.427148f*_Complex_I}
|
||||||
|
},
|
||||||
|
.n = 0.618337,
|
||||||
|
.snri_1l = {3.706176f, 1.461946f, 0.479632f, 4.688490f},
|
||||||
|
.snri_2l = {1.444336f, 2.102567f},
|
||||||
|
.pmi = {3, 1},
|
||||||
|
.ri = 1,
|
||||||
|
.k = 17.0493,
|
||||||
|
},
|
||||||
|
{ /* Test case 14 */
|
||||||
|
.h = {
|
||||||
|
{-0.313424f+0.292955f*_Complex_I, +0.872055f+0.666304f*_Complex_I},
|
||||||
|
{-0.750452f-0.203436f*_Complex_I, +0.461171f+0.499644f*_Complex_I}
|
||||||
|
},
|
||||||
|
.n = 0.835221,
|
||||||
|
.snri_1l = {2.560265f, 0.379539f, 0.976562f, 1.963242f},
|
||||||
|
.snri_2l = {1.380223f, 1.109300f},
|
||||||
|
.pmi = {0, 0},
|
||||||
|
.ri = 1,
|
||||||
|
.k = 10.1729,
|
||||||
|
},
|
||||||
|
{ /* Test case 15 */
|
||||||
|
.h = {
|
||||||
|
{-0.355079f-0.339153f*_Complex_I, +0.104523f+0.238943f*_Complex_I},
|
||||||
|
{+0.958258f-0.278727f*_Complex_I, +0.098617f+0.513019f*_Complex_I}
|
||||||
|
},
|
||||||
|
.n = 0.413901,
|
||||||
|
.snri_1l = {1.633620f, 2.178855f, 0.809297f, 3.003178f},
|
||||||
|
.snri_2l = {1.250898f, 1.512017f},
|
||||||
|
.pmi = {3, 1},
|
||||||
|
.ri = 1,
|
||||||
|
.k = 10.8925,
|
||||||
|
},
|
||||||
|
{ /* Test case 16 */
|
||||||
|
.h = {
|
||||||
|
{-0.015310f+0.675606f*_Complex_I, +0.389486f+0.478144f*_Complex_I},
|
||||||
|
{+0.945468f+0.908349f*_Complex_I, -0.344490f-0.936155f*_Complex_I}
|
||||||
|
},
|
||||||
|
.n = 0.356869,
|
||||||
|
.snri_1l = {5.024121f, 4.926495f, 7.364348f, 2.586268f},
|
||||||
|
.snri_2l = {3.165416f, 3.851590f},
|
||||||
|
.pmi = {2, 1},
|
||||||
|
.ri = 2,
|
||||||
|
.k = 7.7799,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* PMI_SELECT_TEST_H */
|
@ -0,0 +1,284 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2015 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the srsLTE library.
|
||||||
|
*
|
||||||
|
* srsLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* srsLTE 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Affero General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <complex.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "srslte/phy/utils/mat.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Generic implementation for complex reciprocal */
|
||||||
|
inline cf_t srslte_mat_cf_recip_gen(cf_t a) {
|
||||||
|
return conjf(a) / (crealf(a) * crealf(a) + cimagf(a) * cimagf(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generic implementation for 2x2 determinant */
|
||||||
|
inline cf_t srslte_mat_2x2_det_gen(cf_t a00, cf_t a01, cf_t a10, cf_t a11) {
|
||||||
|
return a00 * a11 - a01 * a10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2x2 Matrix inversion, generic implementation */
|
||||||
|
inline void srslte_mat_2x2_inv_gen(cf_t a00, cf_t a01, cf_t a10, cf_t a11,
|
||||||
|
cf_t *r00, cf_t *r01, cf_t *r10, cf_t *r11) {
|
||||||
|
cf_t div = srslte_mat_cf_recip_gen(srslte_mat_2x2_det_gen(a00, a01, a10, a11));
|
||||||
|
*r00 = a11 * div;
|
||||||
|
*r01 = -a01 * div;
|
||||||
|
*r10 = -a10 * div;
|
||||||
|
*r11 = a00 * div;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generic implementation for Zero Forcing (ZF) solver */
|
||||||
|
inline void srslte_mat_2x2_zf_gen(cf_t y0, cf_t y1, cf_t h00, cf_t h01, cf_t h10, cf_t h11,
|
||||||
|
cf_t *x0, cf_t *x1, float norm) {
|
||||||
|
cf_t _norm = srslte_mat_cf_recip_gen(srslte_mat_2x2_det_gen(h00, h01, h10, h11)) * norm;
|
||||||
|
*x0 = (y0 * h11 - h01 * y1) * _norm;
|
||||||
|
*x1 = (y1 * h00 - h10 * y0) * _norm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generic implementation for Minimum Mean Squared Error (MMSE) solver */
|
||||||
|
inline void srslte_mat_2x2_mmse_gen(cf_t y0, cf_t y1, cf_t h00, cf_t h01, cf_t h10, cf_t h11,
|
||||||
|
cf_t *x0, cf_t *x1, float noise_estimate, float norm) {
|
||||||
|
/* Create conjugated matrix */
|
||||||
|
cf_t _h00 = conjf(h00);
|
||||||
|
cf_t _h01 = conjf(h01);
|
||||||
|
cf_t _h10 = conjf(h10);
|
||||||
|
cf_t _h11 = conjf(h11);
|
||||||
|
|
||||||
|
/* 1. A = H' x H + No*/
|
||||||
|
cf_t a00 = _h00 * h00 + _h10 * h10 + noise_estimate;
|
||||||
|
cf_t a01 = _h00 * h01 + _h10 * h11;
|
||||||
|
cf_t a10 = _h01 * h00 + _h11 * h10;
|
||||||
|
cf_t a11 = _h01 * h01 + _h11 * h11 + noise_estimate;
|
||||||
|
|
||||||
|
/* 2. B = inv(H' x H + No) = inv(A) */
|
||||||
|
cf_t b00 = a11;
|
||||||
|
cf_t b01 = -a01;
|
||||||
|
cf_t b10 = -a10;
|
||||||
|
cf_t b11 = a00;
|
||||||
|
cf_t _norm = norm * srslte_mat_cf_recip_gen(srslte_mat_2x2_det_gen(a00, a01, a10, a11));
|
||||||
|
|
||||||
|
|
||||||
|
/* 3. W = inv(H' x H + No) x H' = B x H' */
|
||||||
|
cf_t w00 = b00 * _h00 + b01 * _h01;
|
||||||
|
cf_t w01 = b00 * _h10 + b01 * _h11;
|
||||||
|
cf_t w10 = b10 * _h00 + b11 * _h01;
|
||||||
|
cf_t w11 = b10 * _h10 + b11 * _h11;
|
||||||
|
|
||||||
|
/* 4. X = W x Y */
|
||||||
|
*x0 = (y0 * w00 + y1 * w01) * _norm;
|
||||||
|
*x1 = (y0 * w10 + y1 * w11) * _norm;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float srslte_mat_2x2_cn(cf_t h00, cf_t h01, cf_t h10, cf_t h11) {
|
||||||
|
/* 1. A = H * H' (A = A') */
|
||||||
|
float a00 =
|
||||||
|
crealf(h00) * crealf(h00) + crealf(h01) * crealf(h01) + cimagf(h00) * cimagf(h00) + cimagf(h01) * cimagf(h01);
|
||||||
|
cf_t a01 = h00 * conjf(h10) + h01 * conjf(h11);
|
||||||
|
//cf_t a10 = h10*conjf(h00) + h11*conjf(h01) = conjf(a01);
|
||||||
|
float a11 =
|
||||||
|
crealf(h10) * crealf(h10) + crealf(h11) * crealf(h11) + cimagf(h10) * cimagf(h10) + cimagf(h11) * cimagf(h11);
|
||||||
|
|
||||||
|
/* 2. |H * H' - {λ0, λ1}| = 0 -> aλ² + bλ + c = 0 */
|
||||||
|
float b = a00 + a11;
|
||||||
|
float c = a00 * a11 - (crealf(a01) * crealf(a01) + cimagf(a01) * cimagf(a01));
|
||||||
|
|
||||||
|
/* 3. λ = (-b ± sqrt(b² - 4 * c))/2 */
|
||||||
|
float sqr = sqrtf(b * b - 4.0f * c);
|
||||||
|
float xmax = b + sqr;
|
||||||
|
float xmin = b - sqr;
|
||||||
|
|
||||||
|
/* 4. κ = sqrt(λ_max / λ_min) */
|
||||||
|
return 10 * log10f(xmax / xmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef LV_HAVE_SSE
|
||||||
|
#include <smmintrin.h>
|
||||||
|
|
||||||
|
/* SSE implementation for complex reciprocal */
|
||||||
|
inline __m128 srslte_mat_cf_recip_sse(__m128 a) {
|
||||||
|
__m128 conj = _MM_CONJ_PS(a);
|
||||||
|
__m128 sqabs = _mm_mul_ps(a, a);
|
||||||
|
sqabs = _mm_add_ps(_mm_movehdup_ps(sqabs), _mm_moveldup_ps(sqabs));
|
||||||
|
|
||||||
|
__m128 recp = _mm_rcp_ps(sqabs);
|
||||||
|
|
||||||
|
return _mm_mul_ps(recp, conj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SSE implementation for 2x2 determinant */
|
||||||
|
inline __m128 srslte_mat_2x2_det_sse(__m128 a00, __m128 a01, __m128 a10, __m128 a11) {
|
||||||
|
return _mm_sub_ps(_MM_PROD_PS(a00, a11), _MM_PROD_PS(a01, a10));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SSE implementation for Zero Forcing (ZF) solver */
|
||||||
|
inline void srslte_mat_2x2_zf_sse(__m128 y0, __m128 y1, __m128 h00, __m128 h01, __m128 h10, __m128 h11,
|
||||||
|
__m128 *x0, __m128 *x1, float norm) {
|
||||||
|
__m128 detmult1 = _MM_PROD_PS(h00, h11);
|
||||||
|
__m128 detmult2 = _MM_PROD_PS(h01, h10);
|
||||||
|
|
||||||
|
__m128 det = _mm_sub_ps(detmult1, detmult2);
|
||||||
|
__m128 detrec = _mm_mul_ps(srslte_mat_cf_recip_sse(det), _mm_set1_ps(norm));
|
||||||
|
|
||||||
|
*x0 = _MM_PROD_PS(_mm_sub_ps(_MM_PROD_PS(h11, y0), _MM_PROD_PS(h01, y1)), detrec);
|
||||||
|
*x1 = _MM_PROD_PS(_mm_sub_ps(_MM_PROD_PS(h00, y1), _MM_PROD_PS(h10, y0)), detrec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SSE implementation for Minimum Mean Squared Error (MMSE) solver */
|
||||||
|
inline void srslte_mat_2x2_mmse_sse(__m128 y0, __m128 y1, __m128 h00, __m128 h01, __m128 h10, __m128 h11,
|
||||||
|
__m128 *x0, __m128 *x1, float noise_estimate, float norm) {
|
||||||
|
__m128 _noise_estimate = _mm_set_ps(0.0f, noise_estimate, 0.0f, noise_estimate);
|
||||||
|
__m128 _norm = _mm_set1_ps(norm);
|
||||||
|
|
||||||
|
/* Create conjugated matrix */
|
||||||
|
__m128 _h00 = _MM_CONJ_PS(h00);
|
||||||
|
__m128 _h01 = _MM_CONJ_PS(h01);
|
||||||
|
__m128 _h10 = _MM_CONJ_PS(h10);
|
||||||
|
__m128 _h11 = _MM_CONJ_PS(h11);
|
||||||
|
|
||||||
|
/* 1. A = H' x H + No*/
|
||||||
|
__m128 a00 = _mm_add_ps(_mm_add_ps(_MM_SQMOD_PS(h00), _MM_SQMOD_PS(h10)), _noise_estimate);
|
||||||
|
__m128 a01 = _mm_add_ps(_MM_PROD_PS(_h00, h01), _MM_PROD_PS(_h10, h11));
|
||||||
|
__m128 a10 = _mm_add_ps(_MM_PROD_PS(_h01, h00), _MM_PROD_PS(_h11, h10));
|
||||||
|
__m128 a11 = _mm_add_ps(_mm_add_ps(_MM_SQMOD_PS(h01), _MM_SQMOD_PS(h11)), _noise_estimate);
|
||||||
|
|
||||||
|
/* 2. B = inv(H' x H + No) = inv(A) */
|
||||||
|
__m128 b00 = a11;
|
||||||
|
__m128 b01 = _mm_xor_ps(a01, _mm_set1_ps(-0.0f));
|
||||||
|
__m128 b10 = _mm_xor_ps(a10, _mm_set1_ps(-0.0f));
|
||||||
|
__m128 b11 = a00;
|
||||||
|
_norm = _mm_mul_ps(_norm, srslte_mat_cf_recip_sse(srslte_mat_2x2_det_sse(a00, a01, a10, a11)));
|
||||||
|
|
||||||
|
|
||||||
|
/* 3. W = inv(H' x H + No) x H' = B x H' */
|
||||||
|
__m128 w00 = _mm_add_ps(_MM_PROD_PS(b00, _h00), _MM_PROD_PS(b01, _h01));
|
||||||
|
__m128 w01 = _mm_add_ps(_MM_PROD_PS(b00, _h10), _MM_PROD_PS(b01, _h11));
|
||||||
|
__m128 w10 = _mm_add_ps(_MM_PROD_PS(b10, _h00), _MM_PROD_PS(b11, _h01));
|
||||||
|
__m128 w11 = _mm_add_ps(_MM_PROD_PS(b10, _h10), _MM_PROD_PS(b11, _h11));
|
||||||
|
|
||||||
|
/* 4. X = W x Y */
|
||||||
|
*x0 = _MM_PROD_PS(_mm_add_ps(_MM_PROD_PS(y0, w00), _MM_PROD_PS(y1, w01)), _norm);
|
||||||
|
*x1 = _MM_PROD_PS(_mm_add_ps(_MM_PROD_PS(y0, w10), _MM_PROD_PS(y1, w11)), _norm);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* LV_HAVE_SSE */
|
||||||
|
|
||||||
|
#ifdef LV_HAVE_AVX
|
||||||
|
#include <immintrin.h>
|
||||||
|
|
||||||
|
/* AVX implementation for complex reciprocal */
|
||||||
|
inline __m256 srslte_mat_cf_recip_avx(__m256 a) {
|
||||||
|
__m256 conj = _MM256_CONJ_PS(a);
|
||||||
|
__m256 sqabs = _mm256_mul_ps(a, a);
|
||||||
|
sqabs = _mm256_add_ps(_mm256_movehdup_ps(sqabs), _mm256_moveldup_ps(sqabs));
|
||||||
|
|
||||||
|
__m256 recp = _mm256_rcp_ps(sqabs);
|
||||||
|
|
||||||
|
return _mm256_mul_ps(recp, conj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AVX implementation for 2x2 determinant */
|
||||||
|
inline __m256 srslte_mat_2x2_det_avx(__m256 a00, __m256 a01, __m256 a10, __m256 a11) {
|
||||||
|
#ifdef LV_HAVE_FMA
|
||||||
|
return _MM256_PROD_SUB_PS(a00, a11, _MM256_PROD_PS(a01, a10));
|
||||||
|
#else
|
||||||
|
return _mm256_sub_ps(_MM256_PROD_PS(a00, a11), _MM256_PROD_PS(a01, a10));
|
||||||
|
#endif /* LV_HAVE_FMA */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AVX implementation for Zero Forcing (ZF) solver */
|
||||||
|
inline void srslte_mat_2x2_zf_avx(__m256 y0, __m256 y1, __m256 h00, __m256 h01, __m256 h10, __m256 h11,
|
||||||
|
__m256 *x0, __m256 *x1, float norm) {
|
||||||
|
|
||||||
|
__m256 det = srslte_mat_2x2_det_avx(h00, h01, h10, h11);
|
||||||
|
__m256 detrec = _mm256_mul_ps(srslte_mat_cf_recip_avx(det), _mm256_set1_ps(norm));
|
||||||
|
|
||||||
|
#ifdef LV_HAVE_FMA
|
||||||
|
*x0 = _MM256_PROD_PS(_MM256_PROD_SUB_PS(h11, y0, _MM256_PROD_PS(h01, y1)), detrec);
|
||||||
|
*x1 = _MM256_PROD_PS(_MM256_PROD_SUB_PS(h00, y1, _MM256_PROD_PS(h10, y0)), detrec);
|
||||||
|
#else
|
||||||
|
*x0 = _MM256_PROD_PS(_mm256_sub_ps(_MM256_PROD_PS(h11, y0), _MM256_PROD_PS(h01, y1)), detrec);
|
||||||
|
*x1 = _MM256_PROD_PS(_mm256_sub_ps(_MM256_PROD_PS(h00, y1), _MM256_PROD_PS(h10, y0)), detrec);
|
||||||
|
#endif /* LV_HAVE_FMA */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AVX implementation for Minimum Mean Squared Error (MMSE) solver */
|
||||||
|
inline void srslte_mat_2x2_mmse_avx(__m256 y0, __m256 y1, __m256 h00, __m256 h01, __m256 h10, __m256 h11,
|
||||||
|
__m256 *x0, __m256 *x1, float noise_estimate, float norm) {
|
||||||
|
__m256 _noise_estimate = _mm256_set_ps(0.0f, noise_estimate, 0.0f, noise_estimate,
|
||||||
|
0.0f, noise_estimate, 0.0f, noise_estimate);
|
||||||
|
__m256 _norm = _mm256_set1_ps(norm);
|
||||||
|
|
||||||
|
/* Create conjugated matrix */
|
||||||
|
__m256 _h00 = _MM256_CONJ_PS(h00);
|
||||||
|
__m256 _h01 = _MM256_CONJ_PS(h01);
|
||||||
|
__m256 _h10 = _MM256_CONJ_PS(h10);
|
||||||
|
__m256 _h11 = _MM256_CONJ_PS(h11);
|
||||||
|
|
||||||
|
/* 1. A = H' x H + No*/
|
||||||
|
#ifdef LV_HAVE_FMA
|
||||||
|
__m256 a00 = _MM256_SQMOD_ADD_PS(h00, h10, _noise_estimate);
|
||||||
|
__m256 a01 = _MM256_PROD_ADD_PS(_h00, h01, _MM256_PROD_PS(_h10, h11));
|
||||||
|
__m256 a10 = _MM256_PROD_ADD_PS(_h01, h00, _MM256_PROD_PS(_h11, h10));
|
||||||
|
__m256 a11 = _MM256_SQMOD_ADD_PS(h01, h11, _noise_estimate);
|
||||||
|
#else
|
||||||
|
__m256 a00 = _mm256_add_ps(_MM256_SQMOD_PS(h00, h10), _noise_estimate);
|
||||||
|
__m256 a01 = _mm256_add_ps(_MM256_PROD_PS(_h00, h01), _MM256_PROD_PS(_h10, h11));
|
||||||
|
__m256 a10 = _mm256_add_ps(_MM256_PROD_PS(_h01, h00), _MM256_PROD_PS(_h11, h10));
|
||||||
|
__m256 a11 = _mm256_add_ps(_MM256_SQMOD_PS(h01, h11), _noise_estimate);
|
||||||
|
#endif /* LV_HAVE_FMA */
|
||||||
|
|
||||||
|
/* 2. B = inv(H' x H + No) = inv(A) */
|
||||||
|
__m256 b00 = a11;
|
||||||
|
__m256 b01 = _mm256_xor_ps(a01, _mm256_set1_ps(-0.0f));
|
||||||
|
__m256 b10 = _mm256_xor_ps(a10, _mm256_set1_ps(-0.0f));
|
||||||
|
__m256 b11 = a00;
|
||||||
|
_norm = _mm256_mul_ps(_norm, srslte_mat_cf_recip_avx(srslte_mat_2x2_det_avx(a00, a01, a10, a11)));
|
||||||
|
|
||||||
|
|
||||||
|
/* 3. W = inv(H' x H + No) x H' = B x H' */
|
||||||
|
#ifdef LV_HAVE_FMA
|
||||||
|
__m256 w00 = _MM256_PROD_ADD_PS(b00, _h00, _MM256_PROD_PS(b01, _h01));
|
||||||
|
__m256 w01 = _MM256_PROD_ADD_PS(b00, _h10, _MM256_PROD_PS(b01, _h11));
|
||||||
|
__m256 w10 = _MM256_PROD_ADD_PS(b10, _h00, _MM256_PROD_PS(b11, _h01));
|
||||||
|
__m256 w11 = _MM256_PROD_ADD_PS(b10, _h10, _MM256_PROD_PS(b11, _h11));
|
||||||
|
#else
|
||||||
|
__m256 w00 = _mm256_add_ps(_MM256_PROD_PS(b00, _h00), _MM256_PROD_PS(b01, _h01));
|
||||||
|
__m256 w01 = _mm256_add_ps(_MM256_PROD_PS(b00, _h10), _MM256_PROD_PS(b01, _h11));
|
||||||
|
__m256 w10 = _mm256_add_ps(_MM256_PROD_PS(b10, _h00), _MM256_PROD_PS(b11, _h01));
|
||||||
|
__m256 w11 = _mm256_add_ps(_MM256_PROD_PS(b10, _h10), _MM256_PROD_PS(b11, _h11));
|
||||||
|
#endif /* LV_HAVE_FMA */
|
||||||
|
|
||||||
|
/* 4. X = W x Y */
|
||||||
|
#ifdef LV_HAVE_FMA
|
||||||
|
*x0 = _MM256_PROD_PS(_MM256_PROD_ADD_PS(y0, w00, _MM256_PROD_PS(y1, w01)), _norm);
|
||||||
|
*x1 = _MM256_PROD_PS(_MM256_PROD_ADD_PS(y0, w10, _MM256_PROD_PS(y1, w11)), _norm);
|
||||||
|
#else
|
||||||
|
*x0 = _MM256_PROD_PS(_mm256_add_ps(_MM256_PROD_PS(y0, w00), _MM256_PROD_PS(y1, w01)), _norm);
|
||||||
|
*x1 = _MM256_PROD_PS(_mm256_add_ps(_MM256_PROD_PS(y0, w10), _MM256_PROD_PS(y1, w11)), _norm);
|
||||||
|
#endif /* LV_HAVE_FMA */
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* LV_HAVE_AVX */
|
@ -0,0 +1,415 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2015 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the srsLTE library.
|
||||||
|
*
|
||||||
|
* srsLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* srsLTE 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Affero General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <complex.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <immintrin.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include "srslte/phy/utils/mat.h"
|
||||||
|
|
||||||
|
|
||||||
|
bool zf_solver = false;
|
||||||
|
bool mmse_solver = false;
|
||||||
|
bool verbose = false;
|
||||||
|
|
||||||
|
double elapsed_us(struct timeval *ts_start, struct timeval *ts_end) {
|
||||||
|
if (ts_end->tv_usec > ts_start->tv_usec) {
|
||||||
|
return ((double) ts_end->tv_sec - (double) ts_start->tv_sec) * 1000000 +
|
||||||
|
(double) ts_end->tv_usec - (double) ts_start->tv_usec;
|
||||||
|
} else {
|
||||||
|
return ((double) ts_end->tv_sec - (double) ts_start->tv_sec - 1) * 1000000 +
|
||||||
|
((double) ts_end->tv_usec + 1000000) - (double) ts_start->tv_usec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NOF_REPETITIONS 1000
|
||||||
|
#define RUN_TEST(FUNCTION) /*TYPE NAME (void)*/ { \
|
||||||
|
int i;\
|
||||||
|
struct timeval start, end;\
|
||||||
|
gettimeofday(&start, NULL); \
|
||||||
|
bool ret = true; \
|
||||||
|
for (i = 0; i < NOF_REPETITIONS; i++) {ret &= FUNCTION ();}\
|
||||||
|
gettimeofday(&end, NULL);\
|
||||||
|
if (verbose) printf("%32s: %s ... %6.2f us/call\n", #FUNCTION, (ret)?"Pass":"Fail", \
|
||||||
|
elapsed_us(&start, &end)/NOF_REPETITIONS);\
|
||||||
|
passed &= ret;\
|
||||||
|
}
|
||||||
|
|
||||||
|
void usage(char *prog) {
|
||||||
|
printf("Usage: %s [mzvh]\n", prog);
|
||||||
|
printf("\t-m Test Minimum Mean Squared Error (MMSE) solver\n");
|
||||||
|
printf("\t-z Test Zero Forcing (ZF) solver\n");
|
||||||
|
printf("\t-v Verbose\n");
|
||||||
|
printf("\t-h Show this message\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_args(int argc, char **argv) {
|
||||||
|
int opt;
|
||||||
|
while ((opt = getopt(argc, argv, "mzvh")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'm':
|
||||||
|
mmse_solver = true;
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
zf_solver = true;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
verbose = true;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
default:
|
||||||
|
usage(argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_zf_solver_gen(void) {
|
||||||
|
cf_t x0, x1, cf_error0, cf_error1;
|
||||||
|
float error;
|
||||||
|
|
||||||
|
cf_t x0_gold = RANDOM_CF();
|
||||||
|
cf_t x1_gold = RANDOM_CF();
|
||||||
|
cf_t h00 = RANDOM_CF();
|
||||||
|
cf_t h01 = RANDOM_CF();
|
||||||
|
cf_t h10 = RANDOM_CF();
|
||||||
|
cf_t h11 = (1 - h01 * h10) / h00;
|
||||||
|
cf_t y0 = x0_gold * h00 + x1_gold * h01;
|
||||||
|
cf_t y1 = x0_gold * h10 + x1_gold * h11;
|
||||||
|
|
||||||
|
srslte_mat_2x2_zf_gen(y0, y1, h00, h01, h10, h11, &x0, &x1, 1.0f);
|
||||||
|
|
||||||
|
cf_error0 = x0 - x0_gold;
|
||||||
|
cf_error1 = x1 - x1_gold;
|
||||||
|
error = crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) +
|
||||||
|
crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1);
|
||||||
|
|
||||||
|
return (error < 1e-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_mmse_solver_gen(void) {
|
||||||
|
cf_t x0, x1, cf_error0, cf_error1;
|
||||||
|
float error;
|
||||||
|
|
||||||
|
cf_t x0_gold = RANDOM_CF();
|
||||||
|
cf_t x1_gold = RANDOM_CF();
|
||||||
|
cf_t h00 = RANDOM_CF();
|
||||||
|
cf_t h01 = RANDOM_CF();
|
||||||
|
cf_t h10 = RANDOM_CF();
|
||||||
|
cf_t h11 = (1 - h01 * h10) / h00;
|
||||||
|
cf_t y0 = x0_gold * h00 + x1_gold * h01;
|
||||||
|
cf_t y1 = x0_gold * h10 + x1_gold * h11;
|
||||||
|
|
||||||
|
srslte_mat_2x2_mmse_gen(y0, y1, h00, h01, h10, h11, &x0, &x1, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
cf_error0 = x0 - x0_gold;
|
||||||
|
cf_error1 = x1 - x1_gold;
|
||||||
|
error = crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) +
|
||||||
|
crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1);
|
||||||
|
|
||||||
|
return (error < 1e-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef LV_HAVE_SSE
|
||||||
|
|
||||||
|
bool test_zf_solver_sse(void) {
|
||||||
|
cf_t cf_error0, cf_error1;
|
||||||
|
float error = 0.0f;
|
||||||
|
|
||||||
|
cf_t x0_gold_1 = RANDOM_CF();
|
||||||
|
cf_t x1_gold_1 = RANDOM_CF();
|
||||||
|
cf_t h00_1 = RANDOM_CF();
|
||||||
|
cf_t h01_1 = RANDOM_CF();
|
||||||
|
cf_t h10_1 = RANDOM_CF();
|
||||||
|
cf_t h11_1 = (1 - h01_1 * h10_1) / h00_1;
|
||||||
|
cf_t y0_1 = x0_gold_1 * h00_1 + x1_gold_1 * h01_1;
|
||||||
|
cf_t y1_1 = x0_gold_1 * h10_1 + x1_gold_1 * h11_1;
|
||||||
|
|
||||||
|
cf_t x0_gold_2 = RANDOM_CF();
|
||||||
|
cf_t x1_gold_2 = RANDOM_CF();
|
||||||
|
cf_t h00_2 = RANDOM_CF();
|
||||||
|
cf_t h01_2 = RANDOM_CF();
|
||||||
|
cf_t h10_2 = RANDOM_CF();
|
||||||
|
cf_t h11_2 = (1 - h01_2 * h10_2) / h00_2;
|
||||||
|
cf_t y0_2 = x0_gold_2 * h00_2 + x1_gold_2 * h01_2;
|
||||||
|
cf_t y1_2 = x0_gold_2 * h10_2 + x1_gold_2 * h11_2;
|
||||||
|
|
||||||
|
__m128 _y0 = _mm_set_ps(cimagf(y0_1), crealf(y0_1), cimagf(y0_2), crealf(y0_2));
|
||||||
|
__m128 _y1 = _mm_set_ps(cimagf(y1_1), crealf(y1_1), cimagf(y1_2), crealf(y1_2));
|
||||||
|
|
||||||
|
__m128 _h00 = _mm_set_ps(cimagf(h00_1), crealf(h00_1), cimagf(h00_2), crealf(h00_2));
|
||||||
|
__m128 _h01 = _mm_set_ps(cimagf(h01_1), crealf(h01_1), cimagf(h01_2), crealf(h01_2));
|
||||||
|
__m128 _h10 = _mm_set_ps(cimagf(h10_1), crealf(h10_1), cimagf(h10_2), crealf(h10_2));
|
||||||
|
__m128 _h11 = _mm_set_ps(cimagf(h11_1), crealf(h11_1), cimagf(h11_2), crealf(h11_2));
|
||||||
|
|
||||||
|
__m128 _x0, _x1;
|
||||||
|
|
||||||
|
srslte_mat_2x2_zf_sse(_y0, _y1, _h00, _h01, _h10, _h11, &_x0, &_x1, 1.0f);
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__((aligned(128))) cf_t x0[2];
|
||||||
|
__attribute__((aligned(128))) cf_t x1[2];
|
||||||
|
|
||||||
|
_mm_store_ps((float *) x0, _x0);
|
||||||
|
_mm_store_ps((float *) x1, _x1);
|
||||||
|
|
||||||
|
cf_error0 = x0[1] - x0_gold_1;
|
||||||
|
cf_error1 = x1[1] - x1_gold_1;
|
||||||
|
error += crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) +
|
||||||
|
crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1);
|
||||||
|
|
||||||
|
cf_error0 = x0[0] - x0_gold_2;
|
||||||
|
cf_error1 = x1[0] - x1_gold_2;
|
||||||
|
error += crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) +
|
||||||
|
crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1);
|
||||||
|
|
||||||
|
return (error < 1e-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_mmse_solver_sse(void) {
|
||||||
|
cf_t cf_error0, cf_error1;
|
||||||
|
float error = 0.0f;
|
||||||
|
|
||||||
|
cf_t x0_gold_1 = RANDOM_CF();
|
||||||
|
cf_t x1_gold_1 = RANDOM_CF();
|
||||||
|
cf_t h00_1 = RANDOM_CF();
|
||||||
|
cf_t h01_1 = RANDOM_CF();
|
||||||
|
cf_t h10_1 = RANDOM_CF();
|
||||||
|
cf_t h11_1 = (1 - h01_1 * h10_1) / h00_1;
|
||||||
|
cf_t y0_1 = x0_gold_1 * h00_1 + x1_gold_1 * h01_1;
|
||||||
|
cf_t y1_1 = x0_gold_1 * h10_1 + x1_gold_1 * h11_1;
|
||||||
|
|
||||||
|
cf_t x0_gold_2 = RANDOM_CF();
|
||||||
|
cf_t x1_gold_2 = RANDOM_CF();
|
||||||
|
cf_t h00_2 = RANDOM_CF();
|
||||||
|
cf_t h01_2 = RANDOM_CF();
|
||||||
|
cf_t h10_2 = RANDOM_CF();
|
||||||
|
cf_t h11_2 = (1 - h01_2 * h10_2) / h00_2;
|
||||||
|
cf_t y0_2 = x0_gold_2 * h00_2 + x1_gold_2 * h01_2;
|
||||||
|
cf_t y1_2 = x0_gold_2 * h10_2 + x1_gold_2 * h11_2;
|
||||||
|
|
||||||
|
__m128 _y0 = _mm_set_ps(cimagf(y0_1), crealf(y0_1), cimagf(y0_2), crealf(y0_2));
|
||||||
|
__m128 _y1 = _mm_set_ps(cimagf(y1_1), crealf(y1_1), cimagf(y1_2), crealf(y1_2));
|
||||||
|
|
||||||
|
__m128 _h00 = _mm_set_ps(cimagf(h00_1), crealf(h00_1), cimagf(h00_2), crealf(h00_2));
|
||||||
|
__m128 _h01 = _mm_set_ps(cimagf(h01_1), crealf(h01_1), cimagf(h01_2), crealf(h01_2));
|
||||||
|
__m128 _h10 = _mm_set_ps(cimagf(h10_1), crealf(h10_1), cimagf(h10_2), crealf(h10_2));
|
||||||
|
__m128 _h11 = _mm_set_ps(cimagf(h11_1), crealf(h11_1), cimagf(h11_2), crealf(h11_2));
|
||||||
|
|
||||||
|
__m128 _x0, _x1;
|
||||||
|
|
||||||
|
srslte_mat_2x2_mmse_sse(_y0, _y1, _h00, _h01, _h10, _h11, &_x0, &_x1, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__((aligned(128))) cf_t x0[2];
|
||||||
|
__attribute__((aligned(128))) cf_t x1[2];
|
||||||
|
|
||||||
|
_mm_store_ps((float *) x0, _x0);
|
||||||
|
_mm_store_ps((float *) x1, _x1);
|
||||||
|
|
||||||
|
cf_error0 = x0[1] - x0_gold_1;
|
||||||
|
cf_error1 = x1[1] - x1_gold_1;
|
||||||
|
error += crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) +
|
||||||
|
crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1);
|
||||||
|
|
||||||
|
cf_error0 = x0[0] - x0_gold_2;
|
||||||
|
cf_error1 = x1[0] - x1_gold_2;
|
||||||
|
error += crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) +
|
||||||
|
crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1);
|
||||||
|
|
||||||
|
return (error < 1e-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* LV_HAVE_SSE */
|
||||||
|
|
||||||
|
#ifdef LV_HAVE_AVX
|
||||||
|
|
||||||
|
bool test_zf_solver_avx(void) {
|
||||||
|
cf_t cf_error0, cf_error1;
|
||||||
|
float error = 0.0f;
|
||||||
|
|
||||||
|
cf_t x0_gold_1 = RANDOM_CF();
|
||||||
|
cf_t x1_gold_1 = RANDOM_CF();
|
||||||
|
cf_t h00_1 = RANDOM_CF();
|
||||||
|
cf_t h01_1 = RANDOM_CF();
|
||||||
|
cf_t h10_1 = RANDOM_CF();
|
||||||
|
cf_t h11_1 = (1 - h01_1 * h10_1) / h00_1;
|
||||||
|
cf_t y0_1 = x0_gold_1 * h00_1 + x1_gold_1 * h01_1;
|
||||||
|
cf_t y1_1 = x0_gold_1 * h10_1 + x1_gold_1 * h11_1;
|
||||||
|
|
||||||
|
cf_t x0_gold_2 = RANDOM_CF();
|
||||||
|
cf_t x1_gold_2 = RANDOM_CF();
|
||||||
|
cf_t h00_2 = RANDOM_CF();
|
||||||
|
cf_t h01_2 = RANDOM_CF();
|
||||||
|
cf_t h10_2 = RANDOM_CF();
|
||||||
|
cf_t h11_2 = (1 - h01_2 * h10_2) / h00_2;
|
||||||
|
cf_t y0_2 = x0_gold_2 * h00_2 + x1_gold_2 * h01_2;
|
||||||
|
cf_t y1_2 = x0_gold_2 * h10_2 + x1_gold_2 * h11_2;
|
||||||
|
|
||||||
|
__m256 _y0 = _mm256_set_ps(cimagf(y0_1), crealf(y0_1), cimagf(y0_2), crealf(y0_2),
|
||||||
|
cimagf(y0_1), crealf(y0_1), cimagf(y0_2), crealf(y0_2));
|
||||||
|
__m256 _y1 = _mm256_set_ps(cimagf(y1_1), crealf(y1_1), cimagf(y1_2), crealf(y1_2),
|
||||||
|
cimagf(y1_1), crealf(y1_1), cimagf(y1_2), crealf(y1_2));
|
||||||
|
|
||||||
|
__m256 _h00 = _mm256_set_ps(cimagf(h00_1), crealf(h00_1), cimagf(h00_2), crealf(h00_2),
|
||||||
|
cimagf(h00_1), crealf(h00_1), cimagf(h00_2), crealf(h00_2));
|
||||||
|
__m256 _h01 = _mm256_set_ps(cimagf(h01_1), crealf(h01_1), cimagf(h01_2), crealf(h01_2),
|
||||||
|
cimagf(h01_1), crealf(h01_1), cimagf(h01_2), crealf(h01_2));
|
||||||
|
__m256 _h10 = _mm256_set_ps(cimagf(h10_1), crealf(h10_1), cimagf(h10_2), crealf(h10_2),
|
||||||
|
cimagf(h10_1), crealf(h10_1), cimagf(h10_2), crealf(h10_2));
|
||||||
|
__m256 _h11 = _mm256_set_ps(cimagf(h11_1), crealf(h11_1), cimagf(h11_2), crealf(h11_2),
|
||||||
|
cimagf(h11_1), crealf(h11_1), cimagf(h11_2), crealf(h11_2));
|
||||||
|
|
||||||
|
__m256 _x0, _x1;
|
||||||
|
|
||||||
|
srslte_mat_2x2_zf_avx(_y0, _y1, _h00, _h01, _h10, _h11, &_x0, &_x1, 1.0f);
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__((aligned(256))) cf_t x0[4];
|
||||||
|
__attribute__((aligned(256))) cf_t x1[4];
|
||||||
|
|
||||||
|
_mm256_store_ps((float *) x0, _x0);
|
||||||
|
_mm256_store_ps((float *) x1, _x1);
|
||||||
|
|
||||||
|
cf_error0 = x0[1] - x0_gold_1;
|
||||||
|
cf_error1 = x1[1] - x1_gold_1;
|
||||||
|
error += crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) +
|
||||||
|
crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1);
|
||||||
|
|
||||||
|
cf_error0 = x0[0] - x0_gold_2;
|
||||||
|
cf_error1 = x1[0] - x1_gold_2;
|
||||||
|
error += crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) +
|
||||||
|
crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1);
|
||||||
|
|
||||||
|
return (error < 1e-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_mmse_solver_avx(void) {
|
||||||
|
cf_t cf_error0, cf_error1;
|
||||||
|
float error = 0.0f;
|
||||||
|
|
||||||
|
cf_t x0_gold_1 = RANDOM_CF();
|
||||||
|
cf_t x1_gold_1 = RANDOM_CF();
|
||||||
|
cf_t h00_1 = RANDOM_CF();
|
||||||
|
cf_t h01_1 = RANDOM_CF();
|
||||||
|
cf_t h10_1 = RANDOM_CF();
|
||||||
|
cf_t h11_1 = (1 - h01_1 * h10_1) / h00_1;
|
||||||
|
cf_t y0_1 = x0_gold_1 * h00_1 + x1_gold_1 * h01_1;
|
||||||
|
cf_t y1_1 = x0_gold_1 * h10_1 + x1_gold_1 * h11_1;
|
||||||
|
|
||||||
|
cf_t x0_gold_2 = RANDOM_CF();
|
||||||
|
cf_t x1_gold_2 = RANDOM_CF();
|
||||||
|
cf_t h00_2 = RANDOM_CF();
|
||||||
|
cf_t h01_2 = RANDOM_CF();
|
||||||
|
cf_t h10_2 = RANDOM_CF();
|
||||||
|
cf_t h11_2 = (1 - h01_2 * h10_2) / h00_2;
|
||||||
|
cf_t y0_2 = x0_gold_2 * h00_2 + x1_gold_2 * h01_2;
|
||||||
|
cf_t y1_2 = x0_gold_2 * h10_2 + x1_gold_2 * h11_2;
|
||||||
|
|
||||||
|
__m256 _y0 = _mm256_set_ps(cimagf(y0_1), crealf(y0_1), cimagf(y0_2), crealf(y0_2),
|
||||||
|
cimagf(y0_1), crealf(y0_1), cimagf(y0_2), crealf(y0_2));
|
||||||
|
__m256 _y1 = _mm256_set_ps(cimagf(y1_1), crealf(y1_1), cimagf(y1_2), crealf(y1_2),
|
||||||
|
cimagf(y1_1), crealf(y1_1), cimagf(y1_2), crealf(y1_2));
|
||||||
|
|
||||||
|
__m256 _h00 = _mm256_set_ps(cimagf(h00_1), crealf(h00_1), cimagf(h00_2), crealf(h00_2),
|
||||||
|
cimagf(h00_1), crealf(h00_1), cimagf(h00_2), crealf(h00_2));
|
||||||
|
__m256 _h01 = _mm256_set_ps(cimagf(h01_1), crealf(h01_1), cimagf(h01_2), crealf(h01_2),
|
||||||
|
cimagf(h01_1), crealf(h01_1), cimagf(h01_2), crealf(h01_2));
|
||||||
|
__m256 _h10 = _mm256_set_ps(cimagf(h10_1), crealf(h10_1), cimagf(h10_2), crealf(h10_2),
|
||||||
|
cimagf(h10_1), crealf(h10_1), cimagf(h10_2), crealf(h10_2));
|
||||||
|
__m256 _h11 = _mm256_set_ps(cimagf(h11_1), crealf(h11_1), cimagf(h11_2), crealf(h11_2),
|
||||||
|
cimagf(h11_1), crealf(h11_1), cimagf(h11_2), crealf(h11_2));
|
||||||
|
|
||||||
|
__m256 _x0, _x1;
|
||||||
|
|
||||||
|
srslte_mat_2x2_mmse_avx(_y0, _y1, _h00, _h01, _h10, _h11, &_x0, &_x1, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__((aligned(256))) cf_t x0[4];
|
||||||
|
__attribute__((aligned(256))) cf_t x1[4];
|
||||||
|
|
||||||
|
_mm256_store_ps((float *) x0, _x0);
|
||||||
|
_mm256_store_ps((float *) x1, _x1);
|
||||||
|
|
||||||
|
cf_error0 = x0[1] - x0_gold_1;
|
||||||
|
cf_error1 = x1[1] - x1_gold_1;
|
||||||
|
error += crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) +
|
||||||
|
crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1);
|
||||||
|
|
||||||
|
cf_error0 = x0[0] - x0_gold_2;
|
||||||
|
cf_error1 = x1[0] - x1_gold_2;
|
||||||
|
error += crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) +
|
||||||
|
crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1);
|
||||||
|
|
||||||
|
return (error < 1e-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* LV_HAVE_AVX */
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
bool passed = true;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
parse_args(argc, argv);
|
||||||
|
|
||||||
|
if (zf_solver) {
|
||||||
|
RUN_TEST(test_zf_solver_gen);
|
||||||
|
|
||||||
|
#ifdef LV_HAVE_SSE
|
||||||
|
RUN_TEST(test_zf_solver_sse);
|
||||||
|
#endif /* LV_HAVE_SSE */
|
||||||
|
|
||||||
|
#ifdef LV_HAVE_AVX
|
||||||
|
RUN_TEST(test_zf_solver_avx);
|
||||||
|
#endif /* LV_HAVE_AVX */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mmse_solver) {
|
||||||
|
RUN_TEST(test_mmse_solver_gen);
|
||||||
|
|
||||||
|
#ifdef LV_HAVE_SSE
|
||||||
|
RUN_TEST(test_mmse_solver_sse);
|
||||||
|
#endif /* LV_HAVE_SSE */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef LV_HAVE_AVX
|
||||||
|
RUN_TEST(test_mmse_solver_avx);
|
||||||
|
#endif /* LV_HAVE_AVX */
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s!\n", (passed) ? "Ok" : "Failed");
|
||||||
|
|
||||||
|
if (!passed) {
|
||||||
|
exit(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(ret);
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue