From 039f2164a6f51cdbbb0526106bc78884dec436d5 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Fri, 5 Jun 2020 15:51:00 +0200 Subject: [PATCH] Initial Sounding Reference Signals implementation --- .../srslte/phy/ch_estimation/chest_ul.h | 14 +- .../srslte/phy/ch_estimation/refsignal_ul.h | 7 + lib/include/srslte/phy/enb/enb_ul.h | 6 +- lib/src/phy/ch_estimation/chest_ul.c | 193 ++++++++++---- lib/src/phy/ch_estimation/refsignal_ul.c | 31 ++- lib/src/phy/ch_estimation/test/CMakeLists.txt | 13 +- .../phy/ch_estimation/test/chest_test_srs.c | 246 ++++++++++++++++++ .../phy/ch_estimation/test/chest_test_ul.c | 2 +- lib/src/phy/enb/enb_ul.c | 7 +- lib/test/phy/pucch_ca_test.c | 2 +- srsenb/src/phy/cc_worker.cc | 2 +- 11 files changed, 451 insertions(+), 72 deletions(-) create mode 100644 lib/src/phy/ch_estimation/test/chest_test_srs.c diff --git a/lib/include/srslte/phy/ch_estimation/chest_ul.h b/lib/include/srslte/phy/ch_estimation/chest_ul.h index 071d2b31b..c0d345038 100644 --- a/lib/include/srslte/phy/ch_estimation/chest_ul.h +++ b/lib/include/srslte/phy/ch_estimation/chest_ul.h @@ -63,6 +63,9 @@ typedef struct { srslte_refsignal_ul_dmrs_pregen_t dmrs_pregen; bool dmrs_signal_configured; + srslte_refsignal_srs_pregen_t srs_pregen; + bool srs_signal_configured; + cf_t* pilot_estimates; cf_t* pilot_estimates_tmp[4]; cf_t* pilot_recv_signal; @@ -92,7 +95,9 @@ SRSLTE_API void srslte_chest_ul_res_free(srslte_chest_ul_res_t* q); SRSLTE_API int srslte_chest_ul_set_cell(srslte_chest_ul_t* q, srslte_cell_t cell); -SRSLTE_API void srslte_chest_ul_pregen(srslte_chest_ul_t* q, srslte_refsignal_dmrs_pusch_cfg_t* cfg); +SRSLTE_API void srslte_chest_ul_pregen(srslte_chest_ul_t* q, + srslte_refsignal_dmrs_pusch_cfg_t* cfg, + srslte_refsignal_srs_cfg_t* srs_cfg); SRSLTE_API int srslte_chest_ul_estimate_pusch(srslte_chest_ul_t* q, srslte_ul_sf_cfg_t* sf, @@ -106,4 +111,11 @@ SRSLTE_API int srslte_chest_ul_estimate_pucch(srslte_chest_ul_t* q, cf_t* input, srslte_chest_ul_res_t* res); +SRSLTE_API int srslte_chest_ul_estimate_srs(srslte_chest_ul_t* q, + srslte_ul_sf_cfg_t* sf, + srslte_refsignal_srs_cfg_t* cfg, + srslte_refsignal_dmrs_pusch_cfg_t* pusch_cfg, + cf_t* input, + srslte_chest_ul_res_t* res); + #endif // SRSLTE_CHEST_UL_H diff --git a/lib/include/srslte/phy/ch_estimation/refsignal_ul.h b/lib/include/srslte/phy/ch_estimation/refsignal_ul.h index d35a513c9..d255f970c 100644 --- a/lib/include/srslte/phy/ch_estimation/refsignal_ul.h +++ b/lib/include/srslte/phy/ch_estimation/refsignal_ul.h @@ -86,6 +86,7 @@ typedef struct SRSLTE_API { } srslte_refsignal_ul_t; typedef struct { + uint32_t max_prb; cf_t** r[SRSLTE_NOF_CSHIFT][SRSLTE_NOF_SF_X_FRAME]; } srslte_refsignal_ul_dmrs_pregen_t; @@ -173,6 +174,12 @@ SRSLTE_API int srslte_refsignal_srs_put(srslte_refsignal_ul_t* q, cf_t* r_srs, cf_t* sf_symbols); +SRSLTE_API int srslte_refsignal_srs_get(srslte_refsignal_ul_t* q, + srslte_refsignal_srs_cfg_t* cfg, + uint32_t tti, + cf_t* r_srs, + cf_t* sf_symbols); + SRSLTE_API void srslte_refsignal_srs_pusch_shortened(srslte_refsignal_ul_t* q, srslte_ul_sf_cfg_t* sf, srslte_refsignal_srs_cfg_t* srs_cfg, diff --git a/lib/include/srslte/phy/enb/enb_ul.h b/lib/include/srslte/phy/enb/enb_ul.h index 24e539d9f..b00d3b4c8 100644 --- a/lib/include/srslte/phy/enb/enb_ul.h +++ b/lib/include/srslte/phy/enb/enb_ul.h @@ -67,8 +67,10 @@ SRSLTE_API int srslte_enb_ul_init(srslte_enb_ul_t* q, cf_t* in_buffer, uint32_t SRSLTE_API void srslte_enb_ul_free(srslte_enb_ul_t* q); -SRSLTE_API int -srslte_enb_ul_set_cell(srslte_enb_ul_t* q, srslte_cell_t cell, srslte_refsignal_dmrs_pusch_cfg_t* pusch_cfg); +SRSLTE_API int srslte_enb_ul_set_cell(srslte_enb_ul_t* q, + srslte_cell_t cell, + srslte_refsignal_dmrs_pusch_cfg_t* pusch_cfg, + srslte_refsignal_srs_cfg_t* srs_cfg); SRSLTE_API int srslte_enb_ul_add_rnti(srslte_enb_ul_t* q, uint16_t rnti); diff --git a/lib/src/phy/ch_estimation/chest_ul.c b/lib/src/phy/ch_estimation/chest_ul.c index b7800b9fb..ddfa3b1d5 100644 --- a/lib/src/phy/ch_estimation/chest_ul.c +++ b/lib/src/phy/ch_estimation/chest_ul.c @@ -189,18 +189,25 @@ int srslte_chest_ul_set_cell(srslte_chest_ul_t* q, srslte_cell_t cell) return ret; } -void srslte_chest_ul_pregen(srslte_chest_ul_t* q, srslte_refsignal_dmrs_pusch_cfg_t* cfg) +void srslte_chest_ul_pregen(srslte_chest_ul_t* q, + srslte_refsignal_dmrs_pusch_cfg_t* cfg, + srslte_refsignal_srs_cfg_t* srs_cfg) { srslte_refsignal_dmrs_pusch_pregen(&q->dmrs_signal, &q->dmrs_pregen, cfg); q->dmrs_signal_configured = true; + + if (srs_cfg) { + srslte_refsignal_srs_pregen(&q->dmrs_signal, &q->srs_pregen, srs_cfg, cfg); + q->srs_signal_configured = true; + } } /* Uses the difference between the averaged and non-averaged pilot estimates */ -static float estimate_noise_pilots(srslte_chest_ul_t* q, cf_t* ce, uint32_t nrefs, uint32_t n_prb[2]) +static float estimate_noise_pilots(srslte_chest_ul_t* q, cf_t* ce, uint32_t nslots, uint32_t nrefs, uint32_t n_prb[2]) { float power = 0; - for (int i = 0; i < 2; i++) { + for (int i = 0; i < nslots; i++) { power += srslte_chest_estimate_noise_pilots( &q->pilot_estimates[i * nrefs], &ce[SRSLTE_REFSIGNAL_UL_L(i, q->cell.cp) * q->cell.nof_prb * SRSLTE_NRE + n_prb[i] * SRSLTE_NRE], @@ -208,7 +215,7 @@ static float estimate_noise_pilots(srslte_chest_ul_t* q, cf_t* ce, uint32_t nref nrefs); } - power /= 2; + power /= nslots; if (q->smooth_filter_len == 3) { // Calibrated for filter length 3 @@ -222,7 +229,7 @@ static float estimate_noise_pilots(srslte_chest_ul_t* q, cf_t* ce, uint32_t nref // The interpolator currently only supports same frequency allocation for each subframe #define cesymb(i) ce[SRSLTE_RE_IDX(q->cell.nof_prb, i, n_prb[0] * SRSLTE_NRE)] -static void interpolate_pilots(srslte_chest_ul_t* q, cf_t* ce, uint32_t nrefs, uint32_t n_prb[2]) +static void interpolate_pilots(srslte_chest_ul_t* q, cf_t* ce, uint32_t nslots, uint32_t nrefs, uint32_t n_prb[2]) { #ifdef DO_LINEAR_INTERPOLATION uint32_t L1 = SRSLTE_REFSIGNAL_UL_L(0, q->cell.cp); @@ -245,25 +252,26 @@ static void interpolate_pilots(srslte_chest_ul_t* q, cf_t* ce, uint32_t nrefs, u nrefs); #else // Instead of a linear interpolation, we just copy the estimates to all symbols in that subframe - for (int s = 0; s < 2; s++) { + for (int s = 0; s < nslots; s++) { for (int i = 0; i < SRSLTE_CP_NSYMB(q->cell.cp); i++) { int src_symb = SRSLTE_REFSIGNAL_UL_L(s, q->cell.cp); int dst_symb = i + s * SRSLTE_CP_NSYMB(q->cell.cp); // skip the symbol with the estimates if (dst_symb != src_symb) { - memcpy(&ce[(dst_symb * q->cell.nof_prb + n_prb[s]) * SRSLTE_NRE], - &ce[(src_symb * q->cell.nof_prb + n_prb[s]) * SRSLTE_NRE], - nrefs * sizeof(cf_t)); + srslte_vec_cf_copy(&ce[(dst_symb * q->cell.nof_prb + n_prb[s]) * SRSLTE_NRE], + &ce[(src_symb * q->cell.nof_prb + n_prb[s]) * SRSLTE_NRE], + nrefs); } } } #endif } -static void average_pilots(srslte_chest_ul_t* q, cf_t* input, cf_t* ce, uint32_t nrefs, uint32_t n_prb[2]) +static void +average_pilots(srslte_chest_ul_t* q, cf_t* input, cf_t* ce, uint32_t nslots, uint32_t nrefs, uint32_t n_prb[2]) { - for (int i = 0; i < 2; i++) { + for (uint32_t i = 0; i < nslots; i++) { srslte_chest_average_pilots( &input[i * nrefs], &ce[SRSLTE_REFSIGNAL_UL_L(i, q->cell.cp) * q->cell.nof_prb * SRSLTE_NRE + n_prb[i] * SRSLTE_NRE], @@ -274,81 +282,117 @@ static void average_pilots(srslte_chest_ul_t* q, cf_t* input, cf_t* ce, uint32_t } } -int srslte_chest_ul_estimate_pusch(srslte_chest_ul_t* q, - srslte_ul_sf_cfg_t* sf, - srslte_pusch_cfg_t* cfg, - cf_t* input, - srslte_chest_ul_res_t* res) +/** + * Generic PUSCH and DMRS channel estimation. It assumes q->pilot_estimates has been populated with the Least Square + * Estimates + * + * @param q Uplink Channel estimation instance + * @param nslots number of slots (2 for DMRS, 1 for SRS) + * @param nrefs_sym number of reference resource elements per symbols (depends on configuration) + * @param stride sub-carrier distance between reference signal resource elements (1 for DMRS, 2 for SRS) + * @param meas_ta_en enables or disables the Time Alignment error measurement + * @param write_estimates Write channel estimation in res, (true for DMRS and false for SRS) + * @param n_prb Resource block start for the grant, set to zero for Sounding Reference Signals + * @param res UL channel estimation result + */ +static void chest_ul_estimate(srslte_chest_ul_t* q, + uint32_t nslots, + uint32_t nrefs_sym, + uint32_t stride, + bool meas_ta_en, + bool write_estimates, + uint32_t n_prb[SRSLTE_NOF_SLOTS_PER_SF], + srslte_chest_ul_res_t* res) { - if (!q->dmrs_signal_configured) { - ERROR("Error must call srslte_chest_ul_set_cfg() before using the UL estimator\n"); - return SRSLTE_ERROR; - } - - uint32_t nof_prb = cfg->grant.L_prb; - - if (!srslte_dft_precoding_valid_prb(nof_prb)) { - ERROR("Error invalid nof_prb=%d\n", nof_prb); - return SRSLTE_ERROR_INVALID_INPUTS; - } - - int nrefs_sym = nof_prb * SRSLTE_NRE; - int nrefs_sf = nrefs_sym * SRSLTE_NOF_SLOTS_PER_SF; - - /* Get references from the input signal */ - srslte_refsignal_dmrs_pusch_get(&q->dmrs_signal, cfg, input, q->pilot_recv_signal); - - /* Use the known DMRS signal to compute Least-squares estimates */ - srslte_vec_prod_conj_ccc( - q->pilot_recv_signal, q->dmrs_pregen.r[cfg->grant.n_dmrs][sf->tti % 10][nof_prb], q->pilot_estimates, nrefs_sf); - // Calculate time alignment error float ta_err = 0.0f; - if (cfg->meas_ta_en) { - for (int i = 0; i < SRSLTE_NOF_SLOTS_PER_SF; i++) { - ta_err += srslte_vec_estimate_frequency(&q->pilot_estimates[i * nrefs_sym], nrefs_sym) / SRSLTE_NOF_SLOTS_PER_SF; + if (meas_ta_en) { + for (int i = 0; i < nslots; i++) { + ta_err += srslte_vec_estimate_frequency(&q->pilot_estimates[i * nrefs_sym], nrefs_sym) / nslots; } } // Average and store time aligment error if (isnormal(ta_err)) { - res->ta_us = roundf(ta_err / 15e-3 * 10) / 10; + res->ta_us = roundf(ta_err / 15e-2f) / (10.0f * (float)stride); } else { res->ta_us = 0.0f; } - if (cfg->grant.n_prb[0] != cfg->grant.n_prb[1]) { - printf("ERROR: intra-subframe frequency hopping not supported in the estimator!!\n"); + // Check if intra-subframe frequency hopping is enabled + if (n_prb[0] != n_prb[1]) { + ERROR("ERROR: intra-subframe frequency hopping not supported in the estimator!!\n"); } if (res->ce != NULL) { if (q->smooth_filter_len > 0) { - average_pilots(q, q->pilot_estimates, res->ce, nrefs_sym, cfg->grant.n_prb); - interpolate_pilots(q, res->ce, nrefs_sym, cfg->grant.n_prb); + average_pilots(q, q->pilot_estimates, res->ce, nslots, nrefs_sym, n_prb); - /* If averaging, compute noise from difference between received and averaged estimates */ - res->noise_estimate = estimate_noise_pilots(q, res->ce, nrefs_sym, cfg->grant.n_prb); + if (write_estimates) { + interpolate_pilots(q, res->ce, nslots, nrefs_sym, n_prb); + } + + // If averaging, compute noise from difference between received and averaged estimates + res->noise_estimate = estimate_noise_pilots(q, res->ce, nslots, nrefs_sym, n_prb); } else { // Copy estimates to CE vector without averaging - for (int i = 0; i < 2; i++) { - memcpy(&res->ce[SRSLTE_REFSIGNAL_UL_L(i, q->cell.cp) * q->cell.nof_prb * SRSLTE_NRE + - cfg->grant.n_prb[i] * SRSLTE_NRE], - &q->pilot_estimates[i * nrefs_sym], - nrefs_sym * sizeof(cf_t)); + for (int i = 0; i < nslots; i++) { + srslte_vec_cf_copy( + &res->ce[SRSLTE_REFSIGNAL_UL_L(i, q->cell.cp) * q->cell.nof_prb * SRSLTE_NRE + n_prb[i] * SRSLTE_NRE], + &q->pilot_estimates[i * nrefs_sym], + nrefs_sym); + } + if (write_estimates) { + interpolate_pilots(q, res->ce, nslots, nrefs_sym, n_prb); } - interpolate_pilots(q, res->ce, nrefs_sym, cfg->grant.n_prb); res->noise_estimate = 0; } } + // Estimate received pilot power - if (res->noise_estimate) { - res->snr = srslte_vec_avg_power_cf(q->pilot_recv_signal, nrefs_sf) / res->noise_estimate; + if (isnormal(res->noise_estimate)) { + res->snr = srslte_vec_avg_power_cf(q->pilot_recv_signal, nslots * nrefs_sym) / res->noise_estimate; } else { res->snr = NAN; } + // Convert measurements in logarithm scale res->snr_db = srslte_convert_power_to_dB(res->snr); res->noise_estimate_dbm = srslte_convert_power_to_dBm(res->noise_estimate); +} + +int srslte_chest_ul_estimate_pusch(srslte_chest_ul_t* q, + srslte_ul_sf_cfg_t* sf, + srslte_pusch_cfg_t* cfg, + cf_t* input, + srslte_chest_ul_res_t* res) +{ + if (!q->dmrs_signal_configured) { + ERROR("Error must call srslte_chest_ul_set_cfg() before using the UL estimator\n"); + return SRSLTE_ERROR; + } + + uint32_t nof_prb = cfg->grant.L_prb; + + if (!srslte_dft_precoding_valid_prb(nof_prb)) { + ERROR("Error invalid nof_prb=%d\n", nof_prb); + return SRSLTE_ERROR_INVALID_INPUTS; + } + + int nrefs_sym = nof_prb * SRSLTE_NRE; + int nrefs_sf = nrefs_sym * SRSLTE_NOF_SLOTS_PER_SF; + + /* Get references from the input signal */ + srslte_refsignal_dmrs_pusch_get(&q->dmrs_signal, cfg, input, q->pilot_recv_signal); + + // Use the known DMRS signal to compute Least-squares estimates + srslte_vec_prod_conj_ccc(q->pilot_recv_signal, + q->dmrs_pregen.r[cfg->grant.n_dmrs][sf->tti % SRSLTE_NOF_SF_X_FRAME][nof_prb], + q->pilot_estimates, + nrefs_sf); + + // Estimate + chest_ul_estimate(q, SRSLTE_NOF_SLOTS_PER_SF, nrefs_sym, 1, cfg->meas_ta_en, true, cfg->grant.n_prb, res); return 0; } @@ -444,3 +488,40 @@ int srslte_chest_ul_estimate_pucch(srslte_chest_ul_t* q, return 0; } + +int srslte_chest_ul_estimate_srs(srslte_chest_ul_t* q, + srslte_ul_sf_cfg_t* sf, + srslte_refsignal_srs_cfg_t* cfg, + srslte_refsignal_dmrs_pusch_cfg_t* pusch_cfg, + cf_t* input, + srslte_chest_ul_res_t* res) +{ + if (q == NULL || sf == NULL || cfg == NULL || pusch_cfg == NULL || input == NULL || res == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + // Extract parameters + uint32_t n_srs_re = srslte_refsignal_srs_M_sc(&q->dmrs_signal, cfg); + + // Extract Sounding Reference Signal + if (srslte_refsignal_srs_get(&q->dmrs_signal, cfg, sf->tti, q->pilot_recv_signal, input) != SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + // Get Known pilots + cf_t* known_pilots = q->pilot_known_signal; + if (q->srs_signal_configured) { + known_pilots = q->srs_pregen.r[sf->tti % SRSLTE_NOF_SF_X_FRAME]; + } else { + srslte_refsignal_srs_gen(&q->dmrs_signal, cfg, pusch_cfg, sf->tti % SRSLTE_NOF_SF_X_FRAME, known_pilots); + } + + // Compute least squares estimates + srslte_vec_prod_conj_ccc(q->pilot_recv_signal, known_pilots, q->pilot_estimates, n_srs_re); + + // Estimate + uint32_t n_prb[2] = {}; + chest_ul_estimate(q, 1, n_srs_re, 1, true, false, n_prb, res); + + return SRSLTE_SUCCESS; +} \ No newline at end of file diff --git a/lib/src/phy/ch_estimation/refsignal_ul.c b/lib/src/phy/ch_estimation/refsignal_ul.c index 96bdd1324..9a4580d21 100644 --- a/lib/src/phy/ch_estimation/refsignal_ul.c +++ b/lib/src/phy/ch_estimation/refsignal_ul.c @@ -33,7 +33,6 @@ #include "srslte/phy/utils/debug.h" #include "srslte/phy/utils/vector.h" - // n_dmrs_2 table 5.5.2.1.1-1 from 36.211 uint32_t n_dmrs_2[8] = {0, 6, 3, 4, 2, 8, 10, 9}; @@ -354,6 +353,8 @@ static void compute_r(srslte_refsignal_ul_t* q, int srslte_refsignal_dmrs_pusch_pregen_init(srslte_refsignal_ul_dmrs_pregen_t* pregen, uint32_t max_prb) { + pregen->max_prb = max_prb; + for (uint32_t sf_idx = 0; sf_idx < SRSLTE_NOF_SF_X_FRAME; sf_idx++) { for (uint32_t cs = 0; cs < SRSLTE_NOF_CSHIFT; cs++) { pregen->r[cs][sf_idx] = (cf_t**)calloc(sizeof(cf_t*), max_prb + 1); @@ -405,11 +406,9 @@ void srslte_refsignal_dmrs_pusch_pregen_free(srslte_refsignal_ul_t* q, srslte_re for (uint32_t sf_idx = 0; sf_idx < SRSLTE_NOF_SF_X_FRAME; sf_idx++) { for (uint32_t cs = 0; cs < SRSLTE_NOF_CSHIFT; cs++) { if (pregen->r[cs][sf_idx]) { - for (uint32_t n = 0; n <= q->cell.nof_prb; n++) { - if (srslte_dft_precoding_valid_prb(n)) { - if (pregen->r[cs][sf_idx][n]) { - free(pregen->r[cs][sf_idx][n]); - } + for (uint32_t n = 0; n <= pregen->max_prb; n++) { + if (pregen->r[cs][sf_idx][n]) { + free(pregen->r[cs][sf_idx][n]); } } free(pregen->r[cs][sf_idx]); @@ -1005,7 +1004,7 @@ int srslte_refsignal_srs_put(srslte_refsignal_ul_t* q, cf_t* sf_symbols) { int ret = SRSLTE_ERROR_INVALID_INPUTS; - if (r_srs && q) { + if (r_srs && q && sf_symbols && cfg) { uint32_t M_sc = srslte_refsignal_srs_M_sc(q, cfg); uint32_t k0 = srs_k0_ue(cfg, q->cell.nof_prb, tti); for (int i = 0; i < M_sc; i++) { @@ -1015,3 +1014,21 @@ int srslte_refsignal_srs_put(srslte_refsignal_ul_t* q, } return ret; } + +int srslte_refsignal_srs_get(srslte_refsignal_ul_t* q, + srslte_refsignal_srs_cfg_t* cfg, + uint32_t tti, + cf_t* r_srs, + cf_t* sf_symbols) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (r_srs && q && sf_symbols && cfg) { + uint32_t M_sc = srslte_refsignal_srs_M_sc(q, cfg); + uint32_t k0 = srs_k0_ue(cfg, q->cell.nof_prb, tti); + for (int i = 0; i < M_sc; i++) { + r_srs[i] = sf_symbols[SRSLTE_RE_IDX(q->cell.nof_prb, 2 * SRSLTE_CP_NSYMB(q->cell.cp) - 1, k0 + 2 * i)]; + } + ret = SRSLTE_SUCCESS; + } + return ret; +} diff --git a/lib/src/phy/ch_estimation/test/CMakeLists.txt b/lib/src/phy/ch_estimation/test/CMakeLists.txt index 924e92502..d72534f9d 100644 --- a/lib/src/phy/ch_estimation/test/CMakeLists.txt +++ b/lib/src/phy/ch_estimation/test/CMakeLists.txt @@ -46,7 +46,18 @@ target_link_libraries(refsignal_ul_test_all srslte_phy srslte_common) add_test(chest_test_ul_cellid0 chest_test_ul -c 0 -r 50) add_test(chest_test_ul_cellid1 chest_test_ul -c 1 -r 50) -add_test(chest_test_ul_cellid1 chest_test_ul -c 2 -r 50) +add_test(chest_test_ul_cellid1 chest_test_ul -c 2 -r 50) + +######################################################################## +# Uplink Sounding Reference Signals Channel Estimation TEST +######################################################################## + +add_executable(chest_test_srs chest_test_srs.c) +target_link_libraries(chest_test_srs srslte_phy srslte_common) + +foreach (cell_n_prb 6 15 25 50 75 100) + add_test(chest_test_srs_${cell_n_prb} chest_test_srs -c 2 -r ${cell_n_prb}) +endforeach(cell_n_prb 6 15 25 50 75 100) ######################################################################## diff --git a/lib/src/phy/ch_estimation/test/chest_test_srs.c b/lib/src/phy/ch_estimation/test/chest_test_srs.c new file mode 100644 index 000000000..5a7269374 --- /dev/null +++ b/lib/src/phy/ch_estimation/test/chest_test_srs.c @@ -0,0 +1,246 @@ +/* + * 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/common/test_common.h" +#include "srslte/srslte.h" +#include +#include +#include +#include +#include + +static srslte_cell_t cell = {6, // nof_prb + 1, // nof_ports + 1, // cell_id + SRSLTE_CP_NORM, // cyclic prefix + SRSLTE_PHICH_NORM, + SRSLTE_PHICH_R_1, // PHICH length + SRSLTE_FDD}; + +static srslte_refsignal_dmrs_pusch_cfg_t dmrs_pusch_cfg = {}; +static srslte_refsignal_srs_cfg_t srs_cfg = {}; + +static uint32_t test_counter = 0; + +static float snr_db = 10.0f; +static float n0_dbm = 30.0f - 20.0f; +static srslte_channel_awgn_t channel = {}; + +#define CHEST_TEST_SRS_SNR_DB_TOLERANCE 10.0f + +void usage(char* prog) +{ + printf("Usage: %s [recov]\n", prog); + + printf("\t-r nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-e extended cyclic prefix [Default normal]\n"); + + printf("\t-c cell_id [Default %d]\n", cell.id); + + printf("\t-v increase verbosity\n"); +} + +typedef struct { + uint32_t sf_size; + srslte_refsignal_ul_t refsignal_ul; + srslte_refsignal_srs_pregen_t srs_pregen; + srslte_chest_ul_t chest_ul; + cf_t* sf_symbols; + srslte_chest_ul_res_t chest_ul_res; +} srs_test_context_t; + +int srs_test_context_init(srs_test_context_t* q) +{ + q->sf_size = SRSLTE_SF_LEN_RE(SRSLTE_MAX_PRB, cell.cp); + + // Initiate UL ref signals + if (srslte_refsignal_ul_init(&q->refsignal_ul, SRSLTE_MAX_PRB) != SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + // Set cell + if (srslte_refsignal_ul_set_cell(&q->refsignal_ul, cell) != SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + // Pregenerate signals + if (srslte_refsignal_srs_pregen(&q->refsignal_ul, &q->srs_pregen, &srs_cfg, &dmrs_pusch_cfg) != SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + // Allocate buffer + q->sf_symbols = srslte_vec_cf_malloc(q->sf_size); + if (q->sf_symbols == NULL) { + return SRSLTE_ERROR; + } + + // Create UL channel estimator + if (srslte_chest_ul_init(&q->chest_ul, SRSLTE_MAX_PRB) != SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + // Set cell in UL channel estimator + if (srslte_chest_ul_set_cell(&q->chest_ul, cell) != SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + // Initialise UL channel estimator result + if (srslte_chest_ul_res_init(&q->chest_ul_res, SRSLTE_MAX_PRB) != SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + return SRSLTE_SUCCESS; +} + +void srs_test_context_free(srs_test_context_t* q) +{ + srslte_refsignal_ul_free(&q->refsignal_ul); + srslte_refsignal_srs_pregen_free(&q->refsignal_ul, &q->srs_pregen); + srslte_chest_ul_free(&q->chest_ul); + srslte_chest_ul_res_free(&q->chest_ul_res); + if (q->sf_symbols) { + free(q->sf_symbols); + } +} +int srs_test_context_run(srs_test_context_t* q) +{ + srslte_ul_sf_cfg_t ul_sf_cfg = {}; + + INFO(" TEST: bw_cfg=%d; sf_cfg=%d; B=%d; b_hops=%d; n_srs=%d; I_srs=%d;\n", + srs_cfg.bw_cfg, + srs_cfg.subframe_config, + srs_cfg.B, + srs_cfg.b_hop, + srs_cfg.n_srs, + srs_cfg.I_srs); + + for (ul_sf_cfg.tti = 0; ul_sf_cfg.tti < SRSLTE_NOF_SF_X_FRAME; ul_sf_cfg.tti++) { + // Set resource grid to zero + srslte_vec_cf_zero(q->sf_symbols, q->sf_size); + + // Put sounding reference signals + TESTASSERT(srslte_refsignal_srs_put( + &q->refsignal_ul, &srs_cfg, ul_sf_cfg.tti, q->srs_pregen.r[ul_sf_cfg.tti], q->sf_symbols) == + SRSLTE_SUCCESS); + + // Apply AWGN channel + if (!isnan(snr_db) && !isinf(snr_db)) { + srslte_channel_awgn_run_c(&channel, q->sf_symbols, q->sf_symbols, q->sf_size); + } + + // Estimate + TESTASSERT(srslte_chest_ul_estimate_srs( + &q->chest_ul, &ul_sf_cfg, &srs_cfg, &dmrs_pusch_cfg, q->sf_symbols, &q->chest_ul_res) == + SRSLTE_SUCCESS); + + INFO("RESULTS: tti=%d; snr_db=%+.1f; noise_estimate_dbm=%+.1f; ta_us=%+.1f;\n", + ul_sf_cfg.tti, + q->chest_ul_res.snr_db, + q->chest_ul_res.noise_estimate_dbm, + q->chest_ul_res.ta_us); + + // Assert SRS measurements + TESTASSERT(fabsf(q->chest_ul_res.snr_db - snr_db) < CHEST_TEST_SRS_SNR_DB_TOLERANCE); + TESTASSERT(fabsf(q->chest_ul_res.noise_estimate_dbm - n0_dbm) < CHEST_TEST_SRS_SNR_DB_TOLERANCE); + TESTASSERT(fabsf(q->chest_ul_res.ta_us) < 0.1f); + } + + return SRSLTE_SUCCESS; +} + +void parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "recov")) != -1) { + switch (opt) { + case 'r': + cell.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'e': + cell.cp = SRSLTE_CP_EXT; + break; + case 'c': + cell.id = (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) +{ + + srs_test_context_t context = {}; + int ret = SRSLTE_SUCCESS; + + parse_args(argc, argv); + + if (srslte_channel_awgn_init(&channel, 123456789) != SRSLTE_SUCCESS) { + ret = SRSLTE_ERROR; + } + + if (!isnan(snr_db) && !isinf(snr_db)) { + n0_dbm = 30.0f - snr_db; + srslte_channel_awgn_set_n0(&channel, n0_dbm - 33.0f); + } + + for (srs_cfg.bw_cfg = 0; srs_cfg.bw_cfg < 8 && !ret; srs_cfg.bw_cfg++) { + for (srs_cfg.B = 0; srs_cfg.B < 4 && !ret; srs_cfg.B++) { + for (srs_cfg.n_srs = 0; srs_cfg.n_srs < 8 && !ret; srs_cfg.n_srs++) { + // Initialise context + ret = srs_test_context_init(&context); + if (ret) { + printf("Failed setting context: bw_cfg=%d; B=%d; n_srs=%d;\n", srs_cfg.bw_cfg, srs_cfg.B, srs_cfg.n_srs); + } + + for (srs_cfg.subframe_config = 0; srs_cfg.subframe_config < 16 && !ret; srs_cfg.subframe_config++) { + for (srs_cfg.b_hop = 0; srs_cfg.b_hop < 4 && !ret; srs_cfg.b_hop++) { + for (srs_cfg.I_srs = 0; srs_cfg.I_srs < 1024 && !ret; srs_cfg.I_srs += 123) { + // Run actual test + ret = srs_test_context_run(&context); + if (!ret) { + test_counter++; + } + } + } + } + + // Free context + srs_test_context_free(&context); + } + } + } + + srslte_channel_awgn_free(&channel); + + if (!ret) { + printf("OK, %d test passed successfully.\n", test_counter); + } else { + printf("Failed at test %d.\n", test_counter); + } + + return ret; +} diff --git a/lib/src/phy/ch_estimation/test/chest_test_ul.c b/lib/src/phy/ch_estimation/test/chest_test_ul.c index 29d7fa7b7..c07a4f8e1 100644 --- a/lib/src/phy/ch_estimation/test/chest_test_ul.c +++ b/lib/src/phy/ch_estimation/test/chest_test_ul.c @@ -164,7 +164,7 @@ int main(int argc, char** argv) } pusch_cfg.group_hopping_en = group_hopping_en; pusch_cfg.sequence_hopping_en = sequence_hopping_en; - srslte_chest_ul_pregen(&est, &pusch_cfg); + srslte_chest_ul_pregen(&est, &pusch_cfg, NULL); // Loop through subframe idx and cyclic shifts diff --git a/lib/src/phy/enb/enb_ul.c b/lib/src/phy/enb/enb_ul.c index 40ba4a8ba..2990e128a 100644 --- a/lib/src/phy/enb/enb_ul.c +++ b/lib/src/phy/enb/enb_ul.c @@ -107,7 +107,10 @@ void srslte_enb_ul_free(srslte_enb_ul_t* q) } } -int srslte_enb_ul_set_cell(srslte_enb_ul_t* q, srslte_cell_t cell, srslte_refsignal_dmrs_pusch_cfg_t* pusch_cfg) +int srslte_enb_ul_set_cell(srslte_enb_ul_t* q, + srslte_cell_t cell, + srslte_refsignal_dmrs_pusch_cfg_t* pusch_cfg, + srslte_refsignal_srs_cfg_t* srs_cfg) { int ret = SRSLTE_ERROR_INVALID_INPUTS; @@ -136,7 +139,7 @@ int srslte_enb_ul_set_cell(srslte_enb_ul_t* q, srslte_cell_t cell, srslte_refsig } // SRS is a dedicated configuration - srslte_chest_ul_pregen(&q->chest, pusch_cfg); + srslte_chest_ul_pregen(&q->chest, pusch_cfg, srs_cfg); ret = SRSLTE_SUCCESS; } diff --git a/lib/test/phy/pucch_ca_test.c b/lib/test/phy/pucch_ca_test.c index 854ae4272..7101d1b9a 100644 --- a/lib/test/phy/pucch_ca_test.c +++ b/lib/test/phy/pucch_ca_test.c @@ -91,7 +91,7 @@ static int test_pucch_ca(srslte_ack_nack_feedback_mode_t ack_nack_feedback_mode, // Init eNb TESTASSERT(!srslte_enb_ul_init(&enb_ul, buffer, cell.nof_prb)); - TESTASSERT(!srslte_enb_ul_set_cell(&enb_ul, cell, &dmrs_pusch_cfg)); + TESTASSERT(!srslte_enb_ul_set_cell(&enb_ul, cell, &dmrs_pusch_cfg, NULL)); TESTASSERT(!srslte_enb_ul_add_rnti(&enb_ul, rnti)); // The test itself starts here diff --git a/srsenb/src/phy/cc_worker.cc b/srsenb/src/phy/cc_worker.cc index 1318053b5..098c31b8d 100644 --- a/srsenb/src/phy/cc_worker.cc +++ b/srsenb/src/phy/cc_worker.cc @@ -121,7 +121,7 @@ void cc_worker::init(phy_common* phy_, srslte::log* log_h_, uint32_t cc_idx_) return; } - if (srslte_enb_ul_set_cell(&enb_ul, cell, &phy->dmrs_pusch_cfg)) { + if (srslte_enb_ul_set_cell(&enb_ul, cell, &phy->dmrs_pusch_cfg, nullptr)) { ERROR("Error initiating ENB UL\n"); return; }