mirror of https://github.com/pvnis/srsRAN_4G.git
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.master
parent
3fad800ef6
commit
6b0a3669cf
@ -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;
|
||||
}
|
Loading…
Reference in New Issue