mirror of https://github.com/PentHertz/srsLTE.git
Added PDCCH DMRS estimation and measurement
This commit is contained in:
parent
5799100e42
commit
937c52339d
|
@ -27,10 +27,135 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#include "srslte/phy/common/phy_common_nr.h"
|
||||
#include "srslte/phy/resampling/resampler.h"
|
||||
#include "srslte/srslte.h"
|
||||
|
||||
SRSLTE_API int
|
||||
srslte_dmrs_pdcch_put(const srslte_nr_pdcch_cfg_t* cfg, const srslte_dl_sf_cfg_t* dl_sf, cf_t* sf_symbols);
|
||||
/**
|
||||
* @brief Puts in the resource grid the DeModulation Reference Signals for decoding PDCCH.
|
||||
*
|
||||
* \remark: Implemented as specified by TS 38.211 V15.8.0 Section 7.1.4.3
|
||||
|
||||
* @param cfg Configuration that includes Carrier, CORESET, Search Space and PDCCH candidate
|
||||
* @param slot_idx Slot index in the frame
|
||||
* @param sf_symbols is the resource grid where the DMRS resource elements will be written
|
||||
* @return SRSLTE_SUCCESS if the configurations are valid, otherwise it returns an SRSLTE_ERROR code
|
||||
*/
|
||||
SRSLTE_API int srslte_dmrs_pdcch_put(const srslte_nr_pdcch_cfg_t* cfg, uint32_t slot_idx, cf_t* sf_symbols);
|
||||
|
||||
/**
|
||||
* @brief PDCCH DMRS channel estimator object
|
||||
*
|
||||
* @see srslte_dmrs_pdcch_estimator_init
|
||||
* @see srslte_dmrs_pdcch_estimator_free
|
||||
* @see srslte_dmrs_pdcch_estimator_estimate
|
||||
*/
|
||||
typedef struct SRSLTE_API {
|
||||
/// Current estimator carrier configuration
|
||||
srslte_nr_carrier_t carrier;
|
||||
|
||||
/// Current estimator CORESET configuration
|
||||
srslte_coreset_t coreset;
|
||||
|
||||
/// Stores the current CORESET bandwidth in PRB
|
||||
uint32_t coreset_bw;
|
||||
|
||||
/// Stores the current CORESET size in RE
|
||||
uint32_t coreset_sz;
|
||||
|
||||
/// Object for interpolating, it shall be initialised only once
|
||||
srslte_resampler_fft_t interpolator;
|
||||
|
||||
/// Pilots least square estimates, one vector for each possible symbol. Since there are one pilot every 4 sub-carriers
|
||||
/// , each vector is three times the CORESEt band-width
|
||||
cf_t* lse[SRSLTE_CORESET_DURATION_MAX];
|
||||
|
||||
/// Channel estimates, size coreset_sz
|
||||
cf_t* ce;
|
||||
|
||||
/// Stores latest slot index in frame
|
||||
uint32_t slot_idx;
|
||||
} srslte_dmrs_pdcch_estimator_t;
|
||||
|
||||
/**
|
||||
* @brief Initialises the PDCCH DMRS channel estimator from a given carrier and CORESET configuration
|
||||
*
|
||||
* \attention The initialization function expects the object being previously zeroed
|
||||
* \attention Initialization is required every time the carrier and/or CORESET changes. No free is required between
|
||||
* initializations.
|
||||
*
|
||||
* @param q provides PDCCH DMRS estimator object
|
||||
* @param carrier provides the required carrier configuration for some estimation
|
||||
* @param coreset provides the required configuration for initialising the object
|
||||
* @return SRSLTE_SUCCESS if the configurations are valid, otherwise it returns an SRSLTE_ERROR code
|
||||
*/
|
||||
SRSLTE_API int srslte_dmrs_pdcch_estimator_init(srslte_dmrs_pdcch_estimator_t* q,
|
||||
const srslte_nr_carrier_t* carrier,
|
||||
const srslte_coreset_t* coreset);
|
||||
|
||||
/**
|
||||
* @brief Deallocate all memory allocated by a given PDCCH DMRS estimator object
|
||||
* @param q provides PDCCH DMRS estimator object
|
||||
*/
|
||||
SRSLTE_API void srslte_dmrs_pdcch_estimator_free(srslte_dmrs_pdcch_estimator_t* q);
|
||||
|
||||
/**
|
||||
* Estimates the PDCCH channel from the received PDCCH's DMRS. The DMRS are only transmitted in the REG where the
|
||||
* PDCCH is allocated.
|
||||
*
|
||||
* The most efficient way is estimating the overall CORESET independently whether there is data or not. Said so, the
|
||||
* output grid `estimates` shall be at least as big as the CORESET.
|
||||
*
|
||||
* This function is designed to be called prior to the PDCCH blind decoding and shall be called only once for every
|
||||
* CORESET.
|
||||
*
|
||||
* @param cfg Configuration that includes Carrier, CORESET and the Search Space
|
||||
* @param slot_idx Slot index in the frame
|
||||
* @param sf_symbols Received resource grid.
|
||||
* @param estimates CORESET Resource grid with the estimated channel
|
||||
* @return SRSLTE_SUCCESS if the configurations are valid, otherwise it returns an SRSLTE_ERROR code
|
||||
*/
|
||||
SRSLTE_API int srslte_dmrs_pdcch_estimate(srslte_dmrs_pdcch_estimator_t* q, uint32_t slot_idx, const cf_t* sf_symbols);
|
||||
|
||||
/**
|
||||
* @brief PDSCH DMRS measurement results
|
||||
*/
|
||||
typedef struct SRSLTE_API {
|
||||
/// Linear reference signal received power (RSRP). Measure correlation
|
||||
float rsrp;
|
||||
|
||||
/// Energy per resource element (EPRE)
|
||||
float epre;
|
||||
|
||||
/// CFO Measure in Hz (only available for durations 2 and 3)
|
||||
float cfo_hz;
|
||||
|
||||
/// Measure synchronization error in micro-seconds
|
||||
float sync_error_us;
|
||||
} srslte_dmrs_pdcch_measure_t;
|
||||
|
||||
/**
|
||||
* @brief Performs PDCCH DMRS measurements of a given PDCCH candidate for an aggregation level
|
||||
*
|
||||
* @note The measurement is useful for determining whether there is a PDCCH transmission in the given candidate.
|
||||
*
|
||||
* @attention The provided aggregation level and candidate need to be according to the search space.
|
||||
*
|
||||
* @param q provides PDCCH DMRS estimator object
|
||||
* @param search_space provides the search space
|
||||
* @param slot_idx Slot index in the frame
|
||||
* @param aggregation_level Indicates the aggregation level of the candidate to examine
|
||||
* @param candidate Indicates the candidate index of the available
|
||||
* @param rnti Indicates the UE RNTI (only used for UE search space type)
|
||||
* @param measure Provides the structure for storing the channel estimate measurements
|
||||
* @return SRSLTE_SUCCESS if the configurations are valid, otherwise it returns an SRSLTE_ERROR code
|
||||
*/
|
||||
int srslte_dmrs_pdcch_get_measure(srslte_dmrs_pdcch_estimator_t* q,
|
||||
const srslte_search_space_t* search_space,
|
||||
uint32_t slot_idx,
|
||||
uint32_t aggregation_level,
|
||||
uint32_t candidate,
|
||||
uint16_t rnti,
|
||||
srslte_dmrs_pdcch_measure_t* measure);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -39,6 +39,11 @@ extern "C" {
|
|||
*/
|
||||
#define SRSLTE_NR_MAX_NUMEROLOGY 4
|
||||
|
||||
/**
|
||||
* @brief Defines the symbol duration, including cyclic prefix
|
||||
*/
|
||||
#define SRSLTE_SUBC_SPACING(NUM) (15000U << (NUM))
|
||||
|
||||
/**
|
||||
* @brief Defines the number of slots per SF. Defined by TS 38.211 v15.8.0 Table 4.3.2-1.
|
||||
*/
|
||||
|
@ -80,18 +85,18 @@ typedef struct {
|
|||
#define SRSLTE_CORESET_FREQ_DOMAIN_RES_SIZE 45
|
||||
#define SRSLTE_CORESET_SHIFT_INDEX_MAX (SRSLTE_CORESET_NOF_PRB_MAX - 1)
|
||||
|
||||
typedef enum {
|
||||
typedef enum SRSLTE_API {
|
||||
srslte_coreset_mapping_type_interleaved = 0,
|
||||
srslte_coreset_mapping_type_non_interleaved,
|
||||
} srslte_coreset_mapping_type_t;
|
||||
|
||||
typedef enum {
|
||||
typedef enum SRSLTE_API {
|
||||
srslte_coreset_bundle_size_n2 = 0,
|
||||
srslte_coreset_bundle_size_n3,
|
||||
srslte_coreset_bundle_size_n6,
|
||||
} srslte_coreset_bundle_size_t;
|
||||
|
||||
typedef enum {
|
||||
typedef enum SRSLTE_API {
|
||||
srslte_coreset_precoder_granularity_contiguous = 0,
|
||||
srslte_coreset_precoder_granularity_reg_bundle
|
||||
} srslte_coreset_precoder_granularity_t;
|
||||
|
@ -102,11 +107,11 @@ typedef enum {
|
|||
* Fields follow the same order than described in 3GPP 38.331 R15 - ControlResourceSet
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
typedef struct SRSLTE_API {
|
||||
srslte_coreset_mapping_type_t mapping_type;
|
||||
uint32_t id;
|
||||
uint32_t duration;
|
||||
bool freq_domain_resources[SRSLTE_CORESET_FREQ_DOMAIN_RES_SIZE];
|
||||
bool freq_resources[SRSLTE_CORESET_FREQ_DOMAIN_RES_SIZE];
|
||||
srslte_coreset_bundle_size_t interleaver_size;
|
||||
|
||||
bool dmrs_scrambling_id_present;
|
||||
|
@ -117,21 +122,21 @@ typedef struct {
|
|||
/** Missing TCI parameters */
|
||||
} srslte_coreset_t;
|
||||
|
||||
typedef enum {
|
||||
typedef enum SRSLTE_API {
|
||||
srslte_search_space_type_common = 0,
|
||||
srslte_search_space_type_ue,
|
||||
} srslte_search_space_type_t;
|
||||
|
||||
#define SRSLTE_SEARCH_SPACE_NOF_AGGREGATION_LEVELS 5
|
||||
|
||||
typedef struct {
|
||||
typedef struct SRSLTE_API {
|
||||
uint32_t start; // start symbol within slot
|
||||
uint32_t duration; // in slots
|
||||
srslte_search_space_type_t type;
|
||||
uint32_t nof_candidates[SRSLTE_SEARCH_SPACE_NOF_AGGREGATION_LEVELS];
|
||||
} srslte_search_space_t;
|
||||
|
||||
typedef struct {
|
||||
typedef struct SRSLTE_API {
|
||||
srslte_nr_carrier_t carrier;
|
||||
uint16_t rnti;
|
||||
srslte_coreset_t coreset;
|
||||
|
@ -140,6 +145,30 @@ typedef struct {
|
|||
uint32_t aggregation_level;
|
||||
} srslte_nr_pdcch_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Calculates the bandwidth of a given CORESET in physical resource blocks (PRB) . This function uses the
|
||||
* frequency domain resources bit-map for counting the number of PRB.
|
||||
*
|
||||
* @attention Prior to this function call, the frequency domain resources bit-map shall be zeroed beyond the
|
||||
* carrier.nof_prb / 6 index, otherwise the CORESET bandwidth might be greater than the carrier.
|
||||
*
|
||||
* @param coreset provides the given CORESET configuration
|
||||
* @return The number of PRB that the CORESET takes in frequency domain
|
||||
*/
|
||||
SRSLTE_API uint32_t srslte_coreset_get_bw(const srslte_coreset_t* coreset);
|
||||
|
||||
/**
|
||||
* @brief Calculates the number of Physical Resource Elements (time and frequency domain) that a given CORESET uses.
|
||||
* This function uses the frequency domain resources bit-map and duration to compute the number of symbols.
|
||||
*
|
||||
* @attention Prior to this function call, the frequency domain resources bit-map shall be zeroed beyond the
|
||||
* carrier.nof_prb / 6 index, otherwise the CORESET bandwidth might be greater than the carrier.
|
||||
*
|
||||
* @param coreset provides the given CORESET configuration
|
||||
* @return The number of resource elements that compose the coreset
|
||||
*/
|
||||
SRSLTE_API uint32_t srslte_coreset_get_sz(const srslte_coreset_t* coreset);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -21,6 +21,10 @@
|
|||
|
||||
#include "srslte/phy/ch_estimation/dmrs_pdcch.h"
|
||||
|
||||
/// @brief Every frequency resource is 6 Resource blocks, every resource block carries 3 pilots. So 18 possible pilots
|
||||
/// per frequency resource.
|
||||
#define NOF_PILOTS_X_FREQ_RES 18
|
||||
|
||||
uint32_t srslte_pdcch_calculate_Y_p_n(uint32_t coreset_id, uint16_t rnti, int n)
|
||||
{
|
||||
const uint32_t A_p[3] = {39827, 39829, 39839};
|
||||
|
@ -38,27 +42,34 @@ uint32_t srslte_pdcch_calculate_Y_p_n(uint32_t coreset_id, uint16_t rnti, int n)
|
|||
* downlink control channel assignment
|
||||
*
|
||||
*/
|
||||
int srslte_pdcch_get_ncce(const srslte_nr_pdcch_cfg_t* cfg, const srslte_dl_sf_cfg_t* dl_sf)
|
||||
static int srslte_pdcch_get_ncce(const srslte_coreset_t* coreset,
|
||||
const srslte_search_space_t* search_space,
|
||||
uint16_t rnti,
|
||||
uint32_t aggregation_level,
|
||||
uint32_t candidate,
|
||||
const uint32_t slot_idx)
|
||||
{
|
||||
if (cfg->aggregation_level >= SRSLTE_SEARCH_SPACE_NOF_AGGREGATION_LEVELS) {
|
||||
ERROR("Invalid aggregation level %d;\n", cfg->aggregation_level);
|
||||
if (aggregation_level >= SRSLTE_SEARCH_SPACE_NOF_AGGREGATION_LEVELS) {
|
||||
ERROR("Invalid aggregation level %d;\n", aggregation_level);
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
uint32_t L = 1U << cfg->aggregation_level; // Aggregation level
|
||||
uint32_t n_ci = 0; // Carrier indicator field
|
||||
uint32_t m = cfg->candidate; // Selected PDDCH candidate
|
||||
uint32_t M = cfg->search_space.nof_candidates[cfg->aggregation_level]; // Number of aggregation levels
|
||||
uint32_t L = 1U << aggregation_level; // Aggregation level
|
||||
uint32_t n_ci = 0; // Carrier indicator field
|
||||
uint32_t m = candidate; // Selected PDDCH candidate
|
||||
uint32_t M = search_space->nof_candidates[aggregation_level]; // Number of aggregation levels
|
||||
|
||||
if (M == 0) {
|
||||
ERROR("Invalid number of candidates %d for aggregation level %d\n", M, cfg->aggregation_level);
|
||||
ERROR("Invalid number of candidates %d for aggregation level %d\n", M, aggregation_level);
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
// Count number of REG
|
||||
uint32_t N_cce = 0;
|
||||
for (uint32_t i = 0; i < SRSLTE_CORESET_FREQ_DOMAIN_RES_SIZE; i++) {
|
||||
N_cce += cfg->coreset.freq_domain_resources[i] ? cfg->coreset.duration : 0;
|
||||
// Every frequency domain resource is 6 PRB, a REG is 1PRB wide and a CCE is 6 REG. So, for every frequency domain
|
||||
// resource there is one CCE.
|
||||
N_cce += coreset->freq_resources[i] ? coreset->duration : 0;
|
||||
}
|
||||
|
||||
if (N_cce < L) {
|
||||
|
@ -68,9 +79,8 @@ int srslte_pdcch_get_ncce(const srslte_nr_pdcch_cfg_t* cfg, const srslte_dl_sf_c
|
|||
|
||||
// Calculate Y_p_n
|
||||
uint32_t Y_p_n = 0;
|
||||
if (cfg->search_space.type == srslte_search_space_type_ue) {
|
||||
Y_p_n = srslte_pdcch_calculate_Y_p_n(
|
||||
cfg->coreset.id, cfg->rnti, dl_sf->tti % SRSLTE_NR_NSLOTS_PER_FRAME(cfg->carrier.numerology));
|
||||
if (search_space->type == srslte_search_space_type_ue) {
|
||||
Y_p_n = srslte_pdcch_calculate_Y_p_n(coreset->id, rnti, slot_idx);
|
||||
}
|
||||
|
||||
return (int)(L * ((Y_p_n + (m * N_cce) / (L * M) + n_ci) % (N_cce / L)));
|
||||
|
@ -100,21 +110,17 @@ dmrs_pdcch_put_symbol_noninterleaved(const srslte_nr_pdcch_cfg_t* cfg, uint32_t
|
|||
// CORESET Resource Block counter
|
||||
uint32_t rb_coreset_idx = 0;
|
||||
for (uint32_t i = 0; i < nof_freq_res; i++) {
|
||||
// Every frequency resource is 6 Resource blocks, every resource block carries 3 pilots. So 18 possible pilots per
|
||||
// frequency resource.
|
||||
const uint32_t nof_pilots_x_resource = 18;
|
||||
|
||||
// Skip frequency resource if outside of the CORESET
|
||||
if (!cfg->coreset.freq_domain_resources[i]) {
|
||||
if (!cfg->coreset.freq_resources[i]) {
|
||||
// Skip possible DMRS locations in this region
|
||||
sequence_skip += nof_pilots_x_resource;
|
||||
sequence_skip += NOF_PILOTS_X_FREQ_RES;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if the frequency resource highest RB is lower than the first CCE resource block.
|
||||
if ((rb_coreset_idx + 6) <= rb_coreset_idx_begin) {
|
||||
// Skip possible DMRS locations in this region
|
||||
sequence_skip += nof_pilots_x_resource;
|
||||
sequence_skip += NOF_PILOTS_X_FREQ_RES;
|
||||
|
||||
// Since this is part of the CORESET, count the RB as CORESET
|
||||
rb_coreset_idx += 6;
|
||||
|
@ -131,8 +137,8 @@ dmrs_pdcch_put_symbol_noninterleaved(const srslte_nr_pdcch_cfg_t* cfg, uint32_t
|
|||
sequence_skip = 0;
|
||||
|
||||
// Generate pilots
|
||||
cf_t rl[nof_pilots_x_resource];
|
||||
srslte_sequence_state_gen_f(&sequence_state, M_SQRT1_2, (float*)rl, nof_pilots_x_resource * 2);
|
||||
cf_t rl[NOF_PILOTS_X_FREQ_RES];
|
||||
srslte_sequence_state_gen_f(&sequence_state, M_SQRT1_2, (float*)rl, NOF_PILOTS_X_FREQ_RES * 2);
|
||||
|
||||
// For each RB in the frequency resource
|
||||
for (uint32_t j = 0; j < 6; j++) {
|
||||
|
@ -155,16 +161,17 @@ dmrs_pdcch_put_symbol_noninterleaved(const srslte_nr_pdcch_cfg_t* cfg, uint32_t
|
|||
// Calculate sub-carrier index
|
||||
uint32_t k = n * SRSLTE_NRE + 4 * k_prime + 1;
|
||||
|
||||
sf_symbol[k] = rl[(3 * n + k_prime) % nof_pilots_x_resource];
|
||||
sf_symbol[k] = rl[3 * j + k_prime];
|
||||
}
|
||||
rb_coreset_idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int srslte_dmrs_pdcch_put(const srslte_nr_pdcch_cfg_t* cfg, const srslte_dl_sf_cfg_t* dl_sf, cf_t* sf_symbols)
|
||||
int srslte_dmrs_pdcch_put(const srslte_nr_pdcch_cfg_t* cfg, uint32_t slot_idx, cf_t* sf_symbols)
|
||||
{
|
||||
int ncce = srslte_pdcch_get_ncce(cfg, dl_sf);
|
||||
int ncce = srslte_pdcch_get_ncce(
|
||||
&cfg->coreset, &cfg->search_space, cfg->rnti, cfg->aggregation_level, cfg->candidate, slot_idx);
|
||||
if (ncce < SRSLTE_SUCCESS) {
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
@ -190,11 +197,263 @@ int srslte_dmrs_pdcch_put(const srslte_nr_pdcch_cfg_t* cfg, const srslte_dl_sf_c
|
|||
|
||||
for (uint32_t l = 0; l < cfg->coreset.duration; l++) {
|
||||
// Get Cin
|
||||
uint32_t cinit = dmrs_pdcch_get_cinit(dl_sf->tti % SRSLTE_NR_NSLOTS_PER_FRAME(cfg->carrier.numerology), l, n_id);
|
||||
uint32_t cinit = dmrs_pdcch_get_cinit(slot_idx, l, n_id);
|
||||
|
||||
// Put data
|
||||
dmrs_pdcch_put_symbol_noninterleaved(cfg, cinit, ncce, &sf_symbols[cfg->carrier.nof_prb * SRSLTE_NRE * l]);
|
||||
}
|
||||
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
int srslte_dmrs_pdcch_estimator_init(srslte_dmrs_pdcch_estimator_t* q,
|
||||
const srslte_nr_carrier_t* carrier,
|
||||
const srslte_coreset_t* coreset)
|
||||
{
|
||||
if (coreset->duration < SRSLTE_CORESET_DURATION_MIN || coreset->duration > SRSLTE_CORESET_DURATION_MAX) {
|
||||
ERROR("Error CORESET duration %d is out-of-bounds (%d,%d)\n",
|
||||
coreset->duration,
|
||||
SRSLTE_CORESET_DURATION_MIN,
|
||||
SRSLTE_CORESET_DURATION_MAX);
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
// The carrier configuration is not used for initialization, so copy it always
|
||||
q->carrier = *carrier;
|
||||
|
||||
// Detect change in CORESET, if none, return early
|
||||
if (memcmp(&q->coreset, coreset, sizeof(srslte_coreset_t)) == 0) {
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
// Save new CORESET
|
||||
q->coreset = *coreset;
|
||||
|
||||
// The interpolator may return without reconfiguring after the first call
|
||||
if (srslte_resampler_fft_init(&q->interpolator, SRSLTE_RESAMPLER_MODE_INTERPOLATE, 4)) {
|
||||
ERROR("Initiating interpolator\n");
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
// Calculate new bandwidth and size
|
||||
uint32_t coreset_bw = srslte_coreset_get_bw(coreset);
|
||||
uint32_t coreset_sz = srslte_coreset_get_sz(coreset);
|
||||
|
||||
// Reallocate only if the CORESET size or bandwidth changed
|
||||
if (q->coreset_bw != coreset_bw || q->coreset_sz != coreset_sz) {
|
||||
// Iterate all possible symbols
|
||||
for (uint32_t l = 0; l < SRSLTE_CORESET_DURATION_MAX; l++) {
|
||||
// Free if allocated
|
||||
if (q->lse[l] != NULL) {
|
||||
free(q->lse[l]);
|
||||
q->lse[l] = NULL;
|
||||
}
|
||||
|
||||
// Allocate
|
||||
if (l < coreset->duration) {
|
||||
// Allocate for 3 pilots per physical resource block
|
||||
q->lse[l] = srslte_vec_cf_malloc(coreset_bw * 3);
|
||||
}
|
||||
}
|
||||
|
||||
if (q->ce) {
|
||||
free(q->ce);
|
||||
}
|
||||
q->ce = srslte_vec_cf_malloc(coreset_sz);
|
||||
}
|
||||
|
||||
// Save new calculated values
|
||||
q->coreset_bw = coreset_bw;
|
||||
q->coreset_sz = coreset_sz;
|
||||
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
void srslte_dmrs_pdcch_estimator_free(srslte_dmrs_pdcch_estimator_t* q)
|
||||
{
|
||||
if (q->ce) {
|
||||
free(q->ce);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < SRSLTE_CORESET_DURATION_MAX; i++) {
|
||||
if (q->lse[i]) {
|
||||
free(q->lse[i]);
|
||||
}
|
||||
}
|
||||
|
||||
srslte_resampler_fft_free(&q->interpolator);
|
||||
|
||||
memset(q, 0, sizeof(srslte_dmrs_pdcch_estimator_t));
|
||||
}
|
||||
|
||||
static void
|
||||
srslte_dmrs_pdcch_extract(srslte_dmrs_pdcch_estimator_t* q, uint32_t cinit, const cf_t* sf_symbol, cf_t* lse)
|
||||
{
|
||||
// Initialise pseudo-random sequence
|
||||
srslte_sequence_state_t sequence_state = {};
|
||||
srslte_sequence_state_init(&sequence_state, cinit);
|
||||
|
||||
// Counter for skipping unused pilot locations
|
||||
uint32_t sequence_skip = 0;
|
||||
|
||||
// Counts enabled frequency domain resources
|
||||
uint32_t rb_coreset_idx = 0;
|
||||
// Iterate over all possible frequency resources
|
||||
for (uint32_t i = 0; i < SRSLTE_CORESET_FREQ_DOMAIN_RES_SIZE; i++) {
|
||||
// Skip disabled frequency resources
|
||||
if (!q->coreset.freq_resources[i]) {
|
||||
sequence_skip += NOF_PILOTS_X_FREQ_RES;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip unused pilots
|
||||
srslte_sequence_state_advance(&sequence_state, 2 * sequence_skip);
|
||||
sequence_skip = 0;
|
||||
|
||||
// Generate sequence
|
||||
cf_t rl[NOF_PILOTS_X_FREQ_RES];
|
||||
srslte_sequence_state_gen_f(&sequence_state, M_SQRT1_2, (float*)rl, NOF_PILOTS_X_FREQ_RES * 2);
|
||||
|
||||
// Iterate all PRBs in the enabled frequency domain resource
|
||||
for (uint32_t j = 0, idx = rb_coreset_idx * NOF_PILOTS_X_FREQ_RES; j < 6; j++) {
|
||||
// Calculate Grid PRB index (n)
|
||||
uint32_t n = i * 6 + j;
|
||||
|
||||
// For each pilot in the PRB
|
||||
for (uint32_t k_prime = 0; k_prime < 3; k_prime++, idx++) {
|
||||
// Calculate sub-carrier index
|
||||
uint32_t k = n * SRSLTE_NRE + 4 * k_prime + 1;
|
||||
|
||||
// Extract symbol
|
||||
lse[idx] = sf_symbol[k];
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate least squared estimates
|
||||
cf_t* lse_ptr = &lse[rb_coreset_idx * NOF_PILOTS_X_FREQ_RES];
|
||||
srslte_vec_prod_conj_ccc(lse_ptr, rl, lse_ptr, NOF_PILOTS_X_FREQ_RES);
|
||||
|
||||
// Increment frequency domain resource counter
|
||||
rb_coreset_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
int srslte_dmrs_pdcch_estimate(srslte_dmrs_pdcch_estimator_t* q, uint32_t slot_idx, const cf_t* sf_symbols)
|
||||
{
|
||||
// Saves slot index for posterior use
|
||||
q->slot_idx = slot_idx;
|
||||
|
||||
// Use cell id if the DMR scrambling id is not provided by higher layers
|
||||
uint32_t n_id = q->carrier.id;
|
||||
if (q->coreset.dmrs_scrambling_id_present) {
|
||||
n_id = q->coreset.dmrs_scrambling_id;
|
||||
}
|
||||
|
||||
// Extract pilots
|
||||
for (uint32_t l = 0; l < q->coreset.duration; l++) {
|
||||
// Calculate PRN sequence initial state
|
||||
uint32_t cinit = dmrs_pdcch_get_cinit(slot_idx, l, n_id);
|
||||
|
||||
// Extract pilots least square estimates
|
||||
srslte_dmrs_pdcch_extract(q, cinit, &sf_symbols[l * q->carrier.nof_prb * SRSLTE_NRE], q->lse[l]);
|
||||
}
|
||||
|
||||
// Time averaging should be implemented here
|
||||
// ...
|
||||
|
||||
// Interpolator impulse response
|
||||
uint32_t interpolation_delay = srslte_resampler_fft_get_delay(&q->interpolator) / 4;
|
||||
|
||||
// Interpolation, it assumes all frequency domain resources are contiguous
|
||||
for (uint32_t l = 0; l < q->coreset.duration; l++) {
|
||||
cf_t* ce_ptr = &q->ce[SRSLTE_NRE * q->coreset_bw * l];
|
||||
|
||||
srslte_resampler_fft_reset_state(&q->interpolator);
|
||||
|
||||
// Feed inital samples
|
||||
uint32_t discard_initial = SRSLTE_MIN(interpolation_delay, q->coreset_bw * 3);
|
||||
srslte_resampler_fft_run(&q->interpolator, q->lse[l], NULL, discard_initial);
|
||||
uint32_t n = 0;
|
||||
|
||||
// Pad zeroes until impulsional response is covered
|
||||
if (discard_initial < interpolation_delay) {
|
||||
srslte_resampler_fft_run(&q->interpolator, NULL, NULL, interpolation_delay - discard_initial);
|
||||
} else {
|
||||
n = q->coreset_bw * 3 - discard_initial;
|
||||
srslte_resampler_fft_run(&q->interpolator, q->lse[l], ce_ptr, n);
|
||||
}
|
||||
|
||||
srslte_resampler_fft_run(&q->interpolator, NULL, &ce_ptr[n * 4], q->coreset_bw * 3 - n);
|
||||
}
|
||||
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
int srslte_dmrs_pdcch_get_measure(srslte_dmrs_pdcch_estimator_t* q,
|
||||
const srslte_search_space_t* search_space,
|
||||
uint32_t slot_idx,
|
||||
uint32_t aggregation_level,
|
||||
uint32_t candidate,
|
||||
uint16_t rnti,
|
||||
srslte_dmrs_pdcch_measure_t* measure)
|
||||
{
|
||||
uint32_t L = 1U << aggregation_level;
|
||||
int ncce = srslte_pdcch_get_ncce(&q->coreset, search_space, rnti, aggregation_level, candidate, slot_idx);
|
||||
if (ncce < SRSLTE_SUCCESS) {
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
if (q->coreset.mapping_type == srslte_coreset_mapping_type_interleaved) {
|
||||
ERROR("Error interleaved mapping not implemented\n");
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
// Check that CORESET duration is not less than minimum
|
||||
if (q->coreset.duration < SRSLTE_CORESET_DURATION_MIN) {
|
||||
ERROR("Invalid CORESET duration\n");
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
// Get base pilot;
|
||||
uint32_t pilot_idx = (ncce * 18) / q->coreset.duration;
|
||||
uint32_t nof_pilots = (L * 18) / q->coreset.duration;
|
||||
|
||||
float rsrp = 0.0f;
|
||||
float epre = 0.0f;
|
||||
float cfo = 0.0f;
|
||||
float sync_err = 0.0f;
|
||||
cf_t corr[SRSLTE_CORESET_DURATION_MAX] = {};
|
||||
for (uint32_t l = 0; l < q->coreset.duration; l++) {
|
||||
// Correlate DMRS
|
||||
corr[l] = srslte_vec_acc_cc(&q->lse[l][pilot_idx], nof_pilots) / (float)nof_pilots;
|
||||
|
||||
// Measure symbol RSRP
|
||||
rsrp += cabsf(corr[l]);
|
||||
|
||||
// Measure symbol EPRE
|
||||
epre += srslte_vec_avg_power_cf(&q->lse[l][pilot_idx], nof_pilots);
|
||||
|
||||
// Measure CFO only from the second and third symbols
|
||||
if (l != 0) {
|
||||
cfo += cargf(corr[l] * conjf(corr[l - 1]));
|
||||
}
|
||||
|
||||
// Measure synchronization error
|
||||
sync_err += srslte_vec_estimate_frequency(&q->lse[l][pilot_idx], nof_pilots);
|
||||
}
|
||||
|
||||
if (q->coreset.duration > 1) {
|
||||
cfo /= (float)(q->coreset.duration - 1);
|
||||
}
|
||||
|
||||
// Symbol time, including cyclic prefix. Required for CFO estimation
|
||||
float Ts = (71.3541666667f / (float)(1 << q->carrier.numerology));
|
||||
|
||||
measure->rsrp = rsrp / (float)q->coreset.duration;
|
||||
measure->epre = epre / (float)q->coreset.duration;
|
||||
measure->cfo_hz = cfo / (2.0f * (float)M_PI * Ts);
|
||||
measure->sync_error_us =
|
||||
(float)SRSLTE_SUBC_SPACING(q->carrier.numerology) * sync_err / (4.0e-6f * (float)q->coreset.duration);
|
||||
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
|
@ -67,23 +67,34 @@ void parse_args(int argc, char** argv)
|
|||
}
|
||||
}
|
||||
|
||||
static int run_test(const srslte_nr_pdcch_cfg_t* cfg, cf_t* sf_symbols, cf_t* h)
|
||||
static int
|
||||
run_test(srslte_dmrs_pdcch_estimator_t* estimator, const srslte_nr_pdcch_cfg_t* cfg, cf_t* sf_symbols, cf_t* h)
|
||||
{
|
||||
srslte_dl_sf_cfg_t dl_sf = {};
|
||||
for (dl_sf.tti = 0; dl_sf.tti < SRSLTE_NOF_SF_X_FRAME; dl_sf.tti++) {
|
||||
TESTASSERT(srslte_dmrs_pdcch_put(cfg, &dl_sf, sf_symbols) == SRSLTE_SUCCESS);
|
||||
for (uint32_t slot_idx = 0; slot_idx < SRSLTE_NR_NSLOTS_PER_SF(cfg->carrier.numerology); slot_idx++) {
|
||||
uint32_t nof_re = carrier.nof_prb * SRSLTE_NRE * SRSLTE_NR_NSYMB_PER_SLOT;
|
||||
srslte_vec_cf_zero(sf_symbols, nof_re);
|
||||
|
||||
/*srslte_dmrs_pdsch_get_sf(cfg, &dl_sf, sf_symbols, h);
|
||||
TESTASSERT(srslte_dmrs_pdcch_put(cfg, slot_idx, sf_symbols) == SRSLTE_SUCCESS);
|
||||
|
||||
float mse = 0.0f;
|
||||
for (uint32_t i = 0; i < dmrs_pdsch->nof_symbols * dmrs_pdsch->nof_sc * SRSLTE_NRE; i++) {
|
||||
cf_t err = h[i] - 1.0f;
|
||||
mse += cabsf(err);
|
||||
TESTASSERT(srslte_dmrs_pdcch_estimate(estimator, slot_idx, sf_symbols) == SRSLTE_SUCCESS);
|
||||
|
||||
srslte_dmrs_pdcch_measure_t measure = {};
|
||||
TESTASSERT(
|
||||
srslte_dmrs_pdcch_get_measure(
|
||||
estimator, &cfg->search_space, slot_idx, cfg->aggregation_level, cfg->candidate, cfg->rnti, &measure) ==
|
||||
SRSLTE_SUCCESS);
|
||||
|
||||
if (fabsf(measure.rsrp - 1.0f) > 1e-2) {
|
||||
printf("EPRE=%f; RSRP=%f; CFO=%f; SYNC_ERR=%f;\n",
|
||||
measure.epre,
|
||||
measure.rsrp,
|
||||
measure.cfo_hz,
|
||||
measure.sync_error_us);
|
||||
}
|
||||
mse /= (float)dmrs_pdsch->nof_symbols * dmrs_pdsch->nof_sc;
|
||||
|
||||
TESTASSERT(!isnan(mse));
|
||||
TESTASSERT(mse < 1e-6f);*/
|
||||
TESTASSERT(fabsf(measure.epre - 1.0f) < 1e-3f);
|
||||
TESTASSERT(fabsf(measure.rsrp - 1.0f) < 1e-3f);
|
||||
TESTASSERT(fabsf(measure.cfo_hz) < 1e-3f);
|
||||
TESTASSERT(fabsf(measure.sync_error_us) < 1e-3f);
|
||||
}
|
||||
|
||||
return SRSLTE_SUCCESS;
|
||||
|
@ -95,9 +106,10 @@ int main(int argc, char** argv)
|
|||
|
||||
parse_args(argc, argv);
|
||||
|
||||
srslte_nr_pdcch_cfg_t cfg = {};
|
||||
srslte_nr_pdcch_cfg_t cfg = {};
|
||||
srslte_dmrs_pdcch_estimator_t estimator = {};
|
||||
|
||||
uint32_t nof_re = carrier.nof_prb * SRSLTE_NRE * SRSLTE_NOF_SLOTS_PER_SF * SRSLTE_MAX_NSYMB;
|
||||
uint32_t nof_re = carrier.nof_prb * SRSLTE_NRE * SRSLTE_NR_NSYMB_PER_SLOT;
|
||||
cf_t* sf_symbols = srslte_vec_cf_malloc(nof_re);
|
||||
cf_t* h = srslte_vec_cf_malloc(nof_re);
|
||||
|
||||
|
@ -112,8 +124,8 @@ int main(int argc, char** argv)
|
|||
for (uint32_t frequency_resources = 1; frequency_resources < (1U << nof_frequency_resource); frequency_resources++) {
|
||||
uint32_t nof_freq_resources = 0;
|
||||
for (uint32_t i = 0; i < nof_frequency_resource; i++) {
|
||||
uint32_t mask = ((frequency_resources >> i) & 1U);
|
||||
cfg.coreset.freq_domain_resources[i] = (mask == 1);
|
||||
uint32_t mask = ((frequency_resources >> i) & 1U);
|
||||
cfg.coreset.freq_resources[i] = (mask == 1);
|
||||
nof_freq_resources += mask;
|
||||
}
|
||||
|
||||
|
@ -136,7 +148,9 @@ int main(int argc, char** argv)
|
|||
for (cfg.candidate = 0; cfg.candidate < cfg.search_space.nof_candidates[cfg.aggregation_level];
|
||||
cfg.candidate++) {
|
||||
|
||||
if (run_test(&cfg, sf_symbols, h)) {
|
||||
srslte_dmrs_pdcch_estimator_init(&estimator, &cfg.carrier, &cfg.coreset);
|
||||
|
||||
if (run_test(&estimator, &cfg, sf_symbols, h)) {
|
||||
ERROR("Test %d failed\n", test_counter);
|
||||
} else {
|
||||
test_passed++;
|
||||
|
@ -148,6 +162,8 @@ int main(int argc, char** argv)
|
|||
}
|
||||
}
|
||||
|
||||
srslte_dmrs_pdcch_estimator_free(&estimator);
|
||||
|
||||
if (sf_symbols) {
|
||||
free(sf_symbols);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
# and at http://www.gnu.org/licenses/.
|
||||
#
|
||||
|
||||
set(SOURCES phy_common.c phy_common_sl.c sequence.c timestamp.c)
|
||||
set(SOURCES phy_common.c phy_common_sl.c phy_common_nr.c sequence.c timestamp.c)
|
||||
add_library(srslte_phy_common OBJECT ${SOURCES})
|
||||
|
||||
add_subdirectory(test)
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2013-2020 Software Radio Systems Limited
|
||||
*
|
||||
* This file is part of srsLTE.
|
||||
*
|
||||
* srsLTE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* srsLTE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* A copy of the GNU Affero General Public License can be found in
|
||||
* the LICENSE file in the top-level directory of this distribution
|
||||
* and at http://www.gnu.org/licenses/.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <srslte/phy/common/phy_common_nr.h>
|
||||
|
||||
uint32_t srslte_coreset_get_bw(const srslte_coreset_t* coreset)
|
||||
{
|
||||
uint32_t prb_count = 0;
|
||||
|
||||
// Iterate all the frequency domain resources bit-map...
|
||||
for (uint32_t i = 0; i < SRSLTE_CORESET_FREQ_DOMAIN_RES_SIZE; i++) {
|
||||
// ... and count 6 PRB for everu frequency domain resource that it is enabled
|
||||
if (coreset->freq_resources[i]) {
|
||||
prb_count += 6;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the total count of physical resource blocks
|
||||
return prb_count;
|
||||
}
|
||||
|
||||
uint32_t srslte_coreset_get_sz(const srslte_coreset_t* coreset)
|
||||
{
|
||||
// Returns the number of resource elements in time and frequency domains
|
||||
return srslte_coreset_get_bw(coreset) * SRSLTE_NRE * coreset->duration;
|
||||
}
|
Loading…
Reference in New Issue