diff --git a/lib/include/srslte/phy/ch_estimation/dmrs_pucch.h b/lib/include/srslte/phy/ch_estimation/dmrs_pucch.h index 7bd6c7f52..91cddd708 100644 --- a/lib/include/srslte/phy/ch_estimation/dmrs_pucch.h +++ b/lib/include/srslte/phy/ch_estimation/dmrs_pucch.h @@ -65,4 +65,40 @@ SRSLTE_API int srslte_dmrs_pucch_format1_estimate(const srslte_pucch_nr_t* const cf_t* slot_symbols, srslte_chest_ul_res_t* res); +/** + * @brief Puts NR-PUCCH format 2 DMRS in the provided resource grid + * @param[in] q NR-PUCCH encoder/decoder object + * @param[in] carrier Carrier configuration + * @param[in] cfg PUCCH common configuration + * @param[in] slot slot configuration + * @param[in] resource PUCCH format 2 resource + * @param[out] slot_symbols Resource grid of the given slot + * @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise + */ +int srslte_dmrs_pucch_format2_put(const srslte_pucch_nr_t* q, + const srslte_carrier_nr_t* carrier, + const srslte_pucch_nr_common_cfg_t* cfg, + const srslte_dl_slot_cfg_t* slot, + const srslte_pucch_nr_resource_t* resource, + cf_t* slot_symbols); + +/** + * @brief Estimates NR-PUCCH format 2 resource elements from their DMRS in the provided resource grid + * @param[in] q NR-PUCCH encoder/decoder object + * @param[in] carrier Carrier configuration + * @param[in] cfg PUCCH common configuration + * @param[in] slot slot configuration + * @param[in] resource PUCCH format 2 resource + * @param[in] slot_symbols Resource grid of the given slot + * @param[out] res UL Channel estimator result + * @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise + */ +int srslte_dmrs_pucch_format2_estimate(const srslte_pucch_nr_t* q, + const srslte_carrier_nr_t* carrier, + const srslte_pucch_nr_common_cfg_t* cfg, + const srslte_dl_slot_cfg_t* slot, + const srslte_pucch_nr_resource_t* resource, + const cf_t* slot_symbols, + srslte_chest_ul_res_t* res); + #endif // SRSLTE_DMRS_PUCCH_H diff --git a/lib/include/srslte/phy/common/phy_common_nr.h b/lib/include/srslte/phy/common/phy_common_nr.h index 3a133ad4e..21ed49489 100644 --- a/lib/include/srslte/phy/common/phy_common_nr.h +++ b/lib/include/srslte/phy/common/phy_common_nr.h @@ -23,7 +23,7 @@ extern "C" { /** * @brief Defines the number of symbols per slot. Defined by TS 38.211 v15.8.0 Table 4.3.2-1. */ -#define SRSLTE_NSYMB_PER_SLOT_NR 14 +#define SRSLTE_NSYMB_PER_SLOT_NR 14U /** * @brief Defines the resource grid size in physical resource elements (frequency and time domain) diff --git a/lib/include/srslte/phy/common/sequence.h b/lib/include/srslte/phy/common/sequence.h index aa3ed9b71..c3857ecb9 100644 --- a/lib/include/srslte/phy/common/sequence.h +++ b/lib/include/srslte/phy/common/sequence.h @@ -25,6 +25,8 @@ #include "srslte/config.h" #include "srslte/phy/common/phy_common.h" +#define SRSLTE_SEQUENCE_MOD(X) ((X) & (uint32_t)INT32_MAX) + typedef struct SRSLTE_API { uint32_t x1; uint32_t x2; diff --git a/lib/include/srslte/phy/phch/pucch_nr.h b/lib/include/srslte/phy/phch/pucch_nr.h index 996867b70..6ddc1507d 100644 --- a/lib/include/srslte/phy/phch/pucch_nr.h +++ b/lib/include/srslte/phy/phch/pucch_nr.h @@ -54,6 +54,7 @@ typedef struct SRSLTE_API { srslte_uci_nr_t uci; uint8_t* b; cf_t* d; + cf_t* ce; } srslte_pucch_nr_t; /** diff --git a/lib/src/phy/ch_estimation/dmrs_pdcch.c b/lib/src/phy/ch_estimation/dmrs_pdcch.c index 345e8dd17..e9675cb9c 100644 --- a/lib/src/phy/ch_estimation/dmrs_pdcch.c +++ b/lib/src/phy/ch_estimation/dmrs_pdcch.c @@ -31,9 +31,8 @@ static uint32_t dmrs_pdcch_get_cinit(uint32_t slot_idx, uint32_t symbol_idx, uint32_t n_id) { - return (uint32_t)( - ((1UL << 17UL) * (SRSLTE_NSYMB_PER_SLOT_NR * slot_idx + symbol_idx + 1UL) * (2UL * n_id + 1UL) + 2UL * n_id) % - INT32_MAX); + return SRSLTE_SEQUENCE_MOD((((SRSLTE_NSYMB_PER_SLOT_NR * slot_idx + symbol_idx + 1UL) * (2UL * n_id + 1UL)) << 17U) + + 2UL * n_id); } static void dmrs_pdcch_put_symbol_noninterleaved(const srslte_carrier_nr_t* carrier, diff --git a/lib/src/phy/ch_estimation/dmrs_pucch.c b/lib/src/phy/ch_estimation/dmrs_pucch.c index 4e38701c3..9de97ef19 100644 --- a/lib/src/phy/ch_estimation/dmrs_pucch.c +++ b/lib/src/phy/ch_estimation/dmrs_pucch.c @@ -11,6 +11,7 @@ */ #include "srslte/phy/ch_estimation/dmrs_pucch.h" +#include "srslte/phy/common/sequence.h" #include "srslte/phy/utils/debug.h" #include "srslte/phy/utils/vector.h" @@ -160,7 +161,8 @@ int srslte_dmrs_pucch_format1_estimate(const srslte_pucch_nr_t* q, srslte_chest_ul_res_t* res) { - if (q == NULL || carrier == NULL || cfg == NULL || slot == NULL || resource == NULL || slot_symbols == NULL) { + if (q == NULL || carrier == NULL || cfg == NULL || slot == NULL || resource == NULL || slot_symbols == NULL || + res == NULL) { return SRSLTE_ERROR_INVALID_INPUTS; } @@ -223,7 +225,7 @@ int srslte_dmrs_pucch_format1_estimate(const srslte_pucch_nr_t* q, float epre = 0.0f; float ta_err = 0.0f; for (uint32_t m = 0; m < n_pucch; m++) { - cf_t corr = srslte_vec_acc_cc(ce[m], SRSLTE_NRE); + cf_t corr = srslte_vec_acc_cc(ce[m], SRSLTE_NRE) / SRSLTE_NRE; rsrp += __real__ corr * __real__ corr + __imag__ corr * __imag__ corr; epre += srslte_vec_avg_power_cf(ce[m], SRSLTE_NRE); ta_err += srslte_vec_estimate_frequency(ce[m], SRSLTE_NRE); @@ -236,10 +238,14 @@ int srslte_dmrs_pucch_format1_estimate(const srslte_pucch_nr_t* q, // Set power measures rsrp = SRSLTE_MIN(rsrp, epre); + res->rsrp = rsrp; + res->rsrp_dBfs = srslte_convert_power_to_dB(rsrp); + res->epre = epre; + res->epre_dBfs = srslte_convert_power_to_dB(epre); res->noise_estimate = epre - rsrp; res->noise_estimate_dbm = srslte_convert_power_to_dB(res->noise_estimate); res->snr = rsrp / res->noise_estimate; - res->snr_db = srslte_convert_power_to_dB(res->snr_db); + res->snr_db = srslte_convert_power_to_dB(res->snr); // Compute Time Aligment error in microseconds if (isnormal(ta_err)) { @@ -280,6 +286,152 @@ int srslte_dmrs_pucch_format1_estimate(const srslte_pucch_nr_t* q, return SRSLTE_SUCCESS; } +static uint32_t dmrs_pucch_format2_cinit(const srslte_carrier_nr_t* carrier, + const srslte_pucch_nr_common_cfg_t* cfg, + const srslte_dl_slot_cfg_t* slot, + uint32_t l) +{ + uint32_t n = SRSLTE_SLOT_NR_MOD(slot->idx, carrier->numerology); + uint32_t n_id = (cfg->scrambling_id_present) ? cfg->scambling_id : carrier->id; + + return SRSLTE_SEQUENCE_MOD((((SRSLTE_NSYMB_PER_SLOT_NR * n + l + 1U) * (2U * n_id + 1U)) << 17U) + 2U * n_id); +} + +int srslte_dmrs_pucch_format2_put(const srslte_pucch_nr_t* q, + const srslte_carrier_nr_t* carrier, + const srslte_pucch_nr_common_cfg_t* cfg, + const srslte_dl_slot_cfg_t* slot, + const srslte_pucch_nr_resource_t* resource, + cf_t* slot_symbols) +{ + if (q == NULL || carrier == NULL || cfg == NULL || slot == NULL || resource == NULL || slot_symbols == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (srslte_pucch_nr_cfg_resource_valid(resource) < SRSLTE_SUCCESS) { + ERROR("Invalid PUCCH format 1 resource\n"); + return SRSLTE_ERROR; + } + + uint32_t l_start = resource->start_symbol_idx; + uint32_t l_end = resource->start_symbol_idx + resource->nof_symbols; + uint32_t k_start = SRSLTE_MIN(carrier->nof_prb - 1, resource->starting_prb) * SRSLTE_NRE + 1; + uint32_t k_end = SRSLTE_MIN(carrier->nof_prb, resource->starting_prb + resource->nof_prb) * SRSLTE_NRE; + for (uint32_t l = l_start; l < l_end; l++) { + // Compute sequence initial state + uint32_t cinit = dmrs_pucch_format2_cinit(carrier, cfg, slot, l); + srslte_sequence_state_t sequence = {}; + srslte_sequence_state_init(&sequence, cinit); + + // Skip PRBs to start + srslte_sequence_state_advance(&sequence, 2 * 4 * resource->starting_prb); + + // Generate sequence + cf_t r_l[SRSLTE_PUCCH_NR_FORMAT2_MAX_NPRB * 4]; + srslte_sequence_state_gen_f(&sequence, M_SQRT1_2, (float*)r_l, 2 * 4 * resource->nof_prb); + + // Put sequence in k = 3 * m + 1 + for (uint32_t k = k_start, i = 0; k < k_end; k += 3, i++) { + slot_symbols[l * carrier->nof_prb * SRSLTE_NRE + k] = r_l[i]; + } + } + return SRSLTE_SUCCESS; +} + +int srslte_dmrs_pucch_format2_estimate(const srslte_pucch_nr_t* q, + const srslte_carrier_nr_t* carrier, + const srslte_pucch_nr_common_cfg_t* cfg, + const srslte_dl_slot_cfg_t* slot, + const srslte_pucch_nr_resource_t* resource, + const cf_t* slot_symbols, + srslte_chest_ul_res_t* res) +{ + if (q == NULL || carrier == NULL || cfg == NULL || slot == NULL || resource == NULL || slot_symbols == NULL || + res == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (srslte_pucch_nr_cfg_resource_valid(resource) < SRSLTE_SUCCESS) { + ERROR("Invalid PUCCH format 1 resource\n"); + return SRSLTE_ERROR; + } + + cf_t ce[SRSLTE_PUCCH_NR_FORMAT2_MAX_NSYMB][SRSLTE_PUCCH_NR_FORMAT2_MAX_NPRB * 4]; + + uint32_t l_start = resource->start_symbol_idx; + uint32_t l_end = resource->start_symbol_idx + resource->nof_symbols; + uint32_t k_start = SRSLTE_MIN(carrier->nof_prb - 1, resource->starting_prb) * SRSLTE_NRE + 1; + uint32_t k_end = SRSLTE_MIN(carrier->nof_prb, resource->starting_prb + resource->nof_prb) * SRSLTE_NRE; + uint32_t nof_ref = 4 * resource->nof_prb; + for (uint32_t l = l_start, j = 0; l < l_end; l++, j++) { + // Compute sequence initial state + uint32_t cinit = dmrs_pucch_format2_cinit(carrier, cfg, slot, l); + srslte_sequence_state_t sequence = {}; + srslte_sequence_state_init(&sequence, cinit); + + // Skip PRBs to start + srslte_sequence_state_advance(&sequence, 2 * 4 * resource->starting_prb); + + // Generate sequence + cf_t r_l[SRSLTE_PUCCH_NR_FORMAT2_MAX_NPRB * 4]; + srslte_sequence_state_gen_f(&sequence, M_SQRT1_2, (float*)r_l, 2 * nof_ref); + + // Put sequence in k = 3 * m + 1 + for (uint32_t k = k_start, i = 0; k < k_end; k += 3, i++) { + ce[j][i] = slot_symbols[l * carrier->nof_prb * SRSLTE_NRE + k]; + } + + srslte_vec_prod_conj_ccc(ce[j], r_l, ce[j], nof_ref); + } + + // Perform measurements + float epre = 0.0f; + float rsrp = 0.0f; + float ta_err = 0.0f; + for (uint32_t i = 0; i < resource->nof_symbols; i++) { + cf_t corr = srslte_vec_acc_cc(ce[i], nof_ref) / nof_ref; + rsrp += __real__ corr * __real__ corr + __imag__ corr * __imag__ corr; + epre += srslte_vec_avg_power_cf(ce[i], nof_ref); + ta_err += srslte_vec_estimate_frequency(ce[i], nof_ref); + } + epre /= resource->nof_symbols; + rsrp /= resource->nof_symbols; + ta_err /= resource->nof_symbols; + + // Set power measures + rsrp = SRSLTE_MIN(rsrp, epre); + res->rsrp = rsrp; + res->rsrp_dBfs = srslte_convert_power_to_dB(rsrp); + res->epre = epre; + res->epre_dBfs = srslte_convert_power_to_dB(epre); + res->noise_estimate = epre - rsrp; + res->noise_estimate_dbm = srslte_convert_power_to_dB(res->noise_estimate); + res->snr = rsrp / res->noise_estimate; + res->snr_db = srslte_convert_power_to_dB(res->snr); + + // Compute Time Aligment error in microseconds + if (isnormal(ta_err)) { + ta_err /= 15e3f * (float)(1U << carrier->numerology) * 3; // Convert from normalized frequency to seconds + ta_err *= 1e6f; // Convert to micro-seconds + ta_err = roundf(ta_err * 10.0f) / 10.0f; // Round to one tenth of micro-second + res->ta_us = ta_err; + } else { + res->ta_us = 0.0f; + } + + // Perform averaging + // ... + + // Zero order hold + for (uint32_t l = l_start, j = 0; l < l_end; l++, j++) { + for (uint32_t k = k_start - 1, i = 0; k < k_end; k++, i++) { + res->ce[l * carrier->nof_prb * SRSLTE_NRE + k] = ce[j][i / 3]; + } + } + + return SRSLTE_SUCCESS; +} + int srslte_dmrs_pucch_format_3_4_get_symbol_idx(const srslte_pucch_nr_resource_t* resource, uint32_t idx[SRSLTE_DMRS_PUCCH_FORMAT_3_4_MAX_NSYMB]) { diff --git a/lib/src/phy/phch/pucch_nr.c b/lib/src/phy/phch/pucch_nr.c index 1ea11638a..4dcbe93c7 100644 --- a/lib/src/phy/phch/pucch_nr.c +++ b/lib/src/phy/phch/pucch_nr.c @@ -165,6 +165,13 @@ int srslte_pucch_nr_init(srslte_pucch_nr_t* q, const srslte_pucch_nr_args_t* arg return SRSLTE_ERROR; } + // Allocate temporal channel estimates + q->ce = srslte_vec_cf_malloc(max_encoded_bits / 2); + if (q->ce == NULL) { + ERROR("Malloc\n"); + return SRSLTE_ERROR; + } + return SRSLTE_SUCCESS; } @@ -187,6 +194,10 @@ void srslte_pucch_nr_free(srslte_pucch_nr_t* q) free(q->d); } + if (q->ce != NULL) { + free(q->ce); + } + SRSLTE_MEM_ZERO(q, srslte_pucch_nr_t, 1); } @@ -582,14 +593,26 @@ static int pucch_nr_format2_decode(srslte_pucch_nr_t* q, uint32_t k_end = (resource->starting_prb + resource->nof_prb) * SRSLTE_NRE; for (uint32_t l = l_start, i = 0; l < l_end; l++) { cf_t* symbol_ptr = &slot_symbols[l * carrier->nof_prb * SRSLTE_NRE]; + cf_t* ce_ptr = &chest_res->ce[l * carrier->nof_prb * SRSLTE_NRE]; for (uint32_t k = k_start; k < k_end; k += 3) { - q->d[i++] = symbol_ptr[k]; - q->d[i++] = symbol_ptr[k + 2]; + q->d[i] = symbol_ptr[k]; + q->ce[i] = ce_ptr[k]; + i++; + q->d[i] = symbol_ptr[k + 2]; + q->ce[i] = ce_ptr[k + 2]; + i++; } } + if (SRSLTE_DEBUG_ENABLED && srslte_verbose >= SRSLTE_VERBOSE_INFO && !handler_registered) { + INFO("d="); + srslte_vec_fprint_c(stdout, q->d, resource->nof_symbols * resource->nof_prb * (SRSLTE_NRE - 4)); + INFO("ce="); + srslte_vec_fprint_c(stdout, q->ce, resource->nof_symbols * resource->nof_prb * (SRSLTE_NRE - 4)); + } + // Equalise - if (srslte_predecoding_single(q->d, chest_res->ce, q->d, NULL, E, 1.0f, chest_res->noise_estimate) < SRSLTE_SUCCESS) { + if (srslte_predecoding_single(q->d, q->ce, q->d, NULL, E, 1.0f, chest_res->noise_estimate) < SRSLTE_SUCCESS) { ERROR("Error Pre-decoding\n"); return SRSLTE_ERROR; } diff --git a/lib/src/phy/phch/test/pucch_nr_test.c b/lib/src/phy/phch/test/pucch_nr_test.c index 5d2ce7f0b..9f024b008 100644 --- a/lib/src/phy/phch/test/pucch_nr_test.c +++ b/lib/src/phy/phch/test/pucch_nr_test.c @@ -12,16 +12,14 @@ #include "srslte/common/test_common.h" #include "srslte/phy/ch_estimation/dmrs_pucch.h" +#include "srslte/phy/channel/ch_awgn.h" #include "srslte/phy/phch/pucch_nr.h" #include "srslte/phy/phch/ra_ul_nr.h" #include "srslte/phy/utils/debug.h" #include "srslte/phy/utils/random.h" #include "srslte/phy/utils/vector.h" #include -#include -#include #include -#include #include static srslte_carrier_nr_t carrier = { @@ -32,10 +30,12 @@ static srslte_carrier_nr_t carrier = { 1 // max_mimo_layers }; -static uint32_t starting_prb_stride = 4; -static uint32_t starting_symbol_stride = 4; -static srslte_random_t random_gen = NULL; -static int format = -1; +static uint32_t starting_prb_stride = 4; +static uint32_t starting_symbol_stride = 4; +static srslte_random_t random_gen = NULL; +static int format = -1; +static float snr_db = 30.0f; +static srslte_channel_awgn_t awgn = {}; static int test_pucch_format0(srslte_pucch_nr_t* pucch, const srslte_pucch_nr_common_cfg_t* cfg, cf_t* slot_symbols) { @@ -122,10 +122,18 @@ static int test_pucch_format1(srslte_pucch_nr_t* pucch, TESTASSERT(srslte_dmrs_pucch_format1_put(pucch, &carrier, cfg, &slot, &resource, slot_symbols) == SRSLTE_SUCCESS); + // Apply AWGN + srslte_channel_awgn_run_c( + &awgn, slot_symbols, slot_symbols, carrier.nof_prb * SRSLTE_NRE * SRSLTE_NSYMB_PER_SLOT_NR); + // Estimate channel TESTASSERT(srslte_dmrs_pucch_format1_estimate( pucch, &carrier, cfg, &slot, &resource, slot_symbols, chest_res) == SRSLTE_SUCCESS); + TESTASSERT(fabsf(chest_res->rsrp_dBfs - 0.0f) < 3.0f); + TESTASSERT(fabsf(chest_res->epre_dBfs - 0.0f) < 3.0f); + TESTASSERT(fabsf(chest_res->snr_db - snr_db) < 10.0f); + // Decode PUCCH uint8_t b_rx[SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS]; TESTASSERT(srslte_pucch_nr_format1_decode( @@ -199,15 +207,19 @@ static int test_pucch_format2(srslte_pucch_nr_t* pucch, SRSLTE_SUCCESS); // Put DMRS - // TESTASSERT(srslte_dmrs_pucch_format1_put(pucch, &carrier, cfg, &slot, &resource, - // slot_symbols) == - // SRSLTE_SUCCESS); + TESTASSERT(srslte_dmrs_pucch_format2_put(pucch, &carrier, cfg, &slot, &resource, slot_symbols) == + SRSLTE_SUCCESS); + + // Apply AWGN + srslte_channel_awgn_run_c( + &awgn, slot_symbols, slot_symbols, carrier.nof_prb * SRSLTE_NRE * SRSLTE_NSYMB_PER_SLOT_NR); // Estimate channel - // TESTASSERT(srslte_dmrs_pucch_format1_estimate( - // pucch, &carrier, cfg, &slot, &resource, slot_symbols, chest_res) == - // SRSLTE_SUCCESS); - srslte_chest_ul_res_set_identity(chest_res); + TESTASSERT(srslte_dmrs_pucch_format2_estimate( + pucch, &carrier, cfg, &slot, &resource, slot_symbols, chest_res) == SRSLTE_SUCCESS); + TESTASSERT(fabsf(chest_res->rsrp_dBfs - 0.0f) < 3.0f); + TESTASSERT(fabsf(chest_res->epre_dBfs - 0.0f) < 3.0f); + TESTASSERT(fabsf(chest_res->snr_db - snr_db) < 10.0f); // Decode PUCCH srslte_uci_value_nr_t uci_value_rx = {}; @@ -238,13 +250,14 @@ static void usage(char* prog) printf("\t-c cell id [Default %d]\n", carrier.id); printf("\t-n nof_prb [Default %d]\n", carrier.nof_prb); printf("\t-f format [Default %d]\n", format); + printf("\t-s SNR in dB [Default %.2f]\n", snr_db); printf("\t-v [set verbose to debug, default none]\n"); } static void parse_args(int argc, char** argv) { int opt; - while ((opt = getopt(argc, argv, "cnfv")) != -1) { + while ((opt = getopt(argc, argv, "cnfsv")) != -1) { switch (opt) { case 'c': carrier.id = (uint32_t)strtol(argv[optind], NULL, 10); @@ -255,6 +268,9 @@ static void parse_args(int argc, char** argv) case 'f': format = (int)strtol(argv[optind], NULL, 10); break; + case 's': + snr_db = strtof(argv[optind], NULL); + break; case 'v': srslte_verbose++; break; @@ -297,6 +313,16 @@ int main(int argc, char** argv) goto clean_exit; } + if (srslte_channel_awgn_init(&awgn, 1234) < SRSLTE_SUCCESS) { + ERROR("AWGN init\n"); + goto clean_exit; + } + + if (srslte_channel_awgn_set_n0(&awgn, -snr_db) < SRSLTE_SUCCESS) { + ERROR("AWGN set N0\n"); + goto clean_exit; + } + srslte_pucch_nr_common_cfg_t common_cfg = {}; // Test Format 0 @@ -331,7 +357,7 @@ clean_exit: srslte_pucch_nr_free(&pucch); srslte_chest_ul_res_free(&chest_res); - + srslte_channel_awgn_free(&awgn); srslte_random_free(random_gen); if (ret == SRSLTE_SUCCESS) {