Added CSI-RS unit test and fix defects

master
Xavier Arteaga 4 years ago committed by Andre Puschmann
parent 6127681102
commit d6b5229dbe

@ -88,8 +88,8 @@ typedef struct SRSLTE_API {
typedef struct SRSLTE_API { typedef struct SRSLTE_API {
srslte_csi_rs_resource_mapping_t resource_mapping; srslte_csi_rs_resource_mapping_t resource_mapping;
int8_t power_control_offset; // -8..15 dB float power_control_offset; // -8..15 dB
int8_t power_control_offset_ss; // -3, 0, 3, 6 dB float power_control_offset_ss; // -3, 0, 3, 6 dB
uint32_t scrambling_id; // 0..1023 uint32_t scrambling_id; // 0..1023
@ -107,6 +107,10 @@ typedef struct SRSLTE_API {
float rsrp_dB; float rsrp_dB;
float epre; float epre;
float epre_dB; float epre_dB;
float n0;
float n0_dB;
float snr_dB;
uint32_t nof_re;
} srslte_csi_rs_measure_t; } srslte_csi_rs_measure_t;
SRSLTE_API int srslte_csi_rs_nzp_measure(const srslte_carrier_nr_t* carrier, SRSLTE_API int srslte_csi_rs_nzp_measure(const srslte_carrier_nr_t* carrier,
@ -115,4 +119,6 @@ SRSLTE_API int srslte_csi_rs_nzp_measure(const srslte_carrier_nr_t* car
const cf_t* grid, const cf_t* grid,
srslte_csi_rs_measure_t* measure); srslte_csi_rs_measure_t* measure);
SRSLTE_API uint32_t srslte_csi_rs_measure_info(const srslte_csi_rs_measure_t* measure, char* str, uint32_t str_len);
#endif // SRSLTE_CSI_RS_H_ #endif // SRSLTE_CSI_RS_H_

@ -75,7 +75,7 @@ static int csi_rs_location_get_k_list(const srslte_csi_rs_resource_mapping_t* re
resource->density == srslte_csi_rs_resource_mapping_density_three && resource->cdm == srslte_csi_rs_cdm_nocdm) { resource->density == srslte_csi_rs_resource_mapping_density_three && resource->cdm == srslte_csi_rs_cdm_nocdm) {
k_list[0] = k0; k_list[0] = k0;
k_list[1] = k0 + 4; k_list[1] = k0 + 4;
k_list[3] = k0 + 8; k_list[2] = k0 + 8;
return 3; return 3;
} }
@ -151,6 +151,37 @@ uint32_t csi_rs_count(srslte_csi_rs_density_t density, uint32_t nprb)
return 0; return 0;
} }
uint32_t csi_rs_rb_begin(const srslte_carrier_nr_t* carrier, const srslte_csi_rs_resource_mapping_t* m)
{
uint32_t ret = SRSLTE_MAX(carrier->start, m->freq_band.start_rb);
if ((m->density == srslte_csi_rs_resource_mapping_density_dot5_even && ret % 2 == 1) ||
(m->density == srslte_csi_rs_resource_mapping_density_dot5_odd && ret % 2 == 0)) {
ret++;
}
return ret;
}
uint32_t csi_rs_rb_end(const srslte_carrier_nr_t* carrier, const srslte_csi_rs_resource_mapping_t* m)
{
return SRSLTE_MIN(carrier->start + carrier->nof_prb, m->freq_band.start_rb + m->freq_band.nof_rb);
}
uint32_t csi_rs_rb_stride(const srslte_csi_rs_resource_mapping_t* m)
{
uint32_t ret = 1;
// Special .5 density cases
if (m->density == srslte_csi_rs_resource_mapping_density_dot5_even ||
m->density == srslte_csi_rs_resource_mapping_density_dot5_odd) {
// Skip one RB
ret = 2;
}
return ret;
}
int srslte_csi_rs_nzp_put(const srslte_carrier_nr_t* carrier, int srslte_csi_rs_nzp_put(const srslte_carrier_nr_t* carrier,
const srslte_dl_slot_cfg_t* slot_cfg, const srslte_dl_slot_cfg_t* slot_cfg,
const srslte_csi_rs_nzp_resource_t* resource, const srslte_csi_rs_nzp_resource_t* resource,
@ -173,9 +204,9 @@ int srslte_csi_rs_nzp_put(const srslte_carrier_nr_t* carrier,
} }
// Calculate Resource Block boundaries // Calculate Resource Block boundaries
uint32_t rb_begin = resource->resource_mapping.freq_band.start_rb; uint32_t rb_begin = csi_rs_rb_begin(carrier, &resource->resource_mapping);
uint32_t rb_end = resource->resource_mapping.freq_band.start_rb + resource->resource_mapping.freq_band.nof_rb; uint32_t rb_end = csi_rs_rb_end(carrier, &resource->resource_mapping);
uint32_t rb_stride = 1; uint32_t rb_stride = csi_rs_rb_stride(&resource->resource_mapping);
// Calculate power allocation // Calculate power allocation
float beta = srslte_convert_dB_to_amplitude((float)resource->power_control_offset); float beta = srslte_convert_dB_to_amplitude((float)resource->power_control_offset);
@ -183,21 +214,6 @@ int srslte_csi_rs_nzp_put(const srslte_carrier_nr_t* carrier,
beta = 1.0f; beta = 1.0f;
} }
// Special .5 density cases
if (resource->resource_mapping.density == srslte_csi_rs_resource_mapping_density_dot5_even ||
resource->resource_mapping.density == srslte_csi_rs_resource_mapping_density_dot5_odd) {
// Increase the start by one if:
// - Even and starts with odd
// - Odd and starts with even
if ((resource->resource_mapping.density == srslte_csi_rs_resource_mapping_density_dot5_even && rb_begin % 2 == 1) ||
(resource->resource_mapping.density == srslte_csi_rs_resource_mapping_density_dot5_odd && rb_begin % 2 == 0)) {
rb_begin++;
}
// Skip one RB
rb_stride = 2;
}
for (int l_idx = 0; l_idx < nof_l; l_idx++) { for (int l_idx = 0; l_idx < nof_l; l_idx++) {
// Get symbol index // Get symbol index
uint32_t l = l_list[l_idx]; uint32_t l = l_list[l_idx];
@ -259,30 +275,9 @@ int srslte_csi_rs_nzp_measure(const srslte_carrier_nr_t* carrier,
} }
// Calculate Resource Block boundaries // Calculate Resource Block boundaries
uint32_t rb_begin = resource->resource_mapping.freq_band.start_rb; uint32_t rb_begin = csi_rs_rb_begin(carrier, &resource->resource_mapping);
uint32_t rb_end = resource->resource_mapping.freq_band.start_rb + resource->resource_mapping.freq_band.nof_rb; uint32_t rb_end = csi_rs_rb_end(carrier, &resource->resource_mapping);
uint32_t rb_stride = 1; uint32_t rb_stride = csi_rs_rb_stride(&resource->resource_mapping);
// Calculate power allocation
float beta = srslte_convert_dB_to_amplitude((float)resource->power_control_offset);
if (!isnormal(beta)) {
beta = 1.0f;
}
// Special .5 density cases
if (resource->resource_mapping.density == srslte_csi_rs_resource_mapping_density_dot5_even ||
resource->resource_mapping.density == srslte_csi_rs_resource_mapping_density_dot5_odd) {
// Increase the start by one if:
// - Even and starts with odd
// - Odd and starts with even
if ((resource->resource_mapping.density == srslte_csi_rs_resource_mapping_density_dot5_even && rb_begin % 2 == 1) ||
(resource->resource_mapping.density == srslte_csi_rs_resource_mapping_density_dot5_odd && rb_begin % 2 == 0)) {
rb_begin++;
}
// Skip one RB
rb_stride = 2;
}
// Accumulators // Accumulators
float epre_acc = 0.0f; float epre_acc = 0.0f;
@ -314,14 +309,14 @@ int srslte_csi_rs_nzp_measure(const srslte_carrier_nr_t* carrier,
// Do we need more r? // Do we need more r?
if (r_idx >= 64) { if (r_idx >= 64) {
// ... Generate a bunch of it! // ... Generate a bunch of it!
srslte_sequence_state_gen_f(&sequence_state, M_SQRT1_2 / beta, (float*)r, 64 * 2); srslte_sequence_state_gen_f(&sequence_state, M_SQRT1_2, (float*)r, 64 * 2);
r_idx = 0; r_idx = 0;
} }
// Take CSI-RS from grid and measure // Take CSI-RS from grid and measure
cf_t tmp = grid[l * SRSLTE_NRE * carrier->nof_prb + k] * conjf(r[r_idx++]); cf_t tmp = grid[l * SRSLTE_NRE * carrier->nof_prb + k] * conjf(r[r_idx++]);
rsrp_acc += tmp; rsrp_acc += tmp;
epre_acc = __real__ tmp * __real__ tmp + __imag__ tmp * __imag__ tmp; epre_acc += __real__ tmp * __real__ tmp + __imag__ tmp * __imag__ tmp;
count++; count++;
} }
} }
@ -329,8 +324,33 @@ int srslte_csi_rs_nzp_measure(const srslte_carrier_nr_t* carrier,
if (count) { if (count) {
measure->epre = epre_acc / (float)count; measure->epre = epre_acc / (float)count;
measure->rsrp = cabsf(rsrp_acc) / (float)count; rsrp_acc /= (float)count;
measure->rsrp = (__real__ rsrp_acc * __real__ rsrp_acc + __imag__ rsrp_acc * __imag__ rsrp_acc);
if (measure->epre > measure->rsrp) {
measure->n0 = measure->epre - measure->rsrp;
} else {
measure->n0 = 0.0f;
} }
}
measure->rsrp_dB = srslte_convert_power_to_dB(measure->rsrp);
measure->epre_dB = srslte_convert_power_to_dB(measure->epre);
measure->n0_dB = srslte_convert_power_to_dB(measure->n0);
measure->snr_dB = measure->rsrp_dB - measure->n0_dB;
measure->nof_re = count;
return SRSLTE_SUCCESS; return SRSLTE_SUCCESS;
} }
uint32_t srslte_csi_rs_measure_info(const srslte_csi_rs_measure_t* measure, char* str, uint32_t str_len)
{
return srslte_print_check(str,
str_len,
0,
"rsrp=%+.1f, epre=%+.1f, n0=%+.1f, snr=%+.1f, nof_re=%d",
measure->rsrp_dB,
measure->epre_dB,
measure->n0_dB,
measure->snr_dB,
measure->nof_re);
}

@ -88,3 +88,13 @@ target_link_libraries(dmrs_pdcch_test srslte_phy)
add_test(dmrs_pdcch_test dmrs_pdcch_test) add_test(dmrs_pdcch_test dmrs_pdcch_test)
########################################################################
# NR CSI RS Measurement test
########################################################################
add_executable(csi_rs_test csi_rs_test.c)
target_link_libraries(csi_rs_test srslte_phy)
add_test(csi_rs_test csi_rs_test -o 3 -S 0 -L 150 -f 3 -p 15)

@ -0,0 +1,210 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2020 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srslte/common/test_common.h"
#include "srslte/phy/ch_estimation/csi_rs.h"
#include "srslte/phy/utils/vector.h"
#include <getopt.h>
#include <srslte/srslte.h>
#include <stdlib.h>
static srslte_carrier_nr_t carrier = {
0, // cell_id
0, // numerology
50, // nof_prb
0, // start
1 // max_mimo_layers
};
static float snr_dB = 20.0;
static float power_control_offset = NAN;
static uint32_t start_rb = UINT32_MAX;
static uint32_t nof_rb = UINT32_MAX;
static uint32_t first_symbol = UINT32_MAX;
static int test(const srslte_dl_slot_cfg_t* slot_cfg,
const srslte_csi_rs_nzp_resource_t* resource,
srslte_channel_awgn_t* awgn,
cf_t* grid)
{
srslte_csi_rs_measure_t measure = {};
// Put NZP-CSI-RS
TESTASSERT(srslte_csi_rs_nzp_put(&carrier, slot_cfg, resource, grid) == SRSLTE_SUCCESS);
// Configure N0 and add Noise
TESTASSERT(srslte_channel_awgn_set_n0(awgn, (float)resource->power_control_offset - snr_dB) == SRSLTE_SUCCESS);
srslte_channel_awgn_run_c(awgn, grid, grid, SRSLTE_SLOT_LEN_RE_NR(carrier.nof_prb));
TESTASSERT(srslte_csi_rs_nzp_measure(&carrier, slot_cfg, resource, grid, &measure) == SRSLTE_SUCCESS);
const float rsrp_dB_gold = (float)resource->power_control_offset;
const float epre_dB_gold =
srslte_convert_power_to_dB(srslte_convert_dB_to_power(rsrp_dB_gold) + awgn->std_dev * awgn->std_dev);
const float n0_dB_gold = srslte_convert_amplitude_to_dB(awgn->std_dev);
if (srslte_verbose >= SRSLTE_VERBOSE_INFO) {
char str[128] = {};
srslte_csi_rs_measure_info(&measure, str, sizeof(str));
INFO("Measure: %s\n", str);
}
TESTASSERT(fabsf(measure.rsrp_dB - rsrp_dB_gold) < 1.0f);
TESTASSERT(fabsf(measure.epre_dB - epre_dB_gold) < 1.0f);
TESTASSERT(fabsf(measure.n0_dB - n0_dB_gold) < 2.0f);
TESTASSERT(fabsf(measure.snr_dB - snr_dB) < 2.0f);
return SRSLTE_SUCCESS;
}
static void usage(char* prog)
{
printf("Usage: %s [recov]\n", prog);
printf("\t-p nof_prb [Default %d]\n", carrier.nof_prb);
printf("\t-c cell_id [Default %d]\n", carrier.id);
printf("\t-s SNR in dB [Default %.2f]\n", snr_dB);
printf("\t-S Start RB index [Default %d]\n", start_rb);
printf("\t-L Number of RB [Default %d]\n", nof_rb);
printf("\t-f First symbol index [Default %d]\n", first_symbol);
printf("\t-o Power control offset [Default %.2f]\n", power_control_offset);
printf("\t-v increase verbosity\n");
}
static void parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "pcosSLfv")) != -1) {
switch (opt) {
case 'p':
carrier.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'c':
carrier.id = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'o':
power_control_offset = strtof(argv[optind], NULL);
break;
case 's':
snr_dB = strtof(argv[optind], NULL);
break;
case 'S':
start_rb = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'L':
nof_rb = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'f':
first_symbol = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'v':
srslte_verbose++;
break;
default:
usage(argv[0]);
exit(-1);
}
}
}
int main(int argc, char** argv)
{
int ret = SRSLTE_ERROR;
srslte_dl_slot_cfg_t slot_cfg = {};
srslte_csi_rs_nzp_resource_t resource = {};
srslte_channel_awgn_t awgn = {};
parse_args(argc, argv);
cf_t* grid = srslte_vec_cf_malloc(SRSLTE_SLOT_LEN_RE_NR(carrier.nof_prb));
if (grid == NULL) {
ERROR("Alloc\n");
goto clean_exit;
}
if (srslte_channel_awgn_init(&awgn, 1234) < SRSLTE_SUCCESS) {
ERROR("AWGN Init\n");
goto clean_exit;
}
// Fixed parameters, other params are not implemented
resource.resource_mapping.cdm = srslte_csi_rs_cdm_nocdm;
resource.resource_mapping.density = srslte_csi_rs_resource_mapping_density_three;
resource.resource_mapping.row = srslte_csi_rs_resource_mapping_row_1;
resource.resource_mapping.ports = 1;
// Row 1 supported only!
uint32_t nof_freq_dom_alloc = SRSLTE_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW1;
uint32_t first_symbol_begin = (first_symbol != UINT32_MAX) ? first_symbol : 0;
uint32_t first_symbol_end = (first_symbol != UINT32_MAX) ? first_symbol : 13;
for (resource.resource_mapping.first_symbol_idx = first_symbol_begin;
resource.resource_mapping.first_symbol_idx <= first_symbol_end;
resource.resource_mapping.first_symbol_idx++) {
// Iterate over possible power control offset
float power_control_offset_begin = isnormal(power_control_offset) ? power_control_offset : -8.0f;
float power_control_offset_end = isnormal(power_control_offset) ? power_control_offset : 15.0f;
for (resource.power_control_offset = power_control_offset_begin;
resource.power_control_offset <= power_control_offset_end;
resource.power_control_offset += 1.0f) {
// Iterate over all possible starting number of PRB
uint32_t start_rb_begin = (start_rb != UINT32_MAX) ? start_rb : 0;
uint32_t start_rb_end = (start_rb != UINT32_MAX) ? start_rb : carrier.nof_prb - 24;
for (resource.resource_mapping.freq_band.start_rb = start_rb_begin;
resource.resource_mapping.freq_band.start_rb <= start_rb_end;
resource.resource_mapping.freq_band.start_rb += 4) {
// Iterate over all possible number of PRB
uint32_t nof_rb_begin = (nof_rb != UINT32_MAX) ? nof_rb : 24;
uint32_t nof_rb_end =
(nof_rb != UINT32_MAX) ? nof_rb : (carrier.nof_prb - resource.resource_mapping.freq_band.start_rb);
for (resource.resource_mapping.freq_band.nof_rb = nof_rb_begin;
resource.resource_mapping.freq_band.nof_rb <= nof_rb_end;
resource.resource_mapping.freq_band.nof_rb += 4) {
// Iterate for all slot numbers
for (slot_cfg.idx = 0; slot_cfg.idx < SRSLTE_NSLOTS_PER_FRAME_NR(carrier.numerology); slot_cfg.idx++) {
// Steer Frequency allocation
for (uint32_t freq_dom_alloc = 0; freq_dom_alloc < nof_freq_dom_alloc; freq_dom_alloc++) {
for (uint32_t i = 0; i < nof_freq_dom_alloc; i++) {
resource.resource_mapping.frequency_domain_alloc[i] = i == freq_dom_alloc;
}
// Call actual test
if (test(&slot_cfg, &resource, &awgn, grid) < SRSLTE_SUCCESS) {
goto clean_exit;
}
}
}
}
}
}
}
ret = SRSLTE_SUCCESS;
clean_exit:
if (grid) {
free(grid);
}
srslte_channel_awgn_free(&awgn);
if (ret == SRSLTE_SUCCESS) {
printf("Passed!\n");
} else {
printf("Failed!\n");
}
return ret;
}

@ -124,7 +124,7 @@ static const golden_t gold[] = {{.mapping_type = srslte_pdsch_mapping_type_A,
.sc_idx = {0, 2, 4, 6, 8, 10}}, .sc_idx = {0, 2, 4, 6, 8, 10}},
{}}; {}};
void usage(char* prog) static void usage(char* prog)
{ {
printf("Usage: %s [recov]\n", prog); printf("Usage: %s [recov]\n", prog);
@ -135,7 +135,7 @@ void usage(char* prog)
printf("\t-v increase verbosity\n"); printf("\t-v increase verbosity\n");
} }
void parse_args(int argc, char** argv) static void parse_args(int argc, char** argv)
{ {
int opt; int opt;
while ((opt = getopt(argc, argv, "rcov")) != -1) { while ((opt = getopt(argc, argv, "rcov")) != -1) {

Loading…
Cancel
Save