Created PDSCH-NR configuration structures and refactored DMRS

master
Xavier Arteaga 4 years ago committed by Xavier Arteaga
parent 009f300c5e
commit 8d34d344a8

@ -27,6 +27,7 @@ extern "C" {
#endif
#include "srslte/phy/common/phy_common_nr.h"
#include "srslte/phy/phch/pdsch_cfg_nr.h"
#include "srslte/srslte.h"
#include <stdint.h>
@ -34,76 +35,122 @@ extern "C" {
#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;
/**
* @brief PDSCH DMRS estimator object
*
* @note The DMRS PDSCH object has to be initialised and the carrier parameters needs to be set.
*
* @see srslte_dmrs_pdsch_init
* @see srslte_dmrs_pdsch_set_carrier
* @see srslte_dmrs_pdsch_free
* @see srslte_dmrs_pdsch_put_sf
* @see srslte_dmrs_pdsch_estimate
*/
typedef struct {
bool is_ue;
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;
srslte_carrier_nr_t carrier;
srslte_interp_lin_t interpolator_type1; /// Type 1 DMRS: 1 pilot every 2 RE
srslte_interp_lin_t interpolator_type2; /// Type 2 DMRS: 2 consecutive pilots every 6 RE
uint32_t max_nof_prb;
cf_t* pilot_estimates; /// Pilots least squares estimates
cf_t* temp; /// Temporal data vector of size SRSLTE_NRE * carrier.nof_prb
} srslte_dmrs_pdsch_t;
/**
* Determines whether the first pilot goes into symbol index 2 or 3
* @brief Computes the symbol indexes carrying DMRS and stores them in symbols_idx
* @param cfg PDSCH configuration that includes DMRS, PDSCH and grant parameters
* @param symbols_idx is the destination pointer where the symbols indexes are stored
* @return It returns the number of symbols if inputs are valid, otherwise, it returns SRSLTE_ERROR code.
*/
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;
SRSLTE_API int srslte_dmrs_pdsch_get_symbols_idx(const srslte_pdsch_cfg_nr_t* pdsch_cfg,
uint32_t symbols_idx[SRSLTE_DMRS_PDSCH_MAX_SYMBOLS]);
/**
* Determines additional symbols if possible to be added
* @brief Computes the sub-carrier indexes carrying DMRS
*
* @param cfg PDSCH DMRS configuration provided by upper layers
* @param max_count is the number of sub-carriers to generate
* @param sc_idx is the destination pointer where the sub-carrier indexes are stored
*
* @return It returns the number of sub-carriers if inputs are valid, otherwise, it returns SRSLTE_ERROR code.
*/
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 struct {
/// Parameters provided by DMRS-DownlinkConfig
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;
uint32_t duration;
/// Parameters provided by PDSCH-TimeDomainResourceAllocation
srslte_pdsch_mapping_type_t mapping_type;
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;
SRSLTE_API int srslte_dmrs_pdsch_get_sc_idx(const srslte_pdsch_dmrs_cfg_t* cfg, uint32_t max_count, uint32_t* sc_idx);
uint32_t symbols_idx[SRSLTE_DMRS_PDSCH_MAX_SYMBOLS];
uint32_t nof_symbols;
uint32_t sc_idx[SRSLTE_NRE];
uint32_t nof_sc;
/**
* @brief Stringifies the PDSCH DMRS configuration
*
* @param cfg PDSCH DMRS configuration
* @param msg Pointer to the destination array
* @param max_len Maximum number of characters to write
*
* @return It returns the number of characters written in the vector if no error occurs, otherwise it returns
* SRSLTE_ERROR code
*/
SRSLTE_API int srslte_dmrs_pdsch_cfg_to_str(const srslte_pdsch_dmrs_cfg_t* cfg, char* msg, uint32_t max_len);
} srslte_dmrs_pdsch_t;
/**
* @brief Initialises DMRS PDSCH object
*
* @param q DMRS PDSCH object
* @param is_ue indicates whethe the object is for a UE (in this case, it shall initialise as an estimator)
* @return it returns SRSLTE_ERROR code if an error occurs, otherwise it returns SRSLTE_SUCCESS
*/
SRSLTE_API int srslte_dmrs_pdsch_init(srslte_dmrs_pdsch_t* q, bool is_ue);
SRSLTE_API int srslte_dmrs_pdsch_cfg_to_str(const srslte_dmrs_pdsch_cfg_t* cfg, char* msg, uint32_t max_len);
/**
* @brief Frees DMRS PDSCH object
*
* @param q DMRS PDSCH object
*/
SRSLTE_API void srslte_dmrs_pdsch_free(srslte_dmrs_pdsch_t* q);
SRSLTE_API int srslte_dmrs_pdsch_init(srslte_dmrs_pdsch_t* q, const srslte_dmrs_pdsch_cfg_t* cfg);
/**
* @brief Sets the carrier configuration. if the PDSCH DMRS object is configured as UE, it will resize internal buffers
* and objects.
*
* @param q DMRS PDSCH object
* @param carrier Carrier configuration
*
* @return it returns SRSLTE_ERROR code if an error occurs, otherwise it returns SRSLTE_SUCCESS
*/
SRSLTE_API int srslte_dmrs_pdsch_set_carrier(srslte_dmrs_pdsch_t* q, const srslte_carrier_nr_t* carrier);
SRSLTE_API int srslte_dmrs_pdsch_put_sf(srslte_dmrs_pdsch_t* q, const srslte_dl_sf_cfg_t* sf, cf_t* sf_symbols);
/**
* @brief Puts PDSCH DMRS into a given resource grid
*
* @param q DMRS PDSCH object
* @param slot_cfg Slot configuration
* @param pdsch_cfg PDSCH transmission configuration
* @param sf_symbols Resource grid
*
* @return it returns SRSLTE_ERROR code if an error occurs, otherwise it returns SRSLTE_SUCCESS
*/
SRSLTE_API int srslte_dmrs_pdsch_put_sf(srslte_dmrs_pdsch_t* q,
const srslte_dl_slot_cfg_t* slot_cfg,
const srslte_pdsch_cfg_nr_t* pdsch_cfg,
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);
/**
* @brief Estimates the channel for PDSCH from the DMRS
*
* @attention Current implementation is thought for type1 PDSCH DMRS (1 pilot every 2 RE)
*
* @param q DMRS-PDSCH object
* @param slot_cfg Slot configuration
* @param sf_symbols Received resource grid
* @param[out] ce Channel estimates
*
* @return it returns SRSLTE_ERROR code if an error occurs, otherwise it returns SRSLTE_SUCCESS
*/
SRSLTE_API int srslte_dmrs_pdsch_estimate(srslte_dmrs_pdsch_t* q,
const srslte_dl_slot_cfg_t* slot_cfg,
const srslte_pdsch_cfg_nr_t* pdsch_cfg,
const cf_t* sf_symbols,
srslte_chest_dl_res_t* chest_res);
#ifdef __cplusplus
}

@ -123,7 +123,7 @@ typedef enum SRSLTE_API {
} srslte_search_space_type_t;
/**
* @brief NR carrier parameters
* @brief NR carrier parameters. It is a combination of fixed cell and bandwidth-part (BWP)
*/
typedef struct {
uint32_t id;
@ -133,18 +133,38 @@ typedef struct {
} srslte_carrier_nr_t;
/**
* CORESET related constants
* @brief NR Slot parameters. It contains parameters that change in a slot basis.
*/
typedef struct {
/// Slot index in the radio frame
uint32_t idx;
/// Left for future parameters
/// ...
} srslte_dl_slot_cfg_t;
/**
* @brief Min number of OFDM symbols in a control resource set.
*/
#define SRSLTE_CORESET_DURATION_MIN 1
/**
* @brief Max number of OFDM symbols in a control resource set. Specified in TS 38.331 V15.10.0 as maxCoReSetDuration
*/
#define SRSLTE_CORESET_DURATION_MAX 3
/**
* @brief Number of possible CORESET frequency resources.
*/
#define SRSLTE_CORESET_FREQ_DOMAIN_RES_SIZE 45
/**
* @brief Max value for shift index
*/
#define SRSLTE_CORESET_SHIFT_INDEX_MAX (SRSLTE_CORESET_NOF_PRB_MAX - 1)
/**
* CORESET structure
*
* Fields follow the same order than described in 3GPP 38.331 R15 - ControlResourceSet
*
* @brief CORESET parameters as defined in TS 38.331 V15.10.0 - ControlResourceSet
*/
typedef struct SRSLTE_API {
srslte_coreset_mapping_type_t mapping_type;
@ -171,6 +191,9 @@ typedef struct SRSLTE_API {
uint32_t nof_candidates[SRSLTE_SEARCH_SPACE_NOF_AGGREGATION_LEVELS];
} srslte_search_space_t;
/**
* @brief PDCCH configuration
*/
typedef struct SRSLTE_API {
srslte_carrier_nr_t carrier;
uint16_t rnti;

@ -30,12 +30,68 @@
#ifndef SRSLTE_PDSCH_CFG_NR_H
#define SRSLTE_PDSCH_CFG_NR_H
#include "srslte/phy/ch_estimation/dmrs_pdsch.h"
#include "srslte/phy/common/phy_common_nr.h"
#include "srslte/phy/fec/cbsegm.h"
#include "srslte/phy/fec/softbuffer.h"
#include "srslte/phy/phch/ra.h"
/**
* @brief PDSCH DMRS type
*/
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;
/**
* @brief PDSCH DMRS length in symbols
*/
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;
/**
* @brief 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;
/**
* @brief 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;
/**
* @brief Provides PDSCH DMRS configuration from higher layers
* @remark Parameters described in TS 38.331 V15.10.0
*/
typedef struct {
/// Parameters provided by IE DMRS-DownlinkConfig
srslte_dmrs_pdsch_type_t type;
srslte_dmrs_pdsch_add_pos_t additional_pos;
srslte_dmrs_pdsch_len_t length;
srslte_dmrs_pdsch_typeA_pos_t typeA_pos;
bool scrambling_id0_present;
uint32_t scrambling_id0;
bool scrambling_id1_present;
uint32_t scrambling_id1;
/// Parameters provided by ServingCellConfig
bool lte_CRS_to_match_around;
/// Parameters provided by FeatureSetDownlink-v1540
bool additional_DMRS_DL_Alt;
} srslte_pdsch_dmrs_cfg_t;
/**
* @brief flatten PDSCH time domain allocation parameters
* @remark Described in TS 38.331 V15.10.0 Section PDSCH-TimeDomainResourceAllocationList
@ -54,17 +110,28 @@ typedef struct SRSLTE_API {
} srslte_pdsch_allocation_t;
typedef struct SRSLTE_API {
/// TBD
} srslte_pdsch_grant_nr_t;
/// UE identifier
uint16_t rnti;
typedef struct SRSLTE_API {
srslte_pdsch_allocation_t allocation;
/// Time domain resources
uint32_t k0;
uint32_t S;
uint32_t L;
srslte_pdsch_mapping_type_t mapping;
/// Frequency domain resources
bool prb_idx[SRSLTE_MAX_PRB_NR];
/// PDSCH Start symbol index
uint32_t S;
/// DMRS Scrambling sequence initialization (false: 0 or true: 1)
bool n_scid;
/// PDSCH length in number of symbols
uint32_t L;
/// Transport block
/// ....
} srslte_pdsch_grant_nr_t;
typedef struct SRSLTE_API {
srslte_pdsch_grant_nr_t grant;
srslte_pdsch_dmrs_cfg_t dmrs_cfg;
} srslte_pdsch_cfg_nr_t;
#endif // SRSLTE_PDSCH_CFG_NR_H

@ -22,7 +22,7 @@
/******************************************************************************
* @file ue_dl_nr.h
*
* Description: UE downlink object for NR dataplane.
* Description: UE downlink object for NR data.
*
* This module is a frontend to all the downlink data channel processing modules.
*
@ -36,37 +36,32 @@
#include "srslte/phy/phch/pdsch_cfg_nr.h"
/**
* @brief Calculates the PDSCH time resource provided by higher layers
* @brief Calculates the PDSCH time resource provided by higher layers and stores it in the provided PDSCH NR grant.
*
* @remark Defined by TS 36.214 V15.10.0 section 5.1.2.1.1 Determination of the resource allocation table to be used for
* PDSCH
*
* @param pdsch_alloc Flattened PHY PDSCH allocation configuration provided from higher layers
* @param[out] S Start symbol
* @param[out] L PDSCH transmission length in symbols
* @param[out] grant PDSCH mapping type
* @return Returns SRSLTE_SUCCESS if the provided allocation is valid, otherwise it returns SRSLTE_ERROR code
*/
SRSLTE_API int
srslte_ue_dl_nr_pdsch_time_resource_hl(const srslte_pdsch_allocation_t* pdsch_alloc, uint32_t* S, uint32_t* L);
SRSLTE_API int srslte_ue_dl_nr_pdsch_time_resource_hl(const srslte_pdsch_allocation_t* pdsch_alloc,
srslte_pdsch_grant_nr_t* grant);
/**
* @brief Calculates the PDSCH time resource default A. This can be used by SI-RNTI, RA-RNTI, P-RNTI and C-RNTI. See
* Table 5.1.2.1.1-1 for more details about the usage.
* @brief Calculates the PDSCH time resource default A and stores it in the provided PDSCH NR grant. This can be used by
* SI-RNTI, RA-RNTI, P-RNTI and C-RNTI. See Table 5.1.2.1.1-1 for more details about the usage.
*
* @remark Defined by TS 36.214 V15.10.0 Table 5.1.2.1.1-2: Default PDSCH time domain resource allocation A for normal
* @remark Defined by TS 38.214 V15.10.0 Table 5.1.2.1.1-2: Default PDSCH time domain resource allocation A for normal
* CP
*
* @attention k0 shall be zero.
*
* @param m Time domain resource assignment field value m of the DCI
* @param dmrs_typeA_pos DMRS TypeA position provided by higher layers
* @param[out] S Start symbol
* @param[out] L PDSCH transmission length in symbols
* @param[out] grant PDSCH mapping type
* @return Returns SRSLTE_SUCCESS if the provided allocation is valid, otherwise it returns SRSLTE_ERROR code
*/
SRSLTE_API int srslte_ue_dl_nr_pdsch_time_resource_default_A(uint32_t m,
srslte_dmrs_pdsch_typeA_pos_t dmrs_typeA_pos,
uint32_t* S,
uint32_t* L);
srslte_pdsch_grant_nr_t* grant);
#endif // SRSLTE_UE_DL_NR_DATA_H

@ -22,7 +22,7 @@
#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 srslte_dmrs_pdsch_cfg_to_str(const srslte_pdsch_dmrs_cfg_t* cfg, char* msg, uint32_t max_len)
{
int type = (int)cfg->type + 1;
int typeA_pos = (int)cfg->typeA_pos + 2;
@ -31,33 +31,207 @@ int srslte_dmrs_pdsch_cfg_to_str(const srslte_dmrs_pdsch_cfg_t* cfg, char* msg,
: 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_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);
const char* len = cfg->length == srslte_dmrs_pdsch_len_1 ? "single" : "double";
return srslte_print_check(
msg, max_len, 0, "type=%d, typeA_pos=%d, add_pos=%d, len=%s", type, typeA_pos, additional_pos, len);
}
static uint32_t
srslte_dmrs_get_pilots_type1(uint32_t start_prb, uint32_t nof_prb, uint32_t delta, const cf_t* symbols, cf_t* pilots)
{
uint32_t count = 0;
uint32_t n_begin = start_prb * 3;
uint32_t n_enb = n_begin + nof_prb * 3;
for (uint32_t n = n_begin; n < n_enb; n++) {
for (uint32_t k_prime = 0; k_prime < 2; k_prime++, count++) {
pilots[count] = symbols[4 * n + 2 * k_prime + delta];
}
}
return count;
}
static uint32_t
srslte_dmrs_get_pilots_type2(uint32_t start_prb, uint32_t nof_prb, uint32_t delta, const cf_t* symbols, cf_t* pilots)
{
uint32_t count = 0;
uint32_t n_begin = start_prb * 2;
uint32_t n_enb = n_begin + nof_prb * 2;
for (uint32_t n = n_begin; n < n_enb; n++) {
for (uint32_t k_prime = 0; k_prime < 2; k_prime++, count++) {
pilots[count] = symbols[6 * n + k_prime + delta];
}
}
return count;
}
static uint32_t srslte_dmrs_get_lse(srslte_dmrs_pdsch_t* q,
srslte_sequence_state_t* sequence_state,
srslte_dmrs_pdsch_type_t dmrs_type,
uint32_t start_prb,
uint32_t nof_prb,
uint32_t delta,
const cf_t* symbols,
cf_t* least_square_estimates)
{
uint32_t count = 0;
switch (dmrs_type) {
case srslte_dmrs_pdsch_type_1:
count = srslte_dmrs_get_pilots_type1(start_prb, nof_prb, delta, symbols, least_square_estimates);
break;
case srslte_dmrs_pdsch_type_2:
count = srslte_dmrs_get_pilots_type2(start_prb, nof_prb, delta, symbols, least_square_estimates);
break;
default:
ERROR("Unknown DMRS type.\n");
}
// Generate sequence for the given pilots
srslte_sequence_state_gen_f(sequence_state, M_SQRT1_2, (float*)q->temp, count * 2);
// Calculate least square estimates
srslte_vec_prod_conj_ccc(least_square_estimates, q->temp, least_square_estimates, count);
return count;
}
static uint32_t
srslte_dmrs_put_pilots_type1(uint32_t start_prb, uint32_t nof_prb, uint32_t delta, cf_t* symbols, const cf_t* pilots)
{
uint32_t count = 0;
uint32_t n_begin = start_prb * 3;
uint32_t n_enb = n_begin + nof_prb * 3;
for (uint32_t n = n_begin; n < n_enb; n++) {
for (uint32_t k_prime = 0; k_prime < 2; k_prime++, count++) {
symbols[4 * n + 2 * k_prime + delta] = pilots[count];
}
}
return count;
}
static uint32_t
srslte_dmrs_put_pilots_type2(uint32_t start_prb, uint32_t nof_prb, uint32_t delta, cf_t* symbols, const cf_t* pilots)
{
uint32_t count = 0;
uint32_t n_begin = start_prb * 2;
uint32_t n_enb = n_begin + nof_prb * 2;
for (uint32_t n = n_begin; n < n_enb; n++) {
for (uint32_t k_prime = 0; k_prime < 2; k_prime++, count++) {
symbols[6 * n + k_prime + delta] = pilots[count];
}
}
return count;
}
static uint32_t srslte_dmrs_put_pilots(srslte_dmrs_pdsch_t* q,
srslte_sequence_state_t* sequence_state,
srslte_dmrs_pdsch_type_t dmrs_type,
uint32_t start_prb,
uint32_t nof_prb,
uint32_t delta,
cf_t* symbols)
{
uint32_t count = (dmrs_type == srslte_dmrs_pdsch_type_1) ? nof_prb * 6 : nof_prb * 4;
// Generate sequence for the given pilots
srslte_sequence_state_gen_f(sequence_state, M_SQRT1_2, (float*)q->temp, count * 2);
switch (dmrs_type) {
case srslte_dmrs_pdsch_type_1:
count = srslte_dmrs_put_pilots_type1(start_prb, nof_prb, delta, symbols, q->temp);
break;
case srslte_dmrs_pdsch_type_2:
count = srslte_dmrs_put_pilots_type2(start_prb, nof_prb, delta, symbols, q->temp);
break;
default:
ERROR("Unknown DMRS type.\n");
}
return count;
}
static int srslte_dmrs_pdsch_put_symbol(srslte_dmrs_pdsch_t* q,
const srslte_pdsch_cfg_nr_t* pdsch_cfg,
uint32_t cinit,
uint32_t delta,
cf_t* symbols)
{
uint32_t prb_count = 0; // Counts consecutive used PRB
uint32_t prb_start = 0; // Start consecutive used PRB
uint32_t prb_skip = 0; // Number of PRB to skip
uint32_t nof_pilots_x_prb = pdsch_cfg->dmrs_cfg.type == srslte_dmrs_pdsch_type_1 ? 6 : 4;
uint32_t pilot_count = 0;
// Initialise sequence
srslte_sequence_state_t sequence_state = {};
srslte_sequence_state_init(&sequence_state, cinit);
// Iterate over PRBs
for (uint32_t prb_idx = 0; prb_idx < q->carrier.nof_prb; prb_idx++) {
// If the PRB is used for PDSCH transmission count
if (pdsch_cfg->grant.prb_idx[prb_idx]) {
// If it is the first PRB...
if (prb_count == 0) {
// ... save first consecutive PRB in the group
prb_start = prb_idx;
// ... discard unused pilots and reset counter
srslte_sequence_state_advance(&sequence_state, prb_skip * nof_pilots_x_prb * 2);
prb_skip = 0;
}
prb_count++;
continue;
}
// Increase number of PRB to skip
prb_skip++;
// End of consecutive PRB, skip copying if no PRB was counted
if (prb_count == 0) {
continue;
}
// Get contiguous pilots
pilot_count += srslte_dmrs_put_pilots(
q, &sequence_state, pdsch_cfg->dmrs_cfg.type, prb_start, prb_count, delta, &symbols[prb_start * SRSLTE_NRE]);
// Reset counter
prb_count = 0;
}
if (prb_count > 0) {
pilot_count += srslte_dmrs_put_pilots(
q, &sequence_state, pdsch_cfg->dmrs_cfg.type, prb_start, prb_count, delta, &symbols[prb_start * SRSLTE_NRE]);
}
return pilot_count;
}
// 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,
static int srslte_dmrs_pdsch_get_symbols_idx_mapping_type_A_single(const srslte_pdsch_cfg_nr_t* cfg,
uint32_t symbols[SRSLTE_DMRS_PDSCH_MAX_SYMBOLS])
{
int count = 0;
if (cfg->duration < SRSLTE_DMRS_PDSCH_TYPEA_SINGLE_DURATION_MIN) {
if (cfg->grant.L < 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;
int l0 = (cfg->dmrs_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:
@ -65,23 +239,23 @@ static int srslte_dmrs_pdsch_get_symbols_idx_mapping_type_A_single(const srslte_
// - 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) {
if (cfg->dmrs_cfg.lte_CRS_to_match_around && cfg->dmrs_cfg.additional_pos == srslte_dmrs_pdsch_add_pos_1 &&
cfg->dmrs_cfg.typeA_pos == srslte_dmrs_pdsch_typeA_pos_3 && cfg->dmrs_cfg.additional_DMRS_DL_Alt) {
l1 = 12;
}
symbols[count] = l0;
count++;
if (cfg->duration < 8 || cfg->additional_pos == srslte_dmrs_pdsch_add_pos_0) {
if (cfg->grant.L < 8 || cfg->dmrs_cfg.additional_pos == srslte_dmrs_pdsch_add_pos_0) {
return count;
}
if (cfg->duration < 10) {
if (cfg->grant.L < 10) {
symbols[count] = 7;
count++;
} else if (cfg->duration < 12) {
if (cfg->additional_pos > srslte_dmrs_pdsch_add_pos_2) {
} else if (cfg->grant.L < 12) {
if (cfg->dmrs_cfg.additional_pos > srslte_dmrs_pdsch_add_pos_2) {
symbols[count] = 6;
count++;
}
@ -89,8 +263,8 @@ static int srslte_dmrs_pdsch_get_symbols_idx_mapping_type_A_single(const srslte_
symbols[count] = 9;
count++;
} else if (cfg->duration == 12) {
switch (cfg->additional_pos) {
} else if (cfg->grant.L == 12) {
switch (cfg->dmrs_cfg.additional_pos) {
case srslte_dmrs_pdsch_add_pos_1:
symbols[count] = 9;
count++;
@ -110,7 +284,7 @@ static int srslte_dmrs_pdsch_get_symbols_idx_mapping_type_A_single(const srslte_
count++;
}
} else {
switch (cfg->additional_pos) {
switch (cfg->dmrs_cfg.additional_pos) {
case srslte_dmrs_pdsch_add_pos_1:
symbols[count] = l1;
count++;
@ -135,28 +309,28 @@ static int srslte_dmrs_pdsch_get_symbols_idx_mapping_type_A_single(const srslte_
}
// 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,
static int srslte_dmrs_pdsch_get_symbols_idx_mapping_type_A_double(const srslte_pdsch_cfg_nr_t* cfg,
uint32_t symbols[SRSLTE_DMRS_PDSCH_MAX_SYMBOLS])
{
int count = 0;
if (cfg->duration < SRSLTE_DMRS_PDSCH_TYPEA_DOUBLE_DURATION_MIN) {
if (cfg->grant.L < 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;
int l0 = (cfg->dmrs_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) {
if (cfg->grant.L < 10 || cfg->dmrs_cfg.additional_pos == srslte_dmrs_pdsch_add_pos_0) {
return count;
}
if (cfg->duration < 13) {
if (cfg->grant.L < 13) {
symbols[count] = 8;
count++;
symbols[count] = symbols[count - 1] + 1;
@ -171,12 +345,13 @@ static int srslte_dmrs_pdsch_get_symbols_idx_mapping_type_A_double(const srslte_
return count;
}
static int srslte_dmrs_pdsch_get_symbols_idx(const srslte_dmrs_pdsch_cfg_t* cfg, uint32_t* symbols)
int srslte_dmrs_pdsch_get_symbols_idx(const srslte_pdsch_cfg_nr_t* cfg, uint32_t symbols[SRSLTE_DMRS_PDSCH_MAX_SYMBOLS])
{
switch (cfg->mapping_type) {
switch (cfg->grant.mapping) {
case srslte_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) {
if (cfg->dmrs_cfg.typeA_pos != srslte_dmrs_pdsch_typeA_pos_2 &&
cfg->dmrs_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;
@ -184,13 +359,13 @@ static int srslte_dmrs_pdsch_get_symbols_idx(const srslte_dmrs_pdsch_cfg_t* cfg,
// 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) {
if ((cfg->grant.L == 3 || cfg->grant.L == 4) && cfg->dmrs_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) {
if (cfg->dmrs_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);
@ -202,20 +377,20 @@ static int srslte_dmrs_pdsch_get_symbols_idx(const srslte_dmrs_pdsch_cfg_t* cfg,
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 srslte_dmrs_pdsch_get_sc_idx(const srslte_pdsch_dmrs_cfg_t* cfg, uint32_t max_count, uint32_t* sc_idx)
{
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++) {
for (uint32_t n = 0; count < max_count; n += 4) {
for (uint32_t k_prime = 0; k_prime < 2 && count < max_count; 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++) {
for (uint32_t n = 0; count < max_count; n += 6) {
for (uint32_t k_prime = 0; k_prime < 2 && count < max_count; k_prime++) {
sc_idx[count++] = n + k_prime + delta;
}
}
@ -224,122 +399,280 @@ static int srslte_dmrs_pdsch_get_sc_idx(const srslte_dmrs_pdsch_cfg_t* cfg, uint
return count;
}
static uint32_t srslte_dmrs_pdsch_seed(const srslte_dmrs_pdsch_cfg_t* cfg, uint32_t slot_idx, uint32_t symbol_idx)
static uint32_t srslte_dmrs_pdsch_seed(const srslte_carrier_nr_t* carrier,
const srslte_pdsch_cfg_nr_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)) &
// Calculate scrambling IDs
uint32_t n_id = carrier->id;
uint32_t n_scid = (cfg->grant.n_scid) ? 1 : 0;
if (!cfg->grant.n_scid && cfg->dmrs_cfg.scrambling_id0_present) {
// n_scid = 0 and ID0 present
n_id = cfg->dmrs_cfg.scrambling_id0;
} else if (cfg->grant.n_scid && cfg->dmrs_cfg.scrambling_id1_present) {
// n_scid = 1 and ID1 present
n_id = cfg->dmrs_cfg.scrambling_id1;
}
return (uint32_t)(((((SRSLTE_MAX_NSYMB * slot_idx + symbol_idx + 1UL) * (2UL * n_id + 1UL)) << 17UL) +
(2UL * carrier->id + n_scid)) &
(uint64_t)INT32_MAX);
}
int srslte_dmrs_pdsch_init(srslte_dmrs_pdsch_t* q, const srslte_dmrs_pdsch_cfg_t* cfg)
int srslte_dmrs_pdsch_init(srslte_dmrs_pdsch_t* q, bool is_ue)
{
// 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;
if (q == NULL) {
return SRSLTE_ERROR_INVALID_INPUTS;
}
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;
if (is_ue) {
q->is_ue = true;
}
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)
void srslte_dmrs_pdsch_free(srslte_dmrs_pdsch_t* q)
{
uint32_t delta = 0;
if (q == NULL) {
return;
}
// 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;
}
srslte_interp_linear_free(&q->interpolator_type1);
srslte_interp_linear_free(&q->interpolator_type2);
if (q->pilot_estimates) {
free(q->pilot_estimates);
}
if (q->temp) {
free(q->temp);
}
}
int srslte_dmrs_pdsch_set_carrier(srslte_dmrs_pdsch_t* q, const srslte_carrier_nr_t* carrier)
{
bool nof_prb_changed = q->carrier.nof_prb != carrier->nof_prb;
// Set carrier
q->carrier = *carrier;
if (!q->is_ue) {
return SRSLTE_SUCCESS;
}
// Free interpolator
if (nof_prb_changed) {
srslte_interp_linear_free(&q->interpolator_type1);
srslte_interp_linear_free(&q->interpolator_type2);
if (srslte_interp_linear_init(&q->interpolator_type1, carrier->nof_prb * SRSLTE_NRE / 2, 2) != SRSLTE_SUCCESS) {
return SRSLTE_ERROR;
}
if (srslte_interp_linear_init(&q->interpolator_type2, carrier->nof_prb * SRSLTE_NRE / 3, 6) != SRSLTE_SUCCESS) {
return SRSLTE_ERROR;
}
}
if (q->max_nof_prb < carrier->nof_prb) {
q->max_nof_prb = carrier->nof_prb;
if (q->pilot_estimates) {
free(q->pilot_estimates);
}
// The maximum number of pilots is for Type 1
q->pilot_estimates = srslte_vec_cf_malloc(SRSLTE_DMRS_PDSCH_MAX_SYMBOLS * q->max_nof_prb * SRSLTE_NRE / 2);
if (!q->pilot_estimates) {
ERROR("malloc\n");
return SRSLTE_ERROR;
}
if (q->temp) {
free(q->temp);
}
q->temp = srslte_vec_cf_malloc(q->max_nof_prb * SRSLTE_NRE);
if (!q->temp) {
ERROR("malloc\n");
return SRSLTE_ERROR;
}
}
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)
int srslte_dmrs_pdsch_put_sf(srslte_dmrs_pdsch_t* q,
const srslte_dl_slot_cfg_t* slot_cfg,
const srslte_pdsch_cfg_nr_t* pdsch_cfg,
cf_t* sf_symbols)
{
uint32_t delta = 0;
if (q == NULL || slot_cfg == NULL || pdsch_cfg == NULL || sf_symbols == NULL) {
return SRSLTE_ERROR_INVALID_INPUTS;
}
uint32_t symbol_sz = q->carrier.nof_prb * SRSLTE_NRE; // Symbol size in resource elements
// Get symbols indexes
uint32_t symbols[SRSLTE_DMRS_PDSCH_MAX_SYMBOLS] = {};
int nof_symbols = srslte_dmrs_pdsch_get_symbols_idx(pdsch_cfg, symbols);
if (nof_symbols < SRSLTE_SUCCESS) {
return SRSLTE_ERROR;
}
// 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;
for (uint32_t i = 0; i < nof_symbols; i++) {
uint32_t l = symbols[i]; // Symbol index inside the slot
uint32_t slot_idx = slot_cfg->idx; // Slot index in the frame
uint32_t cinit = srslte_dmrs_pdsch_seed(&q->carrier, pdsch_cfg, slot_idx, l);
srslte_dmrs_pdsch_put_symbol(q, pdsch_cfg, cinit, delta, &sf_symbols[symbol_sz * l]);
}
return SRSLTE_SUCCESS;
}
static int srslte_dmrs_pdsch_get_symbol(srslte_dmrs_pdsch_t* q,
const srslte_pdsch_cfg_nr_t* pdsch_cfg,
uint32_t cinit,
uint32_t delta,
const cf_t* symbols,
cf_t* least_square_estimates)
{
uint32_t prb_count = 0; // Counts consecutive used PRB
uint32_t prb_start = 0; // Start consecutive used PRB
uint32_t prb_skip = 0; // Number of PRB to skip
uint32_t nof_pilots_x_prb = pdsch_cfg->dmrs_cfg.type == srslte_dmrs_pdsch_type_1 ? 6 : 4;
uint32_t pilot_count = 0;
// Initialise sequence
srslte_sequence_state_t sequence_state = {};
srslte_sequence_state_init(&sequence_state, cinit);
// Iterate over PRBs
for (uint32_t prb_idx = 0; prb_idx < q->carrier.nof_prb; prb_idx++) {
// If the PRB is used for PDSCH transmission count
if (pdsch_cfg->grant.prb_idx[prb_idx]) {
// If it is the first PRB...
if (prb_count == 0) {
// ... save first consecutive PRB in the group
prb_start = prb_idx;
// ... discard unused pilots and reset counter
srslte_sequence_state_advance(&sequence_state, prb_skip * nof_pilots_x_prb * 2);
prb_skip = 0;
}
prb_count++;
continue;
}
// Increase number of PRB to skip
prb_skip++;
// End of consecutive PRB, skip copying if no PRB was counted
if (prb_count == 0) {
continue;
}
// Get contiguous pilots
pilot_count += srslte_dmrs_get_lse(q,
&sequence_state,
pdsch_cfg->dmrs_cfg.type,
prb_start,
prb_count,
delta,
&symbols[prb_start * SRSLTE_NRE],
&least_square_estimates[pilot_count]);
// Reset counter
prb_count = 0;
}
return SRSLTE_SUCCESS;
if (prb_count > 0) {
pilot_count += srslte_dmrs_get_lse(q,
&sequence_state,
pdsch_cfg->dmrs_cfg.type,
prb_start,
prb_count,
delta,
&symbols[prb_start * SRSLTE_NRE],
&least_square_estimates[pilot_count]);
}
return pilot_count;
}
int srslte_dmrs_pdsch_estimate(srslte_dmrs_pdsch_t* q,
const srslte_dl_slot_cfg_t* slot_cfg,
const srslte_pdsch_cfg_nr_t* pdsch_cfg,
const cf_t* sf_symbols,
srslte_chest_dl_res_t* chest_res)
{
const uint32_t delta = 0;
if (q == NULL || slot_cfg == NULL || sf_symbols == NULL || chest_res == NULL) {
return SRSLTE_ERROR_INVALID_INPUTS;
}
cf_t* ce = chest_res->ce[0][0];
uint32_t symbol_sz = q->carrier.nof_prb * SRSLTE_NRE; // Symbol size in resource elements
// Get symbols indexes
uint32_t symbols[SRSLTE_DMRS_PDSCH_MAX_SYMBOLS] = {};
int nof_symbols = srslte_dmrs_pdsch_get_symbols_idx(pdsch_cfg, symbols);
if (nof_symbols < SRSLTE_SUCCESS) {
return SRSLTE_ERROR;
}
uint32_t nof_pilots_x_symbol = 0;
// Iterate symbols
for (uint32_t i = 0; i < nof_symbols; i++) {
uint32_t l = symbols[i]; // Symbol index inside the slot
uint32_t cinit = srslte_dmrs_pdsch_seed(&q->carrier, pdsch_cfg, slot_cfg->idx, l);
nof_pilots_x_symbol = srslte_dmrs_pdsch_get_symbol(
q, pdsch_cfg, cinit, delta, &sf_symbols[symbol_sz * l], &q->pilot_estimates[nof_pilots_x_symbol * i]);
}
// Perform measurements here
// ...
// Average over time, only if more than one DMRS symbol
for (uint32_t i = 1; i < nof_symbols; i++) {
srslte_vec_sum_ccc(
q->pilot_estimates, &q->pilot_estimates[nof_pilots_x_symbol * i], q->pilot_estimates, nof_pilots_x_symbol);
}
if (nof_symbols > 0) {
srslte_vec_sc_prod_cfc(q->pilot_estimates, 1.0f / (float)nof_symbols, q->pilot_estimates, nof_pilots_x_symbol);
}
// Frequency domain interpolate
uint32_t nof_re_x_symbol =
(pdsch_cfg->dmrs_cfg.type == srslte_dmrs_pdsch_type_1) ? nof_pilots_x_symbol * 2 : nof_pilots_x_symbol * 3;
if (pdsch_cfg->dmrs_cfg.type == srslte_dmrs_pdsch_type_1) {
// Prepare interpolator
srslte_interp_linear_resize(&q->interpolator_type1, nof_pilots_x_symbol, 2);
// Interpolate
srslte_interp_linear_offset(&q->interpolator_type1, q->pilot_estimates, ce, delta, 2 - delta);
} else {
// Prepare interpolator
srslte_interp_linear_resize(&q->interpolator_type2, nof_pilots_x_symbol, 3);
// Interpolate
srslte_interp_linear_offset(&q->interpolator_type2, q->pilot_estimates, ce, delta, 3 - delta);
}
// Time domain hold
for (uint32_t i = 1; i < pdsch_cfg->grant.L; i++) {
srslte_vec_cf_copy(&ce[i * nof_re_x_symbol], ce, nof_re_x_symbol);
}
// Set other values in the estimation result
chest_res->nof_re = nof_re_x_symbol * pdsch_cfg->grant.L;
return SRSLTE_SUCCESS;
}

@ -20,32 +20,32 @@
*/
#include "srslte/common/test_common.h"
#include "srslte/phy/ch_estimation/dmrs_pdsch.h"
#include "srslte/phy/ue/ue_dl_nr_data.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};
static srslte_carrier_nr_t carrier = {
0, // cell_id
0, // numerology
50, // nof_prb
0 // start
};
typedef struct {
srslte_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;
srslte_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
@ -136,10 +136,9 @@ 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-r nof_prb [Default %d]\n", carrier.nof_prb);
printf("\t-c cell_id [Default %d]\n", cell.id);
printf("\t-c cell_id [Default %d]\n", carrier.id);
printf("\t-v increase verbosity\n");
}
@ -147,16 +146,13 @@ void usage(char* prog)
void parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "recov")) != -1) {
while ((opt = getopt(argc, argv, "rcov")) != -1) {
switch (opt) {
case 'r':
cell.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'e':
cell.cp = SRSLTE_CP_EXT;
carrier.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'c':
cell.id = (uint32_t)strtol(argv[optind], NULL, 10);
carrier.id = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'v':
srslte_verbose++;
@ -168,37 +164,48 @@ void parse_args(int argc, char** argv)
}
}
static int assert_cfg(srslte_dmrs_pdsch_t* dmrs_pdsch)
static int assert_cfg(const srslte_pdsch_cfg_nr_t* pdsch_cfg)
{
for (uint32_t i = 0; gold[i].nof_sc != 0; i++) {
if (dmrs_pdsch->cfg.mapping_type != gold[i].mapping_type) {
// Gold examples are done for more than 12 symbols
if (pdsch_cfg->grant.L <= 12) {
continue;
}
if (pdsch_cfg->grant.mapping != gold[i].mapping_type) {
continue;
}
if (dmrs_pdsch->cfg.typeA_pos != gold[i].typeA_pos) {
if (pdsch_cfg->dmrs_cfg.typeA_pos != gold[i].typeA_pos) {
continue;
}
if (dmrs_pdsch->cfg.additional_pos != gold[i].additional_pos) {
if (pdsch_cfg->dmrs_cfg.additional_pos != gold[i].additional_pos) {
continue;
}
if (dmrs_pdsch->cfg.length != gold[i].max_length) {
if (pdsch_cfg->dmrs_cfg.length != gold[i].max_length) {
continue;
}
if (dmrs_pdsch->cfg.type != gold[i].type) {
if (pdsch_cfg->dmrs_cfg.type != gold[i].type) {
continue;
}
TESTASSERT(dmrs_pdsch->nof_symbols == gold[i].nof_symbols);
uint32_t symbols[SRSLTE_DMRS_PDSCH_MAX_SYMBOLS] = {};
int nof_symbols = srslte_dmrs_pdsch_get_symbols_idx(pdsch_cfg, symbols);
TESTASSERT(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]);
TESTASSERT(symbols[j] == gold[i].symbol_idx[j]);
}
uint32_t sc[SRSLTE_NRE] = {};
srslte_dmrs_pdsch_get_sc_idx(&pdsch_cfg->dmrs_cfg, SRSLTE_NRE, sc);
for (uint32_t j = 0; j < gold[i].nof_sc; j++) {
TESTASSERT(dmrs_pdsch->sc_idx[j] == gold[i].sc_idx[j]);
TESTASSERT(sc[j] == gold[i].sc_idx[j]);
}
return SRSLTE_SUCCESS;
@ -207,25 +214,25 @@ static int assert_cfg(srslte_dmrs_pdsch_t* dmrs_pdsch)
return SRSLTE_SUCCESS;
}
static int run_test(srslte_dmrs_pdsch_t* dmrs_pdsch, cf_t* sf_symbols, cf_t* h)
static int run_test(srslte_dmrs_pdsch_t* dmrs_pdsch,
const srslte_pdsch_cfg_nr_t* pdsch_cfg,
cf_t* sf_symbols,
srslte_chest_dl_res_t* chest_res)
{
TESTASSERT(dmrs_pdsch->nof_symbols > 0);
TESTASSERT(dmrs_pdsch->nof_sc > 0);
TESTASSERT(assert_cfg(dmrs_pdsch) == SRSLTE_SUCCESS);
TESTASSERT(assert_cfg(pdsch_cfg) == 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_dl_slot_cfg_t slot_cfg = {};
for (slot_cfg.idx = 0; slot_cfg.idx < SRSLTE_NSLOTS_PER_FRAME_NR(dmrs_pdsch->carrier.numerology); slot_cfg.idx++) {
srslte_dmrs_pdsch_put_sf(dmrs_pdsch, &slot_cfg, pdsch_cfg, sf_symbols);
srslte_dmrs_pdsch_get_sf(dmrs_pdsch, &dl_sf, sf_symbols, h);
srslte_dmrs_pdsch_estimate(dmrs_pdsch, &slot_cfg, pdsch_cfg, sf_symbols, chest_res);
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;
for (uint32_t i = 0; i < chest_res->nof_re; i++) {
cf_t err = chest_res->ce[0][0][i] - 1.0f;
mse += cabsf(err);
}
mse /= (float)dmrs_pdsch->nof_symbols * dmrs_pdsch->nof_sc;
mse /= (float)chest_res->nof_re;
TESTASSERT(!isnan(mse));
TESTASSERT(mse < 1e-6f);
@ -240,66 +247,95 @@ int main(int argc, char** argv)
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;
srslte_dmrs_pdsch_t dmrs_pdsch = {};
srslte_pdsch_cfg_nr_t pdsch_cfg = {};
srslte_chest_dl_res_t chest_dl_res = {};
uint32_t nof_re = cell.nof_prb * SRSLTE_NRE * SRSLTE_NOF_SLOTS_PER_SF * SRSLTE_MAX_NSYMB;
uint32_t nof_re = carrier.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;
// Initialise object DMRS for PDSCH
if (srslte_dmrs_pdsch_init(&dmrs_pdsch, true) != SRSLTE_SUCCESS) {
ERROR("Init\n");
goto clean_exit;
}
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;
// Set carrier configuration
if (srslte_dmrs_pdsch_set_carrier(&dmrs_pdsch, &carrier) != SRSLTE_SUCCESS) {
ERROR("Setting carrier\n");
goto clean_exit;
}
if (cfg.typeA_pos == srslte_dmrs_pdsch_typeA_pos_3) {
add_pos_end = srslte_dmrs_pdsch_add_pos_1;
}
if (srslte_chest_dl_res_init(&chest_dl_res, carrier.nof_prb) != SRSLTE_SUCCESS) {
ERROR("Initiating channel estimation result\n");
goto clean_exit;
}
for (cfg.additional_pos = add_pos_begin; cfg.additional_pos <= add_pos_end; cfg.additional_pos++) {
// For each DCI m param
for (uint32_t m = 0; m < 16; m++) {
srslte_dmrs_pdsch_type_t type_begin = srslte_dmrs_pdsch_type_1;
srslte_dmrs_pdsch_type_t type_end = srslte_dmrs_pdsch_type_2;
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 (pdsch_cfg.dmrs_cfg.type = type_begin; pdsch_cfg.dmrs_cfg.type <= type_end; pdsch_cfg.dmrs_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.length = max_len_begin; cfg.length <= max_len_end; cfg.length++) {
srslte_dmrs_pdsch_t dmrs_pdsch = {};
for (pdsch_cfg.dmrs_cfg.typeA_pos = typeA_pos_begin; pdsch_cfg.dmrs_cfg.typeA_pos <= typeA_pos_end;
pdsch_cfg.dmrs_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;
// 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 (pdsch_cfg.dmrs_cfg.typeA_pos == srslte_dmrs_pdsch_typeA_pos_3) {
add_pos_end = srslte_dmrs_pdsch_add_pos_1;
}
if (n == SRSLTE_SUCCESS) {
test_passed++;
} else {
char str[64] = {};
srslte_dmrs_pdsch_cfg_to_str(&cfg, str, 64);
for (pdsch_cfg.dmrs_cfg.additional_pos = add_pos_begin; pdsch_cfg.dmrs_cfg.additional_pos <= add_pos_end;
pdsch_cfg.dmrs_cfg.additional_pos++) {
ERROR("Test %d failed. %s.\n", test_counter, str);
}
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 (pdsch_cfg.dmrs_cfg.length = max_len_begin; pdsch_cfg.dmrs_cfg.length <= max_len_end;
pdsch_cfg.dmrs_cfg.length++) {
for (uint32_t bw = 1; bw <= carrier.nof_prb; bw++) {
for (uint32_t i = 0; i < carrier.nof_prb; i++) {
pdsch_cfg.grant.prb_idx[i] = (i < bw);
}
// Load default type A grant
srslte_ue_dl_nr_pdsch_time_resource_default_A(0, pdsch_cfg.dmrs_cfg.typeA_pos, &pdsch_cfg.grant);
int n = run_test(&dmrs_pdsch, &pdsch_cfg, sf_symbols, &chest_dl_res);
test_counter++;
if (n == SRSLTE_SUCCESS) {
test_passed++;
} else {
char str[64] = {};
srslte_dmrs_pdsch_cfg_to_str(&pdsch_cfg.dmrs_cfg, str, 64);
ERROR("Test %d failed. %s.\n", test_counter, str);
}
test_counter++;
}
}
}
}
}
}
clean_exit:
if (sf_symbols) {
free(sf_symbols);
}
if (h) {
free(h);
}
srslte_chest_dl_res_free(&chest_dl_res);
srslte_dmrs_pdsch_free(&dmrs_pdsch);
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);

@ -19,6 +19,7 @@
*
*/
#include "srslte/phy/ue/ue_dl_nr_data.h"
#include "srslte/phy/utils/debug.h"
static int srslte_ue_dl_nr_pdsch_time_resource_hl_A(uint32_t sliv, uint32_t* S, uint32_t* L)
{
@ -63,26 +64,28 @@ static int srslte_ue_dl_nr_pdsch_time_resource_hl_B(uint32_t sliv, uint32_t* S,
return SRSLTE_ERROR;
}
int srslte_ue_dl_nr_pdsch_time_resource_hl(const srslte_pdsch_allocation_t* pdsch_alloc, uint32_t* S, uint32_t* L)
int srslte_ue_dl_nr_pdsch_time_resource_hl(const srslte_pdsch_allocation_t* pdsch_alloc, srslte_pdsch_grant_nr_t* grant)
{
if (pdsch_alloc == NULL || S == NULL || L == NULL) {
if (pdsch_alloc == NULL || grant == NULL) {
return SRSLTE_ERROR_INVALID_INPUTS;
}
grant->k0 = pdsch_alloc->k0;
grant->mapping = pdsch_alloc->mapping_type;
if (pdsch_alloc->mapping_type == srslte_pdsch_mapping_type_A) {
return srslte_ue_dl_nr_pdsch_time_resource_hl_A(pdsch_alloc->sliv, S, L);
return srslte_ue_dl_nr_pdsch_time_resource_hl_A(pdsch_alloc->sliv, &grant->S, &grant->L);
}
return srslte_ue_dl_nr_pdsch_time_resource_hl_B(pdsch_alloc->sliv, S, L);
return srslte_ue_dl_nr_pdsch_time_resource_hl_B(pdsch_alloc->sliv, &grant->S, &grant->L);
}
int srslte_ue_dl_nr_pdsch_time_resource_default_A(uint32_t m,
srslte_dmrs_pdsch_typeA_pos_t dmrs_typeA_pos,
uint32_t* S,
uint32_t* L)
srslte_pdsch_grant_nr_t* grant)
{
if (S == NULL || L == NULL) {
if (grant == NULL) {
return SRSLTE_ERROR_INVALID_INPUTS;
}
@ -91,20 +94,43 @@ int srslte_ue_dl_nr_pdsch_time_resource_default_A(uint32_t
return SRSLTE_ERROR_INVALID_INPUTS;
}
// Select k0
grant->k0 = 0;
// Select PDSCH mapping
static srslte_pdsch_mapping_type_t pdsch_mapping_lut[16] = {srslte_pdsch_mapping_type_A,
srslte_pdsch_mapping_type_A,
srslte_pdsch_mapping_type_A,
srslte_pdsch_mapping_type_A,
srslte_pdsch_mapping_type_A,
srslte_pdsch_mapping_type_B,
srslte_pdsch_mapping_type_B,
srslte_pdsch_mapping_type_B,
srslte_pdsch_mapping_type_B,
srslte_pdsch_mapping_type_B,
srslte_pdsch_mapping_type_B,
srslte_pdsch_mapping_type_A,
srslte_pdsch_mapping_type_A,
srslte_pdsch_mapping_type_A,
srslte_pdsch_mapping_type_B,
srslte_pdsch_mapping_type_B};
grant->mapping = pdsch_mapping_lut[m];
static uint32_t S_pos2[16] = {2, 2, 2, 2, 2, 9, 4, 5, 5, 9, 12, 1, 1, 2, 4, 8};
static uint32_t L_pos2[16] = {12, 10, 9, 7, 5, 4, 4, 7, 2, 2, 2, 13, 6, 4, 7, 4};
static uint32_t S_pos3[16] = {3, 3, 3, 3, 3, 10, 6, 5, 5, 9, 12, 1, 1, 2, 4, 8};
static uint32_t L_pos3[16] = {11, 9, 8, 6, 4, 4, 4, 7, 2, 2, 2, 13, 6, 4, 7, 4};
// Select start symbol (S) and length (L)
switch (dmrs_typeA_pos) {
case srslte_dmrs_pdsch_typeA_pos_2:
*S = S_pos2[m];
*L = L_pos2[m];
grant->S = S_pos2[m];
grant->L = L_pos2[m];
break;
case srslte_dmrs_pdsch_typeA_pos_3:
*S = S_pos3[m];
*L = L_pos3[m];
grant->S = S_pos3[m];
grant->L = L_pos3[m];
break;
default:
ERROR("Undefined case (%d)\n", dmrs_typeA_pos);

Loading…
Cancel
Save