mirror of https://github.com/pvnis/srsRAN_4G.git
Renaming common UL/DL DMRS related types and initial NR PUSCH implementation
parent
cc5fdb68f3
commit
b3200d9ef5
@ -0,0 +1,93 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2020 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRSLTE_PUSCH_NR_H
|
||||||
|
#define SRSLTE_PUSCH_NR_H
|
||||||
|
|
||||||
|
#include "srslte/config.h"
|
||||||
|
#include "srslte/phy/ch_estimation/dmrs_sch.h"
|
||||||
|
#include "srslte/phy/phch/phch_cfg_nr.h"
|
||||||
|
#include "srslte/phy/phch/regs.h"
|
||||||
|
#include "srslte/phy/phch/sch_nr.h"
|
||||||
|
#include "srslte/phy/scrambling/scrambling.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PUSCH encoder and decoder initialization arguments
|
||||||
|
*/
|
||||||
|
typedef struct SRSLTE_API {
|
||||||
|
srslte_sch_nr_args_t sch;
|
||||||
|
bool measure_evm;
|
||||||
|
bool measure_time;
|
||||||
|
} srslte_pusch_nr_args_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PDSCH NR object
|
||||||
|
*/
|
||||||
|
typedef struct SRSLTE_API {
|
||||||
|
uint32_t max_prb; ///< Maximum number of allocated prb
|
||||||
|
uint32_t max_layers; ///< Maximum number of allocated layers
|
||||||
|
uint32_t max_cw; ///< Maximum number of allocated code words
|
||||||
|
srslte_carrier_nr_t carrier; ///< NR carrier configuration
|
||||||
|
srslte_sch_nr_t sch; ///< SCH Encoder/Decoder Object
|
||||||
|
uint8_t* b[SRSLTE_MAX_CODEWORDS]; ///< SCH Encoded and scrambled data
|
||||||
|
cf_t* d[SRSLTE_MAX_CODEWORDS]; ///< PDSCH modulated bits
|
||||||
|
cf_t* x[SRSLTE_MAX_LAYERS_NR]; ///< PDSCH modulated bits
|
||||||
|
srslte_modem_table_t modem_tables[SRSLTE_MOD_NITEMS]; ///< Modulator tables
|
||||||
|
srslte_evm_buffer_t* evm_buffer;
|
||||||
|
bool meas_time_en;
|
||||||
|
uint32_t meas_time_us;
|
||||||
|
} srslte_pusch_nr_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint8_t* payload;
|
||||||
|
bool crc;
|
||||||
|
float evm;
|
||||||
|
} srslte_pusch_res_nr_t;
|
||||||
|
|
||||||
|
SRSLTE_API int srslte_pusch_nr_init_enb(srslte_pusch_nr_t* q, const srslte_pusch_nr_args_t* args);
|
||||||
|
|
||||||
|
SRSLTE_API int srslte_pusch_nr_init_ue(srslte_pusch_nr_t* q, const srslte_pusch_nr_args_t* args);
|
||||||
|
|
||||||
|
SRSLTE_API void srslte_pusch_nr_free(srslte_pusch_nr_t* q);
|
||||||
|
|
||||||
|
SRSLTE_API int srslte_pusch_nr_set_carrier(srslte_pusch_nr_t* q, const srslte_carrier_nr_t* carrier);
|
||||||
|
|
||||||
|
SRSLTE_API int srslte_pusch_nr_encode(srslte_pusch_nr_t* q,
|
||||||
|
const srslte_sch_cfg_nr_t* cfg,
|
||||||
|
const srslte_sch_grant_nr_t* grant,
|
||||||
|
uint8_t* data[SRSLTE_MAX_TB],
|
||||||
|
cf_t* sf_symbols[SRSLTE_MAX_PORTS]);
|
||||||
|
|
||||||
|
SRSLTE_API int srslte_pusch_nr_decode(srslte_pusch_nr_t* q,
|
||||||
|
const srslte_sch_cfg_nr_t* cfg,
|
||||||
|
const srslte_sch_grant_nr_t* grant,
|
||||||
|
srslte_chest_dl_res_t* channel,
|
||||||
|
cf_t* sf_symbols[SRSLTE_MAX_PORTS],
|
||||||
|
srslte_pusch_res_nr_t data[SRSLTE_MAX_TB]);
|
||||||
|
|
||||||
|
SRSLTE_API uint32_t srslte_pusch_nr_rx_info(const srslte_pusch_nr_t* q,
|
||||||
|
const srslte_sch_cfg_nr_t* cfg,
|
||||||
|
const srslte_sch_grant_nr_t* grant,
|
||||||
|
const srslte_pusch_res_nr_t* res,
|
||||||
|
char* str,
|
||||||
|
uint32_t str_len);
|
||||||
|
|
||||||
|
SRSLTE_API uint32_t srslte_pusch_nr_tx_info(const srslte_pusch_nr_t* q,
|
||||||
|
const srslte_sch_cfg_nr_t* cfg,
|
||||||
|
const srslte_sch_grant_nr_t* grant,
|
||||||
|
char* str,
|
||||||
|
uint32_t str_len);
|
||||||
|
|
||||||
|
#endif // SRSLTE_PUSCH_NR_H
|
@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2020 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* @file ue_dl_nr.h
|
||||||
|
*
|
||||||
|
* Description: NR UE uplink physical layer procedures for data
|
||||||
|
*
|
||||||
|
* This module is a frontend to all the uplink data channel processing modules.
|
||||||
|
*
|
||||||
|
* Reference:
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef SRSLTE_UE_UL_DATA_H
|
||||||
|
#define SRSLTE_UE_UL_DATA_H
|
||||||
|
|
||||||
|
#include "srslte/phy/common/phy_common_nr.h"
|
||||||
|
#include "srslte/phy/phch/phch_cfg_nr.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates the PUSCH time resource default A and stores it in the provided PUSCH NR grant.
|
||||||
|
*
|
||||||
|
* @remark Defined by TS 38.214 V15.10.0 Table 6.1.2.1.1-2: Default PUSCH time domain resource allocation A for normal
|
||||||
|
* CP
|
||||||
|
*
|
||||||
|
* @param m Time domain resource assignment field value m of the DCI
|
||||||
|
* @param[out] grant PUSCH grant
|
||||||
|
* @return Returns SRSLTE_SUCCESS if the provided allocation is valid, otherwise it returns SRSLTE_ERROR code
|
||||||
|
*/
|
||||||
|
SRSLTE_API int srslte_ue_ul_nr_pdsch_time_resource_default_A(uint32_t m, srslte_sch_grant_nr_t* grant);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates the number of PUSCH-DMRS CDM groups without data for DCI format 0_0
|
||||||
|
*
|
||||||
|
* @remark Defined by TS 38.214 V15.10.0 6.2.2 UE DM-RS transmission procedure
|
||||||
|
*
|
||||||
|
* @param cfg PUSCH NR configuration by upper layers
|
||||||
|
* @param[out] grant Provides grant pointer to fill
|
||||||
|
* @return Returns SRSLTE_SUCCESS if the provided data is valid, otherwise it returns SRSLTE_ERROR code
|
||||||
|
*/
|
||||||
|
SRSLTE_API int srslte_ue_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(const srslte_sch_cfg_nr_t* cfg,
|
||||||
|
srslte_sch_grant_nr_t* grant);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // SRSLTE_UE_UL_DATA_H
|
@ -0,0 +1,769 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2020 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "srslte/phy/phch/pusch_nr.h"
|
||||||
|
#include "srslte/phy/common/phy_common_nr.h"
|
||||||
|
#include "srslte/phy/phch/ra_nr.h"
|
||||||
|
|
||||||
|
int pusch_nr_init_common(srslte_pusch_nr_t* q, const srslte_pusch_nr_args_t* args)
|
||||||
|
{
|
||||||
|
for (srslte_mod_t mod = SRSLTE_MOD_BPSK; mod < SRSLTE_MOD_NITEMS; mod++) {
|
||||||
|
if (srslte_modem_table_lte(&q->modem_tables[mod], mod) < SRSLTE_SUCCESS) {
|
||||||
|
ERROR("Error initialising modem table for %s\n", srslte_mod_string(mod));
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
if (args->measure_evm) {
|
||||||
|
srslte_modem_table_bytes(&q->modem_tables[mod]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int srslte_pusch_nr_init_enb(srslte_pusch_nr_t* q, const srslte_pusch_nr_args_t* args)
|
||||||
|
{
|
||||||
|
if (q == NULL) {
|
||||||
|
return SRSLTE_ERROR_INVALID_INPUTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pusch_nr_init_common(q, args) < SRSLTE_SUCCESS) {
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srslte_sch_nr_init_tx(&q->sch, &args->sch)) {
|
||||||
|
ERROR("Initialising SCH\n");
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int srslte_pusch_nr_init_ue(srslte_pusch_nr_t* q, const srslte_pusch_nr_args_t* args)
|
||||||
|
{
|
||||||
|
if (q == NULL || args == NULL) {
|
||||||
|
return SRSLTE_ERROR_INVALID_INPUTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pusch_nr_init_common(q, args) < SRSLTE_SUCCESS) {
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srslte_sch_nr_init_rx(&q->sch, &args->sch)) {
|
||||||
|
ERROR("Initialising SCH\n");
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args->measure_evm) {
|
||||||
|
q->evm_buffer = srslte_evm_buffer_alloc(8);
|
||||||
|
if (q->evm_buffer == NULL) {
|
||||||
|
ERROR("Initialising EVM\n");
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
q->meas_time_en = args->measure_time;
|
||||||
|
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int srslte_pusch_nr_set_carrier(srslte_pusch_nr_t* q, const srslte_carrier_nr_t* carrier)
|
||||||
|
{
|
||||||
|
// Set carrier
|
||||||
|
q->carrier = *carrier;
|
||||||
|
|
||||||
|
// Reallocate symbols if necessary
|
||||||
|
if (q->max_layers < carrier->max_mimo_layers || q->max_prb < carrier->nof_prb) {
|
||||||
|
q->max_layers = carrier->max_mimo_layers;
|
||||||
|
q->max_prb = carrier->nof_prb;
|
||||||
|
|
||||||
|
// Free current allocations
|
||||||
|
for (uint32_t i = 0; i < SRSLTE_MAX_LAYERS_NR; i++) {
|
||||||
|
if (q->x[i] != NULL) {
|
||||||
|
free(q->x[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate for new sizes
|
||||||
|
for (uint32_t i = 0; i < q->max_layers; i++) {
|
||||||
|
q->x[i] = srslte_vec_cf_malloc(SRSLTE_SLOT_LEN_RE_NR(q->max_prb));
|
||||||
|
if (q->x[i] == NULL) {
|
||||||
|
ERROR("Malloc");
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate code words according to table 7.3.1.3-1
|
||||||
|
uint32_t max_cw = (q->max_layers > 5) ? 2 : 1;
|
||||||
|
if (q->max_cw < max_cw) {
|
||||||
|
q->max_cw = max_cw;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < max_cw; i++) {
|
||||||
|
if (q->b[i] == NULL) {
|
||||||
|
q->b[i] = srslte_vec_u8_malloc(SRSLTE_SLOT_MAX_NOF_BITS_NR);
|
||||||
|
if (q->b[i] == NULL) {
|
||||||
|
ERROR("Malloc");
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q->d[i] == NULL) {
|
||||||
|
q->d[i] = srslte_vec_cf_malloc(SRSLTE_SLOT_MAX_LEN_RE_NR);
|
||||||
|
if (q->d[i] == NULL) {
|
||||||
|
ERROR("Malloc");
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set carrier in SCH
|
||||||
|
if (srslte_sch_nr_set_carrier(&q->sch, carrier) < SRSLTE_SUCCESS) {
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q->evm_buffer != NULL) {
|
||||||
|
srslte_evm_buffer_resize(q->evm_buffer, SRSLTE_SLOT_LEN_RE_NR(q->max_prb) * SRSLTE_MAX_QM);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void srslte_pusch_nr_free(srslte_pusch_nr_t* q)
|
||||||
|
{
|
||||||
|
if (q == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t cw = 0; cw < SRSLTE_MAX_CODEWORDS; cw++) {
|
||||||
|
if (q->b[cw]) {
|
||||||
|
free(q->b[cw]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q->d[cw]) {
|
||||||
|
free(q->d[cw]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
srslte_sch_nr_free(&q->sch);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < SRSLTE_MAX_LAYERS_NR; i++) {
|
||||||
|
if (q->x[i]) {
|
||||||
|
free(q->x[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (srslte_mod_t mod = SRSLTE_MOD_BPSK; mod < SRSLTE_MOD_NITEMS; mod++) {
|
||||||
|
srslte_modem_table_free(&q->modem_tables[mod]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q->evm_buffer != NULL) {
|
||||||
|
srslte_evm_free(q->evm_buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief copies a number of countiguous Resource Elements
|
||||||
|
* @param sf_symbols slot symbols in frequency domain
|
||||||
|
* @param symbols resource elements
|
||||||
|
* @param count number of resource elements to copy
|
||||||
|
* @param put Direction, symbols are copied into sf_symbols if put is true, otherwise sf_symbols are copied into symbols
|
||||||
|
*/
|
||||||
|
static void srslte_pusch_re_cp(cf_t* sf_symbols, cf_t* symbols, uint32_t count, bool put)
|
||||||
|
{
|
||||||
|
if (put) {
|
||||||
|
srslte_vec_cf_copy(sf_symbols, symbols, count);
|
||||||
|
} else {
|
||||||
|
srslte_vec_cf_copy(symbols, sf_symbols, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* As a RB is 12 RE wide, positions marked as 1 will be used for the 1st CDM group, and the same with group 2:
|
||||||
|
*
|
||||||
|
* +---+---+---+---+---+---+---+---+---+---+---+---+
|
||||||
|
* | 1 | 1 | 2 | 2 | 1 | 1 | 2 | 2 | 1 | 1 | 2 | 2 |
|
||||||
|
* +---+---+---+---+---+---+---+---+---+---+---+---+
|
||||||
|
* -- k -->
|
||||||
|
*
|
||||||
|
* If the number of DMRS CDM groups without data is set to:
|
||||||
|
* - 1, data is mapped in RE marked as 2
|
||||||
|
* - Otherwise, no data is mapped in this symbol
|
||||||
|
*/
|
||||||
|
static uint32_t srslte_pusch_nr_cp_dmrs_type1(const srslte_pusch_nr_t* q,
|
||||||
|
const srslte_sch_grant_nr_t* grant,
|
||||||
|
cf_t* symbols,
|
||||||
|
cf_t* sf_symbols,
|
||||||
|
bool put)
|
||||||
|
{
|
||||||
|
uint32_t count = 0;
|
||||||
|
uint32_t delta = 0;
|
||||||
|
|
||||||
|
if (grant->nof_dmrs_cdm_groups_without_data != 1) {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < q->carrier.nof_prb; i++) {
|
||||||
|
if (grant->prb_idx[i]) {
|
||||||
|
for (uint32_t j = 0; j < SRSLTE_NRE; j += 2) {
|
||||||
|
if (put) {
|
||||||
|
sf_symbols[i * SRSLTE_NRE + delta + j + 1] = symbols[count++];
|
||||||
|
} else {
|
||||||
|
symbols[count++] = sf_symbols[i * SRSLTE_NRE + delta + j + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* As a RB is 12 RE wide, positions marked as 1 will be used for the 1st CDM group, and the same with groups 2 and 3:
|
||||||
|
*
|
||||||
|
* +---+---+---+---+---+---+---+---+---+---+---+---+
|
||||||
|
* | 1 | 1 | 2 | 2 | 3 | 3 | 1 | 1 | 2 | 2 | 3 | 3 |
|
||||||
|
* +---+---+---+---+---+---+---+---+---+---+---+---+
|
||||||
|
* -- k -->
|
||||||
|
*
|
||||||
|
* If the number of DMRS CDM groups without data is set to:
|
||||||
|
* - 1, data is mapped in RE marked as 2 and 3
|
||||||
|
* - 2, data is mapped in RE marked as 3
|
||||||
|
* - otherwise, no data is mapped in this symbol
|
||||||
|
*/
|
||||||
|
static uint32_t srslte_pusch_nr_cp_dmrs_type2(const srslte_pusch_nr_t* q,
|
||||||
|
const srslte_sch_grant_nr_t* grant,
|
||||||
|
cf_t* symbols,
|
||||||
|
cf_t* sf_symbols,
|
||||||
|
bool put)
|
||||||
|
{
|
||||||
|
uint32_t count = 0;
|
||||||
|
|
||||||
|
if (grant->nof_dmrs_cdm_groups_without_data != 1 && grant->nof_dmrs_cdm_groups_without_data != 2) {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t re_offset = (grant->nof_dmrs_cdm_groups_without_data == 1) ? 2 : 4;
|
||||||
|
uint32_t re_count = (grant->nof_dmrs_cdm_groups_without_data == 1) ? 4 : 2;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < q->carrier.nof_prb; i++) {
|
||||||
|
if (grant->prb_idx[i]) {
|
||||||
|
// Copy RE between pilot pairs
|
||||||
|
srslte_pusch_re_cp(&sf_symbols[i * SRSLTE_NRE + re_offset], &symbols[count], re_count, put);
|
||||||
|
count += re_count;
|
||||||
|
|
||||||
|
// Copy RE after second pilot
|
||||||
|
srslte_pusch_re_cp(&sf_symbols[(i + 1) * SRSLTE_NRE - re_count], &symbols[count], re_count, put);
|
||||||
|
count += re_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t srslte_pusch_nr_cp_dmrs(const srslte_pusch_nr_t* q,
|
||||||
|
const srslte_sch_cfg_nr_t* cfg,
|
||||||
|
const srslte_sch_grant_nr_t* grant,
|
||||||
|
cf_t* symbols,
|
||||||
|
cf_t* sf_symbols,
|
||||||
|
bool put)
|
||||||
|
{
|
||||||
|
uint32_t count = 0;
|
||||||
|
|
||||||
|
const srslte_dmrs_sch_cfg_t* dmrs_cfg =
|
||||||
|
grant->mapping == srslte_sch_mapping_type_A ? &cfg->dmrs_typeA : &cfg->dmrs_typeB;
|
||||||
|
|
||||||
|
switch (dmrs_cfg->type) {
|
||||||
|
case srslte_dmrs_sch_type_1:
|
||||||
|
count = srslte_pusch_nr_cp_dmrs_type1(q, grant, symbols, sf_symbols, put);
|
||||||
|
break;
|
||||||
|
case srslte_dmrs_sch_type_2:
|
||||||
|
count = srslte_pusch_nr_cp_dmrs_type2(q, grant, symbols, sf_symbols, put);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t srslte_pusch_nr_cp_clean(const srslte_pusch_nr_t* q,
|
||||||
|
const srslte_sch_grant_nr_t* grant,
|
||||||
|
cf_t* symbols,
|
||||||
|
cf_t* sf_symbols,
|
||||||
|
bool put)
|
||||||
|
{
|
||||||
|
uint32_t count = 0;
|
||||||
|
uint32_t start = 0; // Index of the start of continuous data
|
||||||
|
uint32_t length = 0; // End of continuous RE
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < q->carrier.nof_prb; i++) {
|
||||||
|
if (grant->prb_idx[i]) {
|
||||||
|
// If fist continuous block, save start
|
||||||
|
if (length == 0) {
|
||||||
|
start = i * SRSLTE_NRE;
|
||||||
|
}
|
||||||
|
length += SRSLTE_NRE;
|
||||||
|
} else {
|
||||||
|
// Consecutive block is finished
|
||||||
|
if (put) {
|
||||||
|
srslte_vec_cf_copy(&sf_symbols[start], &symbols[count], length);
|
||||||
|
} else {
|
||||||
|
srslte_vec_cf_copy(&symbols[count], &sf_symbols[start], length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increase RE count
|
||||||
|
count += length;
|
||||||
|
|
||||||
|
// Reset consecutive block
|
||||||
|
length = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy last contiguous block
|
||||||
|
if (length > 0) {
|
||||||
|
if (put) {
|
||||||
|
srslte_vec_cf_copy(&sf_symbols[start], &symbols[count], length);
|
||||||
|
} else {
|
||||||
|
srslte_vec_cf_copy(&symbols[count], &sf_symbols[start], length);
|
||||||
|
}
|
||||||
|
count += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int srslte_pusch_nr_cp(const srslte_pusch_nr_t* q,
|
||||||
|
const srslte_sch_cfg_nr_t* cfg,
|
||||||
|
const srslte_sch_grant_nr_t* grant,
|
||||||
|
cf_t* symbols,
|
||||||
|
cf_t* sf_symbols,
|
||||||
|
bool put)
|
||||||
|
{
|
||||||
|
uint32_t count = 0;
|
||||||
|
uint32_t dmrs_l_idx[SRSLTE_DMRS_SCH_MAX_SYMBOLS] = {};
|
||||||
|
uint32_t dmrs_l_count = 0;
|
||||||
|
|
||||||
|
// Get symbol indexes carrying DMRS
|
||||||
|
int32_t nof_dmrs_symbols = srslte_dmrs_sch_get_symbols_idx(cfg, grant, dmrs_l_idx);
|
||||||
|
if (nof_dmrs_symbols < SRSLTE_SUCCESS) {
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SRSLTE_DEBUG_ENABLED && srslte_verbose >= SRSLTE_VERBOSE_DEBUG && !handler_registered) {
|
||||||
|
DEBUG("dmrs_l_idx=");
|
||||||
|
srslte_vec_fprint_i(stdout, (int32_t*)dmrs_l_idx, nof_dmrs_symbols);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t l = grant->S; l < grant->S + grant->L; l++) {
|
||||||
|
// Advance DMRS symbol counter until:
|
||||||
|
// - the current DMRS symbol index is greater or equal than current symbol l
|
||||||
|
// - no more DMRS symbols
|
||||||
|
while (dmrs_l_idx[dmrs_l_count] < l && dmrs_l_count < nof_dmrs_symbols) {
|
||||||
|
dmrs_l_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (l == dmrs_l_idx[dmrs_l_count]) {
|
||||||
|
count += srslte_pusch_nr_cp_dmrs(
|
||||||
|
q, cfg, grant, &symbols[count], &sf_symbols[l * q->carrier.nof_prb * SRSLTE_NRE], put);
|
||||||
|
} else {
|
||||||
|
count +=
|
||||||
|
srslte_pusch_nr_cp_clean(q, grant, &symbols[count], &sf_symbols[l * q->carrier.nof_prb * SRSLTE_NRE], put);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int srslte_pusch_nr_put(const srslte_pusch_nr_t* q,
|
||||||
|
const srslte_sch_cfg_nr_t* cfg,
|
||||||
|
const srslte_sch_grant_nr_t* grant,
|
||||||
|
cf_t* symbols,
|
||||||
|
cf_t* sf_symbols)
|
||||||
|
{
|
||||||
|
return srslte_pusch_nr_cp(q, cfg, grant, symbols, sf_symbols, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int srslte_pusch_nr_get(const srslte_pusch_nr_t* q,
|
||||||
|
const srslte_sch_cfg_nr_t* cfg,
|
||||||
|
const srslte_sch_grant_nr_t* grant,
|
||||||
|
cf_t* symbols,
|
||||||
|
cf_t* sf_symbols)
|
||||||
|
{
|
||||||
|
return srslte_pusch_nr_cp(q, cfg, grant, symbols, sf_symbols, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
pusch_nr_cinit(const srslte_carrier_nr_t* carrier, const srslte_sch_cfg_nr_t* cfg, uint16_t rnti, uint32_t cw_idx)
|
||||||
|
{
|
||||||
|
uint32_t n_id = carrier->id;
|
||||||
|
if (cfg->scrambling_id_present && SRSLTE_RNTI_ISUSER(rnti)) {
|
||||||
|
n_id = cfg->scambling_id;
|
||||||
|
}
|
||||||
|
uint32_t cinit = (((uint32_t)rnti) << 15U) + (cw_idx << 14U) + n_id;
|
||||||
|
|
||||||
|
INFO("PUSCH: RNTI=%d (0x%x); nid=%d; cinit=%d (0x%x);\n", rnti, rnti, n_id, cinit, cinit);
|
||||||
|
|
||||||
|
return cinit;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int pusch_nr_encode_codeword(srslte_pusch_nr_t* q,
|
||||||
|
const srslte_sch_cfg_nr_t* cfg,
|
||||||
|
const srslte_sch_tb_t* tb,
|
||||||
|
const uint8_t* data,
|
||||||
|
uint16_t rnti)
|
||||||
|
{
|
||||||
|
// Early return if TB is not enabled
|
||||||
|
if (!tb->enabled) {
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check codeword index
|
||||||
|
if (tb->cw_idx >= q->max_cw) {
|
||||||
|
ERROR("Unsupported codeword index %d\n", tb->cw_idx);
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check modulation
|
||||||
|
if (tb->mod >= SRSLTE_MOD_NITEMS) {
|
||||||
|
ERROR("Invalid modulation %s\n", srslte_mod_string(tb->mod));
|
||||||
|
return SRSLTE_ERROR_OUT_OF_BOUNDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode SCH
|
||||||
|
if (srslte_ulsch_nr_encode(&q->sch, &cfg->sch_cfg, tb, data, q->b[tb->cw_idx]) < SRSLTE_SUCCESS) {
|
||||||
|
ERROR("Error in DL-SCH encoding\n");
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SRSLTE_DEBUG_ENABLED && srslte_verbose >= SRSLTE_VERBOSE_DEBUG && !handler_registered) {
|
||||||
|
DEBUG("b=");
|
||||||
|
srslte_vec_fprint_b(stdout, q->b[tb->cw_idx], tb->nof_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.3.1.1 Scrambling
|
||||||
|
uint32_t cinit = pusch_nr_cinit(&q->carrier, cfg, rnti, tb->cw_idx);
|
||||||
|
srslte_sequence_apply_bit(q->b[tb->cw_idx], q->b[tb->cw_idx], tb->nof_bits, cinit);
|
||||||
|
|
||||||
|
// 7.3.1.2 Modulation
|
||||||
|
srslte_mod_modulate(&q->modem_tables[tb->mod], q->b[tb->cw_idx], q->d[tb->cw_idx], tb->nof_bits);
|
||||||
|
|
||||||
|
if (SRSLTE_DEBUG_ENABLED && srslte_verbose >= SRSLTE_VERBOSE_DEBUG && !handler_registered) {
|
||||||
|
DEBUG("d=");
|
||||||
|
srslte_vec_fprint_c(stdout, q->d[tb->cw_idx], tb->nof_re);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int srslte_pusch_nr_encode(srslte_pusch_nr_t* q,
|
||||||
|
const srslte_sch_cfg_nr_t* cfg,
|
||||||
|
const srslte_sch_grant_nr_t* grant,
|
||||||
|
uint8_t* data[SRSLTE_MAX_TB],
|
||||||
|
cf_t* sf_symbols[SRSLTE_MAX_PORTS])
|
||||||
|
{
|
||||||
|
|
||||||
|
// Check input pointers
|
||||||
|
if (!q || !cfg || !grant || !data || !sf_symbols) {
|
||||||
|
return SRSLTE_ERROR_INVALID_INPUTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval t[3];
|
||||||
|
if (q->meas_time_en) {
|
||||||
|
gettimeofday(&t[1], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check number of layers
|
||||||
|
if (q->max_layers < grant->nof_layers) {
|
||||||
|
ERROR("Error number of layers (%d) exceeds configured maximum (%d)\n", grant->nof_layers, q->max_layers);
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.3.1.1 and 7.3.1.2
|
||||||
|
uint32_t nof_cw = 0;
|
||||||
|
for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) {
|
||||||
|
nof_cw += grant->tb[tb].enabled ? 1 : 0;
|
||||||
|
|
||||||
|
if (pusch_nr_encode_codeword(q, cfg, &grant->tb[tb], data[tb], grant->rnti) < SRSLTE_SUCCESS) {
|
||||||
|
ERROR("Error encoding TB %d\n", tb);
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.3.1.3 Layer mapping
|
||||||
|
cf_t** x = q->d;
|
||||||
|
if (grant->nof_layers > 1) {
|
||||||
|
x = q->x;
|
||||||
|
srslte_layermap_nr(q->d, nof_cw, x, grant->nof_layers, grant->nof_layers);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.3.1.4 Antenna port mapping
|
||||||
|
// ... Not implemented
|
||||||
|
|
||||||
|
// 7.3.1.5 Mapping to virtual resource blocks
|
||||||
|
// ... Not implemented
|
||||||
|
|
||||||
|
// 7.3.1.6 Mapping from virtual to physical resource blocks
|
||||||
|
int n = srslte_pusch_nr_put(q, cfg, grant, x[0], sf_symbols[0]);
|
||||||
|
if (n < SRSLTE_SUCCESS) {
|
||||||
|
ERROR("Putting NR PUSCH resources\n");
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n != grant->tb[0].nof_re) {
|
||||||
|
ERROR("Unmatched number of RE (%d != %d)\n", n, grant->tb[0].nof_re);
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q->meas_time_en) {
|
||||||
|
gettimeofday(&t[2], NULL);
|
||||||
|
get_time_interval(t);
|
||||||
|
q->meas_time_us = (uint32_t)t[0].tv_usec;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int pusch_nr_decode_codeword(srslte_pusch_nr_t* q,
|
||||||
|
const srslte_sch_cfg_nr_t* cfg,
|
||||||
|
const srslte_sch_tb_t* tb,
|
||||||
|
srslte_pusch_res_nr_t* res,
|
||||||
|
uint16_t rnti)
|
||||||
|
{
|
||||||
|
// Early return if TB is not enabled
|
||||||
|
if (!tb->enabled) {
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check codeword index
|
||||||
|
if (tb->cw_idx >= q->max_cw) {
|
||||||
|
ERROR("Unsupported codeword index %d\n", tb->cw_idx);
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check modulation
|
||||||
|
if (tb->mod >= SRSLTE_MOD_NITEMS) {
|
||||||
|
ERROR("Invalid modulation %s\n", srslte_mod_string(tb->mod));
|
||||||
|
return SRSLTE_ERROR_OUT_OF_BOUNDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SRSLTE_DEBUG_ENABLED && srslte_verbose >= SRSLTE_VERBOSE_DEBUG && !handler_registered) {
|
||||||
|
DEBUG("d=");
|
||||||
|
srslte_vec_fprint_c(stdout, q->d[tb->cw_idx], tb->nof_re);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Demodulation
|
||||||
|
int8_t* llr = (int8_t*)q->b[tb->cw_idx];
|
||||||
|
if (srslte_demod_soft_demodulate_b(tb->mod, q->d[tb->cw_idx], llr, tb->nof_re)) {
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EVM
|
||||||
|
if (q->evm_buffer != NULL) {
|
||||||
|
res->evm = srslte_evm_run_b(q->evm_buffer, &q->modem_tables[tb->mod], q->d[tb->cw_idx], llr, tb->nof_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change LLR sign
|
||||||
|
for (uint32_t i = 0; i < tb->nof_bits; i++) {
|
||||||
|
llr[i] = -llr[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Descrambling
|
||||||
|
srslte_sequence_apply_c(llr, llr, tb->nof_bits, pusch_nr_cinit(&q->carrier, cfg, rnti, tb->cw_idx));
|
||||||
|
|
||||||
|
if (SRSLTE_DEBUG_ENABLED && srslte_verbose >= SRSLTE_VERBOSE_DEBUG && !handler_registered) {
|
||||||
|
DEBUG("b=");
|
||||||
|
srslte_vec_fprint_b(stdout, q->b[tb->cw_idx], tb->nof_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode SCH
|
||||||
|
if (srslte_ulsch_nr_decode(&q->sch, &cfg->sch_cfg, tb, llr, res->payload, &res->crc) < SRSLTE_SUCCESS) {
|
||||||
|
ERROR("Error in DL-SCH encoding\n");
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int srslte_pusch_nr_decode(srslte_pusch_nr_t* q,
|
||||||
|
const srslte_sch_cfg_nr_t* cfg,
|
||||||
|
const srslte_sch_grant_nr_t* grant,
|
||||||
|
srslte_chest_dl_res_t* channel,
|
||||||
|
cf_t* sf_symbols[SRSLTE_MAX_PORTS],
|
||||||
|
srslte_pusch_res_nr_t data[SRSLTE_MAX_TB])
|
||||||
|
{
|
||||||
|
// Check input pointers
|
||||||
|
if (!q || !cfg || !grant || !data || !sf_symbols) {
|
||||||
|
return SRSLTE_ERROR_INVALID_INPUTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval t[3];
|
||||||
|
if (q->meas_time_en) {
|
||||||
|
gettimeofday(&t[1], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t nof_cw = 0;
|
||||||
|
for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) {
|
||||||
|
nof_cw += grant->tb[tb].enabled ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t nof_re = srslte_ra_dl_nr_slot_nof_re(cfg, grant);
|
||||||
|
|
||||||
|
if (channel->nof_re != nof_re) {
|
||||||
|
ERROR("Inconsistent number of RE (%d!=%d)\n", channel->nof_re, nof_re);
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Demapping from virtual to physical resource blocks
|
||||||
|
uint32_t nof_re_get = srslte_pusch_nr_get(q, cfg, grant, q->x[0], sf_symbols[0]);
|
||||||
|
if (nof_re_get != nof_re) {
|
||||||
|
ERROR("Inconsistent number of RE (%d!=%d)\n", nof_re_get, nof_re);
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SRSLTE_DEBUG_ENABLED && srslte_verbose >= SRSLTE_VERBOSE_DEBUG && !handler_registered) {
|
||||||
|
DEBUG("ce=");
|
||||||
|
srslte_vec_fprint_c(stdout, channel->ce[0][0], nof_re);
|
||||||
|
DEBUG("x=");
|
||||||
|
srslte_vec_fprint_c(stdout, q->x[0], nof_re);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Demapping to virtual resource blocks
|
||||||
|
// ... Not implemented
|
||||||
|
|
||||||
|
// Antenna port demapping
|
||||||
|
// ... Not implemented
|
||||||
|
srslte_predecoding_type(
|
||||||
|
q->x, channel->ce, q->d, NULL, 1, 1, 1, 0, nof_re, SRSLTE_TXSCHEME_PORT0, 1.0f, channel->noise_estimate);
|
||||||
|
|
||||||
|
// Layer demapping
|
||||||
|
if (grant->nof_layers > 1) {
|
||||||
|
srslte_layerdemap_nr(q->d, nof_cw, q->x, grant->nof_layers, nof_re);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SCH decode
|
||||||
|
for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) {
|
||||||
|
nof_cw += grant->tb[tb].enabled ? 1 : 0;
|
||||||
|
|
||||||
|
if (pusch_nr_decode_codeword(q, cfg, &grant->tb[tb], &data[tb], grant->rnti) < SRSLTE_SUCCESS) {
|
||||||
|
ERROR("Error encoding TB %d\n", tb);
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q->meas_time_en) {
|
||||||
|
gettimeofday(&t[2], NULL);
|
||||||
|
get_time_interval(t);
|
||||||
|
q->meas_time_us = (uint32_t)t[0].tv_usec;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t srslte_pusch_nr_grant_info(const srslte_sch_cfg_nr_t* cfg,
|
||||||
|
const srslte_sch_grant_nr_t* grant,
|
||||||
|
char* str,
|
||||||
|
uint32_t str_len)
|
||||||
|
{
|
||||||
|
uint32_t len = 0;
|
||||||
|
len = srslte_print_check(str, str_len, len, "rnti=0x%x", grant->rnti);
|
||||||
|
|
||||||
|
// Append time-domain resource mapping
|
||||||
|
len = srslte_print_check(str,
|
||||||
|
str_len,
|
||||||
|
len,
|
||||||
|
",k0=%d,S=%d,L=%d,mapping=%s",
|
||||||
|
grant->k0,
|
||||||
|
grant->S,
|
||||||
|
grant->L,
|
||||||
|
srslte_sch_mapping_type_to_str(grant->mapping));
|
||||||
|
|
||||||
|
// Skip frequency domain resources...
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Append spatial resources
|
||||||
|
len = srslte_print_check(str, str_len, len, ",Nl=%d", grant->nof_layers);
|
||||||
|
|
||||||
|
// Append scrambling ID
|
||||||
|
len = srslte_print_check(str, str_len, len, ",n_scid=%d,", grant->n_scid);
|
||||||
|
|
||||||
|
// Append TB info
|
||||||
|
for (uint32_t i = 0; i < SRSLTE_MAX_TB; i++) {
|
||||||
|
len += srslte_sch_nr_tb_info(&grant->tb[i], &str[len], str_len - len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t srslte_pusch_nr_rx_info(const srslte_pusch_nr_t* q,
|
||||||
|
const srslte_sch_cfg_nr_t* cfg,
|
||||||
|
const srslte_sch_grant_nr_t* grant,
|
||||||
|
const srslte_pusch_res_nr_t res[SRSLTE_MAX_CODEWORDS],
|
||||||
|
char* str,
|
||||||
|
uint32_t str_len)
|
||||||
|
{
|
||||||
|
|
||||||
|
uint32_t len = 0;
|
||||||
|
|
||||||
|
len += srslte_pusch_nr_grant_info(cfg, grant, &str[len], str_len - len);
|
||||||
|
|
||||||
|
if (q->evm_buffer != NULL) {
|
||||||
|
len = srslte_print_check(str, str_len, len, ",evm={", 0);
|
||||||
|
for (uint32_t i = 0; i < SRSLTE_MAX_CODEWORDS; i++) {
|
||||||
|
if (grant->tb[i].enabled && !isnan(res[i].evm)) {
|
||||||
|
len = srslte_print_check(str, str_len, len, "%.2f", res[i].evm);
|
||||||
|
if (i < SRSLTE_MAX_CODEWORDS - 1) {
|
||||||
|
if (grant->tb[i + 1].enabled) {
|
||||||
|
len = srslte_print_check(str, str_len, len, ",", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
len = srslte_print_check(str, str_len, len, "}", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res != NULL) {
|
||||||
|
len = srslte_print_check(str, str_len, len, ",crc={", 0);
|
||||||
|
for (uint32_t i = 0; i < SRSLTE_MAX_CODEWORDS; i++) {
|
||||||
|
if (grant->tb[i].enabled) {
|
||||||
|
len = srslte_print_check(str, str_len, len, "%s", res[i].crc ? "OK" : "KO");
|
||||||
|
if (i < SRSLTE_MAX_CODEWORDS - 1) {
|
||||||
|
if (grant->tb[i + 1].enabled) {
|
||||||
|
len = srslte_print_check(str, str_len, len, ",", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
len = srslte_print_check(str, str_len, len, "}", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q->meas_time_en) {
|
||||||
|
len = srslte_print_check(str, str_len, len, ", t=%d us\n", q->meas_time_us);
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t srslte_pusch_nr_tx_info(const srslte_pusch_nr_t* q,
|
||||||
|
const srslte_sch_cfg_nr_t* cfg,
|
||||||
|
const srslte_sch_grant_nr_t* grant,
|
||||||
|
char* str,
|
||||||
|
uint32_t str_len)
|
||||||
|
{
|
||||||
|
|
||||||
|
uint32_t len = 0;
|
||||||
|
|
||||||
|
len += srslte_pusch_nr_grant_info(cfg, grant, &str[len], str_len - len);
|
||||||
|
|
||||||
|
if (q->meas_time_en) {
|
||||||
|
len = srslte_print_check(str, str_len, len, ", t=%d us\n", q->meas_time_us);
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
@ -0,0 +1,301 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2020 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srslte/phy/phch/pusch_nr.h"
|
||||||
|
#include "srslte/phy/phch/ra_nr.h"
|
||||||
|
#include "srslte/phy/ue/ue_ul_nr_data.h"
|
||||||
|
#include "srslte/phy/utils/debug.h"
|
||||||
|
#include "srslte/phy/utils/random.h"
|
||||||
|
#include "srslte/phy/utils/vector.h"
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
static srslte_carrier_nr_t carrier = {
|
||||||
|
1, // cell_id
|
||||||
|
0, // numerology
|
||||||
|
SRSLTE_MAX_PRB_NR, // nof_prb
|
||||||
|
0, // start
|
||||||
|
1 // max_mimo_layers
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint32_t n_prb = 0; // Set to 0 for steering
|
||||||
|
static uint32_t mcs = 30; // Set to 30 for steering
|
||||||
|
static srslte_sch_cfg_nr_t pusch_cfg = {};
|
||||||
|
static srslte_sch_grant_nr_t pusch_grant = {};
|
||||||
|
static uint16_t rnti = 0x1234;
|
||||||
|
|
||||||
|
void usage(char* prog)
|
||||||
|
{
|
||||||
|
printf("Usage: %s [pTL] \n", prog);
|
||||||
|
printf("\t-p Number of grant PRB, set to 0 for steering [Default %d]\n", n_prb);
|
||||||
|
printf("\t-m MCS PRB, set to >28 for steering [Default %d]\n", mcs);
|
||||||
|
printf("\t-T Provide MCS table (64qam, 256qam, 64qamLowSE) [Default %s]\n",
|
||||||
|
srslte_mcs_table_to_str(pusch_cfg.sch_cfg.mcs_table));
|
||||||
|
printf("\t-L Provide number of layers [Default %d]\n", carrier.max_mimo_layers);
|
||||||
|
printf("\t-v [set srslte_verbose to debug, default none]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_args(int argc, char** argv)
|
||||||
|
{
|
||||||
|
int opt;
|
||||||
|
while ((opt = getopt(argc, argv, "pmTLv")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'p':
|
||||||
|
n_prb = (uint32_t)strtol(argv[optind], NULL, 10);
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
mcs = (uint32_t)strtol(argv[optind], NULL, 10);
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
pusch_cfg.sch_cfg.mcs_table = srslte_mcs_table_from_str(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
carrier.max_mimo_layers = (uint32_t)strtol(argv[optind], NULL, 10);
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
srslte_verbose++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage(argv[0]);
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
int ret = SRSLTE_ERROR;
|
||||||
|
srslte_pusch_nr_t pusch_tx = {};
|
||||||
|
srslte_pusch_nr_t pusch_rx = {};
|
||||||
|
srslte_chest_dl_res_t chest = {};
|
||||||
|
srslte_pusch_res_nr_t pusch_res[SRSLTE_MAX_TB] = {};
|
||||||
|
srslte_random_t rand_gen = srslte_random_init(1234);
|
||||||
|
|
||||||
|
uint8_t* data_tx[SRSLTE_MAX_TB] = {};
|
||||||
|
uint8_t* data_rx[SRSLTE_MAX_CODEWORDS] = {};
|
||||||
|
cf_t* sf_symbols[SRSLTE_MAX_LAYERS_NR] = {};
|
||||||
|
|
||||||
|
// Set default PUSCH configuration
|
||||||
|
pusch_cfg.sch_cfg.mcs_table = srslte_mcs_table_64qam;
|
||||||
|
|
||||||
|
if (parse_args(argc, argv) < SRSLTE_SUCCESS) {
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
srslte_pusch_nr_args_t pusch_args = {};
|
||||||
|
pusch_args.sch.disable_simd = false;
|
||||||
|
pusch_args.measure_evm = true;
|
||||||
|
|
||||||
|
if (srslte_pusch_nr_init_enb(&pusch_tx, &pusch_args) < SRSLTE_SUCCESS) {
|
||||||
|
ERROR("Error initiating PUSCH for Tx\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srslte_pusch_nr_init_ue(&pusch_rx, &pusch_args) < SRSLTE_SUCCESS) {
|
||||||
|
ERROR("Error initiating SCH NR for Rx\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srslte_pusch_nr_set_carrier(&pusch_tx, &carrier)) {
|
||||||
|
ERROR("Error setting SCH NR carrier\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srslte_pusch_nr_set_carrier(&pusch_rx, &carrier)) {
|
||||||
|
ERROR("Error setting SCH NR carrier\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < carrier.max_mimo_layers; i++) {
|
||||||
|
sf_symbols[i] = srslte_vec_cf_malloc(SRSLTE_SLOT_LEN_RE_NR(carrier.nof_prb));
|
||||||
|
if (sf_symbols[i] == NULL) {
|
||||||
|
ERROR("Error malloc\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < pusch_tx.max_cw; i++) {
|
||||||
|
data_tx[i] = srslte_vec_u8_malloc(SRSLTE_SLOT_MAX_NOF_BITS_NR);
|
||||||
|
data_rx[i] = srslte_vec_u8_malloc(SRSLTE_SLOT_MAX_NOF_BITS_NR);
|
||||||
|
if (data_tx[i] == NULL || data_rx[i] == NULL) {
|
||||||
|
ERROR("Error malloc\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
pusch_res[i].payload = data_rx[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
srslte_softbuffer_tx_t softbuffer_tx = {};
|
||||||
|
srslte_softbuffer_rx_t softbuffer_rx = {};
|
||||||
|
|
||||||
|
if (srslte_softbuffer_tx_init_guru(&softbuffer_tx, SRSLTE_SCH_NR_MAX_NOF_CB_LDPC, SRSLTE_LDPC_MAX_LEN_ENCODED_CB) <
|
||||||
|
SRSLTE_SUCCESS) {
|
||||||
|
ERROR("Error init soft-buffer\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srslte_softbuffer_rx_init_guru(&softbuffer_rx, SRSLTE_SCH_NR_MAX_NOF_CB_LDPC, SRSLTE_LDPC_MAX_LEN_ENCODED_CB) <
|
||||||
|
SRSLTE_SUCCESS) {
|
||||||
|
ERROR("Error init soft-buffer\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use grant default A time resources with m=0
|
||||||
|
if (srslte_ue_ul_nr_pdsch_time_resource_default_A(0, &pusch_grant) < SRSLTE_SUCCESS) {
|
||||||
|
ERROR("Error loading default grant\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load number of DMRS CDM groups without data
|
||||||
|
if (srslte_ue_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(&pusch_cfg, &pusch_grant) < SRSLTE_SUCCESS) {
|
||||||
|
ERROR("Error loading number of DMRS CDM groups without data\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
pusch_grant.nof_layers = carrier.max_mimo_layers;
|
||||||
|
pusch_grant.dci_format = srslte_dci_format_nr_1_0;
|
||||||
|
pusch_grant.rnti = rnti;
|
||||||
|
|
||||||
|
uint32_t n_prb_start = 1;
|
||||||
|
uint32_t n_prb_end = carrier.nof_prb + 1;
|
||||||
|
if (n_prb > 0) {
|
||||||
|
n_prb_start = SRSLTE_MIN(n_prb, n_prb_end - 1);
|
||||||
|
n_prb_end = SRSLTE_MIN(n_prb + 1, n_prb_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t mcs_start = 0;
|
||||||
|
uint32_t mcs_end = pusch_cfg.sch_cfg.mcs_table == srslte_mcs_table_256qam ? 28 : 29;
|
||||||
|
if (mcs < mcs_end) {
|
||||||
|
mcs_start = SRSLTE_MIN(mcs, mcs_end - 1);
|
||||||
|
mcs_end = SRSLTE_MIN(mcs + 1, mcs_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srslte_chest_dl_res_init(&chest, carrier.nof_prb) < SRSLTE_SUCCESS) {
|
||||||
|
ERROR("Initiating chest\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (n_prb = n_prb_start; n_prb < n_prb_end; n_prb++) {
|
||||||
|
for (mcs = mcs_start; mcs < mcs_end; mcs++) {
|
||||||
|
|
||||||
|
for (uint32_t n = 0; n < SRSLTE_MAX_PRB_NR; n++) {
|
||||||
|
pusch_grant.prb_idx[n] = (n < n_prb);
|
||||||
|
}
|
||||||
|
|
||||||
|
pusch_grant.dci_format = srslte_dci_format_nr_0_0;
|
||||||
|
if (srslte_ra_nr_fill_tb(&pusch_cfg, &pusch_grant, mcs, &pusch_grant.tb[0]) < SRSLTE_SUCCESS) {
|
||||||
|
ERROR("Error filing tb\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) {
|
||||||
|
// Skip TB if no allocated
|
||||||
|
if (data_tx[tb] == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < pusch_grant.tb[tb].tbs; i++) {
|
||||||
|
data_tx[tb][i] = (uint8_t)srslte_random_uniform_int_dist(rand_gen, 0, UINT8_MAX);
|
||||||
|
}
|
||||||
|
pusch_grant.tb[tb].softbuffer.tx = &softbuffer_tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srslte_pusch_nr_encode(&pusch_tx, &pusch_cfg, &pusch_grant, data_tx, sf_symbols) < SRSLTE_SUCCESS) {
|
||||||
|
ERROR("Error encoding\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) {
|
||||||
|
pusch_grant.tb[tb].softbuffer.rx = &softbuffer_rx;
|
||||||
|
srslte_softbuffer_rx_reset(pusch_grant.tb[tb].softbuffer.rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < pusch_grant.tb->nof_re; i++) {
|
||||||
|
chest.ce[0][0][i] = 1.0f;
|
||||||
|
}
|
||||||
|
chest.nof_re = pusch_grant.tb->nof_re;
|
||||||
|
|
||||||
|
if (srslte_pusch_nr_decode(&pusch_rx, &pusch_cfg, &pusch_grant, &chest, sf_symbols, pusch_res) < SRSLTE_SUCCESS) {
|
||||||
|
ERROR("Error encoding\n");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pusch_res->evm > 0.001f) {
|
||||||
|
ERROR("Error PUSCH EVM is too high %f\n", pusch_res->evm);
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
float mse = 0.0f;
|
||||||
|
uint32_t nof_re = srslte_ra_dl_nr_slot_nof_re(&pusch_cfg, &pusch_grant);
|
||||||
|
for (uint32_t i = 0; i < pusch_grant.nof_layers; i++) {
|
||||||
|
for (uint32_t j = 0; j < nof_re; j++) {
|
||||||
|
mse += cabsf(pusch_tx.d[i][j] - pusch_rx.d[i][j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nof_re * pusch_grant.nof_layers > 0) {
|
||||||
|
mse = mse / (nof_re * pusch_grant.nof_layers);
|
||||||
|
}
|
||||||
|
if (mse > 0.001) {
|
||||||
|
ERROR("MSE error (%f) is too high\n", mse);
|
||||||
|
for (uint32_t i = 0; i < pusch_grant.nof_layers; i++) {
|
||||||
|
printf("d_tx[%d]=", i);
|
||||||
|
srslte_vec_fprint_c(stdout, pusch_tx.d[i], nof_re);
|
||||||
|
printf("d_rx[%d]=", i);
|
||||||
|
srslte_vec_fprint_c(stdout, pusch_rx.d[i], nof_re);
|
||||||
|
}
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pusch_res[0].crc) {
|
||||||
|
ERROR("Failed to match CRC; n_prb=%d; mcs=%d; TBS=%d;\n", n_prb, mcs, pusch_grant.tb[0].tbs);
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(data_tx[0], data_rx[0], pusch_grant.tb[0].tbs / 8) != 0) {
|
||||||
|
ERROR("Failed to match Tx/Rx data; n_prb=%d; mcs=%d; TBS=%d;\n", n_prb, mcs, pusch_grant.tb[0].tbs);
|
||||||
|
printf("Tx data: ");
|
||||||
|
srslte_vec_fprint_byte(stdout, data_tx[0], pusch_grant.tb[0].tbs / 8);
|
||||||
|
printf("Rx data: ");
|
||||||
|
srslte_vec_fprint_byte(stdout, data_rx[0], pusch_grant.tb[0].tbs / 8);
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("n_prb=%d; mcs=%d; TBS=%d; EVM=%f; PASSED!\n", n_prb, mcs, pusch_grant.tb[0].tbs, pusch_res[0].evm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = SRSLTE_SUCCESS;
|
||||||
|
|
||||||
|
clean_exit:
|
||||||
|
srslte_chest_dl_res_free(&chest);
|
||||||
|
srslte_random_free(rand_gen);
|
||||||
|
srslte_pusch_nr_free(&pusch_tx);
|
||||||
|
srslte_pusch_nr_free(&pusch_rx);
|
||||||
|
for (uint32_t i = 0; i < SRSLTE_MAX_CODEWORDS; i++) {
|
||||||
|
if (data_tx[i]) {
|
||||||
|
free(data_tx[i]);
|
||||||
|
}
|
||||||
|
if (data_rx[i]) {
|
||||||
|
free(data_rx[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint32_t i = 0; i < SRSLTE_MAX_LAYERS_NR; i++) {
|
||||||
|
if (sf_symbols[i]) {
|
||||||
|
free(sf_symbols[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
srslte_softbuffer_tx_free(&softbuffer_tx);
|
||||||
|
srslte_softbuffer_rx_free(&softbuffer_rx);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2020 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "srslte/phy/ue/ue_ul_nr_data.h"
|
||||||
|
#include "srslte/phy/utils/debug.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
srslte_sch_mapping_type_t mapping;
|
||||||
|
uint32_t K2;
|
||||||
|
uint32_t S;
|
||||||
|
uint32_t L;
|
||||||
|
} ue_ul_time_resource_t;
|
||||||
|
|
||||||
|
static const ue_ul_time_resource_t ue_ul_default_A_lut[16] = {{srslte_sch_mapping_type_A, 0, 0, 14},
|
||||||
|
{srslte_sch_mapping_type_A, 0, 0, 12},
|
||||||
|
{srslte_sch_mapping_type_A, 0, 0, 10},
|
||||||
|
{srslte_sch_mapping_type_B, 0, 2, 10},
|
||||||
|
{srslte_sch_mapping_type_B, 0, 4, 10},
|
||||||
|
{srslte_sch_mapping_type_B, 0, 4, 8},
|
||||||
|
{srslte_sch_mapping_type_B, 0, 4, 6},
|
||||||
|
{srslte_sch_mapping_type_A, 1, 0, 14},
|
||||||
|
{srslte_sch_mapping_type_A, 1, 0, 12},
|
||||||
|
{srslte_sch_mapping_type_A, 1, 0, 10},
|
||||||
|
{srslte_sch_mapping_type_A, 2, 0, 14},
|
||||||
|
{srslte_sch_mapping_type_A, 2, 0, 12},
|
||||||
|
{srslte_sch_mapping_type_A, 2, 0, 10},
|
||||||
|
{srslte_sch_mapping_type_B, 0, 8, 6},
|
||||||
|
{srslte_sch_mapping_type_A, 3, 0, 14},
|
||||||
|
{srslte_sch_mapping_type_A, 3, 0, 10}};
|
||||||
|
|
||||||
|
int srslte_ue_ul_nr_pdsch_time_resource_default_A(uint32_t m, srslte_sch_grant_nr_t* grant)
|
||||||
|
{
|
||||||
|
if (grant == NULL) {
|
||||||
|
return SRSLTE_ERROR_INVALID_INPUTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m >= 16) {
|
||||||
|
ERROR("m (%d) is out-of-range\n", m);
|
||||||
|
return SRSLTE_ERROR_INVALID_INPUTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select mapping
|
||||||
|
grant->mapping = ue_ul_default_A_lut[m].mapping;
|
||||||
|
grant->k2 = ue_ul_default_A_lut[m].K2;
|
||||||
|
grant->S = ue_ul_default_A_lut[m].S;
|
||||||
|
grant->L = ue_ul_default_A_lut[m].L;
|
||||||
|
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int srslte_ue_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(const srslte_sch_cfg_nr_t* cfg,
|
||||||
|
srslte_sch_grant_nr_t* grant)
|
||||||
|
{
|
||||||
|
if (cfg == NULL || grant == NULL) {
|
||||||
|
return SRSLTE_ERROR_INVALID_INPUTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* According to TS 38.214 V15.10.0 6.2.2 UE DM-RS transmission procedure:
|
||||||
|
* For PUSCH scheduled by DCI format 0_0 or by activation DCI format 0_0 with CRC scrambled by CS-RNTI, the UE
|
||||||
|
* shall assume the number of DM-RS CDM groups without data is 1 which corresponds to CDM group 0 for the case of
|
||||||
|
* PUSCH with allocation duration of 2 or less OFDM symbols with transform precoding disabled, the UE shall assume
|
||||||
|
* that the number of DM-RS CDM groups without data is 3 which corresponds to CDM group {0,1,2} for the case of PUSCH
|
||||||
|
* scheduled by activation DCI format 0_0 and the dmrs-Type in cg-DMRS-Configuration equal to 'type2' and the PUSCH
|
||||||
|
* allocation duration being more than 2 OFDM symbols, and the UE shall assume that the number of DM-RS CDM groups
|
||||||
|
* without data is 2 which corresponds to CDM group {0,1} for all other cases.
|
||||||
|
*/
|
||||||
|
if (grant->L <= 2 && !cfg->enable_transform_precoder) {
|
||||||
|
grant->nof_dmrs_cdm_groups_without_data = 1;
|
||||||
|
// } else if (grant->L > 2 && cfg->dmrs_cg.type == srslte_dmrs_sch_type_2){
|
||||||
|
// grant->nof_dmrs_cdm_groups_without_data = 3;
|
||||||
|
} else {
|
||||||
|
grant->nof_dmrs_cdm_groups_without_data = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
Loading…
Reference in New Issue