diff --git a/lib/include/srslte/phy/ch_estimation/dmrs_pucch.h b/lib/include/srslte/phy/ch_estimation/dmrs_pucch.h new file mode 100644 index 000000000..9c0ee006b --- /dev/null +++ b/lib/include/srslte/phy/ch_estimation/dmrs_pucch.h @@ -0,0 +1,56 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2020 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. + * + */ + +#ifndef SRSLTE_DMRS_PUCCH_H +#define SRSLTE_DMRS_PUCCH_H + +#include "srslte/config.h" +#include "srslte/phy/ch_estimation/chest_ul.h" +#include "srslte/phy/phch/pucch_nr.h" + +/** + * @brief Puts NR-PUCCH format 1 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 1 resource + * @param[out] slot_symbols Resource grid of the given slot + * @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise + */ +SRSLTE_API int srslte_dmrs_pucch_format1_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_format1_t* resource, + cf_t* slot_symbols); + +/** + * @brief Estimates NR-PUCCH format 1 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 1 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 + */ +SRSLTE_API int srslte_dmrs_pucch_format1_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_format1_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/phch/pucch_nr.h b/lib/include/srslte/phy/phch/pucch_nr.h index 4fd52f8eb..15f0d418f 100644 --- a/lib/include/srslte/phy/phch/pucch_nr.h +++ b/lib/include/srslte/phy/phch/pucch_nr.h @@ -17,14 +17,20 @@ #include "srslte/phy/common/phy_common_nr.h" #include "srslte/phy/common/zc_sequence.h" #include "srslte/phy/modem/modem_table.h" +#include #include #include /** - * @brief Maximum number of NR-PUCCH format 1 symbols (without DMRS) + * @brief Maximum number of symbols (without DMRS) that NR-PUCCH format 1 can transmit */ #define SRSLTE_PUCCH_NR_FORMAT1_N_MAX 7 +/** + * @brief Maximum number of bit that NR-PUCCH format 1 can carry + */ +#define SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS 2 + typedef enum SRSLTE_API { SRSLTE_PUCCH_NR_FORMAT_0 = 0, SRSLTE_PUCCH_NR_FORMAT_1, @@ -124,16 +130,58 @@ typedef struct SRSLTE_API { * @param q Object * @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise */ -int srslte_pucch_nr_init(srslte_pucch_nr_t* q); +SRSLTE_API int srslte_pucch_nr_init(srslte_pucch_nr_t* q); /** * @brief Deallocates an NR-PUCCH encoder/decoder object * @param q Object */ -void srslte_pucch_nr_free(srslte_pucch_nr_t* q); +SRSLTE_API void srslte_pucch_nr_free(srslte_pucch_nr_t* q); + +/** + * @brief Computes the NR-PUCCH group sequence + * @remark Implemented according to TS 38.211 clause 6.3.2.2.1 Group and sequence hopping + * @param[in] carrier Serving cell and UL BWP configuration + * @param[in] cfg PUCCH common configuration + * @param[out] u_ Group sequence (u) + * @param[out] v_ Base sequence (v) + * @return SRSLTE_SUCCESS if provide arguments are right, SRSLTE_ERROR code otherwise + */ +SRSLTE_API int srslte_pucch_nr_group_sequence(const srslte_carrier_nr_t* carrier, + const srslte_pucch_nr_common_cfg_t* cfg, + uint32_t* u_, + uint32_t* v_); + +/** + * @brief Computes the NR alpha index (1-NRE) + * @param[in] carrier Serving cell and UL BWP configuration + * @param[in] cfg PUCCH common configuration + * @param[in] slot slot configuration + * @param[in] l OFDM Symbol, relative to the NR-PUCCH transmission start + * @param[in] l_prime Initial OFDM symbol, relative to the transmission slot start + * @param[in] m0 Initial cyclic shift + * @param[in] m_cs Set to zero expect for format 0 + * @param[out] alpha_idx Computed alpha index + * @return SRSLTE_SUCCESS if provide arguments are right, SRSLTE_ERROR code otherwise + */ +SRSLTE_API int srslte_pucch_nr_alpha_idx(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 l_prime, + uint32_t m0, + uint32_t m_cs, + uint32_t* alpha_idx); + +/** + * @brief Validates a PUCCH format 1 resource configuration provided by upper layers + * @param resource Resource configuration to validate + * @return SRSLTE_SUCCESS if valid, SRSLTE_ERROR code otherwise + */ +SRSLTE_API int srslte_pucch_nr_format0_resource_valid(const srslte_pucch_nr_resource_format0_t* resource); /** - * @brief Puts NR-PUCCH format 0 in the resource grid + * @brief Encode and writes NR-PUCCH format 0 in the resource grid * @remark Described in TS 38.211 clause 6.3.2.3 PUCCH format 0 * @param[in] q NR-PUCCH encoder/decoder object * @param[in] carrier Carrier configuration @@ -144,13 +192,13 @@ void srslte_pucch_nr_free(srslte_pucch_nr_t* q); * @param[out] slot_symbols Resource grid of the given slot * @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise */ -int srslte_pucch_nr_format0_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, - srslte_pucch_nr_resource_format0_t* resource, - uint32_t m_cs, - cf_t* slot_symbols); +SRSLTE_API int srslte_pucch_nr_format0_encode(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, + srslte_pucch_nr_resource_format0_t* resource, + uint32_t m_cs, + cf_t* slot_symbols); /** * @brief Measures PUCCH format 0 in the resource grid @@ -164,13 +212,76 @@ int srslte_pucch_nr_format0_put(const srslte_pucch_nr_t* q, * @param[out] measure Measurement structure * @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise */ -int srslte_pucch_nr_format0_measure(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, - srslte_pucch_nr_resource_format0_t* resource, - uint32_t m_cs, - const cf_t* slot_symbols, - srslte_pucch_nr_measure_t* measure); +SRSLTE_API int srslte_pucch_nr_format0_measure(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, + srslte_pucch_nr_resource_format0_t* resource, + uint32_t m_cs, + const cf_t* slot_symbols, + srslte_pucch_nr_measure_t* measure); + +/** + * @brief Validates a PUCCH format 1 resource configuration provided by upper layers + * @param resource Resource configuration to validate + * @return SRSLTE_SUCCESS if valid, SRSLTE_ERROR code otherwise + */ +SRSLTE_API int srslte_pucch_nr_format1_resource_valid(const srslte_pucch_nr_resource_format1_t* resource); + +/** + * @brief Get NR-PUCCH orthogonal sequence w + * @remark Defined by TS 38.211 Table 6.3.2.4.1-2: Orthogonal sequences ... for PUCCH format 1 + * @param[in] q NR-PUCCH encoder/decoder object + * @param[in] n_pucch Number of PUCCH symbols + * @param[in] i sequence index + * @param m OFDM symbol index + * @return Orthogonal sequence complex number if valid, NAN otherwise + */ +SRSLTE_API cf_t srslte_pucch_nr_format1_w(const srslte_pucch_nr_t* q, uint32_t n_pucch, uint32_t i, uint32_t m); + +/** + * @brief Encodes and puts NR-PUCCH format 1 in the resource grid + * @remark Described in TS 38.211 clause 6.3.2.4 PUCCH format 1 + * @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 1 resource + * @param[in] b Bits to encode in the message + * @param[in] nof_bits Number of bits to encode in the message + * @param[out] slot_symbols Resource grid of the given slot + * @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise + */ +SRSLTE_API int srslte_pucch_nr_format1_encode(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_format1_t* resource, + uint8_t* b, + uint32_t nof_bits, + cf_t* slot_symbols); + +/** + * @brief Decodes NR-PUCCH format 1 + * @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 1 resource + * @param[in] chest_res Channel estimator result + * @param[in] slot_symbols Resource grid of the given slot + * @param[out] b Bits to decode + * @param[in] nof_bits Number of bits to decode in the message + * @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise + */ +SRSLTE_API int srslte_pucch_nr_format1_decode(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_format1_t* resource, + srslte_chest_ul_res_t* chest_res, + cf_t* slot_symbols, + uint8_t b[SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS], + uint32_t nof_bits); #endif // SRSLTE_PUCCH_NR_H diff --git a/lib/src/phy/ch_estimation/dmrs_pucch.c b/lib/src/phy/ch_estimation/dmrs_pucch.c new file mode 100644 index 000000000..daf0150e4 --- /dev/null +++ b/lib/src/phy/ch_estimation/dmrs_pucch.c @@ -0,0 +1,281 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2020 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 "srslte/phy/ch_estimation/dmrs_pucch.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + +// Implements TS 38.211 table 6.4.1.3.1.1-1: Number of DM-RS symbols and the corresponding N_PUCCH... +static uint32_t dmrs_pucch_format1_n_pucch(const srslte_pucch_nr_resource_format1_t* resource, uint32_t m_prime) +{ + if (resource->intra_slot_hopping) { + if (m_prime == 0) { + switch (resource->nof_symbols) { + case 4: + case 5: + return 1; + case 6: + case 7: + case 8: + case 9: + return 2; + case 10: + case 11: + case 12: + case 13: + return 3; + case 14: + return 4; + default:; // Do nothing + } + } else { + switch (resource->nof_symbols) { + case 4: + case 6: + return 1; + case 5: + case 7: + case 8: + case 10: + return 2; + case 9: + case 11: + case 12: + case 14: + return 3; + case 13: + return 4; + default:; // Do nothing + } + } + } else if (m_prime == 0) { + switch (resource->nof_symbols) { + case 4: + return 2; + case 5: + case 6: + return 3; + case 7: + case 8: + return 4; + case 9: + case 10: + return 5; + case 11: + case 12: + return 6; + case 13: + case 14: + return 7; + default:; // Do nothing + } + } + + ERROR("Invalid case nof_symbols=%d and m_prime=%d\n", resource->nof_symbols, m_prime); + return 0; +} + +int srslte_dmrs_pucch_format1_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_format1_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_format1_resource_valid(resource) < SRSLTE_SUCCESS) { + ERROR("Invalid PUCCH format 1 resource\n"); + return SRSLTE_ERROR; + } + + // Get group sequence + uint32_t u = 0; + uint32_t v = 0; + if (srslte_pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) { + ERROR("Error getting group sequence\n"); + return SRSLTE_ERROR; + } + + uint32_t n_pucch = dmrs_pucch_format1_n_pucch(resource, 0); + if (n_pucch == 0) { + ERROR("Error getting number of symbols\n"); + return SRSLTE_ERROR; + } + + uint32_t l_prime = resource->start_symbol_idx; + for (uint32_t m = 0; m < n_pucch; m++) { + // Clause 6.4.1.3.1.2 specifies l=0,2,4... + uint32_t l = m * 2; + + // Get start of the sequence in resource grid + cf_t* slot_symbols_ptr = &slot_symbols[(carrier->nof_prb * (l + l_prime) + resource->starting_prb) * SRSLTE_NRE]; + + // Get Alpha index + uint32_t alpha_idx = 0; + if (srslte_pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) < + SRSLTE_SUCCESS) { + ERROR("Calculating alpha\n"); + } + + // get r_uv sequence from LUT object + const cf_t* r_uv = srslte_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); + if (r_uv == NULL) { + ERROR("Getting r_uv sequence\n"); + return SRSLTE_ERROR; + } + + // Get w_i_m + cf_t w_i_m = srslte_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); + + // Compute z(n) = w(i) * r_uv(n) + cf_t z[SRSLTE_NRE]; + srslte_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSLTE_NRE); + + // Put z in the grid + srslte_vec_cf_copy(slot_symbols_ptr, z, SRSLTE_NRE); + } + + return SRSLTE_SUCCESS; +} + +int srslte_dmrs_pucch_format1_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_format1_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) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (srslte_pucch_nr_format1_resource_valid(resource) < SRSLTE_SUCCESS) { + ERROR("Invalid PUCCH format 1 resource\n"); + return SRSLTE_ERROR; + } + + // Get group sequence + uint32_t u = 0; + uint32_t v = 0; + if (srslte_pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) { + ERROR("Error getting group sequence\n"); + return SRSLTE_ERROR; + } + + uint32_t n_pucch = dmrs_pucch_format1_n_pucch(resource, 0); + if (n_pucch == 0) { + ERROR("Error getting number of symbols\n"); + return SRSLTE_ERROR; + } + + cf_t ce[SRSLTE_PUCCH_NR_FORMAT1_N_MAX][SRSLTE_NRE]; + uint32_t l_prime = resource->start_symbol_idx; + for (uint32_t m = 0; m < n_pucch; m++) { + // Clause 6.4.1.3.1.2 specifies l=0,2,4... + uint32_t l = m * 2; + + // Get start of the sequence in resource grid + const cf_t* slot_symbols_ptr = + &slot_symbols[(carrier->nof_prb * (l + l_prime) + resource->starting_prb) * SRSLTE_NRE]; + + // Get Alpha index + uint32_t alpha_idx = 0; + if (srslte_pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) < + SRSLTE_SUCCESS) { + ERROR("Calculating alpha\n"); + } + + // get r_uv sequence from LUT object + const cf_t* r_uv = srslte_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); + if (r_uv == NULL) { + ERROR("Getting r_uv sequence\n"); + return SRSLTE_ERROR; + } + + // Get w_i_m + cf_t w_i_m = srslte_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); + + // Compute z(n) = w(i) * r_uv(n) + cf_t z[SRSLTE_NRE]; + srslte_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSLTE_NRE); + + // Calculate least square estimates for this symbol + srslte_vec_prod_conj_ccc(slot_symbols_ptr, z, ce[m], SRSLTE_NRE); + } + + // Perform measurements + float rsrp = 0.0f; + 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); + 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); + } + + // Average measurements + rsrp /= n_pucch; + epre /= n_pucch; + ta_err /= n_pucch; + + // Set power measures + rsrp = SRSLTE_MIN(rsrp, 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); + + // Compute Time Aligment error in microseconds + if (isnormal(ta_err)) { + ta_err /= 15e3f * (float)(1U << carrier->numerology); // 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; + } + + // Measure CFO + res->cfo = NAN; // Not implemented + + // Do averaging here + // ... Not implemented + + // Interpolates between DMRS symbols + for (uint32_t m = 0; m < n_pucch; m++) { + uint32_t l = m * 2 + 1; + cf_t* ce_ptr = &res->ce[(carrier->nof_prb * (l + l_prime) + resource->starting_prb) * SRSLTE_NRE]; + + if (m != n_pucch - 1) { + // If it is not the last symbol with DMRS, average between + srslte_vec_sum_ccc(ce[m], ce[m + 1], ce_ptr, SRSLTE_NRE); + srslte_vec_sc_prod_cfc(ce_ptr, 0.5f, ce_ptr, SRSLTE_NRE); + } else if (m != 0) { + // Extrapolate for the last if more than 1 are provided + srslte_vec_sc_prod_cfc(ce[m], 3.0f, ce_ptr, SRSLTE_NRE); + srslte_vec_sub_ccc(ce_ptr, ce[m - 1], ce_ptr, SRSLTE_NRE); + srslte_vec_sc_prod_cfc(ce_ptr, 0.5f, ce_ptr, SRSLTE_NRE); + } else { + // Simply copy the + srslte_vec_cf_copy(ce_ptr, ce[m], SRSLTE_NRE); + } + } + + return SRSLTE_SUCCESS; +} diff --git a/lib/src/phy/phch/pucch_nr.c b/lib/src/phy/phch/pucch_nr.c index 6000c1e13..49851a1a5 100644 --- a/lib/src/phy/phch/pucch_nr.c +++ b/lib/src/phy/phch/pucch_nr.c @@ -18,9 +18,9 @@ #include "srslte/phy/utils/debug.h" #include "srslte/phy/utils/vector.h" #include +#include -// Implements TS 38.211 clause 6.3.2.2.1 Group and sequence hopping -static int pucch_nr_group_sequence(const srslte_carrier_nr_t* carrier, +int srslte_pucch_nr_group_sequence(const srslte_carrier_nr_t* carrier, const srslte_pucch_nr_common_cfg_t* cfg, uint32_t* u_, uint32_t* v_) @@ -57,14 +57,19 @@ static int pucch_nr_group_sequence(const srslte_carrier_nr_t* carrier, } // Implements TS 38.211 clause 6.3.2.2.2 Cyclic shift hopping -static uint32_t pucch_nr_alpha_idx(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 l_prime, - uint32_t m0, - uint32_t m_cs) +int srslte_pucch_nr_alpha_idx(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 l_prime, + uint32_t m0, + uint32_t m_cs, + uint32_t* alpha_idx) { + if (carrier == NULL || cfg == NULL || slot == NULL || alpha_idx == NULL) { + return SRSLTE_ERROR; + } + // Compute number of slot uint32_t n_slot = slot->idx % SRSLTE_NSLOTS_PER_FRAME_NR(carrier->numerology); @@ -80,10 +85,12 @@ static uint32_t pucch_nr_alpha_idx(const srslte_carrier_nr_t* carrier, n_cs += cs[SRSLTE_NSYMB_PER_SLOT_NR * n_slot + (l + l_prime) * 8 + m] << m; } - return (m0 + m_cs + n_cs) % SRSLTE_NRE; + *alpha_idx = (m0 + m_cs + n_cs) % SRSLTE_NRE; + + return SRSLTE_SUCCESS; } -static int srslte_pucch_nr_format0_resource_valid(const srslte_pucch_nr_resource_format0_t* resource) +int srslte_pucch_nr_format0_resource_valid(const srslte_pucch_nr_resource_format0_t* resource) { if (resource == NULL) { return SRSLTE_ERROR_INVALID_INPUTS; @@ -107,7 +114,7 @@ static int srslte_pucch_nr_format0_resource_valid(const srslte_pucch_nr_resource return SRSLTE_SUCCESS; } -static int srslte_pucch_nr_format1_resource_valid(const srslte_pucch_nr_resource_format1_t* resource) +int srslte_pucch_nr_format1_resource_valid(const srslte_pucch_nr_resource_format1_t* resource) { if (resource == NULL) { return SRSLTE_ERROR_INVALID_INPUTS; @@ -152,8 +159,6 @@ static uint32_t {{}, {}, {}, {}, {}, {0, 5, 4, 3, 2, 1}, {0, 5, 3, 1, 6, 4, 2}}, {{}, {}, {}, {}, {}, {}, {0, 6, 5, 4, 3, 2, 1}}}; -#define SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS 2 - int srslte_pucch_nr_init(srslte_pucch_nr_t* q) { if (q == NULL) { @@ -205,13 +210,13 @@ void srslte_pucch_nr_free(srslte_pucch_nr_t* q) SRSLTE_MEM_ZERO(q, srslte_pucch_nr_t, 1); } -int srslte_pucch_nr_format0_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, - srslte_pucch_nr_resource_format0_t* resource, - uint32_t m_cs, - cf_t* slot_symbols) +int srslte_pucch_nr_format0_encode(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, + srslte_pucch_nr_resource_format0_t* resource, + uint32_t m_cs, + cf_t* slot_symbols) { if (carrier == NULL || cfg == NULL || slot == NULL || resource == NULL || slot_symbols == NULL) { return SRSLTE_ERROR_INVALID_INPUTS; @@ -224,7 +229,7 @@ int srslte_pucch_nr_format0_put(const srslte_pucch_nr_t* q, uint32_t u = 0; uint32_t v = 0; - if (pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) { + if (srslte_pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) { ERROR("Error getting group sequence\n"); return SRSLTE_ERROR; } @@ -232,7 +237,11 @@ int srslte_pucch_nr_format0_put(const srslte_pucch_nr_t* q, uint32_t l_prime = resource->start_symbol_idx; for (uint32_t l = 0; l < resource->nof_symbols; l++) { // Get Alpha index - uint32_t alpha_idx = pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs); + uint32_t alpha_idx = 0; + if (srslte_pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs, &alpha_idx) < + SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } // get r_uv sequence from LUT object const cf_t* r_uv = srslte_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); @@ -271,7 +280,7 @@ int srslte_pucch_nr_format0_measure(const srslte_pucch_nr_t* q, uint32_t u = 0; uint32_t v = 0; - if (pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) { + if (srslte_pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) { ERROR("Error getting group sequence\n"); return SRSLTE_ERROR; } @@ -281,7 +290,11 @@ int srslte_pucch_nr_format0_measure(const srslte_pucch_nr_t* q, float rsrp = 0.0f; for (uint32_t l = 0; l < resource->nof_symbols; l++) { // Get Alpha index - uint32_t alpha_idx = pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs); + uint32_t alpha_idx = 0; + if (srslte_pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs, &alpha_idx) < + SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } // get r_uv sequence from LUT object const cf_t* r_uv = srslte_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); @@ -320,7 +333,7 @@ int srslte_pucch_nr_format0_measure(const srslte_pucch_nr_t* q, } // Implements TS 38.211 table 6.3.2.4.1-1 Number of PUCCH symbols and the corresponding N_PUC... -uint32_t pucch_nr_format1_n_pucch(const srslte_pucch_nr_resource_format1_t* resource, uint32_t m_prime) +static uint32_t pucch_nr_format1_n_pucch(const srslte_pucch_nr_resource_format1_t* resource, uint32_t m_prime) { if (resource->intra_slot_hopping) { if (m_prime == 0) { @@ -337,7 +350,7 @@ uint32_t pucch_nr_format1_n_pucch(const srslte_pucch_nr_resource_format1_t* reso return resource->nof_symbols / 2; } -static cf_t pucch_nr_format1_w(const srslte_pucch_nr_t* q, uint32_t n_pucch, uint32_t i, uint32_t m) +cf_t srslte_pucch_nr_format1_w(const srslte_pucch_nr_t* q, uint32_t n_pucch, uint32_t i, uint32_t m) { if (n_pucch < 1 || n_pucch > SRSLTE_PUCCH_NR_FORMAT1_N_MAX) { ERROR("Invalid n_pucch\n"); @@ -356,14 +369,14 @@ static cf_t pucch_nr_format1_w(const srslte_pucch_nr_t* q, uint32_t n_pucch, uin return q->format1_w_i_m[i][n_pucch - 1][m]; } -int srslte_pucch_nr_put_format1(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_format1_t* resource, - uint8_t b[SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS], - uint32_t nof_bits, - cf_t* slot_symbols) +int srslte_pucch_nr_format1_encode(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_format1_t* resource, + uint8_t* b, + uint32_t nof_bits, + cf_t* slot_symbols) { uint32_t m_cs = 0; @@ -392,7 +405,7 @@ int srslte_pucch_nr_put_format1(srslte_pucch_nr_t* q, // Get group sequence uint32_t u = 0; uint32_t v = 0; - if (pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) { + if (srslte_pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) { ERROR("Error getting group sequence\n"); return SRSLTE_ERROR; } @@ -406,7 +419,11 @@ int srslte_pucch_nr_put_format1(srslte_pucch_nr_t* q, cf_t* slot_symbols_ptr = &slot_symbols[(carrier->nof_prb * (l + l_prime) + resource->starting_prb) * SRSLTE_NRE]; // Get Alpha index - uint32_t alpha_idx = pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs); + uint32_t alpha_idx = 0; + if (srslte_pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs, &alpha_idx) < + SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } // get r_uv sequence from LUT object const cf_t* r_uv = srslte_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); @@ -420,7 +437,7 @@ int srslte_pucch_nr_put_format1(srslte_pucch_nr_t* q, srslte_vec_sc_prod_ccc(r_uv, d, y, SRSLTE_NRE); // Get w_i_m - cf_t w_i_m = pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); + cf_t w_i_m = srslte_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); // Compute z(n) = w(i) * y(n) cf_t z[SRSLTE_NRE]; @@ -430,5 +447,91 @@ int srslte_pucch_nr_put_format1(srslte_pucch_nr_t* q, srslte_vec_cf_copy(slot_symbols_ptr, z, SRSLTE_NRE); } + return SRSLTE_SUCCESS; +} + +int srslte_pucch_nr_format1_decode(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_format1_t* resource, + srslte_chest_ul_res_t* chest_res, + cf_t* slot_symbols, + uint8_t b[SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS], + uint32_t nof_bits) +{ + uint32_t m_cs = 0; + + if (carrier == NULL || cfg == NULL || slot == NULL || resource == NULL || b == NULL || slot_symbols == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (srslte_pucch_nr_format1_resource_valid(resource) < SRSLTE_SUCCESS) { + ERROR("Invalid PUCCH format 1 resource\n"); + return SRSLTE_SUCCESS; + } + + if (nof_bits > 2) { + ERROR("Invalid number of bits (%d)\n", nof_bits); + return SRSLTE_ERROR; + } + + // Received symbol d + cf_t d = 0; + + // Get group sequence + uint32_t u = 0; + uint32_t v = 0; + if (srslte_pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) { + ERROR("Error getting group sequence\n"); + return SRSLTE_ERROR; + } + + // Calculate number of symbols carrying PUCCH (No DMRS) + uint32_t n_pucch = pucch_nr_format1_n_pucch(resource, 0); + + uint32_t l_prime = resource->start_symbol_idx; + for (uint32_t l = 1, m = 0; l < resource->nof_symbols; l += 2, m++) { + // Get start of the sequence in resource grid + cf_t* slot_symbols_ptr = &slot_symbols[(carrier->nof_prb * (l + l_prime) + resource->starting_prb) * SRSLTE_NRE]; + cf_t* ce_ptr = &chest_res->ce[(carrier->nof_prb * (l + l_prime) + resource->starting_prb) * SRSLTE_NRE]; + + // Equalise x = w(i) * d' * r_uv(n) + cf_t x[SRSLTE_NRE]; + srslte_predecoding_single(slot_symbols_ptr, ce_ptr, x, NULL, SRSLTE_NRE, 1.0f, chest_res->noise_estimate); + + // Get Alpha index + uint32_t alpha_idx = 0; + if (srslte_pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs, &alpha_idx) < + SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + // get r_uv sequence from LUT object + const cf_t* r_uv = srslte_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); + if (r_uv == NULL) { + ERROR("Getting r_uv sequence\n"); + return SRSLTE_ERROR; + } + // Get w_i_m + cf_t w_i_m = srslte_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); + + // Compute z(n) = w(i) * r_uv(n) + cf_t z[SRSLTE_NRE]; + srslte_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSLTE_NRE); + + // Compute d = sum(x * conj(w(i) * r_uv(n))) = sum(w(i) * d' * r_uv(n) * conj(w(i) * r_uv(n))) = d' + d += srslte_vec_dot_prod_conj_ccc(x, z, SRSLTE_NRE); + } + + // Demodulate d + float llr[SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS]; + srslte_demod_soft_demodulate((nof_bits == 1) ? SRSLTE_MOD_BPSK : SRSLTE_MOD_QPSK, &d, llr, 1); + + // Hard decision + for (uint32_t i = 0; i < nof_bits; i++) { + b[i] = llr[i] > 0.0f ? 1 : 0; + } + return SRSLTE_SUCCESS; } \ No newline at end of file diff --git a/lib/src/phy/phch/test/pucch_nr_test.c b/lib/src/phy/phch/test/pucch_nr_test.c index 44e505220..2594615e2 100644 --- a/lib/src/phy/phch/test/pucch_nr_test.c +++ b/lib/src/phy/phch/test/pucch_nr_test.c @@ -11,6 +11,7 @@ */ #include "srslte/common/test_common.h" +#include "srslte/phy/ch_estimation/dmrs_pucch.h" #include "srslte/phy/phch/pucch_nr.h" #include "srslte/phy/utils/debug.h" #include "srslte/phy/utils/vector.h" @@ -47,7 +48,7 @@ static int test_pucch_format0(srslte_pucch_nr_t* pucch, const srslte_pucch_nr_co for (resource.initial_cyclic_shift = 0; resource.initial_cyclic_shift <= 11; resource.initial_cyclic_shift++) { for (uint32_t m_cs = 0; m_cs <= 6; m_cs += 2) { - TESTASSERT(srslte_pucch_nr_format0_put(pucch, &carrier, cfg, &slot, &resource, m_cs, slot_symbols) == + TESTASSERT(srslte_pucch_nr_format0_encode(pucch, &carrier, cfg, &slot, &resource, m_cs, slot_symbols) == SRSLTE_SUCCESS); // Measure PUCCH format 0 for all possible values of m_cs @@ -77,6 +78,66 @@ static int test_pucch_format0(srslte_pucch_nr_t* pucch, const srslte_pucch_nr_co return SRSLTE_SUCCESS; } +static int test_pucch_format1(srslte_pucch_nr_t* pucch, + const srslte_pucch_nr_common_cfg_t* cfg, + srslte_chest_ul_res_t* chest_res, + cf_t* slot_symbols) +{ + srslte_dl_slot_cfg_t slot = {}; + srslte_pucch_nr_resource_format1_t resource = {}; + + for (slot.idx = 0; slot.idx < SRSLTE_NSLOTS_PER_FRAME_NR(carrier.numerology); slot.idx++) { + for (resource.starting_prb = 0; resource.starting_prb < carrier.nof_prb; + resource.starting_prb += starting_prb_stride) { + for (resource.nof_symbols = 4; resource.nof_symbols <= 14; resource.nof_symbols++) { + for (resource.start_symbol_idx = 0; + resource.start_symbol_idx <= SRSLTE_NSYMB_PER_SLOT_NR - resource.nof_symbols; + resource.start_symbol_idx += starting_symbol_stride) { + for (resource.time_domain_occ = 0; resource.time_domain_occ <= 6; resource.time_domain_occ++) { + for (resource.initial_cyclic_shift = 0; resource.initial_cyclic_shift <= 11; + resource.initial_cyclic_shift++) { + for (uint32_t nof_bits = 1; nof_bits <= SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS; nof_bits++) { + for (uint32_t word = 0; word < (1U << nof_bits); word++) { + // Generate bits + uint8_t b[SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS] = {}; + for (uint32_t i = 0; i < nof_bits; i++) { + b[i] = (word >> i) & 1U; + } + + // Encode PUCCH + TESTASSERT(srslte_pucch_nr_format1_encode( + pucch, &carrier, cfg, &slot, &resource, b, nof_bits, slot_symbols) == SRSLTE_SUCCESS); + + // Put DMRS + TESTASSERT(srslte_dmrs_pucch_format1_put(pucch, &carrier, cfg, &slot, &resource, slot_symbols) == + SRSLTE_SUCCESS); + + // Estimate channel + TESTASSERT(srslte_dmrs_pucch_format1_estimate( + pucch, &carrier, cfg, &slot, &resource, slot_symbols, chest_res) == SRSLTE_SUCCESS); + + // Decode PUCCH + uint8_t b_rx[SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS]; + TESTASSERT(srslte_pucch_nr_format1_decode( + pucch, &carrier, cfg, &slot, &resource, chest_res, slot_symbols, b_rx, nof_bits) == + SRSLTE_SUCCESS); + + // Check received bits + for (uint32_t i = 0; i < nof_bits; i++) { + TESTASSERT(b[i] == b_rx[i]); + } + } + } + } + } + } + } + } + } + + return SRSLTE_SUCCESS; +} + static void usage(char* prog) { printf("Usage: %s [csNnv]\n", prog); @@ -111,9 +172,10 @@ int main(int argc, char** argv) int ret = SRSLTE_ERROR; parse_args(argc, argv); - uint32_t nof_re = carrier.nof_prb * SRSLTE_NRE * SRSLTE_NSYMB_PER_SLOT_NR; - cf_t* slot_symb = srslte_vec_cf_malloc(nof_re); - srslte_pucch_nr_t pucch = {}; + uint32_t nof_re = carrier.nof_prb * SRSLTE_NRE * SRSLTE_NSYMB_PER_SLOT_NR; + cf_t* slot_symb = srslte_vec_cf_malloc(nof_re); + srslte_pucch_nr_t pucch = {}; + srslte_chest_ul_res_t chest_res = {}; if (slot_symb == NULL) { ERROR("Alloc\n"); @@ -125,12 +187,25 @@ int main(int argc, char** argv) goto clean_exit; } + if (srslte_chest_ul_res_init(&chest_res, carrier.nof_prb)) { + ERROR("Chest UL\n"); + goto clean_exit; + } + srslte_pucch_nr_common_cfg_t common_cfg = {}; + + // Test Format 0 if (test_pucch_format0(&pucch, &common_cfg, slot_symb) < SRSLTE_SUCCESS) { ERROR("Failed PUCCH format 0\n"); goto clean_exit; } + // Test Format 1 + if (test_pucch_format1(&pucch, &common_cfg, &chest_res, slot_symb) < SRSLTE_SUCCESS) { + ERROR("Failed PUCCH format 1\n"); + goto clean_exit; + } + ret = SRSLTE_SUCCESS; clean_exit: if (slot_symb) { @@ -138,6 +213,7 @@ clean_exit: } srslte_pucch_nr_free(&pucch); + srslte_chest_ul_res_free(&chest_res); if (ret == SRSLTE_SUCCESS) { printf("Test passed!\n");