diff --git a/lib/include/srsran/phy/phch/pdsch_nr.h b/lib/include/srsran/phy/phch/pdsch_nr.h index 63190ee03..e581e3359 100644 --- a/lib/include/srsran/phy/phch/pdsch_nr.h +++ b/lib/include/srsran/phy/phch/pdsch_nr.h @@ -37,6 +37,8 @@ typedef struct SRSRAN_API { srsran_sch_nr_args_t sch; bool measure_evm; bool measure_time; + bool disable_zero_re_around_dc; ///< PDSCH NR sets the LLR around the DC to zero to avoid noise + uint32_t nof_zero_re_around_dc; ///< Number of RE to set to zero around DC. It uses default value if 0. } srsran_pdsch_nr_args_t; /** @@ -57,6 +59,8 @@ typedef struct SRSRAN_API { uint32_t meas_time_us; srsran_re_pattern_t dmrs_re_pattern; uint32_t nof_rvd_re; + uint32_t nof_zero_re_around_dc; ///< Sets a number of RE surrounding the center of the resource grid to zero. Set to 0 + ///< for disabling. } srsran_pdsch_nr_t; /** diff --git a/lib/src/phy/phch/pdsch_nr.c b/lib/src/phy/phch/pdsch_nr.c index 30fff7475..ea7972a80 100644 --- a/lib/src/phy/phch/pdsch_nr.c +++ b/lib/src/phy/phch/pdsch_nr.c @@ -16,10 +16,14 @@ #include "srsran/phy/mimo/layermap.h" #include "srsran/phy/mimo/precoding.h" #include "srsran/phy/modem/demod_soft.h" -#include "srsran/phy/phch/ra_nr.h" + +///@brief Default number of zero RE around DC +#define PDSCH_NR_DEFAULT_NOF_ZERO_RE_AROUND_DC 3 int pdsch_nr_init_common(srsran_pdsch_nr_t* q, const srsran_pdsch_nr_args_t* args) { + SRSRAN_MEM_ZERO(q, srsran_pdsch_nr_t, 1); + for (srsran_mod_t mod = SRSRAN_MOD_BPSK; mod < SRSRAN_MOD_NITEMS; mod++) { if (srsran_modem_table_lte(&q->modem_tables[mod], mod) < SRSRAN_SUCCESS) { ERROR("Error initialising modem table for %s", srsran_mod_string(mod)); @@ -30,6 +34,14 @@ int pdsch_nr_init_common(srsran_pdsch_nr_t* q, const srsran_pdsch_nr_args_t* arg } } + if (!args->disable_zero_re_around_dc) { + if (args->nof_zero_re_around_dc == 0) { + q->nof_zero_re_around_dc = PDSCH_NR_DEFAULT_NOF_ZERO_RE_AROUND_DC; + } else { + q->nof_zero_re_around_dc = args->nof_zero_re_around_dc; + } + } + return SRSRAN_SUCCESS; } @@ -236,7 +248,23 @@ static int srsran_pdsch_nr_cp(const srsran_pdsch_nr_t* q, if (put) { count += pdsch_nr_put_rb(&sf_symbols[re_idx], &symbols[count], &rvd_mask[rb * SRSRAN_NRE]); } else { - count += pdsch_nr_get_rb(&symbols[count], &sf_symbols[re_idx], &rvd_mask[rb * SRSRAN_NRE]); + uint32_t k_begin = rb * SRSRAN_NRE; + uint32_t k_end = (rb + 1) * SRSRAN_NRE; + uint32_t k_dc_begin = q->carrier.nof_prb * SRSRAN_NRE / 2 - q->nof_zero_re_around_dc / 2; + uint32_t k_dc_end = q->carrier.nof_prb * SRSRAN_NRE / 2 + SRSRAN_CEIL(q->nof_zero_re_around_dc, 2); + if (k_begin <= k_dc_end && k_end >= k_dc_begin && q->nof_zero_re_around_dc > 0) { + for (uint32_t k = k_begin; k < k_end; k++) { + if (!rvd_mask[k]) { + if (k >= k_dc_begin && k < k_dc_end) { + symbols[count++] = 0.0f; + } else { + symbols[count++] = sf_symbols[q->carrier.nof_prb * l * SRSRAN_NRE + k]; + } + } + } + } else { + count += pdsch_nr_get_rb(&symbols[count], &sf_symbols[re_idx], &rvd_mask[rb * SRSRAN_NRE]); + } } } } @@ -544,7 +572,7 @@ static uint32_t srsran_pdsch_nr_grant_info(const srsran_sch_cfg_nr_t* cfg, uint32_t str_len) { uint32_t len = 0; - len = srsran_print_check(str, str_len, len, "rnti=0x%x", grant->rnti); + len = srsran_print_check(str, str_len, len, "rnti=0x%x ", grant->rnti); uint32_t first_prb = SRSRAN_MAX_PRB_NR; for (uint32_t i = 0; i < SRSRAN_MAX_PRB_NR && first_prb == SRSRAN_MAX_PRB_NR; i++) { @@ -557,7 +585,9 @@ static uint32_t srsran_pdsch_nr_grant_info(const srsran_sch_cfg_nr_t* cfg, len = srsran_print_check(str, str_len, len, - ",k0=%d,prb=%d:%d,symb=%d:%d,mapping=%s", + "beta_dmrs=%.3f CDM-grp=%d k0=%d prb=%d:%d symb=%d:%d mapping=%s ", + isnormal(grant->beta_dmrs) ? grant->beta_dmrs : 1.0f, + grant->nof_dmrs_cdm_groups_without_data, grant->k, first_prb, grant->nof_prb, @@ -569,10 +599,10 @@ static uint32_t srsran_pdsch_nr_grant_info(const srsran_sch_cfg_nr_t* cfg, // ... // Append spatial resources - len = srsran_print_check(str, str_len, len, ",Nl=%d", grant->nof_layers); + len = srsran_print_check(str, str_len, len, "Nl=%d ", grant->nof_layers); // Append scrambling ID - len = srsran_print_check(str, str_len, len, ",n_scid=%d,", grant->n_scid); + len = srsran_print_check(str, str_len, len, "n_scid=%d ", grant->n_scid); // Append TB info for (uint32_t i = 0; i < SRSRAN_MAX_TB; i++) { diff --git a/lib/src/phy/phch/sch_nr.c b/lib/src/phy/phch/sch_nr.c index 759c277de..66cbb37c2 100644 --- a/lib/src/phy/phch/sch_nr.c +++ b/lib/src/phy/phch/sch_nr.c @@ -714,7 +714,7 @@ int srsran_sch_nr_tb_info(const srsran_sch_tb_t* tb, char* str, uint32_t str_len len += srsran_print_check(str, str_len, len, - "tb={mod=%s,Nl=%d,tbs=%d,R=%.3f,rv=%d,Nre=%d,Nbit=%d,cw=%d}", + "CW0: mod=%s Nl=%d tbs=%d R=%.3f rv=%d Nre=%d Nbit=%d cw=%d", srsran_mod_string(tb->mod), tb->N_L, tb->tbs / 8, diff --git a/lib/test/phy/phy_dl_nr_test.c b/lib/test/phy/phy_dl_nr_test.c index ad1cc48f0..f9214a2ca 100644 --- a/lib/test/phy/phy_dl_nr_test.c +++ b/lib/test/phy/phy_dl_nr_test.c @@ -33,6 +33,8 @@ static uint32_t mcs = 30; // Set to 30 for steering static srsran_sch_cfg_nr_t pdsch_cfg = {}; static uint32_t nof_slots = 10; static uint32_t rv_idx = 0; +static uint32_t delay_n = 4; // Integer delay +static float cfo_hz = 100.0f; // CFO Hz static void usage(char* prog) { @@ -46,13 +48,15 @@ static void usage(char* prog) srsran_mcs_table_to_str(pdsch_cfg.sch_cfg.mcs_table)); printf("\t-R Reserve RE: [rb_begin] [rb_end] [rb_stride] [sc_mask] [symbol_mask]\n"); printf("\t-L Provide number of layers [Default %d]\n", carrier.max_mimo_layers); + printf("\t-D Delay signal an integer number of samples [Default %d samples]\n", delay_n); + printf("\t-C Frequency shift (CFO) signal in Hz [Default %+.0f Hz]\n", cfo_hz); printf("\t-v [set srsran_verbose to debug, default none]\n"); } static int parse_args(int argc, char** argv) { int opt; - while ((opt = getopt(argc, argv, "rRPpmnTLv")) != -1) { + while ((opt = getopt(argc, argv, "rRPpmnTLDCv")) != -1) { switch (opt) { case 'P': carrier.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10); @@ -93,6 +97,12 @@ static int parse_args(int argc, char** argv) case 'L': carrier.max_mimo_layers = (uint32_t)strtol(argv[optind], NULL, 10); break; + case 'D': + delay_n = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'C': + cfo_hz = strtof(argv[optind], NULL); + break; case 'v': srsran_verbose++; break; @@ -187,22 +197,26 @@ int main(int argc, char** argv) uint8_t* data_tx[SRSRAN_MAX_TB] = {}; uint8_t* data_rx[SRSRAN_MAX_CODEWORDS] = {}; - cf_t* buffer = NULL; + cf_t* buffer_gnb[SRSRAN_MAX_PORTS] = {}; + cf_t* buffer_ue[SRSRAN_MAX_PORTS] = {}; - buffer = srsran_vec_cf_malloc(SRSRAN_SF_LEN_PRB(carrier.nof_prb)); - if (buffer == NULL) { + uint32_t sf_len = SRSRAN_SF_LEN_PRB(carrier.nof_prb); + buffer_gnb[0] = srsran_vec_cf_malloc(sf_len); + buffer_ue[0] = srsran_vec_cf_malloc(sf_len); + if (buffer_gnb[0] == NULL || buffer_ue[0] == NULL) { ERROR("Error malloc"); goto clean_exit; } - srsran_ue_dl_nr_args_t ue_dl_args = {}; - ue_dl_args.nof_rx_antennas = 1; - ue_dl_args.pdsch.sch.disable_simd = false; - ue_dl_args.pdsch.sch.decoder_use_flooded = false; - ue_dl_args.pdsch.measure_evm = true; - ue_dl_args.pdcch.disable_simd = false; - ue_dl_args.pdcch.measure_evm = true; - ue_dl_args.nof_max_prb = carrier.nof_prb; + srsran_ue_dl_nr_args_t ue_dl_args = {}; + ue_dl_args.nof_rx_antennas = 1; + ue_dl_args.pdsch.sch.disable_simd = false; + ue_dl_args.pdsch.sch.decoder_use_flooded = false; + ue_dl_args.pdsch.measure_evm = true; + ue_dl_args.pdsch.disable_zero_re_around_dc = true; + ue_dl_args.pdcch.disable_simd = false; + ue_dl_args.pdcch.measure_evm = true; + ue_dl_args.nof_max_prb = carrier.nof_prb; srsran_enb_dl_nr_args_t enb_dl_args = {}; enb_dl_args.nof_tx_antennas = 1; @@ -239,12 +253,12 @@ int main(int argc, char** argv) search_space->nof_candidates[L] = srsran_pdcch_nr_max_candidates_coreset(coreset, L); } - if (srsran_ue_dl_nr_init(&ue_dl, &buffer, &ue_dl_args)) { + if (srsran_ue_dl_nr_init(&ue_dl, buffer_ue, &ue_dl_args)) { ERROR("Error UE DL"); goto clean_exit; } - if (srsran_enb_dl_nr_init(&enb_dl, &buffer, &enb_dl_args)) { + if (srsran_enb_dl_nr_init(&enb_dl, buffer_gnb, &enb_dl_args)) { ERROR("Error UE DL"); goto clean_exit; } @@ -309,6 +323,7 @@ int main(int argc, char** argv) pdsch_cfg.grant.nof_layers = carrier.max_mimo_layers; pdsch_cfg.grant.dci_format = srsran_dci_format_nr_1_0; pdsch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; + pdsch_cfg.grant.beta_dmrs = srsran_convert_dB_to_amplitude(3); pdsch_cfg.grant.rnti_type = srsran_rnti_type_c; pdsch_cfg.grant.rnti = 0x4601; pdsch_cfg.grant.tb[0].rv = rv_idx; @@ -381,6 +396,22 @@ int main(int argc, char** argv) get_time_interval(t); pdsch_encode_us += (size_t)(t[0].tv_sec * 1e6 + t[0].tv_usec); + // Emulate channel delay + if (delay_n >= sf_len) { + ERROR("Delay exceeds SF length"); + goto clean_exit; + } + srsran_vec_cf_copy(&buffer_ue[0][0], &buffer_gnb[0][delay_n], sf_len - delay_n); + srsran_vec_cf_copy(&buffer_ue[0][sf_len - delay_n], &buffer_gnb[0][0], delay_n); + + // Emulate channel CFO + if (isnormal(cfo_hz) && ue_dl.fft[0].cfg.symbol_sz > 0) { + srsran_vec_apply_cfo(buffer_ue[0], + cfo_hz / (ue_dl.fft[0].cfg.symbol_sz * SRSRAN_SUBC_SPACING_NR(carrier.numerology)), + buffer_ue[0], + sf_len); + } + for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) { pdsch_cfg.grant.tb[tb].softbuffer.rx = &softbuffer_rx; srsran_softbuffer_rx_reset(pdsch_cfg.grant.tb[tb].softbuffer.rx); @@ -395,7 +426,7 @@ int main(int argc, char** argv) get_time_interval(t); pdsch_decode_us += (size_t)(t[0].tv_sec * 1e6 + t[0].tv_usec); - if (pdsch_res->evm > 0.001f) { + if (pdsch_res->evm > 0.02f) { ERROR("Error PDSCH EVM is too high %f", pdsch_res->evm); goto clean_exit; } @@ -447,8 +478,11 @@ clean_exit: free(data_rx[i]); } } - if (buffer) { - free(buffer); + if (buffer_gnb[0]) { + free(buffer_gnb[0]); + } + if (buffer_ue[0]) { + free(buffer_ue[0]); } srsran_softbuffer_tx_free(&softbuffer_tx); srsran_softbuffer_rx_free(&softbuffer_rx);