mirror of https://github.com/pvnis/srsRAN_4G.git
Initial NR DMRS for PDSCH
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
|
@ -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;
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue