Added PDCCH DMRS estimation and measurement

This commit is contained in:
Xavier Arteaga 2020-09-29 18:45:10 +02:00 committed by Xavier Arteaga
parent 5799100e42
commit 937c52339d
6 changed files with 527 additions and 54 deletions

View File

@ -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
}

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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)

View File

@ -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;
}