Provide PUSCH BLER test

The new test measures BLER and received throughput for the PUSCH. For
now, only AWGN channel and perfect equalization are considered.
This commit is contained in:
dvdgrgrtt 2021-12-07 16:11:16 +01:00 committed by dvdgrgrtt
parent 3fad800ef6
commit 6b0a3669cf
2 changed files with 379 additions and 0 deletions

View File

@ -678,6 +678,9 @@ add_nr_test(pusch_nr_ack2_csi4_test pusch_nr_test -p 50 -m 20 -A 2 -C 4)
add_nr_test(pusch_nr_ack4_csi4_test pusch_nr_test -p 50 -m 20 -A 4 -C 4)
add_nr_test(pusch_nr_ack20_csi4_test pusch_nr_test -p 50 -m 20 -A 20 -C 4)
add_executable(pusch_nr_bler_test EXCLUDE_FROM_ALL pusch_nr_bler_test.c)
target_link_libraries(pusch_nr_bler_test srsran_phy)
# this is just for performance evaluation, not for unit testing
add_executable(pdcch_nr_test pdcch_nr_test.c)
target_link_libraries(pdcch_nr_test srsran_phy)

View File

@ -0,0 +1,376 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 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 "srsran/phy/channel/ch_awgn.h"
#include "srsran/phy/phch/pusch_nr.h"
#include "srsran/phy/phch/ra_nr.h"
#include "srsran/phy/phch/ra_ul_nr.h"
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/random.h"
#include "srsran/phy/utils/vector.h"
#include <getopt.h>
static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR;
static uint32_t n_prb = 0; // Set to 0 for steering
static uint32_t mcs = 30; // Set to 30 for steering
static srsran_sch_cfg_nr_t pusch_cfg = {};
static uint16_t rnti = 0x1234;
static uint32_t nof_ack_bits = 0;
static uint32_t nof_csi_bits = 0;
static float snr = 10;
static bool full_check = false;
void usage(char* prog)
{
printf("Usage: %s [pmTLACsv] \n", prog);
printf("\t-p Number of grant PRB, set to 0 for steering [Default %d]\n", n_prb);
printf("\t-m MCS PRB, set to >28 for steering [Default %d]\n", mcs);
printf("\t-T Provide MCS table (64qam, 256qam, 64qamLowSE) [Default %s]\n",
srsran_mcs_table_to_str(pusch_cfg.sch_cfg.mcs_table));
printf("\t-L Provide number of layers [Default %d]\n", carrier.max_mimo_layers);
printf("\t-A Provide a number of HARQ-ACK bits [Default %d]\n", nof_ack_bits);
printf("\t-C Provide a number of CSI bits [Default %d]\n", nof_csi_bits);
printf("\t-s Signal-to-Noise Ratio in dB [Default %.1f]\n", snr);
printf("\t-f Perform full BLER check instead of CRC only [Default %s]\n", full_check ? "true" : "false");
printf("\t-v [set srsran_verbose to debug, default none]\n");
}
int parse_args(int argc, char** argv)
{
int opt = 0;
while ((opt = getopt(argc, argv, "p:m:T:L:A:C:s:fv")) != -1) {
switch (opt) {
case 'p':
n_prb = (uint32_t)strtol(optarg, NULL, 10);
break;
case 'm':
mcs = (uint32_t)strtol(optarg, NULL, 10);
break;
case 'T':
pusch_cfg.sch_cfg.mcs_table = srsran_mcs_table_from_str(optarg);
break;
case 'L':
carrier.max_mimo_layers = (uint32_t)strtol(optarg, NULL, 10);
break;
case 'A':
nof_ack_bits = (uint32_t)strtol(optarg, NULL, 10);
break;
case 'C':
nof_csi_bits = (uint32_t)strtol(optarg, NULL, 10);
break;
case 's':
snr = strtof(optarg, NULL);
break;
case 'f':
full_check = true;
break;
case 'v':
increase_srsran_verbose_level();
break;
default:
usage(argv[0]);
return SRSRAN_ERROR;
}
}
return SRSRAN_SUCCESS;
}
int main(int argc, char** argv)
{
int ret = SRSRAN_ERROR;
srsran_pusch_nr_t pusch_tx = {};
srsran_pusch_nr_t pusch_rx = {};
srsran_chest_dl_res_t chest = {};
srsran_random_t rand_gen = srsran_random_init(1234);
srsran_pusch_data_nr_t data_tx = {};
srsran_pusch_res_nr_t data_rx = {};
cf_t* sf_symbols_tx[SRSRAN_MAX_LAYERS_NR] = {};
cf_t* sf_symbols_rx[SRSRAN_MAX_LAYERS_NR] = {};
// Set default PUSCH configuration
pusch_cfg.sch_cfg.mcs_table = srsran_mcs_table_64qam;
if (parse_args(argc, argv) < SRSRAN_SUCCESS) {
goto clean_exit;
}
srsran_pusch_nr_args_t pusch_args = {};
pusch_args.sch.disable_simd = false;
pusch_args.measure_evm = true;
if (srsran_pusch_nr_init_ue(&pusch_tx, &pusch_args) < SRSRAN_SUCCESS) {
ERROR("Error initiating PUSCH for Tx");
goto clean_exit;
}
if (srsran_pusch_nr_init_gnb(&pusch_rx, &pusch_args) < SRSRAN_SUCCESS) {
ERROR("Error initiating SCH NR for Rx");
goto clean_exit;
}
if (srsran_pusch_nr_set_carrier(&pusch_tx, &carrier)) {
ERROR("Error setting SCH NR carrier");
goto clean_exit;
}
if (srsran_pusch_nr_set_carrier(&pusch_rx, &carrier)) {
ERROR("Error setting SCH NR carrier");
goto clean_exit;
}
uint32_t slot_length = SRSRAN_SLOT_LEN_RE_NR(carrier.nof_prb);
for (uint32_t i = 0; i < carrier.max_mimo_layers; i++) {
sf_symbols_tx[i] = srsran_vec_cf_malloc(slot_length);
sf_symbols_rx[i] = srsran_vec_cf_malloc(slot_length);
if (sf_symbols_tx[i] == NULL || sf_symbols_rx[i] == NULL) {
ERROR("Error malloc");
goto clean_exit;
}
}
for (uint32_t i = 0; i < pusch_tx.max_cw; i++) {
data_tx.payload[i] = srsran_vec_u8_malloc(SRSRAN_SLOT_MAX_NOF_BITS_NR);
data_rx.tb[i].payload = srsran_vec_u8_malloc(SRSRAN_SLOT_MAX_NOF_BITS_NR);
if (data_tx.payload[i] == NULL || data_rx.tb[i].payload == NULL) {
ERROR("Error malloc");
goto clean_exit;
}
}
srsran_softbuffer_tx_t softbuffer_tx = {};
srsran_softbuffer_rx_t softbuffer_rx = {};
if (srsran_softbuffer_tx_init_guru(&softbuffer_tx, SRSRAN_SCH_NR_MAX_NOF_CB_LDPC, SRSRAN_LDPC_MAX_LEN_ENCODED_CB) <
SRSRAN_SUCCESS) {
ERROR("Error init soft-buffer");
goto clean_exit;
}
if (srsran_softbuffer_rx_init_guru(&softbuffer_rx, SRSRAN_SCH_NR_MAX_NOF_CB_LDPC, SRSRAN_LDPC_MAX_LEN_ENCODED_CB) <
SRSRAN_SUCCESS) {
ERROR("Error init soft-buffer");
goto clean_exit;
}
// Use grant default A time resources with m=0
if (srsran_ra_ul_nr_pusch_time_resource_default_A(carrier.scs, 0, &pusch_cfg.grant) < SRSRAN_SUCCESS) {
ERROR("Error loading default grant");
goto clean_exit;
}
// Set PUSCH grant without considering any procedure
pusch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; // No need for MIMO
pusch_cfg.grant.nof_layers = carrier.max_mimo_layers;
pusch_cfg.grant.dci_format = srsran_dci_format_nr_1_0;
pusch_cfg.grant.rnti = rnti;
// Check input: PRB
if (n_prb > carrier.nof_prb) {
ERROR("Invalid number of PRB");
goto clean_exit;
}
// Check input: MCS
uint32_t mcs_end = pusch_cfg.sch_cfg.mcs_table == srsran_mcs_table_256qam ? 28 : 29;
if (mcs > mcs_end) {
ERROR("Invalid MCS");
goto clean_exit;
}
srsran_sch_hl_cfg_nr_t sch_hl_cfg = {};
sch_hl_cfg.scaling = 1.0F;
sch_hl_cfg.beta_offsets.fix_ack = 12.625F;
sch_hl_cfg.beta_offsets.fix_csi1 = 2.25F;
sch_hl_cfg.beta_offsets.fix_csi2 = 2.25F;
if (srsran_chest_dl_res_init(&chest, carrier.nof_prb) < SRSRAN_SUCCESS) {
ERROR("Initiating chest");
goto clean_exit;
}
for (uint32_t n = 0; n < SRSRAN_MAX_PRB_NR; n++) {
pusch_cfg.grant.prb_idx[n] = (n < n_prb);
}
pusch_cfg.grant.nof_prb = n_prb;
pusch_cfg.grant.dci_format = srsran_dci_format_nr_0_0;
pusch_cfg.grant.nof_dmrs_cdm_groups_without_data = 2;
pusch_cfg.dmrs.type = srsran_dmrs_sch_type_1;
pusch_cfg.dmrs.length = srsran_dmrs_sch_len_1;
pusch_cfg.dmrs.additional_pos = srsran_dmrs_sch_add_pos_2;
if (srsran_ra_nr_fill_tb(&pusch_cfg, &pusch_cfg.grant, mcs, &pusch_cfg.grant.tb[0]) < SRSRAN_SUCCESS) {
ERROR("Error filling tb");
goto clean_exit;
}
uint32_t n_blocks = 0;
uint32_t n_errors = 0;
float evm = 0;
for (; n_blocks < 2000000 && n_errors < 100; n_blocks++) {
// Generate SCH payload
for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) {
// Skip TB if no allocated
if (data_tx.payload[tb] == NULL) {
continue;
}
// load payload with bytes
for (uint32_t i = 0; i < pusch_cfg.grant.tb[tb].tbs / 8 + 1; i++) {
data_tx.payload[tb][i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, UINT8_MAX);
}
pusch_cfg.grant.tb[tb].softbuffer.tx = &softbuffer_tx;
}
// Generate HARQ ACK bits
if (nof_ack_bits > 0) {
pusch_cfg.uci.ack.count = nof_ack_bits;
for (uint32_t i = 0; i < nof_ack_bits; i++) {
data_tx.uci.ack[i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, 1);
}
}
// Generate CSI report bits
uint8_t csi_report_tx[SRSRAN_UCI_NR_MAX_CSI1_BITS] = {};
uint8_t csi_report_rx[SRSRAN_UCI_NR_MAX_CSI1_BITS] = {};
if (nof_csi_bits > 0) {
pusch_cfg.uci.csi[0].cfg.quantity = SRSRAN_CSI_REPORT_QUANTITY_NONE;
pusch_cfg.uci.csi[0].K_csi_rs = nof_csi_bits;
pusch_cfg.uci.nof_csi = 1;
data_tx.uci.csi[0].none = csi_report_tx;
for (uint32_t i = 0; i < nof_csi_bits; i++) {
csi_report_tx[i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, 1);
}
data_rx.uci.csi[0].none = csi_report_rx;
}
if (srsran_ra_ul_set_grant_uci_nr(&carrier, &sch_hl_cfg, &pusch_cfg.uci, &pusch_cfg) < SRSRAN_SUCCESS) {
ERROR("Setting UCI");
goto clean_exit;
}
if (srsran_pusch_nr_encode(&pusch_tx, &pusch_cfg, &pusch_cfg.grant, &data_tx, sf_symbols_tx) < SRSRAN_SUCCESS) {
ERROR("Error encoding");
goto clean_exit;
}
float noise_std_1d = srsran_convert_dB_to_amplitude(-snr - 3.0103F);
for (uint32_t i = 0; i < carrier.max_mimo_layers; i++) {
srsran_ch_awgn_f((float*)sf_symbols_tx[i], (float*)sf_symbols_rx[i], noise_std_1d, 2 * slot_length);
// memcpy(sf_symbols_rx[i], sf_symbols_tx[i], slot_length * sizeof(cf_t));
}
if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO) {
uint32_t nof_re_total = carrier.nof_prb * SRSRAN_NRE;
uint32_t nof_re_used = pusch_cfg.grant.nof_prb * SRSRAN_NRE;
for (int i_layer = 0; i_layer < carrier.max_mimo_layers; i_layer++) {
INFO("Layer %d", i_layer);
float tx_power = 0;
float rx_power = 0;
uint8_t n_symbols = 0;
for (int i = 0; i < SRSRAN_NSYMB_PER_SLOT_NR; i++) {
if (!pusch_tx.dmrs_re_pattern.symbol[i]) {
n_symbols++;
tx_power += srsran_vec_avg_power_cf(sf_symbols_tx[0] + i * nof_re_total, nof_re_total);
rx_power += srsran_vec_avg_power_cf(sf_symbols_rx[0] + i * nof_re_total, nof_re_total);
}
}
tx_power *= (float)nof_re_total / nof_re_used; // compensate for unused REs
INFO(" Tx power: %.3f", tx_power / n_symbols);
INFO(" Rx power: %.3f", rx_power / n_symbols);
INFO(" SNR: %.3f dB", srsran_convert_power_to_dB(tx_power / (rx_power - tx_power)));
}
}
for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) {
pusch_cfg.grant.tb[tb].softbuffer.rx = &softbuffer_rx;
srsran_softbuffer_rx_reset(pusch_cfg.grant.tb[tb].softbuffer.rx);
}
// assume perfect channel estimation (including noise variance)
for (uint32_t i = 0; i < pusch_cfg.grant.tb->nof_re; i++) {
chest.ce[0][0][i] = 1.0F;
}
chest.nof_re = pusch_cfg.grant.tb->nof_re;
chest.noise_estimate = 4 * noise_std_1d * noise_std_1d;
if (srsran_pusch_nr_decode(&pusch_rx, &pusch_cfg, &pusch_cfg.grant, &chest, sf_symbols_rx, &data_rx) <
SRSRAN_SUCCESS) {
ERROR("Error encoding");
goto clean_exit;
}
evm += data_rx.evm[0];
// Validate UL-SCH CRC check
if (!data_rx.tb[0].crc) {
n_errors++;
printf("*");
fflush(stdout);
if (n_errors % 20 == 0) {
printf("\n");
}
}
if (full_check) {
// Validate by comparing payload (recall, payload is represented in bytes)
if ((memcmp(data_rx.tb[0].payload, data_tx.payload[0], pusch_cfg.grant.tb[0].tbs * sizeof(uint8_t) / 8) == 0) !=
data_rx.tb[0].crc) {
printf("\nWarning! Bit comparison and CRC do not match!\n");
}
}
}
char str[512];
srsran_pusch_nr_rx_info(&pusch_rx, &pusch_cfg, &pusch_cfg.grant, &data_rx, str, (uint32_t)sizeof(str));
char str_extra[2048];
srsran_sch_cfg_nr_info(&pusch_cfg, str_extra, (uint32_t)sizeof(str_extra));
printf("\nPUSCH: %s\n%s", str, str_extra);
printf("\nNominal SNR: %.1f dB\n", snr);
printf("Average EVM: %.3f\n", evm / n_blocks);
printf("BLER: %.3e (%d errors out of %d blocks)\n", (double)n_errors / n_blocks, n_errors, n_blocks);
printf("Tx Throughput: %.3e Mbps -- Rx Throughput: %.3e Mbps (%.2f%%)\n",
pusch_cfg.grant.tb[0].tbs / 1e3,
(n_blocks - n_errors) / 1e3 * pusch_cfg.grant.tb[0].tbs / n_blocks,
100.0F * (n_blocks - n_errors) / n_blocks);
ret = SRSRAN_SUCCESS;
clean_exit:
srsran_chest_dl_res_free(&chest);
srsran_random_free(rand_gen);
srsran_pusch_nr_free(&pusch_tx);
srsran_pusch_nr_free(&pusch_rx);
for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; i++) {
if (data_tx.payload[i]) {
free(data_tx.payload[i]);
}
if (data_rx.tb[i].payload) {
free(data_rx.tb[i].payload);
}
}
for (uint32_t i = 0; i < SRSRAN_MAX_LAYERS_NR; i++) {
if (sf_symbols_tx[i]) {
free(sf_symbols_tx[i]);
}
if (sf_symbols_rx[i]) {
free(sf_symbols_rx[i]);
}
}
srsran_softbuffer_tx_free(&softbuffer_tx);
srsran_softbuffer_rx_free(&softbuffer_rx);
return ret;
}