From 1d00e1acaa2168888f3ae250f3c925523b82590f Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Thu, 3 Aug 2017 13:56:57 +0200 Subject: [PATCH] Merge with next_with_matlab --- lib/examples/pdsch_enodeb.c | 36 +- lib/examples/pdsch_ue.c | 21 +- lib/include/srslte/phy/common/phy_common.h | 2 + lib/include/srslte/phy/mimo/precoding.h | 13 +- lib/include/srslte/phy/phch/pdsch.h | 4 +- lib/include/srslte/phy/phch/pdsch_cfg.h | 1 + lib/include/srslte/phy/phch/ra.h | 7 + lib/include/srslte/phy/rf/rf.h | 5 + lib/include/srslte/phy/ue/ue_dl.h | 4 +- lib/include/srslte/phy/utils/algebra.h | 70 ++ lib/src/phy/common/phy_common.c | 2 +- lib/src/phy/mimo/precoding.c | 710 +++++++++++++++++++-- lib/src/phy/mimo/test/CMakeLists.txt | 11 +- lib/src/phy/mimo/test/precoder_test.c | 19 +- lib/src/phy/phch/pdsch.c | 139 +++- lib/src/phy/phch/test/CMakeLists.txt | 95 ++- lib/src/phy/phch/test/pdsch_test.c | 92 +-- lib/src/phy/phch/test/pdsch_test_mex.c | 19 +- lib/src/phy/ue/ue_dl.c | 78 ++- lib/src/phy/utils/algebra.c | 72 +++ 20 files changed, 1240 insertions(+), 160 deletions(-) create mode 100644 lib/include/srslte/phy/utils/algebra.h create mode 100644 lib/src/phy/utils/algebra.c diff --git a/lib/examples/pdsch_enodeb.c b/lib/examples/pdsch_enodeb.c index b1304958c..1275360a4 100644 --- a/lib/examples/pdsch_enodeb.c +++ b/lib/examples/pdsch_enodeb.c @@ -70,6 +70,8 @@ uint32_t mcs_idx = 1, last_mcs_idx = 1; int nof_frames = -1; char mimo_type_str[32] = "single"; uint32_t nof_tb = 1; +uint32_t multiplex_pmi = 0; +uint32_t multiplex_nof_layers = 1; char *rf_args = ""; float rf_amp = 0.8, rf_gain = 70.0, rf_freq = 2400000000; @@ -104,7 +106,7 @@ uint8_t *data[2], data2[DATA_BUFF_SZ]; uint8_t data_tmp[DATA_BUFF_SZ]; void usage(char *prog) { - printf("Usage: %s [agmfoncvpuM]\n", prog); + printf("Usage: %s [agmfoncvpuxb]\n", prog); #ifndef DISABLE_RF printf("\t-a RF args [Default %s]\n", rf_args); printf("\t-l RF amplitude [Default %.2f]\n", rf_amp); @@ -118,14 +120,18 @@ void usage(char *prog) { printf("\t-n number of frames [Default %d]\n", nof_frames); printf("\t-c cell id [Default %d]\n", cell.id); printf("\t-p nof_prb [Default %d]\n", cell.nof_prb); - printf("\t-M Transmission mode[single|diversity|cdd] [Default %s]\n", mimo_type_str); + printf("\t-x Transmission mode[single|diversity|cdd|multiplex] [Default %s]\n", mimo_type_str); + printf("\t-b Precoding Matrix Index (multiplex mode only)* [Default %d]\n", multiplex_pmi); + printf("\t-w Number of codewords/layers (multiplex mode only)* [Default %d]\n", multiplex_nof_layers); printf("\t-u listen TCP port for input data (-1 is random) [Default %d]\n", net_port); printf("\t-v [set srslte_verbose to debug, default none]\n"); + printf("\n"); + printf("\t*: See 3GPP 36.212 Table 5.3.3.1.5-4 for more information\n"); } void parse_args(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "aglfmoncpvutM")) != -1) { + while ((opt = getopt(argc, argv, "aglfmoncpvutxbw")) != -1) { switch (opt) { case 'a': rf_args = argv[optind]; @@ -157,9 +163,15 @@ void parse_args(int argc, char **argv) { case 'c': cell.id = atoi(argv[optind]); break; - case 'M': + case 'x': strncpy(mimo_type_str, argv[optind], 32); break; + case 'b': + multiplex_pmi = (uint32_t) atoi(argv[optind]); + break; + case 'w': + multiplex_nof_layers = (uint32_t) atoi(argv[optind]); + break; case 'v': srslte_verbose++; break; @@ -202,6 +214,11 @@ void base_init() { pdsch_cfg.nof_layers = 2; nof_tb = 2; break; + case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: + cell.nof_ports = 2; + pdsch_cfg.nof_layers = multiplex_nof_layers; + nof_tb = multiplex_nof_layers; + break; default: ERROR("Transmission mode not implemented."); exit(-1); @@ -695,7 +712,7 @@ int main(int argc, char **argv) { } if (send_data) { - srslte_dci_format_t dci_format = SRSLTE_DCI_FORMAT1; + srslte_dci_format_t dci_format; switch(pdsch_cfg.mimo_type) { case SRSLTE_MIMO_TYPE_SINGLE_ANTENNA: dci_format = SRSLTE_DCI_FORMAT1; @@ -705,6 +722,13 @@ int main(int argc, char **argv) { dci_format = SRSLTE_DCI_FORMAT2A; break; case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: + dci_format = SRSLTE_DCI_FORMAT2; + if (multiplex_nof_layers == 1) { + ra_dl.pinfo = (uint8_t) (multiplex_pmi + 1); + } else { + ra_dl.pinfo = (uint8_t) multiplex_pmi; + } + break; default: fprintf(stderr, "Wrong MIMO configuration\n"); exit(SRSLTE_ERROR); @@ -720,7 +744,7 @@ int main(int argc, char **argv) { /* Configure pdsch_cfg parameters */ srslte_ra_dl_grant_t grant; srslte_ra_dl_dci_to_grant(&ra_dl, cell.nof_prb, UE_CRNTI, &grant); - if (srslte_pdsch_cfg_multi(&pdsch_cfg, cell, &grant, cfi, sf_idx, 0, 0)) { + if (srslte_pdsch_cfg_multi(&pdsch_cfg, cell, &grant, cfi, sf_idx, 0, 0, pdsch_cfg.mimo_type, multiplex_pmi)) { fprintf(stderr, "Error configuring PDSCH\n"); exit(-1); } diff --git a/lib/examples/pdsch_ue.c b/lib/examples/pdsch_ue.c index 80f960508..1c6059d9f 100644 --- a/lib/examples/pdsch_ue.c +++ b/lib/examples/pdsch_ue.c @@ -511,7 +511,8 @@ int main(int argc, char **argv) { // Variables for measurements uint32_t nframes=0; - float rsrp0=0.0, rsrp1=0.0, rsrq=0.0, noise=0.0, enodebrate = 0.0, uerate = 0.0; + uint32_t ri = 0, pmi = 0; + float rsrp0=0.0, rsrp1=0.0, rsrq=0.0, noise=0.0, enodebrate = 0.0, uerate = 0.0, sinr = 0.0; bool decode_pdsch = false; #ifndef DISABLE_RF @@ -616,6 +617,16 @@ int main(int argc, char **argv) { noise = SRSLTE_VEC_EMA(srslte_chest_dl_get_noise_estimate(&ue_dl.chest), noise, 0.05); enodebrate = SRSLTE_VEC_EMA((ue_dl.pdsch_cfg.grant.mcs.tbs + ue_dl.pdsch_cfg.grant.mcs2.tbs)/1000.0, enodebrate, 0.05); uerate = SRSLTE_VEC_EMA((n>0)?(ue_dl.pdsch_cfg.grant.mcs.tbs + ue_dl.pdsch_cfg.grant.mcs2.tbs)/1000.0:0.0, uerate, 0.01); + + if (ue_dl.cell.nof_ports == 2 && ue_dl.pdsch.nof_rx_antennas == 2) { + float _sinr; + srslte_ue_dl_ri_pmi_select(&ue_dl, &ri, &pmi, &_sinr); + + if (!isinff(_sinr) && !isnanf(_sinr)) { + sinr = SRSLTE_VEC_EMA(_sinr, sinr, 0.05f); + } + } + nframes++; if (isnan(rsrq)) { rsrq = 0; @@ -651,7 +662,8 @@ int main(int argc, char **argv) { "SNR: %+5.1f dB | %+5.1f dB, " "Rb: %6.2f / %6.2f Mbps, " "PDCCH-Miss: %5.2f%%, " - "PDSCH-BLER: %5.2f%%\r", + "PDSCH-BLER: %5.2f%%, " + "SINR: %3.1f dB RI: %d PMI: %d \r", srslte_ue_sync_get_cfo(&ue_sync) / 1000, 10 * log10(rsrp0 / noise), @@ -659,7 +671,10 @@ int main(int argc, char **argv) { uerate, enodebrate, 100 * (1 - (float) ue_dl.nof_detected / nof_trials), - (float) 100 * ue_dl.pkt_errors / ue_dl.pkts_total); + (float) 100 * ue_dl.pkt_errors / ue_dl.pkts_total, + 10 * log10(sinr), + ri, + pmi); } } break; diff --git a/lib/include/srslte/phy/common/phy_common.h b/lib/include/srslte/phy/common/phy_common.h index b891b7330..e1c6f1b0d 100644 --- a/lib/include/srslte/phy/common/phy_common.h +++ b/lib/include/srslte/phy/common/phy_common.h @@ -55,6 +55,8 @@ #define SRSLTE_MAX_CODEBLOCKS 32 +#define SRSLTE_MAX_CODEBOOKS 4 + #define SRSLTE_LTE_CRC24A 0x1864CFB #define SRSLTE_LTE_CRC24B 0X1800063 #define SRSLTE_LTE_CRC16 0x11021 diff --git a/lib/include/srslte/phy/mimo/precoding.h b/lib/include/srslte/phy/mimo/precoding.h index 62fad08ef..222c085b6 100644 --- a/lib/include/srslte/phy/mimo/precoding.h +++ b/lib/include/srslte/phy/mimo/precoding.h @@ -65,8 +65,9 @@ SRSLTE_API int srslte_precoding_cdd(cf_t *x[SRSLTE_MAX_LAYERS], SRSLTE_API int srslte_precoding_type(cf_t *x[SRSLTE_MAX_LAYERS], cf_t *y[SRSLTE_MAX_PORTS], int nof_layers, - int nof_ports, - int nof_symbols, + int nof_ports, + int codebook_idx, + int nof_symbols, srslte_mimo_type_t type); /* Estimates the vector "x" based on the received signal "y" and the channel estimates "h" @@ -112,8 +113,16 @@ SRSLTE_API int srslte_predecoding_type_multi(cf_t *y[SRSLTE_MAX_PORTS], int nof_rxant, int nof_ports, int nof_layers, + int codebook_idx, int nof_symbols, srslte_mimo_type_t type, float noise_estimate); +int srslte_precoding_pmi_select (cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + uint32_t nof_symbols, + float noise_estimate, + int nof_layers, + uint32_t *pmi, + float sinr[SRSLTE_MAX_CODEBOOKS]); + #endif /* PRECODING_H_ */ diff --git a/lib/include/srslte/phy/phch/pdsch.h b/lib/include/srslte/phy/phch/pdsch.h index 7bdaaef9c..fc250a7d5 100644 --- a/lib/include/srslte/phy/phch/pdsch.h +++ b/lib/include/srslte/phy/phch/pdsch.h @@ -115,7 +115,9 @@ SRSLTE_API int srslte_pdsch_cfg_multi(srslte_pdsch_cfg_t *cfg, uint32_t cfi, uint32_t sf_idx, uint32_t rvidx, - uint32_t rvidx2); + uint32_t rvidx2, + srslte_mimo_type_t mimo_type, + uint32_t pmi); SRSLTE_API int srslte_pdsch_encode(srslte_pdsch_t *q, srslte_pdsch_cfg_t *cfg, diff --git a/lib/include/srslte/phy/phch/pdsch_cfg.h b/lib/include/srslte/phy/phch/pdsch_cfg.h index bec326338..9664d826d 100644 --- a/lib/include/srslte/phy/phch/pdsch_cfg.h +++ b/lib/include/srslte/phy/phch/pdsch_cfg.h @@ -49,6 +49,7 @@ typedef struct SRSLTE_API { uint32_t rv2; uint32_t sf_idx; uint32_t nof_layers; + uint32_t codebook_idx; srslte_mimo_type_t mimo_type; } srslte_pdsch_cfg_t; diff --git a/lib/include/srslte/phy/phch/ra.h b/lib/include/srslte/phy/phch/ra.h index 3c7c3d93c..5fab0f10b 100644 --- a/lib/include/srslte/phy/phch/ra.h +++ b/lib/include/srslte/phy/phch/ra.h @@ -216,6 +216,13 @@ SRSLTE_API void srslte_ra_dl_grant_to_nbits_multi(srslte_ra_dl_grant_t *grant, srslte_ra_nbits_t *nbits, srslte_ra_nbits_t *nbits2); +SRSLTE_API void srslte_ra_dl_grant_to_nbits_multi(srslte_ra_dl_grant_t *grant, + uint32_t cfi, + srslte_cell_t cell, + uint32_t sf_idx, + srslte_ra_nbits_t *nbits, + srslte_ra_nbits_t *nbits2); + SRSLTE_API uint32_t srslte_ra_dl_approx_nof_re(srslte_cell_t cell, uint32_t nof_prb, uint32_t nof_ctrl_symbols); diff --git a/lib/include/srslte/phy/rf/rf.h b/lib/include/srslte/phy/rf/rf.h index d37750749..bc74d17e7 100644 --- a/lib/include/srslte/phy/rf/rf.h +++ b/lib/include/srslte/phy/rf/rf.h @@ -80,6 +80,11 @@ SRSLTE_API int srslte_rf_open_multi2(srslte_rf_t *h, uint32_t nof_tx_antennas, uint32_t nof_rx_antennas); +SRSLTE_API int srslte_rf_open_multi2(srslte_rf_t *h, + char *args, + uint32_t nof_tx_antennas, + uint32_t nof_rx_antennas); + SRSLTE_API int srslte_rf_open_devname(srslte_rf_t *h, char *devname, char *args); diff --git a/lib/include/srslte/phy/ue/ue_dl.h b/lib/include/srslte/phy/ue/ue_dl.h index 408ba0c65..0c77ba911 100644 --- a/lib/include/srslte/phy/ue/ue_dl.h +++ b/lib/include/srslte/phy/ue/ue_dl.h @@ -147,7 +147,9 @@ SRSLTE_API int srslte_ue_dl_cfg_grant_multi(srslte_ue_dl_t *q, uint32_t cfi, uint32_t sf_idx, uint32_t rvidx, - uint32_t rvidx2); + uint32_t rvidx2, + srslte_mimo_type_t mimo_type, + uint32_t pinfo); SRSLTE_API int srslte_ue_dl_find_ul_dci(srslte_ue_dl_t *q, uint32_t cfi, diff --git a/lib/include/srslte/phy/utils/algebra.h b/lib/include/srslte/phy/utils/algebra.h new file mode 100644 index 000000000..41d5e0025 --- /dev/null +++ b/lib/include/srslte/phy/utils/algebra.h @@ -0,0 +1,70 @@ +/** + * + * \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_ALGEBRA_H +#define SRSLTE_ALGEBRA_H + +#include "srslte/config.h" + +#ifdef LV_HAVE_SSE + +#define _MM_MULJ_PS(X) _mm_permute_ps(_MM_CONJ_PS(X), 0b10110001) +#define _MM_CONJ_PS(X) (_mm_xor_ps(X, (__m128){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))) + +SRSLTE_API void srslte_algebra_2x2_zf_sse(__m128 y0, + __m128 y1, + __m128 h00, + __m128 h01, + __m128 h10, + __m128 h11, + __m128 *x0, + __m128 *x1, + float norm); + +#endif /* LV_HAVE_SSE */ + +#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, (__m256){0.0f, -0.0f, 0.0f, -0.0f, 0.0f, -0.0f, 0.0f, -0.0f})) +#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))) + +SRSLTE_API void srslte_algebra_2x2_zf_avx(__m256 y0, + __m256 y1, + __m256 h00, + __m256 h01, + __m256 h10, + __m256 h11, + __m256 *x0, + __m256 *x1, + float norm); + +#endif /* LV_HAVE_AVX */ + +#endif //SRSLTE_ALGEBRA_H diff --git a/lib/src/phy/common/phy_common.c b/lib/src/phy/common/phy_common.c index 69e39b49a..7be57c63a 100644 --- a/lib/src/phy/common/phy_common.c +++ b/lib/src/phy/common/phy_common.c @@ -436,7 +436,7 @@ int srslte_str2mimotype(char *mimo_type_str, srslte_mimo_type_t *type) { *type = SRSLTE_MIMO_TYPE_SINGLE_ANTENNA; } else if (!strcmp(mimo_type_str, "diversity") || !strcmp(mimo_type_str, "TxDiversity")) { *type = SRSLTE_MIMO_TYPE_TX_DIVERSITY; - } else if (!strcmp(mimo_type_str, "multiplex")) { + } else if (!strcmp(mimo_type_str, "multiplex") || !strcmp(mimo_type_str, "SpatialMux")) { *type = SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX; } else if (!strcmp(mimo_type_str, "cdd") || !strcmp(mimo_type_str, "CDD")) { *type = SRSLTE_MIMO_TYPE_CDD; diff --git a/lib/src/phy/mimo/precoding.c b/lib/src/phy/mimo/precoding.c index f93c63479..ba950f196 100644 --- a/lib/src/phy/mimo/precoding.c +++ b/lib/src/phy/mimo/precoding.c @@ -33,6 +33,7 @@ #include "srslte/phy/common/phy_common.h" #include "srslte/phy/mimo/precoding.h" #include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" #ifdef LV_HAVE_SSE #include @@ -43,6 +44,9 @@ int srslte_predecoding_diversity2_sse(cf_t *y[SRSLTE_MAX_PORTS], cf_t *h[SRSLTE_ #ifdef LV_HAVE_AVX #include +#include +#include + int srslte_predecoding_single_avx(cf_t *y[SRSLTE_MAX_PORTS], cf_t *h[SRSLTE_MAX_PORTS], cf_t *x, int nof_rxant, int nof_symbols, float noise_estimate); #endif @@ -527,7 +531,7 @@ int srslte_predecoding_type(cf_t *y_, cf_t *h_[SRSLTE_MAX_PORTS], cf_t *x[SRSLTE h[i][0] = h_[i]; } y[0] = y_; - return srslte_predecoding_type_multi(y, h, x, nof_rxant, nof_ports, nof_layers, nof_symbols, type, noise_estimate); + return srslte_predecoding_type_multi(y, h, x, nof_rxant, nof_ports, nof_layers, 0, nof_symbols, type, noise_estimate); } @@ -565,11 +569,46 @@ int srslte_precoding_mimo_2x2_gen(cf_t W[2][2], cf_t *y[SRSLTE_MAX_PORTS], cf_t return SRSLTE_SUCCESS; } +// SSE implementation of ZF 2x2 CCD equalizer +#ifdef LV_HAVE_AVX +int srslte_predecoding_ccd_2x2_zf_avx(cf_t *y[SRSLTE_MAX_PORTS], + cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + cf_t *x[SRSLTE_MAX_LAYERS], + uint32_t nof_symbols) { + uint32_t i = 0; + for (i = 0; i < nof_symbols; i += 4) { + /* Load channel */ + __m256 h00i = _mm256_load_ps((float *) &h[0][0][i]); + __m256 h01i = _mm256_load_ps((float *) &h[0][1][i]); + __m256 h10i = _mm256_load_ps((float *) &h[1][0][i]); + __m256 h11i = _mm256_load_ps((float *) &h[1][1][i]); + /* Apply precoding */ + __m256 h00 = _mm256_add_ps(h00i, _mm256_xor_ps(h10i, + (__m256) {+0.0f, +0.0f, -0.0f, -0.0f, +0.0f, +0.0f, -0.0f, -0.0f})); + __m256 h10 = _mm256_add_ps(h01i, _mm256_xor_ps(h11i, + (__m256) {+0.0f, +0.0f, -0.0f, -0.0f, +0.0f, +0.0f, -0.0f, -0.0f})); + __m256 h01 = _mm256_add_ps(h00i, _mm256_xor_ps(h10i, + (__m256) {-0.0f, -0.0f, +0.0f, +0.0f, -0.0f, -0.0f, +0.0f, +0.0f})); + __m256 h11 = _mm256_add_ps(h01i, _mm256_xor_ps(h11i, + (__m256) {-0.0f, -0.0f, +0.0f, +0.0f, -0.0f, -0.0f, +0.0f, +0.0f})); + __m256 y0 = _mm256_load_ps((float *) &y[0][i]); + __m256 y1 = _mm256_load_ps((float *) &y[1][i]); + __m256 x0, x1; + + srslte_algebra_2x2_zf_avx(y0, y1, h00, h01, h10, h11, &x0, &x1, 2.0f); + + _mm256_store_ps((float *) &x[0][i], x0); + _mm256_store_ps((float *) &x[1][i], x1); + } + + return nof_symbols; +} +#endif /* LV_HAVE_AVX */ // SSE implementation of ZF 2x2 CCD equalizer #ifdef LV_HAVE_SSE @@ -580,9 +619,6 @@ int srslte_predecoding_ccd_2x2_zf_sse(cf_t *y[SRSLTE_MAX_PORTS], uint32_t nof_symbols) { uint32_t i = 0; - /* Conjugate mask */ - __m128 conj_mask = (__m128) {+0.0f, -0.0f, +0.0f, -0.0f}; - for (i = 0; i < nof_symbols; i += 2) { /* Load channel */ __m128 h00i = _mm_load_ps((float *) &h[0][0][i]); @@ -596,21 +632,12 @@ int srslte_predecoding_ccd_2x2_zf_sse(cf_t *y[SRSLTE_MAX_PORTS], __m128 h01 = _mm_add_ps(h00i, _mm_xor_ps(h10i, (__m128) {-0.0f, -0.0f, +0.0f, +0.0f})); __m128 h11 = _mm_add_ps(h01i, _mm_xor_ps(h11i, (__m128) {-0.0f, -0.0f, +0.0f, +0.0f})); - __m128 detmult1 = PROD(h00, h11); - __m128 detmult2 = PROD(h01, h10); - - __m128 det = _mm_sub_ps(detmult1, detmult2); - __m128 detconj = _mm_xor_ps(det, conj_mask); - __m128 detabs2 = PROD(det, detconj); - __m128 detabs2rec = _mm_rcp_ps(detabs2); - detabs2rec = _mm_shuffle_ps(detabs2rec, detabs2rec, _MM_SHUFFLE(2, 2, 0, 0)); - __m128 detrec = _mm_mul_ps(_mm_mul_ps(detconj, detabs2rec), (__m128) {2.0f, 2.0f, 2.0f, 2.0f}); - __m128 y0 = _mm_load_ps((float *) &y[0][i]); __m128 y1 = _mm_load_ps((float *) &y[1][i]); - __m128 x0 = PROD(_mm_sub_ps(PROD(h11, y0), PROD(h01, y1)), detrec); - __m128 x1 = PROD(_mm_sub_ps(PROD(h00, y1), PROD(h10, y0)), detrec); + __m128 x0, x1; + + srslte_algebra_2x2_zf_sse(y0, y1, h00, h01, h10, h11, &x0, &x1, 2.0f); _mm_store_ps((float *) &x[0][i], x0); _mm_store_ps((float *) &x[1][i], x1); @@ -659,11 +686,15 @@ int srslte_predecoding_ccd_zf(cf_t *y[SRSLTE_MAX_PORTS], cf_t *h[SRSLTE_MAX_PORT { if (nof_ports == 2 && nof_rxant == 2) { if (nof_layers == 2) { +#ifdef LV_HAVE_AVX + return srslte_predecoding_ccd_2x2_zf_avx(y, h, x, nof_symbols); +#else #ifdef LV_HAVE_SSE return srslte_predecoding_ccd_2x2_zf_sse(y, h, x, nof_symbols); #else return srslte_predecoding_ccd_2x2_zf_gen(y, h, x, nof_symbols); -#endif +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX */ } else { fprintf(stderr, "Error predecoding CCD: Invalid number of layers %d\n", nof_layers); return -1; @@ -676,11 +707,535 @@ int srslte_predecoding_ccd_zf(cf_t *y[SRSLTE_MAX_PORTS], cf_t *h[SRSLTE_MAX_PORT return SRSLTE_ERROR; } +/* PMI Select for 1 layer */ +int srslte_precoding_pmi_select_1l (cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], uint32_t nof_symbols, + float noise_estimate, uint32_t *pmi, + float sinr_list[SRSLTE_MAX_CODEBOOKS]) { + +#define SQRT1_2 ((float)M_SQRT1_2); + float max_sinr = 0.0; + uint32_t i, count; + + for (i = 0; i < 4; i++) { + sinr_list[i] = 0; + count = 0; + + for (uint32_t j = 0; j < nof_symbols; j += 100) { + /* 0. Load channel matrix */ + cf_t h00 = h[0][0][j]; + cf_t h01 = h[1][0][j]; + cf_t h10 = h[0][1][j]; + cf_t h11 = h[1][1][j]; + + /* 1. B = W'* H' */ + cf_t a0, a1; + switch(i) { + case 0: + a0 = conjf(h00) + conjf(h01); + a1 = conjf(h10) + conjf(h11); + break; + case 1: + a0 = conjf(h00) - conjf(h01); + a1 = conjf(h10) - conjf(h11); + break; + case 2: + a0 = conjf(h00) - _Complex_I * conjf(h01); + a1 = conjf(h10) - _Complex_I * conjf(h11); + break; + case 3: + a0 = conjf(h00) + _Complex_I * conjf(h01); + a1 = conjf(h10) + _Complex_I * conjf(h11); + break; + } + a0 *= SQRT1_2; + a1 *= SQRT1_2; + + /* 2. B = W' * H' * H = A * H */ + cf_t b0 = a0*h00 + a1*h10; + cf_t b1 = a0*h01 + a1*h11; + + /* 3. C = W' * H' * H * W' = B * W */ + cf_t c; + switch(i) { + case 0: + c = b0 + b1; + break; + case 1: + c = b0 - b1; + break; + case 2: + c = b0 + _Complex_I*b1; + break; + case 3: + c = b0 - _Complex_I*b1; + break; + default: + return SRSLTE_ERROR; + } + c *= SQRT1_2; + + /* Add for averaging */ + sinr_list[i] += crealf(c); + + count ++; + } + + /* Divide average by noise */ + sinr_list[i] /= noise_estimate*count; + + if (sinr_list[i] > max_sinr) { + max_sinr = sinr_list[i]; + *pmi = i; + } + } + + INFO("Precoder PMI Select for 1 layer SINR=[%.1fdB; %.1fdB; %.1fdB; %.1fdB] PMI=%d\n", 10*log10(sinr_list[0]), 10*log10(sinr_list[1]), + 10*log10(sinr_list[2]), 10*log10(sinr_list[3]), *pmi); + + return i; +} + +/* PMI Select for 2 layers */ +int srslte_precoding_pmi_select_2l (cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], uint32_t nof_symbols, + float noise_estimate, uint32_t *pmi, + float sinr_list[SRSLTE_MAX_CODEBOOKS]) { + + float max_sinr = 0.0; + uint32_t i, count; + + for (i = 0; i < 2; i++) { + sinr_list[i] = 0; + count = 0; + + for (uint32_t j = 0; j < nof_symbols; j += 100) { + /* 0. Load channel matrix */ + cf_t h00 = h[0][0][j]; + cf_t h01 = h[1][0][j]; + cf_t h10 = h[0][1][j]; + cf_t h11 = h[1][1][j]; + + /* 1. B = W'* H' */ + cf_t a00, a01, a10, a11; + switch(i) { + case 0: + a00 = conjf(h00) + conjf(h01); + a01 = conjf(h10) + conjf(h11); + a10 = conjf(h00) - conjf(h01); + a11 = conjf(h10) - conjf(h11); + break; + case 1: + a00 = conjf(h00) - _Complex_I*conjf(h01); + a01 = conjf(h10) - _Complex_I*conjf(h11); + a10 = conjf(h00) + _Complex_I*conjf(h01); + a11 = conjf(h10) + _Complex_I*conjf(h11); + break; + default: + return SRSLTE_ERROR; + } + a00 *= 0.5f; + a01 *= 0.5f; + a10 *= 0.5f; + a11 *= 0.5f; + + /* 2. B = W' * H' * H = A * H */ + cf_t b00 = a00*h00 + a01*h10; + cf_t b01 = a00*h01 + a01*h11; + cf_t b10 = a10*h00 + a11*h10; + cf_t b11 = a10*h01 + a11*h11; + + /* 3. C = W' * H' * H * W' = B * W */ + cf_t c00, c01, c10, c11; + switch(i) { + case 0: + c00 = b00 + b01; + c01 = b00 - b01; + c10 = b10 + b11; + c11 = b10 - b11; + break; + case 1: + c00 = b00 + _Complex_I*b01; + c01 = b00 - _Complex_I*b01; + c10 = b10 + _Complex_I*b11; + c11 = b10 - _Complex_I*b11; + break; + default: + return SRSLTE_ERROR; + } + c00 *= 0.5; + c01 *= 0.5; + c10 *= 0.5; + c11 *= 0.5; + + /* 4. C += noise * I */ + c00 += noise_estimate; + c11 += noise_estimate; + + /* 5. detC */ + cf_t detC = c00*c11 - c01*c10; + cf_t inv_detC = conjf(detC)/(crealf(detC)*crealf(detC) + cimagf(detC)*cimagf(detC)); + + cf_t den0 = noise_estimate*c00*inv_detC; + cf_t den1 = noise_estimate*c11*inv_detC; + + float gamma0 = crealf((conjf(den0)/(crealf(den0)*crealf(den0) + cimagf(den0)*cimagf(den0))) - 1); + float gamma1 = crealf((conjf(den1)/(crealf(den1)*crealf(den1) + cimagf(den1)*cimagf(den1))) - 1); + + /* Add for averaging */ + sinr_list[i] += (gamma0 + gamma1); + + count ++; + } + + /* Divide average by noise */ + sinr_list[i] /= (2*count); + + if (sinr_list[i] > max_sinr) { + max_sinr = sinr_list[i]; + *pmi = i; + } + } + + INFO("Precoder PMI Select for 2 layers SINR=[%.1fdB; %.1fdB] PMI=%d\n", 10*log10(sinr_list[0]), 10*log10(sinr_list[1]), *pmi); + + return i; +} + +int srslte_precoding_pmi_select (cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], uint32_t nof_symbols, + float noise_estimate, int nof_layers, uint32_t *pmi, + float sinr[SRSLTE_MAX_CODEBOOKS]) { + int ret; + + if (sinr == NULL || pmi == NULL) { + ERROR("Null pointer"); + ret = SRSLTE_ERROR_INVALID_INPUTS; + } else if (nof_layers == 1) { + ret = srslte_precoding_pmi_select_1l(h, nof_symbols, noise_estimate, pmi, sinr); + } else if (nof_layers == 2) { + ret = srslte_precoding_pmi_select_2l(h, nof_symbols, noise_estimate, pmi, sinr); + } else { + ERROR("Wrong number of layers"); + ret = SRSLTE_ERROR_INVALID_INPUTS; + } + + return ret; +} + +// Generic implementation of ZF 2x2 Spatial Multiplexity equalizer +int srslte_predecoding_multiplex_2x2_zf(cf_t *y[SRSLTE_MAX_PORTS], cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + cf_t *x[SRSLTE_MAX_LAYERS], int codebook_idx, int nof_symbols) { + int i = 0; + float norm = 1.0; + + switch(codebook_idx) { + case 0: + norm = (float) M_SQRT2; + break; + case 1: + case 2: + norm = 2.0f; + break; + default: + fprintf(stderr, "Wrong codebook_idx=%d\n", codebook_idx); + return SRSLTE_ERROR; + } + +#ifdef LV_HAVE_AVX + for (/* i = 0*/; i < nof_symbols; i += 4) { + __m256 _h00 = _mm256_load_ps((float*)&(h[0][0][i])); + __m256 _h01 = _mm256_load_ps((float*)&(h[0][1][i])); + __m256 _h10 = _mm256_load_ps((float*)&(h[1][0][i])); + __m256 _h11 = _mm256_load_ps((float*)&(h[1][1][i])); + + __m256 h00, h01, h10, h11; + switch (codebook_idx) { + case 0: + h00 = _h00; + h01 = _h10; + h10 = _h01; + h11 = _h11; + break; + case 1: + h00 = _mm256_add_ps(_h00, _h10); + h01 = _mm256_sub_ps(_h00, _h10); + h10 = _mm256_add_ps(_h01, _h11); + h11 = _mm256_sub_ps(_h01, _h11); + break; + case 2: + h00 = _mm256_add_ps(_h00, _MM256_MULJ_PS(_h10)); + h01 = _mm256_sub_ps(_h00, _MM256_MULJ_PS(_h10)); + h10 = _mm256_add_ps(_h01, _MM256_MULJ_PS(_h11)); + h11 = _mm256_sub_ps(_h01, _MM256_MULJ_PS(_h11)); + break; + default: + fprintf(stderr, "Wrong codebook_idx=%d\n", codebook_idx); + return SRSLTE_ERROR; + } + + __m256 y0 = _mm256_load_ps((float *) &y[0][i]); + __m256 y1 = _mm256_load_ps((float *) &y[1][i]); + + __m256 x0, x1; + + srslte_algebra_2x2_zf_avx(y0, y1, h00, h01, h10, h11, &x0, &x1, norm); + + _mm256_store_ps((float *) &x[0][i], x0); + _mm256_store_ps((float *) &x[1][i], x1); + + } + if (i > nof_symbols) { + i -= 4; + } +#endif /* LV_HAVE_AVX */ + +#ifdef LV_HAVE_SSE + for (/* i = 0*/; i < nof_symbols; i += 2) { + __m128 _h00 = _mm_load_ps((float*)&(h[0][0][i])); + __m128 _h01 = _mm_load_ps((float*)&(h[0][1][i])); + __m128 _h10 = _mm_load_ps((float*)&(h[1][0][i])); + __m128 _h11 = _mm_load_ps((float*)&(h[1][1][i])); + + __m128 h00, h01, h10, h11; + switch (codebook_idx) { + case 0: + h00 = _h00; + h01 = _h10; + h10 = _h01; + h11 = _h11; + break; + case 1: + h00 = _mm_add_ps(_h00, _h10); + h01 = _mm_sub_ps(_h00, _h10); + h10 = _mm_add_ps(_h01, _h11); + h11 = _mm_sub_ps(_h01, _h11); + break; + case 2: + h00 = _mm_add_ps(_h00, _MM_MULJ_PS(_h10)); + h01 = _mm_sub_ps(_h00, _MM_MULJ_PS(_h10)); + h10 = _mm_add_ps(_h01, _MM_MULJ_PS(_h11)); + h11 = _mm_sub_ps(_h01, _MM_MULJ_PS(_h11)); + break; + default: + fprintf(stderr, "Wrong codebook_idx=%d\n", codebook_idx); + return SRSLTE_ERROR; + } + + __m128 y0 = _mm_load_ps((float *) &y[0][i]); + __m128 y1 = _mm_load_ps((float *) &y[1][i]); + + __m128 x0, x1; + + srslte_algebra_2x2_zf_sse(y0, y1, h00, h01, h10, h11, &x0, &x1, norm); + + _mm_store_ps((float *) &x[0][i], x0); + _mm_store_ps((float *) &x[1][i], x1); + + } + if (i > nof_symbols) { + i -= 2; + } +#endif /* LV_HAVE_SSE */ + + for (/*int i = 0*/; i < nof_symbols; i++) { + cf_t h00, h01, h10, h11, det; + + switch(codebook_idx) { + case 0: + h00 = h[0][0][i]; + h01 = h[1][0][i]; + h10 = h[0][1][i]; + h11 = h[1][1][i]; + break; + case 1: + h00 = h[0][0][i] + h[1][0][i]; + h01 = h[0][0][i] - h[1][0][i]; + h10 = h[0][1][i] + h[1][1][i]; + h11 = h[0][1][i] - h[1][1][i]; + break; + case 2: + h00 = h[0][0][i] + _Complex_I*h[1][0][i]; + h01 = h[0][0][i] - _Complex_I*h[1][0][i]; + h10 = h[0][1][i] + _Complex_I*h[1][1][i]; + h11 = h[0][1][i] - _Complex_I*h[1][1][i]; + break; + default: + fprintf(stderr, "Wrong codebook_idx=%d\n", codebook_idx); + return SRSLTE_ERROR; + } + + det = (h00 * h11 - h01 * h10); + det = conjf(det) * (norm / (crealf(det) * crealf(det) + cimagf(det) * cimagf(det))); + + x[0][i] = (+h11 * y[0][i] - h01 * y[1][i]) * det; + x[1][i] = (-h10 * y[0][i] + h00 * y[1][i]) * det; + } + return SRSLTE_SUCCESS; +} + +// Generic implementation of MRC 2x1 (two antennas into one layer) Spatial Multiplexing equalizer +int srslte_predecoding_multiplex_2x1_mrc(cf_t *y[SRSLTE_MAX_PORTS], cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + cf_t *x[SRSLTE_MAX_LAYERS], int codebook_idx, int nof_symbols) { + int i = 0; + +#ifdef LV_HAVE_AVX + for (/* i = 0*/; i < nof_symbols; i += 4) { + __m256 _h00 = _mm256_load_ps((float*)&(h[0][0][i])); + __m256 _h01 = _mm256_load_ps((float*)&(h[0][1][i])); + __m256 _h10 = _mm256_load_ps((float*)&(h[1][0][i])); + __m256 _h11 = _mm256_load_ps((float*)&(h[1][1][i])); + + __m256 h0, h1; + switch (codebook_idx) { + case 0: + h0 = _mm256_add_ps(_h00, _h10); + h1 = _mm256_add_ps(_h01, _h11); + break; + case 1: + h0 = _mm256_sub_ps(_h00, _h10); + h1 = _mm256_sub_ps(_h01, _h11); + break; + case 2: + h0 = _mm256_add_ps(_h00, _mm256_permute_ps(_MM256_CONJ_PS(_h10), 0b10110001)); + h1 = _mm256_add_ps(_h01, _mm256_permute_ps(_MM256_CONJ_PS(_h11), 0b10110001)); + break; + case 3: + h0 = _mm256_sub_ps(_h00, _mm256_permute_ps(_MM256_CONJ_PS(_h10), 0b10110001)); + h1 = _mm256_sub_ps(_h01, _mm256_permute_ps(_MM256_CONJ_PS(_h11), 0b10110001)); + break; + default: + fprintf(stderr, "Wrong codebook_idx=%d\n", codebook_idx); + return SRSLTE_ERROR; + } + + __m256 h0_2 = _mm256_mul_ps(h0, h0); + __m256 h1_2 = _mm256_mul_ps(h1, h1); + __m256 hh0 = _mm256_add_ps(_mm256_movehdup_ps(h0_2), _mm256_moveldup_ps(h0_2)); + __m256 hh1 = _mm256_add_ps(_mm256_movehdup_ps(h1_2), _mm256_moveldup_ps(h1_2)); + __m256 hh = _mm256_add_ps(hh0, hh1); + __m256 hhrec = _mm256_rcp_ps(hh); + + hhrec = _mm256_mul_ps(hhrec, (__m256){(float) M_SQRT2, (float) M_SQRT2, (float) M_SQRT2, (float) M_SQRT2, + (float) M_SQRT2,(float) M_SQRT2, (float) M_SQRT2, (float) M_SQRT2}); + __m256 y0 = _mm256_load_ps((float*)&y[0][i]); + __m256 y1 = _mm256_load_ps((float*)&y[1][i]); + + __m256 x0 = _mm256_add_ps(_MM256_PROD_PS(_MM256_CONJ_PS(h0), y0), _MM256_PROD_PS(_MM256_CONJ_PS(h1), y1)); + x0 = _mm256_mul_ps(hhrec, x0); + + _mm256_store_ps((float*)&x[0][i], x0); + + } + if (i > nof_symbols) { + i -= 4; + } +#endif /* LV_HAVE_AVX */ + +#ifdef LV_HAVE_SSE + for (/* i = 0*/; i < nof_symbols; i += 2) { + __m128 _h00 = _mm_load_ps((float*)&(h[0][0][i])); + __m128 _h01 = _mm_load_ps((float*)&(h[0][1][i])); + __m128 _h10 = _mm_load_ps((float*)&(h[1][0][i])); + __m128 _h11 = _mm_load_ps((float*)&(h[1][1][i])); + + __m128 h0, h1; + switch (codebook_idx) { + case 0: + h0 = _mm_add_ps(_h00, _h10); + h1 = _mm_add_ps(_h01, _h11); + break; + case 1: + h0 = _mm_sub_ps(_h00, _h10); + h1 = _mm_sub_ps(_h01, _h11); + break; + case 2: + h0 = _mm_add_ps(_h00, _mm_permute_ps(_MM_CONJ_PS(_h10), 0b10110001)); + h1 = _mm_add_ps(_h01, _mm_permute_ps(_MM_CONJ_PS(_h11), 0b10110001)); + break; + case 3: + h0 = _mm_sub_ps(_h00, _mm_permute_ps(_MM_CONJ_PS(_h10), 0b10110001)); + h1 = _mm_sub_ps(_h01, _mm_permute_ps(_MM_CONJ_PS(_h11), 0b10110001)); + break; + default: + fprintf(stderr, "Wrong codebook_idx=%d\n", codebook_idx); + return SRSLTE_ERROR; + } + + __m128 h0_2 = _mm_mul_ps(h0, h0); + __m128 h1_2 = _mm_mul_ps(h1, h1); + __m128 hh0 = _mm_add_ps(_mm_movehdup_ps(h0_2), _mm_moveldup_ps(h0_2)); + __m128 hh1 = _mm_add_ps(_mm_movehdup_ps(h1_2), _mm_moveldup_ps(h1_2)); + __m128 hh = _mm_add_ps(hh0, hh1); + __m128 hhrec = _mm_rcp_ps(hh); + + hhrec = _mm_mul_ps(hhrec, (__m128){(float) M_SQRT2, (float) M_SQRT2, (float) M_SQRT2, (float) M_SQRT2}); + __m128 y0 = _mm_load_ps((float*)&y[0][i]); + __m128 y1 = _mm_load_ps((float*)&y[1][i]); + + __m128 x0 = _mm_add_ps(_MM_PROD_PS(_MM_CONJ_PS(h0), y0), _MM_PROD_PS(_MM_CONJ_PS(h1), y1)); + x0 = _mm_mul_ps(hhrec, x0); + + _mm_store_ps((float*)&x[0][i], x0); + + } + if (i > nof_symbols) { + i -= 2; + } +#endif /* LV_HAVE_SSE */ + + for (/*i = 0*/; i < nof_symbols; i += 1) { + cf_t h0, h1; + float hh; + + switch(codebook_idx) { + case 0: + h0 = h[0][0][i] + h[1][0][i]; + h1 = h[0][1][i] + h[1][1][i]; + break; + case 1: + h0 = h[0][0][i] - h[1][0][i]; + h1 = h[0][1][i] - h[1][1][i]; + break; + case 2: + h0 = h[0][0][i] + _Complex_I * h[1][0][i]; + h1 = h[0][1][i] + _Complex_I * h[1][1][i]; + break; + case 3: + h0 = h[0][0][i] - _Complex_I * h[1][0][i]; + h1 = h[0][1][i] - _Complex_I * h[1][1][i]; + break; + default: + fprintf(stderr, "Wrong codebook_idx=%d\n", codebook_idx); + return SRSLTE_ERROR; + } + + hh = (float) M_SQRT2/(crealf(h0)*crealf(h0) + cimagf(h0)*cimagf(h0) + crealf(h1)*crealf(h1) + cimagf(h1)*cimagf(h1)); + + x[0][i] = (conjf(h0) * y[0][i] + conjf(h1) * y[1][i]) * hh; + } + return SRSLTE_SUCCESS; +} + +int srslte_predecoding_multiplex_zf(cf_t *y[SRSLTE_MAX_PORTS], cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], cf_t *x[SRSLTE_MAX_LAYERS], + int nof_rxant, int nof_ports, int nof_layers, int codebook_idx, int nof_symbols) +{ + if (nof_ports == 2 && nof_rxant == 2) { + if (nof_layers == 2) { + return srslte_predecoding_multiplex_2x2_zf(y, h, x, codebook_idx, nof_symbols); + } else { + return srslte_predecoding_multiplex_2x1_mrc(y, h, x, codebook_idx, nof_symbols); + } + } else if (nof_ports == 4) { + fprintf(stderr, "Error predecoding CCD: Only 2 ports supported\n"); + } else { + fprintf(stderr, "Error predecoding CCD: Invalid combination of ports %d and rx antennax %d\n", nof_ports, nof_rxant); + } + return SRSLTE_ERROR; +} /* 36.211 v10.3.0 Section 6.3.4 */ -int srslte_predecoding_type_multi(cf_t *y[SRSLTE_MAX_PORTS], cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], cf_t *x[SRSLTE_MAX_LAYERS], - int nof_rxant, int nof_ports, int nof_layers, int nof_symbols, srslte_mimo_type_t type, float noise_estimate) { +int srslte_predecoding_type_multi(cf_t *y[SRSLTE_MAX_PORTS], cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + cf_t *x[SRSLTE_MAX_LAYERS], int nof_rxant, int nof_ports, int nof_layers, + int codebook_idx, int nof_symbols, srslte_mimo_type_t type, float noise_estimate) { if (nof_ports > SRSLTE_MAX_PORTS) { fprintf(stderr, "Maximum number of ports is %d (nof_ports=%d)\n", SRSLTE_MAX_PORTS, @@ -722,10 +1277,11 @@ int srslte_predecoding_type_multi(cf_t *y[SRSLTE_MAX_PORTS], cf_t *h[SRSLTE_MAX_ } break; case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: - fprintf(stderr, "Spatial multiplexing not supported\n"); - return -1; + return srslte_predecoding_multiplex_zf(y, h, x, nof_rxant, nof_ports, nof_layers, codebook_idx, nof_symbols); + default: + return SRSLTE_ERROR; } - return 0; + return SRSLTE_ERROR; } @@ -813,9 +1369,70 @@ int srslte_precoding_cdd(cf_t *x[SRSLTE_MAX_LAYERS], cf_t *y[SRSLTE_MAX_PORTS], } } +int srslte_precoding_multiplex(cf_t *x[SRSLTE_MAX_LAYERS], cf_t *y[SRSLTE_MAX_PORTS], int nof_layers, int nof_ports, + int codebook_idx, uint32_t nof_symbols) +{ + int i; + if (nof_ports == 2) { + if (nof_layers == 1) { + switch(codebook_idx) { + case 0: + srslte_vec_sc_prod_cfc(x[0], 1.0f/sqrtf(2.0f), y[0], nof_symbols); + srslte_vec_sc_prod_cfc(x[0], 1.0f/sqrtf(2.0f), y[1], nof_symbols); + break; + case 1: + srslte_vec_sc_prod_cfc(x[0], 1.0f/sqrtf(2.0f), y[0], nof_symbols); + srslte_vec_sc_prod_cfc(x[0], -1.0f/sqrtf(2.0f), y[1], nof_symbols); + break; + case 2: + srslte_vec_sc_prod_cfc(x[0], 1.0f/sqrtf(2.0f), y[0], nof_symbols); + srslte_vec_sc_prod_ccc(x[0], _Complex_I/sqrtf(2.0f), y[1], nof_symbols); + break; + case 3: + srslte_vec_sc_prod_cfc(x[0], 1.0f/sqrtf(2.0f), y[0], nof_symbols); + srslte_vec_sc_prod_ccc(x[0], -_Complex_I/sqrtf(2.0f), y[1], nof_symbols); + break; + default: + fprintf(stderr, "Invalid multiplex combination: codebook_idx=%d, nof_layers=%d, nof_ports=%d\n", + codebook_idx, nof_layers, nof_ports); + return SRSLTE_ERROR; + } + } else if (nof_layers == 2) { + switch(codebook_idx) { + case 0: + srslte_vec_sc_prod_cfc(x[0], 1.0f/sqrtf(2.0f), y[0], nof_symbols); + srslte_vec_sc_prod_cfc(x[1], 1.0f/sqrtf(2.0f), y[1], nof_symbols); + break; + case 1: + for (i = 0; i < nof_symbols; i++) { + y[0][i] = 0.5f*x[0][i] + 0.5f*x[1][i]; + y[1][i] = 0.5f*x[0][i] - 0.5f*x[1][i]; + } + break; + case 2: + for (i = 0; i < nof_symbols; i++) { + y[0][i] = 0.5f*x[0][i] + 0.5f*x[1][i]; + y[1][i] = 0.5f*_Complex_I*x[0][i] - 0.5f*_Complex_I*x[1][i]; + } + break; + case 3: + default: + fprintf(stderr, "Invalid multiplex combination: codebook_idx=%d, nof_layers=%d, nof_ports=%d\n", + codebook_idx, nof_layers, nof_ports); + return SRSLTE_ERROR; + } + } else { + ERROR("Not implemented"); + } + } else { + ERROR("Not implemented"); + } + return SRSLTE_SUCCESS; +} + /* 36.211 v10.3.0 Section 6.3.4 */ int srslte_precoding_type(cf_t *x[SRSLTE_MAX_LAYERS], cf_t *y[SRSLTE_MAX_PORTS], int nof_layers, - int nof_ports, int nof_symbols, srslte_mimo_type_t type) { + int nof_ports, int codebook_idx, int nof_symbols, srslte_mimo_type_t type) { if (nof_ports > SRSLTE_MAX_PORTS) { fprintf(stderr, "Maximum number of ports is %d (nof_ports=%d)\n", SRSLTE_MAX_PORTS, @@ -829,29 +1446,30 @@ int srslte_precoding_type(cf_t *x[SRSLTE_MAX_LAYERS], cf_t *y[SRSLTE_MAX_PORTS], } switch (type) { - case SRSLTE_MIMO_TYPE_CDD: - return srslte_precoding_cdd(x, y, nof_layers, nof_ports, nof_symbols); - case SRSLTE_MIMO_TYPE_SINGLE_ANTENNA: - if (nof_ports == 1 && nof_layers == 1) { - return srslte_precoding_single(x[0], y[0], nof_symbols); - } else { - fprintf(stderr, - "Number of ports and layers must be 1 for transmission on single antenna ports\n"); - return -1; - } - break; - case SRSLTE_MIMO_TYPE_TX_DIVERSITY: - if (nof_ports == nof_layers) { - return srslte_precoding_diversity(x, y, nof_ports, nof_symbols); - } else { - fprintf(stderr, - "Error number of layers must equal number of ports in transmit diversity\n"); - return -1; - } - case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: - fprintf(stderr, "Spatial multiplexing not supported\n"); - return -1; + case SRSLTE_MIMO_TYPE_CDD: + return srslte_precoding_cdd(x, y, nof_layers, nof_ports, nof_symbols); + case SRSLTE_MIMO_TYPE_SINGLE_ANTENNA: + if (nof_ports == 1 && nof_layers == 1) { + return srslte_precoding_single(x[0], y[0], nof_symbols); + } else { + fprintf(stderr, + "Number of ports and layers must be 1 for transmission on single antenna ports\n"); + return -1; + } + break; + case SRSLTE_MIMO_TYPE_TX_DIVERSITY: + if (nof_ports == nof_layers) { + return srslte_precoding_diversity(x, y, nof_ports, nof_symbols); + } else { + fprintf(stderr, + "Error number of layers must equal number of ports in transmit diversity\n"); + return -1; + } + case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: + return srslte_precoding_multiplex(x, y, nof_layers, nof_ports, codebook_idx, nof_symbols); + default: + return SRSLTE_ERROR; } - return 0; + return SRSLTE_ERROR; } diff --git a/lib/src/phy/mimo/test/CMakeLists.txt b/lib/src/phy/mimo/test/CMakeLists.txt index 42e262550..e0894a24d 100644 --- a/lib/src/phy/mimo/test/CMakeLists.txt +++ b/lib/src/phy/mimo/test/CMakeLists.txt @@ -52,7 +52,14 @@ add_test(precoding_single precoding_test -n 1000 -m single) add_test(precoding_diversity2 precoding_test -n 1000 -m diversity -l 2 -p 2) add_test(precoding_diversity4 precoding_test -n 1024 -m diversity -l 4 -p 4) -add_test(precoding_cdd_2x2 precoding_test -m cdd -l 2 -p 2 -r 2) - +add_test(precoding_cdd_2x2 precoding_test -m cdd -l 2 -p 2 -r 2 -n 14000) + +add_test(precoding_multiplex_1l_cb0 precoding_test -m multiplex -l 1 -p 2 -r 2 -n 14000 -c 0) +add_test(precoding_multiplex_1l_cb1 precoding_test -m multiplex -l 1 -p 2 -r 2 -n 14000 -c 1) +add_test(precoding_multiplex_1l_cb2 precoding_test -m multiplex -l 1 -p 2 -r 2 -n 14000 -c 2) +add_test(precoding_multiplex_1l_cb3 precoding_test -m multiplex -l 1 -p 2 -r 2 -n 14000 -c 3) +add_test(precoding_multiplex_2l_cb0 precoding_test -m multiplex -l 2 -p 2 -r 2 -n 14000 -c 0) +add_test(precoding_multiplex_2l_cb1 precoding_test -m multiplex -l 2 -p 2 -r 2 -n 14000 -c 1) +add_test(precoding_multiplex_2l_cb2 precoding_test -m multiplex -l 2 -p 2 -r 2 -n 14000 -c 2) diff --git a/lib/src/phy/mimo/test/precoder_test.c b/lib/src/phy/mimo/test/precoder_test.c index cf8dac89a..bc23004f1 100644 --- a/lib/src/phy/mimo/test/precoder_test.c +++ b/lib/src/phy/mimo/test/precoder_test.c @@ -38,6 +38,7 @@ #define MSE_THRESHOLD 0.0005 int nof_symbols = 1000; +uint32_t codebook_idx = 0; int nof_layers = 1, nof_tx_ports = 1, nof_rx_ports = 1, nof_re = 1; char *mimo_type_name = NULL; @@ -46,11 +47,12 @@ void usage(char *prog) { "Usage: %s -m [single|diversity|multiplex|cdd] -l [nof_layers] -p [nof_tx_ports]\n" " -r [nof_rx_ports]\n", prog); printf("\t-n num_symbols [Default %d]\n", nof_symbols); + printf("\t-c codebook_idx [Default %d]\n\n", codebook_idx); } void parse_args(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "mplnr")) != -1) { + while ((opt = getopt(argc, argv, "mplnrc")) != -1) { switch (opt) { case 'n': nof_symbols = atoi(argv[optind]); @@ -67,6 +69,9 @@ void parse_args(int argc, char **argv) { case 'm': mimo_type_name = argv[optind]; break; + case 'c': + codebook_idx = (uint32_t) atoi(argv[optind]); + break; default: usage(argv[0]); exit(-1); @@ -116,16 +121,13 @@ void populate_channel_single(cf_t *h) { void populate_channel(srslte_mimo_type_t type, cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]) { switch (type) { + case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: case SRSLTE_MIMO_TYPE_CDD: populate_channel_cdd(h, (uint32_t) nof_re); break; case SRSLTE_MIMO_TYPE_TX_DIVERSITY: populate_channel_diversity(h, (uint32_t) nof_re); break; - case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: - fprintf(stderr, "Error: not implemented channel emulator\n"); - exit(-1); - //break; case SRSLTE_MIMO_TYPE_SINGLE_ANTENNA: default: populate_channel_single(h[0][0]); @@ -158,6 +160,9 @@ int main(int argc, char **argv) { case SRSLTE_MIMO_TYPE_TX_DIVERSITY: nof_re = nof_layers*nof_symbols; break; + case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: + nof_re = nof_symbols; + break; case SRSLTE_MIMO_TYPE_CDD: nof_re = nof_symbols*nof_tx_ports/nof_layers; if (nof_rx_ports != 2 || nof_tx_ports != 2) { @@ -223,7 +228,7 @@ int main(int argc, char **argv) { } /* Execute Precoding (Tx) */ - if (srslte_precoding_type(x, y, nof_layers, nof_tx_ports, nof_symbols, type) < 0) { + if (srslte_precoding_type(x, y, nof_layers, nof_tx_ports, codebook_idx, nof_symbols, type) < 0) { fprintf(stderr, "Error layer mapper encoder\n"); exit(-1); } @@ -246,7 +251,7 @@ int main(int argc, char **argv) { struct timeval t[3]; gettimeofday(&t[1], NULL); srslte_predecoding_type_multi(r, h, xr, nof_rx_ports, nof_tx_ports, nof_layers, - nof_re, type, 0); + codebook_idx, nof_re, type, 0); gettimeofday(&t[2], NULL); get_time_interval(t); printf("Execution Time: %ld us\n", t[0].tv_usec); diff --git a/lib/src/phy/phch/pdsch.c b/lib/src/phy/phch/pdsch.c index 5d85a3ec2..8cdfd0884 100644 --- a/lib/src/phy/phch/pdsch.c +++ b/lib/src/phy/phch/pdsch.c @@ -387,7 +387,7 @@ int srslte_pdsch_cfg(srslte_pdsch_cfg_t *cfg, srslte_cell_t cell, srslte_ra_dl_g * If dci_msg is NULL, the grant is assumed to be already stored in cfg->grant */ int srslte_pdsch_cfg_multi(srslte_pdsch_cfg_t *cfg, srslte_cell_t cell, srslte_ra_dl_grant_t *grant, uint32_t cfi, - uint32_t sf_idx, uint32_t rvidx, uint32_t rvidx2) + uint32_t sf_idx, uint32_t rvidx, uint32_t rvidx2, srslte_mimo_type_t mimo_type, uint32_t pmi) { if (cfg) { if (grant) { @@ -399,7 +399,7 @@ int srslte_pdsch_cfg_multi(srslte_pdsch_cfg_t *cfg, srslte_cell_t cell, srslte_r } if (srslte_cbsegm(&cfg->cb_segm2, (uint32_t) cfg->grant.mcs2.tbs)) { - fprintf(stderr, "Error computing Codeblock (2) segmentation for TBS=%d\n", cfg->grant.mcs.tbs); + fprintf(stderr, "Error computing Codeblock (2) segmentation for TBS=%d\n", cfg->grant.mcs2.tbs); return SRSLTE_ERROR; } @@ -407,19 +407,41 @@ int srslte_pdsch_cfg_multi(srslte_pdsch_cfg_t *cfg, srslte_cell_t cell, srslte_r cfg->sf_idx = sf_idx; cfg->rv = rvidx; cfg->rv2 = rvidx2; + cfg->mimo_type = mimo_type; - if (cell.nof_ports == 1 && grant->nof_tb == 1) { - cfg->mimo_type = SRSLTE_MIMO_TYPE_SINGLE_ANTENNA; - cfg->nof_layers = 1; - } else if (cell.nof_ports == 2 && grant->nof_tb == 1) { - cfg->mimo_type = SRSLTE_MIMO_TYPE_TX_DIVERSITY; - cfg->nof_layers = 2; - } else if (cell.nof_ports == 2 && grant->nof_tb == 2) { - cfg->mimo_type = SRSLTE_MIMO_TYPE_CDD; - cfg->nof_layers = 2; - } else { - INFO("nof_ports=%d, nof_tb=%d are not consistent\n", cell.nof_ports, grant->nof_tb); - return SRSLTE_ERROR; + /* Check and configure PDSCH transmission modes */ + switch(mimo_type) { + case SRSLTE_MIMO_TYPE_SINGLE_ANTENNA: + if (grant->nof_tb != 1) { + ERROR("Number of transport blocks is not supported for single transmission mode."); + return SRSLTE_ERROR; + } + cfg->nof_layers = 1; + break; + case SRSLTE_MIMO_TYPE_TX_DIVERSITY: + if (grant->nof_tb != 1) { + ERROR("Number of transport blocks is not supported for transmit diversity mode."); + return SRSLTE_ERROR; + } + cfg->nof_layers = 2; + break; + case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: + if (grant->nof_tb == 1) { + cfg->codebook_idx = pmi; + cfg->nof_layers = 1; + } else { + cfg->codebook_idx = pmi + 1; + cfg->nof_layers = 2; + } + INFO("PDSCH configured for Spatial Multiplex; nof_codewords=%d; nof_layers=%d; codebook_idx=%d;\n", grant->nof_tb, cfg->nof_layers, cfg->codebook_idx); + break; + case SRSLTE_MIMO_TYPE_CDD: + if (grant->nof_tb != 2) { + ERROR("Number of transport blocks is not supported for CDD transmission mode."); + return SRSLTE_ERROR; + } + cfg->nof_layers = 2; + break; } return SRSLTE_SUCCESS; @@ -498,9 +520,9 @@ int srslte_pdsch_decode_multi(srslte_pdsch_t *q, cfg != NULL) { - INFO("Decoding PDSCH SF: %d, RNTI: 0x%x, Mod %s, TBS: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d, C_prb=%d\n", + INFO("Decoding PDSCH SF: %d, RNTI: 0x%x, Mod %s, TBS: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: [%d %d], C_prb=%d\n", cfg->sf_idx, rnti, srslte_mod_string(cfg->grant.mcs.mod), cfg->grant.mcs.tbs, cfg->nbits.nof_re, - cfg->nbits.nof_bits, cfg->rv, cfg->grant.nof_prb); + cfg->nbits.nof_bits, cfg->rv, cfg->rv2, cfg->grant.nof_prb); /* number of layers equals number of ports */ for (i = 0; i < q->cell.nof_ports; i++) { @@ -525,17 +547,19 @@ int srslte_pdsch_decode_multi(srslte_pdsch_t *q, } } } - + + INFO("PDSCH Layer demapper and predecode: mimo_type=%d, nof_layers=%d, nof_tb=%d\n", cfg->mimo_type, + cfg->nof_layers, cfg->grant.nof_tb); if (q->cell.nof_ports == 1) { /* no need for layer demapping */ srslte_predecoding_single_multi(q->symbols, q->ce[0], q->d, q->nof_rx_antennas, cfg->nbits.nof_re, noise_estimate); } else { int nof_symbols [SRSLTE_MAX_CODEWORDS]; - nof_symbols[0] = cfg->nbits.nof_re * cfg->grant.nof_tb / q->cell.nof_ports; - nof_symbols[1] = cfg->nbits2.nof_re * cfg->grant.nof_tb / q->cell.nof_ports; + nof_symbols[0] = cfg->nbits.nof_re * cfg->grant.nof_tb / cfg->nof_layers; + nof_symbols[1] = cfg->nbits2.nof_re * cfg->grant.nof_tb / cfg->nof_layers; srslte_predecoding_type_multi(q->symbols, q->ce, x, q->nof_rx_antennas, q->cell.nof_ports, cfg->nof_layers, - cfg->nbits.nof_re, cfg->mimo_type, 0.0); + cfg->codebook_idx, cfg->nbits.nof_re, cfg->mimo_type, 0.0); srslte_layerdemap_type(x, (cf_t *[SRSLTE_MAX_CODEWORDS]) {q->d, q->d2}, cfg->nof_layers, cfg->grant.nof_tb, nof_symbols[0], nof_symbols, cfg->mimo_type); } @@ -616,6 +640,79 @@ int srslte_pdsch_decode_multi(srslte_pdsch_t *q, } } +int srslte_pdsch_ri_pmi_select(srslte_pdsch_t *q, + srslte_pdsch_cfg_t *cfg, + cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], float noise_estimate, uint32_t nof_ce, + uint32_t *ri, uint32_t *pmi, + float *current_sinr) { + uint32_t best_pmi_1l; + uint32_t best_pmi_2l; + float sinr_1l[SRSLTE_MAX_CODEBOOKS]; + float sinr_2l[SRSLTE_MAX_CODEBOOKS]; + float best_sinr_1l = 0.0; + float best_sinr_2l = 0.0; + int n1, n2; + + if (q->cell.nof_ports == 2 && q->nof_rx_antennas == 2) { + n1 = srslte_precoding_pmi_select(ce, nof_ce, noise_estimate, 1, &best_pmi_1l, sinr_1l); + if (n1 < 0) { + ERROR("PMI Select for 1 layer"); + return SRSLTE_ERROR; + } + + n2 = srslte_precoding_pmi_select(ce, nof_ce, noise_estimate, 2, &best_pmi_2l, sinr_2l); + if (n2 < 0) { + ERROR("PMI Select for 2 layer"); + return SRSLTE_ERROR; + } + + for (int i = 0; i < n1; i++) { + if (sinr_1l[i] > best_sinr_1l) { + best_sinr_1l = sinr_1l[i]; + } + } + + for (int i = 0; i < n2; i++) { + if (sinr_2l[i] > best_sinr_2l) { + best_sinr_2l = sinr_2l[i]; + } + } + + /* Set RI */ + if (ri != NULL) { + *ri = (best_sinr_1l > best_sinr_2l) ? 1 : 2; + } + + /* Set PMI */ + if (pmi != NULL) { + *pmi = (best_sinr_1l > best_sinr_2l) ? best_pmi_1l : best_pmi_2l; + } + + /* Set current condition number */ + if (current_sinr != NULL) { + if (cfg->nof_layers == 1) { + *current_sinr = sinr_1l[cfg->codebook_idx]; + } else if (cfg->nof_layers == 2) { + *current_sinr = sinr_2l[cfg->codebook_idx - 1]; + }else { + ERROR("Not implemented number of layers"); + return SRSLTE_ERROR; + } + } + + /* Print Trace */ + if (ri != NULL && pmi != NULL && current_sinr != NULL) { + INFO("PDSCH Select RI=%d; PMI=%d; Current SINR=%.1fdB (nof_layers=%d, codebook_idx=%d)\n", *ri, *pmi, + 10*log10(*current_sinr), cfg->nof_layers, cfg->codebook_idx); + } + } else { + ERROR("Not implemented configuration"); + return SRSLTE_ERROR_INVALID_INPUTS; + } + + return SRSLTE_SUCCESS; +} + int srslte_pdsch_encode(srslte_pdsch_t *q, srslte_pdsch_cfg_t *cfg, srslte_softbuffer_tx_t *softbuffer, uint8_t *data, uint16_t rnti, cf_t *sf_symbols[SRSLTE_MAX_PORTS]) @@ -785,7 +882,7 @@ int srslte_pdsch_encode_multi(srslte_pdsch_t *q, if (q->cell.nof_ports > 1) { int nof_symbols = srslte_layermap_type((cf_t *[SRSLTE_MAX_CODEWORDS]) {q->d, q->d2}, x, cfg->grant.nof_tb, cfg->nof_layers, (int[SRSLTE_MAX_CODEWORDS]) {cfg->nbits.nof_re, cfg->nbits2.nof_re}, cfg->mimo_type); - srslte_precoding_type(x, q->symbols, q->cell.nof_ports, cfg->nof_layers, + srslte_precoding_type(x, q->symbols, cfg->nof_layers, q->cell.nof_ports, cfg->codebook_idx, nof_symbols, cfg->mimo_type); } else { memcpy(q->symbols[0], q->d, cfg->nbits.nof_re * sizeof(cf_t)); diff --git a/lib/src/phy/phch/test/CMakeLists.txt b/lib/src/phy/phch/test/CMakeLists.txt index 1c9846c76..832f18d1f 100644 --- a/lib/src/phy/phch/test/CMakeLists.txt +++ b/lib/src/phy/phch/test/CMakeLists.txt @@ -85,28 +85,79 @@ target_link_libraries(pdsch_test srslte_phy) add_test(pdsch_test_qpsk pdsch_test -m 10 -n 50 -r 1) add_test(pdsch_test_qam16 pdsch_test -m 20 -n 100) add_test(pdsch_test_qam16 pdsch_test -m 20 -n 100 -r 2) -add_test(pdsch_test_qam64 pdsch_test -m 28 -n 100) - -add_test(pdsch_test_sin_6 pdsch_test -p 1 -a 2 -w 1 -n 6) -add_test(pdsch_test_sin_12 pdsch_test -p 1 -a 2 -w 1 -n 12) -add_test(pdsch_test_sin_25 pdsch_test -p 1 -a 2 -w 1 -n 25) -add_test(pdsch_test_sin_50 pdsch_test -p 1 -a 2 -w 1 -n 50) -add_test(pdsch_test_sin_75 pdsch_test -p 1 -a 2 -w 1 -n 75) -add_test(pdsch_test_sin_100 pdsch_test -p 1 -a 2 -w 1 -n 100 -m 28) - -add_test(pdsch_test_div_6 pdsch_test -p 2 -a 2 -w 1 -n 6) -add_test(pdsch_test_div_12 pdsch_test -p 2 -a 2 -w 1 -n 12) -add_test(pdsch_test_div_25 pdsch_test -p 2 -a 2 -w 1 -n 25) -add_test(pdsch_test_div_50 pdsch_test -p 2 -a 2 -w 1 -n 50) -add_test(pdsch_test_div_75 pdsch_test -p 2 -a 2 -w 1 -n 75) -add_test(pdsch_test_div_100 pdsch_test -p 2 -a 2 -w 1 -n 100 -m 28) - -add_test(pdsch_test_cdd_6 pdsch_test -p 2 -a 2 -w 2 -n 6) -add_test(pdsch_test_cdd_12 pdsch_test -p 2 -a 2 -w 2 -n 12) -add_test(pdsch_test_cdd_25 pdsch_test -p 2 -a 2 -w 2 -n 25) -add_test(pdsch_test_cdd_50 pdsch_test -p 2 -a 2 -w 2 -n 50) -add_test(pdsch_test_cdd_75 pdsch_test -p 2 -a 2 -w 2 -n 75) -add_test(pdsch_test_cdd_100 pdsch_test -p 2 -a 2 -w 2 -n 100 -m 28 -M 28) +add_test(pdsch_test_qam64 pdsch_test -n 100) + +# PDSCH test for single transmision mode and 2 Rx antennas +add_test(pdsch_test_sin_6 pdsch_test -x single -a 2 -n 6) +add_test(pdsch_test_sin_12 pdsch_test -x single -a 2 -n 12) +add_test(pdsch_test_sin_25 pdsch_test -x single -a 2 -n 25) +add_test(pdsch_test_sin_50 pdsch_test -x single -a 2 -n 50) +add_test(pdsch_test_sin_75 pdsch_test -x single -a 2 -n 75) +add_test(pdsch_test_sin_100 pdsch_test -x single -a 2 -n 100) + +# PDSCH test for transmit diversity transmision mode (1 codeword) +add_test(pdsch_test_div_6 pdsch_test -x diversity -a 2 -n 6) +add_test(pdsch_test_div_12 pdsch_test -x diversity -a 2 -n 12) +add_test(pdsch_test_div_25 pdsch_test -x diversity -a 2 -n 25) +add_test(pdsch_test_div_50 pdsch_test -x diversity -a 2 -n 50) +add_test(pdsch_test_div_75 pdsch_test -x diversity -a 2 -n 75) +add_test(pdsch_test_div_100 pdsch_test -x diversity -a 2 -n 100) + +# PDSCH test for CDD transmision mode (2 codeword) +add_test(pdsch_test_cdd_6 pdsch_test -x cdd -a 2 -t 0 -n 6) +add_test(pdsch_test_cdd_12 pdsch_test -x cdd -a 2 -t 0 -n 12) +add_test(pdsch_test_cdd_25 pdsch_test -x cdd -a 2 -t 0 -n 25) +add_test(pdsch_test_cdd_50 pdsch_test -x cdd -a 2 -t 0 -n 50) +add_test(pdsch_test_cdd_75 pdsch_test -x cdd -a 2 -t 0 -n 75) +add_test(pdsch_test_cdd_100 pdsch_test -x cdd -a 2 -t 0 -n 100) + +# PDSCH test for Spatial Multiplex transmision mode with PMI = 0 (1 codeword) +add_test(pdsch_test_multiplex1cw_p0_6 pdsch_test -x multiplex -a 2 -p 0 -n 6) +add_test(pdsch_test_multiplex1cw_p0_12 pdsch_test -x multiplex -a 2 -p 0 -n 12) +add_test(pdsch_test_multiplex1cw_p0_25 pdsch_test -x multiplex -a 2 -p 0 -n 25) +add_test(pdsch_test_multiplex1cw_p0_50 pdsch_test -x multiplex -a 2 -p 0 -n 50) +add_test(pdsch_test_multiplex1cw_p0_75 pdsch_test -x multiplex -a 2 -p 0 -n 75) +add_test(pdsch_test_multiplex1cw_p0_100 pdsch_test -x multiplex -a 2 -p 0 -n 100) + +# PDSCH test for Spatial Multiplex transmision mode with PMI = 1 (1 codeword) +add_test(pdsch_test_multiplex1cw_p1_6 pdsch_test -x multiplex -a 2 -p 1 -n 6) +add_test(pdsch_test_multiplex1cw_p1_12 pdsch_test -x multiplex -a 2 -p 1 -n 12) +add_test(pdsch_test_multiplex1cw_p1_25 pdsch_test -x multiplex -a 2 -p 1 -n 25) +add_test(pdsch_test_multiplex1cw_p1_50 pdsch_test -x multiplex -a 2 -p 1 -n 50) +add_test(pdsch_test_multiplex1cw_p1_75 pdsch_test -x multiplex -a 2 -p 1 -n 75) +add_test(pdsch_test_multiplex1cw_p1_100 pdsch_test -x multiplex -a 2 -p 1 -n 100) + +# PDSCH test for Spatial Multiplex transmision mode with PMI = 2 (1 codeword) +add_test(pdsch_test_multiplex1cw_p2_6 pdsch_test -x multiplex -a 2 -p 2 -n 6) +add_test(pdsch_test_multiplex1cw_p2_12 pdsch_test -x multiplex -a 2 -p 2 -n 12) +add_test(pdsch_test_multiplex1cw_p2_25 pdsch_test -x multiplex -a 2 -p 2 -n 25) +add_test(pdsch_test_multiplex1cw_p2_50 pdsch_test -x multiplex -a 2 -p 2 -n 50) +add_test(pdsch_test_multiplex1cw_p2_75 pdsch_test -x multiplex -a 2 -p 2 -n 75) +add_test(pdsch_test_multiplex1cw_p2_100 pdsch_test -x multiplex -a 2 -p 2 -n 100) + +# PDSCH test for Spatial Multiplex transmision mode with PMI = 3 (1 codeword) +add_test(pdsch_test_multiplex1cw_p3_6 pdsch_test -x multiplex -a 2 -p 3 -n 6) +add_test(pdsch_test_multiplex1cw_p3_12 pdsch_test -x multiplex -a 2 -p 3 -n 12) +add_test(pdsch_test_multiplex1cw_p3_25 pdsch_test -x multiplex -a 2 -p 3 -n 25) +add_test(pdsch_test_multiplex1cw_p3_50 pdsch_test -x multiplex -a 2 -p 3 -n 50) +add_test(pdsch_test_multiplex1cw_p3_75 pdsch_test -x multiplex -a 2 -p 3 -n 75) +add_test(pdsch_test_multiplex1cw_p3_100 pdsch_test -x multiplex -a 2 -p 3 -n 100) + +# PDSCH test for Spatial Multiplex transmision mode with PMI = 0 (2 codeword) +add_test(pdsch_test_multiplex2cw_p0_6 pdsch_test -x multiplex -a 2 -t 0 -p 0 -n 6) +add_test(pdsch_test_multiplex2cw_p0_12 pdsch_test -x multiplex -a 2 -t 0 -p 0 -n 12) +add_test(pdsch_test_multiplex2cw_p0_25 pdsch_test -x multiplex -a 2 -t 0 -p 0 -n 25) +add_test(pdsch_test_multiplex2cw_p0_50 pdsch_test -x multiplex -a 2 -t 0 -p 0 -n 50) +add_test(pdsch_test_multiplex2cw_p0_75 pdsch_test -x multiplex -a 2 -t 0 -p 0 -n 75) +add_test(pdsch_test_multiplex2cw_p0_100 pdsch_test -x multiplex -a 2 -t 0 -p 0 -n 100) + +# PDSCH test for Spatial Multiplex transmision mode with PMI = 1 (2 codeword) +add_test(pdsch_test_multiplex2cw_p1_6 pdsch_test -x multiplex -a 2 -t 0 -p 1 -n 6) +add_test(pdsch_test_multiplex2cw_p1_12 pdsch_test -x multiplex -a 2 -t 0 -p 1 -n 12) +add_test(pdsch_test_multiplex2cw_p1_25 pdsch_test -x multiplex -a 2 -t 0 -p 1 -n 25) +add_test(pdsch_test_multiplex2cw_p1_50 pdsch_test -x multiplex -a 2 -t 0 -p 1 -n 50) +add_test(pdsch_test_multiplex2cw_p1_75 pdsch_test -x multiplex -a 2 -t 0 -p 1 -n 75) +add_test(pdsch_test_multiplex2cw_p1_100 pdsch_test -x multiplex -a 2 -t 0 -p 1 -n 100) ######################################################################## # FILE TEST diff --git a/lib/src/phy/phch/test/pdsch_test.c b/lib/src/phy/phch/test/pdsch_test.c index 2dc21bab4..8c79cc836 100644 --- a/lib/src/phy/phch/test/pdsch_test.c +++ b/lib/src/phy/phch/test/pdsch_test.c @@ -52,15 +52,17 @@ srslte_cell_t cell = { SRSLTE_PHICH_R_1_6 // PHICH resources }; +char mimo_type_str [32] = "single"; +srslte_mimo_type_t mimo_type = SRSLTE_MIMO_TYPE_SINGLE_ANTENNA; uint32_t cfi = 2; uint32_t mcs = 0; uint32_t mcs2 = 0; uint32_t subframe = 1; uint32_t rv_idx = 0; -uint32_t rv_idx2 = 0; +uint32_t rv_idx2 = 1; uint16_t rnti = 1234; -uint32_t nof_tb = 1; uint32_t nof_rx_antennas = 1; +uint32_t pmi = 0; char *input_file = NULL; void usage(char *prog) { @@ -74,16 +76,16 @@ void usage(char *prog) { printf("\t-t rv_idx2 [Default %d]\n", rv_idx2); printf("\t-R rnti [Default %d]\n", rnti); printf("\t-F cfi [Default %d]\n", cfi); - printf("\t-p cell.nof_ports [Default %d]\n", cell.nof_ports); + printf("\t-x Transmission mode [single|diversity|cdd|multiplex] [Default %s]\n", mimo_type_str); printf("\t-n cell.nof_prb [Default %d]\n", cell.nof_prb); - printf("\t-w nof_tb [Default %d]\n", nof_tb); printf("\t-a nof_rx_antennas [Default %d]\n", nof_rx_antennas); + printf("\t-p pmi (multiplex only) [Default %d]\n", pmi); printf("\t-v [set srslte_verbose to debug, default none]\n"); } void parse_args(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "fmMcsrtRFpnwav")) != -1) { + while ((opt = getopt(argc, argv, "fmMcsrtRFpnavx")) != -1) { switch(opt) { case 'f': input_file = argv[optind]; @@ -109,8 +111,11 @@ void parse_args(int argc, char **argv) { case 'F': cfi = atoi(argv[optind]); break; + case 'x': + strncpy(mimo_type_str, argv[optind], 32); + break; case 'p': - cell.nof_ports = atoi(argv[optind]); + pmi = (uint32_t) atoi(argv[optind]); break; case 'n': cell.nof_prb = atoi(argv[optind]); @@ -118,9 +123,6 @@ void parse_args(int argc, char **argv) { case 'c': cell.id = atoi(argv[optind]); break; - case 'w': - nof_tb = (uint32_t) atoi(argv[optind]); - break; case 'a': nof_rx_antennas = (uint32_t) atoi(argv[optind]); break; @@ -165,14 +167,43 @@ int main(int argc, char **argv) { bzero(rx_slot_symbols, sizeof(cf_t*)*SRSLTE_MAX_PORTS); bzero(softbuffers_tx, sizeof(srslte_softbuffer_tx_t)*SRSLTE_MAX_CODEWORDS); bzero(softbuffers_rx, sizeof(srslte_softbuffer_rx_t)*SRSLTE_MAX_CODEWORDS); - + + /* Parse transmission mode */ + if (srslte_str2mimotype(mimo_type_str, &mimo_type)) { + ERROR("Wrong transmission mode."); + goto quit; + } + + switch(mimo_type) { + + case SRSLTE_MIMO_TYPE_SINGLE_ANTENNA: + cell.nof_ports = 1; + break; + case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: + case SRSLTE_MIMO_TYPE_CDD: + if (nof_rx_antennas < 2) { + ERROR("At least two receiving antennas are required"); + goto quit; + } + case SRSLTE_MIMO_TYPE_TX_DIVERSITY: + default: + cell.nof_ports = 2; + break; + } + srslte_ra_dl_dci_t dci; bzero(&dci, sizeof(srslte_ra_dl_dci_t)); - dci.mcs_idx = mcs; - dci.rv_idx = rv_idx; dci.type0_alloc.rbg_bitmask = 0xffffffff; - dci.tb_en[0] = true; - if (nof_tb > 1) { + + /* If transport block 0 is enabled */ + if (mcs != 0 || rv_idx != 1) { + dci.mcs_idx = mcs; + dci.rv_idx = rv_idx; + dci.tb_en[0] = true; + } + + /* If transport block 0 is disabled */ + if (mcs2 != 0 || rv_idx2 != 1) { dci.mcs_idx_1 = mcs2; dci.rv_idx_1 = rv_idx2; dci.tb_en[1] = true; @@ -183,7 +214,9 @@ int main(int argc, char **argv) { fprintf(stderr, "Error computing resource allocation\n"); return ret; } - + + + #ifdef DO_OFDM srslte_ofdm_tx_init(&ofdm_tx, cell.cp, cell.nof_prb); srslte_ofdm_rx_init(&ofdm_rx, cell.cp, cell.nof_prb); @@ -201,24 +234,9 @@ int main(int argc, char **argv) { #endif /* DO_OFDM */ /* Configure PDSCH */ - if (srslte_pdsch_cfg_multi(&pdsch_cfg, cell, &grant, cfi, subframe, rv_idx, rv_idx2)) { + if (srslte_pdsch_cfg_multi(&pdsch_cfg, cell, &grant, cfi, subframe, rv_idx, rv_idx2, mimo_type, pmi)) { fprintf(stderr, "Error configuring PDSCH\n"); - exit(-1); - } - - /* Select MIMO mode */ - if (cell.nof_ports == 1 && nof_tb == 1) { - pdsch_cfg.mimo_type = SRSLTE_MIMO_TYPE_SINGLE_ANTENNA; - pdsch_cfg.nof_layers = 1; - } else if (cell.nof_ports == 2 && nof_tb == 1) { - pdsch_cfg.mimo_type = SRSLTE_MIMO_TYPE_TX_DIVERSITY; - pdsch_cfg.nof_layers = 2; - } else if (cell.nof_ports == 2 && nof_tb == 2) { - pdsch_cfg.mimo_type = SRSLTE_MIMO_TYPE_CDD; - pdsch_cfg.nof_layers = 2; - } else { - fprintf(stderr, "nof_ports=%d, nof_tb=%d are not consistent\n", cell.nof_ports, nof_tb); - exit(-1); + goto quit; } /* init memory */ @@ -250,7 +268,7 @@ int main(int argc, char **argv) { } if (grant.mcs2.tbs) { - data[1] = srslte_vec_malloc(sizeof(uint8_t) * grant.mcs.tbs); + data[1] = srslte_vec_malloc(sizeof(uint8_t) * grant.mcs2.tbs); if (!data[1]) { perror("srslte_vec_malloc"); goto quit; @@ -264,7 +282,7 @@ int main(int argc, char **argv) { srslte_pdsch_set_rnti(&pdsch_rx, rnti); - for (i = 0; i < nof_tb; i++) { + for (i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { if (srslte_softbuffer_rx_init(&softbuffers_rx[i], cell.nof_prb)) { fprintf(stderr, "Error initiating RX soft buffer\n"); goto quit; @@ -306,7 +324,7 @@ int main(int argc, char **argv) { srslte_filesource_t fsrc; if (srslte_filesource_init(&fsrc, input_file, SRSLTE_COMPLEX_FLOAT_BIN)) { fprintf(stderr, "Error opening file %s\n", input_file); - exit(-1); + goto quit; } #ifdef DO_OFDM srslte_filesource_read(&fsrc, rx_slot_symbols, SRSLTE_SF_LEN_PRB(cell.nof_prb)); @@ -332,7 +350,7 @@ int main(int argc, char **argv) { srslte_pdsch_set_rnti(&pdsch_tx, rnti); - for (i = 0; i < nof_tb; i++) { + for (i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { if (srslte_softbuffer_tx_init(&softbuffers_tx[i], cell.nof_prb)) { fprintf(stderr, "Error initiating TX soft buffer\n"); goto quit; @@ -438,7 +456,7 @@ int main(int argc, char **argv) { quit: srslte_pdsch_free(&pdsch_tx); srslte_pdsch_free(&pdsch_rx); - for (i = 0; i < nof_tb; i++) { + for (i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { srslte_softbuffer_tx_free(&softbuffers_tx[i]); srslte_softbuffer_rx_free(&softbuffers_rx[i]); diff --git a/lib/src/phy/phch/test/pdsch_test_mex.c b/lib/src/phy/phch/test/pdsch_test_mex.c index 204154130..5093c326d 100644 --- a/lib/src/phy/phch/test/pdsch_test_mex.c +++ b/lib/src/phy/phch/test/pdsch_test_mex.c @@ -202,10 +202,17 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) ce[i][j] = srslte_vec_malloc(SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t)); } } - uint8_t *data_bytes = srslte_vec_malloc(sizeof(uint8_t) * grant.mcs.tbs/8); - if (!data_bytes) { + uint8_t *data_bytes[SRSLTE_MAX_CODEWORDS]; + data_bytes[0] = srslte_vec_malloc(sizeof(uint8_t) * grant.mcs.tbs/8); + if (!data_bytes[0]) { return; } + + data_bytes[1] = srslte_vec_malloc(sizeof(uint8_t) * grant.mcs2.tbs/8); + if (!data_bytes[1]) { + return; + } + srslte_sch_set_max_noi(&pdsch.dl_sch, max_iterations); bool input_fft_allocated = false; @@ -272,7 +279,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) } uint8_t *data = malloc(grant.mcs.tbs); - srslte_bit_unpack_vector(data_bytes, data, grant.mcs.tbs); + srslte_bit_unpack_vector(data_bytes[0], data, grant.mcs.tbs); if (nlhs >= 1) { plhs[0] = mxCreateLogicalScalar(r == 0); @@ -323,7 +330,11 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) } } } - free(data_bytes); + for (i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { + if (data_bytes[i]) { + free(data_bytes[i]); + } + } free(data); return; diff --git a/lib/src/phy/ue/ue_dl.c b/lib/src/phy/ue/ue_dl.c index 3b1a83466..c068f5899 100644 --- a/lib/src/phy/ue/ue_dl.c +++ b/lib/src/phy/ue/ue_dl.c @@ -38,8 +38,8 @@ #define CURRENT_SFLEN_RE SRSLTE_SF_LEN_RE(q->cell.nof_prb, q->cell.cp) -static srslte_dci_format_t ue_formats[] = {SRSLTE_DCI_FORMAT1A, SRSLTE_DCI_FORMAT1, SRSLTE_DCI_FORMAT2A}; // Only TM1 and TM2 are currently supported -const uint32_t nof_ue_formats = 3; +static srslte_dci_format_t ue_formats[] = {SRSLTE_DCI_FORMAT1A, SRSLTE_DCI_FORMAT1, SRSLTE_DCI_FORMAT2A, SRSLTE_DCI_FORMAT2}; // Only TM1, TM2, TM3 and TM4 are currently supported +const uint32_t nof_ue_formats = 4; static srslte_dci_format_t common_formats[] = {SRSLTE_DCI_FORMAT1A,SRSLTE_DCI_FORMAT1C}; const uint32_t nof_common_formats = 2; @@ -284,12 +284,33 @@ int srslte_ue_dl_decode_estimate(srslte_ue_dl_t *q, uint32_t sf_idx, uint32_t *c int srslte_ue_dl_cfg_grant(srslte_ue_dl_t *q, srslte_ra_dl_grant_t *grant, uint32_t cfi, uint32_t sf_idx, uint32_t rvidx) { - return srslte_pdsch_cfg_multi(&q->pdsch_cfg, q->cell, grant, cfi, sf_idx, rvidx, 0); + return srslte_pdsch_cfg_multi(&q->pdsch_cfg, q->cell, grant, cfi, sf_idx, rvidx, 0, SRSLTE_MIMO_TYPE_SINGLE_ANTENNA, 0); } -int srslte_ue_dl_cfg_grant_multi(srslte_ue_dl_t *q, srslte_ra_dl_grant_t *grant, uint32_t cfi, uint32_t sf_idx, uint32_t rvidx, uint32_t rvidx2) +int srslte_ue_dl_cfg_grant_multi(srslte_ue_dl_t *q, srslte_ra_dl_grant_t *grant, uint32_t cfi, uint32_t sf_idx, + uint32_t rvidx, uint32_t rvidx2, srslte_mimo_type_t mimo_type, uint32_t pinfo) { - return srslte_pdsch_cfg_multi(&q->pdsch_cfg, q->cell, grant, cfi, sf_idx, rvidx, rvidx2); + uint32_t pmi = 0; + + /* Translates Precoding Information (pinfo) to Precoding matrix Index (pmi) as 3GPP 36.212 Table 5.3.3.1.5-4 */ + if (q->pdsch_cfg.mimo_type == SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX) { + if (q->pdsch_cfg.grant.nof_tb == 1) { + if (pinfo > 0 && pinfo < 5) { + pmi = pinfo - 1; + } else { + ERROR("Not Implemented"); + return SRSLTE_ERROR; + } + } else { + if (pinfo < 2) { + pmi = pinfo; + } else { + ERROR("Not Implemented"); + return SRSLTE_ERROR; + } + } + } + return srslte_pdsch_cfg_multi(&q->pdsch_cfg, q->cell, grant, cfi, sf_idx, rvidx, rvidx2, mimo_type, pmi); } int srslte_ue_dl_decode_rnti(srslte_ue_dl_t *q, cf_t *input, uint8_t *data, uint32_t tti, uint16_t rnti) @@ -303,6 +324,7 @@ int srslte_ue_dl_decode_rnti(srslte_ue_dl_t *q, cf_t *input, uint8_t *data, uint int srslte_ue_dl_decode_rnti_multi(srslte_ue_dl_t *q, cf_t *input[SRSLTE_MAX_PORTS], uint8_t *data[SRSLTE_MAX_CODEWORDS], uint32_t tti, uint16_t rnti) { + srslte_mimo_type_t mimo_type; srslte_dci_msg_t dci_msg; srslte_ra_dl_dci_t dci_unpacked; srslte_ra_dl_grant_t grant; @@ -355,7 +377,39 @@ int srslte_ue_dl_decode_rnti_multi(srslte_ue_dl_t *q, cf_t *input[SRSLTE_MAX_POR } } - if (srslte_ue_dl_cfg_grant_multi(q, &grant, cfi, sf_idx, rvidx, rvidx2)) { + switch(dci_msg.format) { + case SRSLTE_DCI_FORMAT1: + case SRSLTE_DCI_FORMAT1A: + mimo_type = SRSLTE_MIMO_TYPE_SINGLE_ANTENNA; + break; + case SRSLTE_DCI_FORMAT2: + if (grant.nof_tb == 1 && dci_unpacked.pinfo == 0) { + mimo_type = SRSLTE_MIMO_TYPE_TX_DIVERSITY; + } else { + mimo_type = SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX; + } + break; + case SRSLTE_DCI_FORMAT2A: + if (grant.nof_tb == 1 && dci_unpacked.pinfo == 0) { + mimo_type = SRSLTE_MIMO_TYPE_TX_DIVERSITY; + } else { + mimo_type = SRSLTE_MIMO_TYPE_CDD; + } + break; + + /* Not implemented formats */ + case SRSLTE_DCI_FORMAT0: + case SRSLTE_DCI_FORMAT1C: + case SRSLTE_DCI_FORMAT1B: + case SRSLTE_DCI_FORMAT1D: + case SRSLTE_DCI_FORMAT2B: + default: + ERROR("Transmission mode not supported."); + return SRSLTE_ERROR; + } + + if (srslte_ue_dl_cfg_grant_multi(q, &grant, cfi, sf_idx, rvidx, rvidx2, mimo_type, dci_unpacked.pinfo)) { + ERROR("Configuing PDSCH"); return SRSLTE_ERROR; } @@ -374,7 +428,10 @@ int srslte_ue_dl_decode_rnti_multi(srslte_ue_dl_t *q, cf_t *input[SRSLTE_MAX_POR q->pkt_errors++; } else if (ret == SRSLTE_ERROR_INVALID_INPUTS) { fprintf(stderr, "Error calling srslte_pdsch_decode()\n"); - } + } + + /* If we are in TM4 (Closed-Loop MIMO), compute condition number */ + } /* @@ -395,6 +452,13 @@ int srslte_ue_dl_decode_rnti_multi(srslte_ue_dl_t *q, cf_t *input[SRSLTE_MAX_POR } } +int srslte_ue_dl_ri_pmi_select(srslte_ue_dl_t *q, uint32_t *ri, uint32_t *pmi, float *current_sinr) { + float noise_estimate = srslte_chest_dl_get_noise_estimate(&q->chest); + return srslte_pdsch_ri_pmi_select(&q->pdsch, &q->pdsch_cfg, q->ce_m, noise_estimate, + SRSLTE_SF_LEN_RE(q->cell.nof_prb, q->cell.cp), + ri, pmi, current_sinr); +} + uint32_t srslte_ue_dl_get_ncce(srslte_ue_dl_t *q) { return q->last_location.ncce; } diff --git a/lib/src/phy/utils/algebra.c b/lib/src/phy/utils/algebra.c new file mode 100644 index 000000000..69ac08749 --- /dev/null +++ b/lib/src/phy/utils/algebra.c @@ -0,0 +1,72 @@ +/** + * + * \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 + +#include "srslte/phy/utils/algebra.h" + +#ifdef LV_HAVE_SSE + +inline void srslte_algebra_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 detconj = _MM_CONJ_PS(det); + __m128 detabs2 = _MM_PROD_PS(det, detconj); + __m128 detabs2rec = _mm_rcp_ps(detabs2); + detabs2rec = _mm_moveldup_ps(detabs2rec); + __m128 detrec = _mm_mul_ps(_mm_mul_ps(detconj, detabs2rec), + (__m128) {norm, norm, norm, 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); +} + +#endif /* LV_HAVE_SSE */ + +#ifdef LV_HAVE_AVX + +inline void srslte_algebra_2x2_zf_avx(__m256 y0, __m256 y1, __m256 h00, __m256 h01, __m256 h10, __m256 h11, + __m256 *x0, __m256 *x1, float norm) { + __m256 detmult1 = _MM256_PROD_PS(h00, h11); + __m256 detmult2 = _MM256_PROD_PS(h01, h10); + + __m256 det = _mm256_sub_ps(detmult1, detmult2); + __m256 detconj = _MM256_CONJ_PS(det); + __m256 sqdet = _mm256_mul_ps(det, det); + __m256 detabs2 = _mm256_add_ps(_mm256_movehdup_ps(sqdet), _mm256_moveldup_ps(sqdet)); + __m256 detabs2rec = _mm256_rcp_ps(detabs2); + __m256 detrec = _mm256_mul_ps(_mm256_mul_ps(detconj, detabs2rec), + (__m256) {norm, norm, norm, norm, norm, norm, norm, norm}); + + *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_AVX */