Initial NR DMRS for PDSCH

master
Xavier Arteaga 4 years ago committed by Xavier Arteaga
parent c75c463263
commit d375e305ec

@ -0,0 +1,114 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* 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_DMRS_PDSCH_H
#define SRSLTE_DMRS_PDSCH_H
#ifdef __cplusplus
extern "C" {
#endif
#include "srslte/phy/common/phy_common.h"
#include "srslte/srslte.h"
#include <stdint.h>
#define SRSLTE_DMRS_PDSCH_MAX_SYMBOLS 4
#define SRSLTE_DMRS_PDSCH_TYPEA_SINGLE_DURATION_MIN 3
#define SRSLTE_DMRS_PDSCH_TYPEA_DOUBLE_DURATION_MIN 4
typedef enum {
srslte_dmrs_pdsch_type_1 = 0, // 1 pilot every 2 sub-carriers (default)
srslte_dmrs_pdsch_type_2 // 2 consecutive pilots every 6 sub-carriers
} srslte_dmrs_pdsch_type_t;
typedef enum {
srslte_dmrs_pdsch_len_1 = 0, // single, 1 symbol long (default)
srslte_dmrs_pdsch_len_2 // double, 2 symbol long
} srslte_dmrs_pdsch_len_t;
/**
* Determines whether the first pilot goes into symbol index 2 or 3
*/
typedef enum {
srslte_dmrs_pdsch_typeA_pos_2 = 0, // Start in slot symbol index 2 (default)
srslte_dmrs_pdsch_typeA_pos_3 // Start in slot symbol index 3
} srslte_dmrs_pdsch_typeA_pos_t;
/**
* Determines additional symbols if possible to be added
*/
typedef enum {
srslte_dmrs_pdsch_add_pos_2 = 0,
srslte_dmrs_pdsch_add_pos_0,
srslte_dmrs_pdsch_add_pos_1,
srslte_dmrs_pdsch_add_pos_3
} srslte_dmrs_pdsch_add_pos_t;
typedef enum {
srslte_dmrs_pdsch_mapping_type_A = 0,
srslte_dmrs_pdsch_mapping_type_B
} srslte_dmrs_pdsch_mapping_type_t;
typedef struct {
srslte_dmrs_pdsch_type_t type;
srslte_dmrs_pdsch_typeA_pos_t typeA_pos;
srslte_dmrs_pdsch_add_pos_t additional_pos;
srslte_dmrs_pdsch_len_t length;
srslte_dmrs_pdsch_mapping_type_t mapping_type;
uint32_t duration;
bool lte_CRS_to_match_around;
bool additional_DMRS_DL_Alt;
uint32_t n_id;
uint32_t n_scid;
uint32_t nof_prb;
float beta;
uint32_t reference_point;
} srslte_dmrs_pdsch_cfg_t;
typedef struct {
srslte_dmrs_pdsch_cfg_t cfg;
uint32_t symbols_idx[SRSLTE_DMRS_PDSCH_MAX_SYMBOLS];
uint32_t nof_symbols;
uint32_t sc_idx[SRSLTE_NRE];
uint32_t nof_sc;
} srslte_dmrs_pdsch_t;
SRSLTE_API int srslte_dmrs_pdsch_cfg_to_str(const srslte_dmrs_pdsch_cfg_t* cfg, char* msg, uint32_t max_len);
SRSLTE_API int srslte_dmrs_pdsch_init(srslte_dmrs_pdsch_t* q, const srslte_dmrs_pdsch_cfg_t* cfg);
SRSLTE_API int srslte_dmrs_pdsch_put_sf(srslte_dmrs_pdsch_t* q, const srslte_dl_sf_cfg_t* sf, cf_t* sf_symbols);
SRSLTE_API int srslte_dmrs_pdsch_get_sf(srslte_dmrs_pdsch_t* q,
const srslte_dl_sf_cfg_t* sf,
const cf_t* sf_symbols,
cf_t* lest_square_estimates);
#ifdef __cplusplus
}
#endif
#endif // SRSLTE_DMRS_PDSCH_H

@ -34,6 +34,15 @@
#include "srslte/config.h" #include "srslte/config.h"
#include "srslte/phy/common/phy_common.h" #include "srslte/phy/common/phy_common.h"
typedef struct SRSLTE_API {
uint32_t x1;
uint32_t x2;
} srslte_sequence_state_t;
void srslte_sequence_state_init(srslte_sequence_state_t* s, uint32_t seed);
void srslte_sequence_state_gen_f(srslte_sequence_state_t* s, float value, float* out, uint32_t length);
typedef struct SRSLTE_API { typedef struct SRSLTE_API {
uint8_t* c; uint8_t* c;
uint8_t* c_bytes; uint8_t* c_bytes;

@ -0,0 +1,345 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* 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 "srslte/phy/ch_estimation/dmrs_pdsch.h"
#include <srslte/phy/utils/debug.h>
int srslte_dmrs_pdsch_cfg_to_str(const srslte_dmrs_pdsch_cfg_t* cfg, char* msg, uint32_t max_len)
{
int type = (int)cfg->type + 1;
int typeA_pos = (int)cfg->typeA_pos + 2;
int additional_pos = cfg->additional_pos == srslte_dmrs_pdsch_add_pos_0
? 0
: cfg->additional_pos == srslte_dmrs_pdsch_add_pos_1
? 1
: cfg->additional_pos == srslte_dmrs_pdsch_add_pos_2 ? 2 : 3;
const char* len = cfg->length == srslte_dmrs_pdsch_len_1 ? "single" : "double";
const char* mapping = cfg->mapping_type == srslte_dmrs_pdsch_mapping_type_A ? "A" : "B";
return srslte_print_check(msg,
max_len,
0,
"mapping=%s, type=%d, typeA_pos=%d, add_pos=%d, len=%s",
mapping,
type,
typeA_pos,
additional_pos,
len);
}
// Implements 3GPP 38.211 R.15 Table 7.4.1.1.2-3 PDSCH mapping type A Single
static int srslte_dmrs_pdsch_get_symbols_idx_mapping_type_A_single(const srslte_dmrs_pdsch_cfg_t* cfg,
uint32_t symbols[SRSLTE_DMRS_PDSCH_MAX_SYMBOLS])
{
int count = 0;
if (cfg->duration < SRSLTE_DMRS_PDSCH_TYPEA_SINGLE_DURATION_MIN) {
ERROR("Duration is below the minimum\n");
return SRSLTE_ERROR;
}
// l0 = 3 if the higher-layer parameter dmrs-TypeA-Position is equal to 'pos3' and l0 = 2 otherwise
int l0 = (cfg->typeA_pos == srslte_dmrs_pdsch_typeA_pos_3) ? 3 : 2;
// For PDSCH mapping Type A single-symbol DM-RS, l1 = 11 except if all of the following conditions are fulfilled in
// which case l1 = 12:
// - the higher-layer parameter lte-CRS-ToMatchAround is configured; and
// - the higher-layer parameters dmrs-AdditionalPosition is equal to 'pos1' and l0 = 3; and
// - the UE has indicated it is capable of additionalDMRS-DL-Alt
int l1 = 11;
if (cfg->lte_CRS_to_match_around && cfg->additional_pos == srslte_dmrs_pdsch_add_pos_1 &&
cfg->typeA_pos == srslte_dmrs_pdsch_typeA_pos_3 && cfg->additional_DMRS_DL_Alt) {
l1 = 12;
}
symbols[count] = l0;
count++;
if (cfg->duration < 8 || cfg->additional_pos == srslte_dmrs_pdsch_add_pos_0) {
return count;
}
if (cfg->duration < 10) {
symbols[count] = 7;
count++;
} else if (cfg->duration < 12) {
if (cfg->additional_pos > srslte_dmrs_pdsch_add_pos_2) {
symbols[count] = 6;
count++;
}
symbols[count] = 9;
count++;
} else if (cfg->duration == 12) {
switch (cfg->additional_pos) {
case srslte_dmrs_pdsch_add_pos_1:
symbols[count] = 9;
count++;
break;
case srslte_dmrs_pdsch_add_pos_2:
symbols[count] = 6;
count++;
symbols[count] = 9;
count++;
break;
default:
symbols[count] = 5;
count++;
symbols[count] = 8;
count++;
symbols[count] = 11;
count++;
}
} else {
switch (cfg->additional_pos) {
case srslte_dmrs_pdsch_add_pos_1:
symbols[count] = l1;
count++;
break;
case srslte_dmrs_pdsch_add_pos_2:
symbols[count] = 7;
count++;
symbols[count] = 11;
count++;
break;
default:
symbols[count] = 5;
count++;
symbols[count] = 8;
count++;
symbols[count] = 11;
count++;
}
}
return count;
}
// Implements 3GPP 38.211 R.15 Table 7.4.1.1.2-4 PDSCH mapping type A Double
static int srslte_dmrs_pdsch_get_symbols_idx_mapping_type_A_double(const srslte_dmrs_pdsch_cfg_t* cfg,
uint32_t symbols[SRSLTE_DMRS_PDSCH_MAX_SYMBOLS])
{
int count = 0;
if (cfg->duration < SRSLTE_DMRS_PDSCH_TYPEA_DOUBLE_DURATION_MIN) {
return SRSLTE_ERROR;
}
// l0 = 3 if the higher-layer parameter dmrs-TypeA-Position is equal to 'pos3' and l0 = 2 otherwise
int l0 = (cfg->typeA_pos == srslte_dmrs_pdsch_typeA_pos_3) ? 3 : 2;
symbols[count] = l0;
count++;
symbols[count] = symbols[count - 1] + 1;
count++;
if (cfg->duration < 10 || cfg->additional_pos == srslte_dmrs_pdsch_add_pos_0) {
return count;
}
if (cfg->duration < 13) {
symbols[count] = 8;
count++;
symbols[count] = symbols[count - 1] + 1;
count++;
} else {
symbols[count] = 10;
count++;
symbols[count] = symbols[count - 1] + 1;
count++;
}
return count;
}
static int srslte_dmrs_pdsch_get_symbols_idx(const srslte_dmrs_pdsch_cfg_t* cfg, uint32_t* symbols)
{
switch (cfg->mapping_type) {
case srslte_dmrs_pdsch_mapping_type_A:
// The case dmrs-AdditionalPosition equals to 'pos3' is only supported when dmrs-TypeA-Position is equal to 'pos2'
if (cfg->typeA_pos != srslte_dmrs_pdsch_typeA_pos_2 && cfg->additional_pos == srslte_dmrs_pdsch_add_pos_3) {
ERROR("The case dmrs-AdditionalPosition equals to 'pos3' is only supported when dmrs-TypeA-Position is equal "
"to 'pos2'\n");
return SRSLTE_ERROR;
}
// For PDSCH mapping type A, ld = 3 and ld = 4 symbols in Tables 7.4.1.1.2-3 and 7.4.1.1.2-4 respectively is only
// applicable when dmrs-TypeA-Position is equal to 'pos2
if ((cfg->duration == 3 || cfg->duration == 4) && cfg->typeA_pos != srslte_dmrs_pdsch_typeA_pos_2) {
ERROR("For PDSCH mapping type A, ld = 3 and ld = 4 symbols in Tables 7.4.1.1.2-3 and 7.4.1.1.2-4 respectively "
"is only applicable when dmrs-TypeA-Position is equal to 'pos2\n");
return SRSLTE_ERROR;
}
if (cfg->length == srslte_dmrs_pdsch_len_1) {
return srslte_dmrs_pdsch_get_symbols_idx_mapping_type_A_single(cfg, symbols);
}
return srslte_dmrs_pdsch_get_symbols_idx_mapping_type_A_double(cfg, symbols);
case srslte_dmrs_pdsch_mapping_type_B:
ERROR("Error PDSCH mapping type B not supported\n");
return SRSLTE_ERROR;
}
return SRSLTE_ERROR;
}
static int srslte_dmrs_pdsch_get_sc_idx(const srslte_dmrs_pdsch_cfg_t* cfg, uint32_t sc_idx[SRSLTE_NRE])
{
int count = 0;
uint32_t delta = 0;
if (cfg->type == srslte_dmrs_pdsch_type_1) {
for (uint32_t n = 0; n < SRSLTE_NRE; n += 4) {
for (uint32_t k_prime = 0; k_prime < 2; k_prime++) {
sc_idx[count++] = n + 2 * k_prime + delta;
}
}
} else {
for (uint32_t n = 0; n < SRSLTE_NRE; n += 6) {
for (uint32_t k_prime = 0; k_prime < 2; k_prime++) {
sc_idx[count++] = n + k_prime + delta;
}
}
}
return count;
}
static uint32_t srslte_dmrs_pdsch_seed(const srslte_dmrs_pdsch_cfg_t* cfg, uint32_t slot_idx, uint32_t symbol_idx)
{
return (uint32_t)(((((SRSLTE_MAX_NSYMB * slot_idx + symbol_idx + 1UL) * (2UL * cfg->n_id + 1UL)) << 17UL) +
(2UL * cfg->n_id + cfg->n_scid)) &
(uint64_t)INT32_MAX);
}
int srslte_dmrs_pdsch_init(srslte_dmrs_pdsch_t* q, const srslte_dmrs_pdsch_cfg_t* cfg)
{
// Copy new configuration
q->cfg = *cfg;
// Calculate the symbols that carry PDSCH DMRS
int n = srslte_dmrs_pdsch_get_symbols_idx(&q->cfg, q->symbols_idx);
if (n < SRSLTE_SUCCESS) {
ERROR("Getting symbols indexes\n");
return n;
}
q->nof_symbols = (uint32_t)n;
// Calculate the sub-carrier index that carry PDSCH DMRS
n = srslte_dmrs_pdsch_get_sc_idx(&q->cfg, q->sc_idx);
if (n < SRSLTE_SUCCESS) {
ERROR("Getting sub-carriers indexes\n");
return n;
}
q->nof_sc = (uint32_t)n;
return SRSLTE_SUCCESS;
}
#define SRSLTE_DMRS_PDSCH_TEMP_PILOT_SIZE 64
int srslte_dmrs_pdsch_put_sf(srslte_dmrs_pdsch_t* q, const srslte_dl_sf_cfg_t* sf, cf_t* sf_symbols)
{
uint32_t delta = 0;
// Iterate symbols
for (uint32_t i = 0; i < q->nof_symbols; i++) {
uint32_t l = q->symbols_idx[i]; // Symbol index inside the sub-frame
uint32_t slot_idx = (sf->tti % SRSLTE_NOF_SF_X_FRAME) * SRSLTE_NOF_SLOTS_PER_SF; // Slot index in the frame
uint32_t symbol_sz = q->cfg.nof_prb * SRSLTE_NRE; // Symbol size in resource elements
srslte_sequence_state_t sequence_state = {};
srslte_sequence_state_init(&sequence_state, srslte_dmrs_pdsch_seed(&q->cfg, slot_idx, l));
// Generate
uint32_t k_end = q->cfg.nof_prb * SRSLTE_NRE;
for (uint32_t k = delta; k < k_end;) {
cf_t temp_pilots[SRSLTE_DMRS_PDSCH_TEMP_PILOT_SIZE] = {};
srslte_sequence_state_gen_f(
&sequence_state, M_SQRT1_2, (float*)temp_pilots, 2 * SRSLTE_DMRS_PDSCH_TEMP_PILOT_SIZE);
switch (q->cfg.type) {
case srslte_dmrs_pdsch_type_1:
for (uint32_t idx = 0; idx < SRSLTE_DMRS_PDSCH_TEMP_PILOT_SIZE && k < k_end; k += 2) {
sf_symbols[l * symbol_sz + k] = temp_pilots[idx++];
}
break;
case srslte_dmrs_pdsch_type_2:
for (uint32_t idx = 0; idx < SRSLTE_DMRS_PDSCH_TEMP_PILOT_SIZE && k < k_end; k += 6) {
sf_symbols[l * symbol_sz + k] = temp_pilots[idx++];
sf_symbols[l * symbol_sz + k + 1] = temp_pilots[idx++];
}
break;
}
}
}
return SRSLTE_SUCCESS;
}
int srslte_dmrs_pdsch_get_sf(srslte_dmrs_pdsch_t* q,
const srslte_dl_sf_cfg_t* sf,
const cf_t* sf_symbols,
cf_t* least_square_estimates)
{
uint32_t delta = 0;
// Iterate symbols
for (uint32_t i = 0; i < q->nof_symbols; i++) {
uint32_t l = q->symbols_idx[i]; // Symbol index inside the sub-frame
uint32_t slot_idx = (sf->tti % SRSLTE_NOF_SF_X_FRAME) * SRSLTE_NOF_SLOTS_PER_SF; // Slot index in the frame
uint32_t symbol_sz = q->cfg.nof_prb * SRSLTE_NRE; // Symbol size in resource elements
srslte_sequence_state_t sequence_state = {};
srslte_sequence_state_init(&sequence_state, srslte_dmrs_pdsch_seed(&q->cfg, slot_idx, l));
uint32_t n_end = q->cfg.nof_prb * SRSLTE_NRE;
for (uint32_t n = 0; n < n_end;) {
cf_t temp_pilots[SRSLTE_DMRS_PDSCH_TEMP_PILOT_SIZE];
srslte_sequence_state_gen_f(
&sequence_state, M_SQRT1_2, (float*)temp_pilots, 2 * SRSLTE_DMRS_PDSCH_TEMP_PILOT_SIZE);
switch (q->cfg.type) {
case srslte_dmrs_pdsch_type_1:
for (uint32_t idx = 0; idx < SRSLTE_DMRS_PDSCH_TEMP_PILOT_SIZE && n < n_end; n += 4) {
for (uint32_t k_prime = 0; k_prime < 2; k_prime++) {
uint32_t k = n + 2 * k_prime + delta;
*(least_square_estimates++) = sf_symbols[l * symbol_sz + k] * conjf(temp_pilots[idx++]);
}
}
break;
case srslte_dmrs_pdsch_type_2:
for (uint32_t idx = 0; idx < SRSLTE_DMRS_PDSCH_TEMP_PILOT_SIZE && n < n_end; n += 6) {
for (uint32_t k_prime = 0; k_prime < 2; k_prime++) {
uint32_t k = n + k_prime + delta;
*(least_square_estimates++) = sf_symbols[l * symbol_sz + k] * conjf(temp_pilots[idx++]);
}
}
break;
}
}
}
return SRSLTE_SUCCESS;
}

@ -79,3 +79,13 @@ add_executable(chest_test_sl chest_test_sl.c)
target_link_libraries(chest_test_sl srslte_phy) target_link_libraries(chest_test_sl srslte_phy)
add_test(chest_test_sl_psbch chest_test_sl) add_test(chest_test_sl_psbch chest_test_sl)
########################################################################
# NR PDSCH DMRS Channel Estimation TEST
########################################################################
add_executable(dmrs_pdsch_test dmrs_pdsch_test.c)
target_link_libraries(dmrs_pdsch_test srslte_phy)
add_test(dmrs_pdsch_test dmrs_pdsch_test)

@ -0,0 +1,308 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* 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 "srslte/common/test_common.h"
#include "srslte/srslte.h"
#include <complex.h>
#include <srslte/phy/ch_estimation/dmrs_pdsch.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
static srslte_cell_t cell = {50, // nof_prb
1, // nof_ports
1, // cell_id
SRSLTE_CP_NORM, // cyclic prefix
SRSLTE_PHICH_NORM,
SRSLTE_PHICH_R_1, // PHICH length
SRSLTE_FDD};
typedef struct {
srslte_dmrs_pdsch_mapping_type_t mapping_type;
srslte_dmrs_pdsch_typeA_pos_t typeA_pos;
srslte_dmrs_pdsch_len_t max_length;
srslte_dmrs_pdsch_add_pos_t additional_pos;
srslte_dmrs_pdsch_type_t type;
uint32_t symbol_idx[SRSLTE_DMRS_PDSCH_MAX_SYMBOLS];
uint32_t nof_symbols;
uint32_t sc_idx[SRSLTE_NRE];
uint32_t nof_sc;
} golden_t;
// Golden values extracted from https://www.sharetechnote.com/html/5G/5G_PDSCH_DMRS.html
static const golden_t gold[] = {{.mapping_type = srslte_dmrs_pdsch_mapping_type_A,
.typeA_pos = srslte_dmrs_pdsch_typeA_pos_2,
.max_length = srslte_dmrs_pdsch_len_1,
.additional_pos = srslte_dmrs_pdsch_add_pos_0,
.type = srslte_dmrs_pdsch_type_2,
.nof_symbols = 1,
.symbol_idx = {2},
.nof_sc = 4,
.sc_idx = {0, 1, 6, 7}},
{.mapping_type = srslte_dmrs_pdsch_mapping_type_A,
.typeA_pos = srslte_dmrs_pdsch_typeA_pos_3,
.max_length = srslte_dmrs_pdsch_len_1,
.additional_pos = srslte_dmrs_pdsch_add_pos_0,
.type = srslte_dmrs_pdsch_type_2,
.nof_symbols = 1,
.symbol_idx = {3},
.nof_sc = 4,
.sc_idx = {0, 1, 6, 7}},
{.mapping_type = srslte_dmrs_pdsch_mapping_type_A,
.typeA_pos = srslte_dmrs_pdsch_typeA_pos_2,
.max_length = srslte_dmrs_pdsch_len_2,
.additional_pos = srslte_dmrs_pdsch_add_pos_0,
.type = srslte_dmrs_pdsch_type_2,
.nof_symbols = 2,
.symbol_idx = {2, 3},
.nof_sc = 4,
.sc_idx = {0, 1, 6, 7}},
{.mapping_type = srslte_dmrs_pdsch_mapping_type_A,
.typeA_pos = srslte_dmrs_pdsch_typeA_pos_2,
.max_length = srslte_dmrs_pdsch_len_1,
.additional_pos = srslte_dmrs_pdsch_add_pos_1,
.type = srslte_dmrs_pdsch_type_2,
.nof_symbols = 2,
.symbol_idx = {2, 11},
.nof_sc = 4,
.sc_idx = {0, 1, 6, 7}},
{.mapping_type = srslte_dmrs_pdsch_mapping_type_A,
.typeA_pos = srslte_dmrs_pdsch_typeA_pos_2,
.max_length = srslte_dmrs_pdsch_len_1,
.additional_pos = srslte_dmrs_pdsch_add_pos_2,
.type = srslte_dmrs_pdsch_type_2,
.nof_symbols = 3,
.symbol_idx = {2, 7, 11},
.nof_sc = 4,
.sc_idx = {0, 1, 6, 7}},
{.mapping_type = srslte_dmrs_pdsch_mapping_type_A,
.typeA_pos = srslte_dmrs_pdsch_typeA_pos_2,
.max_length = srslte_dmrs_pdsch_len_1,
.additional_pos = srslte_dmrs_pdsch_add_pos_3,
.type = srslte_dmrs_pdsch_type_2,
.nof_symbols = 4,
.symbol_idx = {2, 5, 8, 11},
.nof_sc = 4,
.sc_idx = {0, 1, 6, 7}},
{.mapping_type = srslte_dmrs_pdsch_mapping_type_A,
.typeA_pos = srslte_dmrs_pdsch_typeA_pos_2,
.max_length = srslte_dmrs_pdsch_len_1,
.additional_pos = srslte_dmrs_pdsch_add_pos_0,
.type = srslte_dmrs_pdsch_type_1,
.nof_symbols = 1,
.symbol_idx = {2},
.nof_sc = 6,
.sc_idx = {0, 2, 4, 6, 8, 10}},
{.mapping_type = srslte_dmrs_pdsch_mapping_type_A,
.typeA_pos = srslte_dmrs_pdsch_typeA_pos_2,
.max_length = srslte_dmrs_pdsch_len_2,
.additional_pos = srslte_dmrs_pdsch_add_pos_0,
.type = srslte_dmrs_pdsch_type_1,
.nof_symbols = 2,
.symbol_idx = {2, 3},
.nof_sc = 6,
.sc_idx = {0, 2, 4, 6, 8, 10}},
{.mapping_type = srslte_dmrs_pdsch_mapping_type_A,
.typeA_pos = srslte_dmrs_pdsch_typeA_pos_2,
.max_length = srslte_dmrs_pdsch_len_1,
.additional_pos = srslte_dmrs_pdsch_add_pos_3,
.type = srslte_dmrs_pdsch_type_1,
.nof_symbols = 4,
.symbol_idx = {2, 5, 8, 11},
.nof_sc = 6,
.sc_idx = {0, 2, 4, 6, 8, 10}},
{}};
void usage(char* prog)
{
printf("Usage: %s [recov]\n", prog);
printf("\t-r nof_prb [Default %d]\n", cell.nof_prb);
printf("\t-e extended cyclic prefix [Default normal]\n");
printf("\t-c cell_id [Default %d]\n", cell.id);
printf("\t-v increase verbosity\n");
}
void parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "recov")) != -1) {
switch (opt) {
case 'r':
cell.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'e':
cell.cp = SRSLTE_CP_EXT;
break;
case 'c':
cell.id = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'v':
srslte_verbose++;
break;
default:
usage(argv[0]);
exit(-1);
}
}
}
static int assert_cfg(srslte_dmrs_pdsch_t* dmrs_pdsch)
{
for (uint32_t i = 0; gold[i].nof_sc != 0; i++) {
if (dmrs_pdsch->cfg.mapping_type != gold[i].mapping_type) {
continue;
}
if (dmrs_pdsch->cfg.typeA_pos != gold[i].typeA_pos) {
continue;
}
if (dmrs_pdsch->cfg.additional_pos != gold[i].additional_pos) {
continue;
}
if (dmrs_pdsch->cfg.length != gold[i].max_length) {
continue;
}
if (dmrs_pdsch->cfg.type != gold[i].type) {
continue;
}
TESTASSERT(dmrs_pdsch->nof_symbols == gold[i].nof_symbols);
for (uint32_t j = 0; j < gold[i].nof_symbols; j++) {
TESTASSERT(dmrs_pdsch->symbols_idx[j] == gold[i].symbol_idx[j]);
}
for (uint32_t j = 0; j < gold[i].nof_sc; j++) {
TESTASSERT(dmrs_pdsch->sc_idx[j] == gold[i].sc_idx[j]);
}
return SRSLTE_SUCCESS;
}
return SRSLTE_SUCCESS;
}
static int run_test(srslte_dmrs_pdsch_t* dmrs_pdsch, cf_t* sf_symbols, cf_t* h)
{
TESTASSERT(dmrs_pdsch->nof_symbols > 0);
TESTASSERT(dmrs_pdsch->nof_sc > 0);
TESTASSERT(assert_cfg(dmrs_pdsch) == SRSLTE_SUCCESS);
srslte_dl_sf_cfg_t dl_sf = {};
for (dl_sf.tti = 0; dl_sf.tti < SRSLTE_NOF_SF_X_FRAME; dl_sf.tti++) {
srslte_dmrs_pdsch_put_sf(dmrs_pdsch, &dl_sf, sf_symbols);
srslte_dmrs_pdsch_get_sf(dmrs_pdsch, &dl_sf, sf_symbols, h);
float mse = 0.0f;
for (uint32_t i = 0; i < dmrs_pdsch->nof_symbols * dmrs_pdsch->nof_sc * SRSLTE_NRE; i++) {
cf_t err = h[i] - 1.0f;
mse += cabsf(err);
}
mse /= (float)dmrs_pdsch->nof_symbols * dmrs_pdsch->nof_sc;
TESTASSERT(!isnan(mse));
TESTASSERT(mse < 1e-6f);
}
return SRSLTE_SUCCESS;
}
int main(int argc, char** argv)
{
int ret = SRSLTE_ERROR;
parse_args(argc, argv);
srslte_dmrs_pdsch_cfg_t cfg = {};
cfg.duration = SRSLTE_NOF_SLOTS_PER_SF * SRSLTE_MAX_NSYMB;
cfg.nof_prb = cell.nof_prb;
uint32_t nof_re = cell.nof_prb * SRSLTE_NRE * SRSLTE_NOF_SLOTS_PER_SF * SRSLTE_MAX_NSYMB;
cf_t* sf_symbols = srslte_vec_cf_malloc(nof_re);
cf_t* h = srslte_vec_cf_malloc(nof_re);
uint32_t test_counter = 0;
uint32_t test_passed = 0;
for (cfg.type = srslte_dmrs_pdsch_type_1; cfg.type <= srslte_dmrs_pdsch_type_2; cfg.type++) {
srslte_dmrs_pdsch_typeA_pos_t typeA_pos_begin = srslte_dmrs_pdsch_typeA_pos_2;
srslte_dmrs_pdsch_typeA_pos_t typeA_pos_end = srslte_dmrs_pdsch_typeA_pos_3;
for (cfg.typeA_pos = typeA_pos_begin; cfg.typeA_pos <= typeA_pos_end; cfg.typeA_pos++) {
srslte_dmrs_pdsch_add_pos_t add_pos_begin = srslte_dmrs_pdsch_add_pos_2;
srslte_dmrs_pdsch_add_pos_t add_pos_end = srslte_dmrs_pdsch_add_pos_3;
if (cfg.typeA_pos == srslte_dmrs_pdsch_typeA_pos_3) {
add_pos_end = srslte_dmrs_pdsch_add_pos_1;
}
for (cfg.additional_pos = add_pos_begin; cfg.additional_pos <= add_pos_end; cfg.additional_pos++) {
srslte_dmrs_pdsch_len_t max_len_begin = srslte_dmrs_pdsch_len_1;
srslte_dmrs_pdsch_len_t max_len_end = srslte_dmrs_pdsch_len_2;
for (cfg.length = max_len_begin; cfg.length <= max_len_end; cfg.length++) {
srslte_dmrs_pdsch_t dmrs_pdsch = {};
// Initialise object with current configuration
if (srslte_dmrs_pdsch_init(&dmrs_pdsch, &cfg)) {
ERROR("Error initialising PDSCH DMRS\n");
continue;
}
int n = run_test(&dmrs_pdsch, sf_symbols, h);
if (n == SRSLTE_SUCCESS) {
test_passed++;
} else {
char str[64] = {};
srslte_dmrs_pdsch_cfg_to_str(&cfg, str, 64);
ERROR("Test %d failed. %s.\n", test_counter, str);
}
test_counter++;
}
}
}
}
if (sf_symbols) {
free(sf_symbols);
}
if (h) {
free(h);
}
ret = test_passed == test_counter ? SRSLTE_SUCCESS : SRSLTE_ERROR;
printf("%s, %d of %d test passed successfully.\n", ret ? "Failed" : "Passed", test_passed, test_counter);
return ret;
}

@ -215,6 +215,65 @@ static void sequence_gen_LTE_pr(uint8_t* pr, uint32_t len, uint32_t seed)
} }
} }
void srslte_sequence_state_init(srslte_sequence_state_t* s, uint32_t seed)
{
s->x1 = sequence_x1_init;
s->x2 = sequence_get_x2_init(seed);
}
void srslte_sequence_state_gen_f(srslte_sequence_state_t* s, float value, float* out, uint32_t length)
{
uint32_t i = 0;
const float xor [2] = {+0.0F, -0.0F};
if (length >= SEQUENCE_PAR_BITS) {
for (; i < length - (SEQUENCE_PAR_BITS - 1); i += SEQUENCE_PAR_BITS) {
uint32_t c = (uint32_t)(s->x1 ^ s->x2);
uint32_t j = 0;
#ifdef LV_HAVE_SSE
for (; j < SEQUENCE_PAR_BITS - 3; j += 4) {
// Preloads bits of interest in the 4 LSB
__m128i mask = _mm_set1_epi32(c >> j);
// Masks each bit
mask = _mm_and_si128(mask, _mm_setr_epi32(1, 2, 4, 8));
// Get non zero mask
mask = _mm_cmpgt_epi32(mask, _mm_set1_epi32(0));
// And with MSB
mask = _mm_and_si128(mask, (__m128i)_mm_set1_ps(-0.0F));
// Load input
__m128 v = _mm_set1_ps(value);
// Loads input and perform sign XOR
v = _mm_xor_ps((__m128)mask, v);
_mm_storeu_ps(out + i + j, v);
}
#endif
// Finish the parallel bits with generic code
for (; j < SEQUENCE_PAR_BITS; j++) {
*((uint32_t*)&out[i + j]) = *((uint32_t*)&value) ^ *((uint32_t*)&xor[(c >> j) & 1U]);
}
// Step sequences
s->x1 = sequence_gen_LTE_pr_memless_step_par_x1(s->x1);
s->x2 = sequence_gen_LTE_pr_memless_step_par_x2(s->x2);
}
}
for (; i < length; i++) {
*((uint32_t*)&out[i]) = *((uint32_t*)&value) ^ *((uint32_t*)&xor[(s->x1 ^ s->x2) & 1U]);
// Step sequences
s->x1 = sequence_gen_LTE_pr_memless_step_x1(s->x1);
s->x2 = sequence_gen_LTE_pr_memless_step_x2(s->x2);
}
}
// static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int srslte_sequence_set_LTE_pr(srslte_sequence_t* q, uint32_t len, uint32_t seed) int srslte_sequence_set_LTE_pr(srslte_sequence_t* q, uint32_t len, uint32_t seed)
{ {

Loading…
Cancel
Save