diff --git a/lib/include/srslte/phy/common/phy_common_nr.h b/lib/include/srslte/phy/common/phy_common_nr.h index 47a01866f..3a133ad4e 100644 --- a/lib/include/srslte/phy/common/phy_common_nr.h +++ b/lib/include/srslte/phy/common/phy_common_nr.h @@ -37,7 +37,7 @@ extern "C" { /** * @brief Defines the maximum numerology supported. Defined by TS 38.211 v15.8.0 Table 4.3.2-1. */ -#define SRSLTE_NR_MAX_NUMEROLOGY 4 +#define SRSLTE_NR_MAX_NUMEROLOGY 4U /** * @brief Defines the symbol duration, including cyclic prefix diff --git a/lib/include/srslte/phy/phch/pucch_nr.h b/lib/include/srslte/phy/phch/pucch_nr.h index 942ab5c67..4fd52f8eb 100644 --- a/lib/include/srslte/phy/phch/pucch_nr.h +++ b/lib/include/srslte/phy/phch/pucch_nr.h @@ -14,9 +14,17 @@ #define SRSLTE_PUCCH_NR_H #include "srslte/config.h" +#include "srslte/phy/common/phy_common_nr.h" +#include "srslte/phy/common/zc_sequence.h" +#include "srslte/phy/modem/modem_table.h" #include #include +/** + * @brief Maximum number of NR-PUCCH format 1 symbols (without DMRS) + */ +#define SRSLTE_PUCCH_NR_FORMAT1_N_MAX 7 + typedef enum SRSLTE_API { SRSLTE_PUCCH_NR_FORMAT_0 = 0, SRSLTE_PUCCH_NR_FORMAT_1, @@ -26,24 +34,143 @@ typedef enum SRSLTE_API { SRSLTE_PUCCH_NR_FORMAT_ERROR, } srslte_pucch_nr_format_t; +typedef enum SRSLTE_API { + SRSLTE_PUCCH_NR_GROUP_HOPPING_NEITHER = 0, + SRSLTE_PUCCH_NR_GROUP_HOPPING_ENABLE, + SRSLTE_PUCCH_NR_GROUP_HOPPING_DISABLE +} srslte_pucch_nr_group_hopping_t; + +/** + * @brief PUCCH Common configuration + * @remark Defined in TS 38.331 PUCCH-ConfigCommon + */ +typedef struct SRSLTE_API { + uint32_t resource_common; ///< Configures a set of cell-specific PUCCH resources/parameters + srslte_pucch_nr_group_hopping_t group_hopping; ///< Configuration of group and sequence hopping + uint32_t hopping_id; ///< Cell-specific scrambling ID for group hopping and sequence hopping if enabled + bool hopping_id_present; + float p0_nominal; ///< Power control parameter P0 for PUCCH transmissions. Value in dBm. (-202..24) +} srslte_pucch_nr_common_cfg_t; + /** * @brief Generic PUCCH Resource configuration + * @remark Defined in TS 38.331 PUCCH-Config */ typedef struct SRSLTE_API { - // Common + // + uint32_t starting_prb; + bool intra_slot_frequency_hopping; + uint32_t second_hop_prb; + + // Common PUCCH-Resource parameters among all formats srslte_pucch_nr_format_t format; ///< PUCCH format this configuration belongs uint32_t nof_symbols; ///< Number of symbols uint32_t start_symbol_idx; ///< Starting symbol index double max_code_rate; ///< Maximum code rate (0.08, 0.15, 0.25, 0.35, 0.45, 0.60, 0.80) bool enable_pi_bpsk; ///< Enables PI-BPSK - // Other parameters + // Specific PUCCH-Resource uint32_t initial_cyclic_shift; ///< Used by formats 0, 1 uint32_t time_domain_occ; ///< Used by format 1 uint32_t nof_prb; ///< Used by formats 2, 3 uint32_t occ_lenth; ///< Spreading factor, used by format 4 uint32_t occ_index; ///< Used by format 4 +} srslte_pucch_nr_resource_t; + +typedef struct SRSLTE_API { + float rsrp; + float rsrp_dBfs; + float epre; + float epre_dBfs; + float norm_corr; +} srslte_pucch_nr_measure_t; -} srslte_pucch_nr_cfg_t; +/** + * @brief PUCCH Resource configuration for Format 0 + * @remark Defined in TS 38.331 PUCCH-Config + */ +typedef struct SRSLTE_API { + uint32_t starting_prb; + uint32_t initial_cyclic_shift; ///< initialCyclicShift (0..11) + uint32_t start_symbol_idx; ///< startingSymbolIndex (0..13) + uint32_t nof_symbols; ///< nrofSymbols (1..2) +} srslte_pucch_nr_resource_format0_t; + +/** + * @brief PUCCH Resource configuration for Format 1 + * @remark Defined in TS 38.331 PUCCH-Config + */ +typedef struct SRSLTE_API { + uint32_t starting_prb; + uint32_t initial_cyclic_shift; ///< initialCyclicShift (0..11) + uint32_t start_symbol_idx; ///< startingSymbolIndex (0..10) + uint32_t nof_symbols; ///< nrofSymbols (4..14) + uint32_t time_domain_occ; ///< TimeDomainOCC(0..6) + bool intra_slot_hopping; +} srslte_pucch_nr_resource_format1_t; + +/** + * @brief NR-PUCCH encoder/decoder object + */ +typedef struct SRSLTE_API { + srslte_zc_sequence_lut_t r_uv_1prb; + cf_t format1_w_i_m[SRSLTE_PUCCH_NR_FORMAT1_N_MAX][SRSLTE_PUCCH_NR_FORMAT1_N_MAX][SRSLTE_PUCCH_NR_FORMAT1_N_MAX]; + srslte_modem_table_t bpsk; + srslte_modem_table_t qpsk; +} srslte_pucch_nr_t; + +/** + * @brief Initialises an NR-PUCCH encoder/decoder object + * @param q Object + * @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise + */ +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); + +/** + * @brief Puts 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 + * @param[in] cfg PUCCH common configuration + * @param[in] slot slot configuration + * @param[in] resource PUCCH format 0 resource + * @param[in] m_cs Cyclic shift according to TS 38.213 clause 5 + * @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); + +/** + * @brief Measures PUCCH format 0 in the 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 0 resource + * @param[in] m_cs Cyclic shift according to TS 38.213 clause 5 + * @param[in] slot_symbols Resource grid of the given slot + * @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); #endif // SRSLTE_PUCCH_NR_H diff --git a/lib/include/srslte/phy/phch/uci_nr.h b/lib/include/srslte/phy/phch/uci_nr.h index 8f9a3ac36..5c7600493 100644 --- a/lib/include/srslte/phy/phch/uci_nr.h +++ b/lib/include/srslte/phy/phch/uci_nr.h @@ -71,10 +71,10 @@ SRSLTE_API void srslte_uci_nr_free(srslte_uci_nr_t* q); * @param[out] o Output encoded bits * @return Number of encoded bits if encoding is successful, SRSLTE_ERROR code otherwise */ -SRSLTE_API int srslte_uci_nr_encode_pucch(srslte_uci_nr_t* q, - const srslte_pucch_nr_cfg_t* pucch_cfg, - const srslte_uci_cfg_nr_t* uci_cfg, - const srslte_uci_value_nr_t* value, - uint8_t* o); +SRSLTE_API int srslte_uci_nr_encode_pucch(srslte_uci_nr_t* q, + const srslte_pucch_nr_resource_t* pucch_resource, + const srslte_uci_cfg_nr_t* uci_cfg, + const srslte_uci_value_nr_t* value, + uint8_t* o); #endif // SRSLTE_UCI_NR_H diff --git a/lib/src/phy/phch/pucch_nr.c b/lib/src/phy/phch/pucch_nr.c new file mode 100644 index 000000000..6000c1e13 --- /dev/null +++ b/lib/src/phy/phch/pucch_nr.c @@ -0,0 +1,434 @@ +/** + * + * \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/phch/pucch_nr.h" +#include "srslte/phy/common/phy_common_nr.h" +#include "srslte/phy/common/sequence.h" +#include "srslte/phy/common/zc_sequence.h" +#include "srslte/phy/modem/mod.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" +#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, + const srslte_pucch_nr_common_cfg_t* cfg, + uint32_t* u_, + uint32_t* v_) +{ + uint32_t f_gh = 0; + uint32_t f_ss = 0; + uint32_t n_id = cfg->hopping_id_present ? cfg->hopping_id : carrier->id; + + switch (cfg->group_hopping) { + + case SRSLTE_PUCCH_NR_GROUP_HOPPING_NEITHER: + f_ss = n_id % SRSLTE_ZC_SEQUENCE_NOF_GROUPS; + break; + case SRSLTE_PUCCH_NR_GROUP_HOPPING_ENABLE: + ERROR("Group hopping is not implemented\n"); + return SRSLTE_ERROR; + case SRSLTE_PUCCH_NR_GROUP_HOPPING_DISABLE: + ERROR("Hopping is not implemented\n"); + return SRSLTE_ERROR; + } + + uint32_t u = (f_gh + f_ss) % SRSLTE_ZC_SEQUENCE_NOF_GROUPS; + uint32_t v = 0; + + if (u_) { + *u_ = u; + } + + if (v_) { + *v_ = v; + } + + return SRSLTE_SUCCESS; +} + +// 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) +{ + // Compute number of slot + uint32_t n_slot = slot->idx % SRSLTE_NSLOTS_PER_FRAME_NR(carrier->numerology); + + // Generate pseudo-random sequence + uint32_t cinit = cfg->hopping_id_present ? cfg->hopping_id : carrier->id; + uint8_t cs[SRSLTE_NSYMB_PER_SLOT_NR * SRSLTE_NSLOTS_PER_FRAME_NR(SRSLTE_NR_MAX_NUMEROLOGY) * 8U] = {}; + srslte_sequence_apply_bit( + cs, cs, SRSLTE_NSYMB_PER_SLOT_NR * SRSLTE_NSLOTS_PER_FRAME_NR(carrier->numerology) * 8, cinit); + + // Create n_cs parameter + uint32_t n_cs = 0; + for (uint32_t m = 0; m < 8; m++) { + n_cs += cs[SRSLTE_NSYMB_PER_SLOT_NR * n_slot + (l + l_prime) * 8 + m] << m; + } + + return (m0 + m_cs + n_cs) % SRSLTE_NRE; +} + +static int srslte_pucch_nr_format0_resource_valid(const srslte_pucch_nr_resource_format0_t* resource) +{ + if (resource == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (resource->nof_symbols != 1 && resource->nof_symbols != 2) { + ERROR("Invalid number of symbols (%d)\n", resource->nof_symbols); + return SRSLTE_ERROR; + } + + if (resource->initial_cyclic_shift > 11) { + ERROR("Invalid initial cyclic shift (%d)\n", resource->initial_cyclic_shift); + return SRSLTE_ERROR; + } + + if (resource->start_symbol_idx > 13) { + ERROR("Invalid initial start symbol idx (%d)\n", resource->start_symbol_idx); + return SRSLTE_ERROR; + } + + return SRSLTE_SUCCESS; +} + +static int srslte_pucch_nr_format1_resource_valid(const srslte_pucch_nr_resource_format1_t* resource) +{ + if (resource == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (resource->nof_symbols < 4 || resource->nof_symbols > 14) { + ERROR("Invalid number of symbols (%d)\n", resource->nof_symbols); + return SRSLTE_ERROR; + } + + if (resource->initial_cyclic_shift > 11) { + ERROR("Invalid initial cyclic shift (%d)\n", resource->initial_cyclic_shift); + return SRSLTE_ERROR; + } + + if (resource->start_symbol_idx > 10) { + ERROR("Invalid initial start symbol idx (%d)\n", resource->start_symbol_idx); + return SRSLTE_ERROR; + } + + if (resource->time_domain_occ > 6) { + ERROR("Invalid time domain occ (%d)\n", resource->time_domain_occ); + return SRSLTE_ERROR; + } + + if (resource->intra_slot_hopping) { + ERROR("Intra-slot hopping is not implemented\n"); + return SRSLTE_ERROR; + } + + return SRSLTE_SUCCESS; +} + +// TS 38.211 Table 6.3.2.4.1-2: Orthogonal sequences for PUCCH format 1 +static uint32_t + pucch_nr_format1_rho[SRSLTE_PUCCH_NR_FORMAT1_N_MAX][SRSLTE_PUCCH_NR_FORMAT1_N_MAX][SRSLTE_PUCCH_NR_FORMAT1_N_MAX] = + {{{0}, {0, 0}, {0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}}, + {{}, {0, 1}, {0, 1, 2}, {0, 2, 0, 2}, {0, 1, 2, 3, 4}, {0, 1, 2, 3, 4, 5}, {0, 1, 2, 3, 4, 5, 6}}, + {{}, {}, {0, 2, 1}, {0, 0, 2, 2}, {0, 2, 4, 1, 3}, {0, 2, 4, 0, 2, 4}, {0, 2, 4, 6, 1, 3, 5}}, + {{}, {}, {}, {0, 2, 2, 0}, {0, 3, 1, 4, 2}, {0, 3, 0, 3, 0, 3}, {0, 3, 6, 2, 5, 1, 4}}, + {{}, {}, {}, {}, {0, 4, 3, 2, 1}, {0, 4, 2, 0, 4, 2}, {0, 4, 1, 5, 2, 6, 3}}, + {{}, {}, {}, {}, {}, {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) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + // Initialise ZC sequences for 1PRB + float alphas_1prb[SRSLTE_NRE] = {}; + for (uint32_t i = 0; i < SRSLTE_NRE; i++) { + alphas_1prb[i] = 2.0f * (float)M_PI * (float)i / (float)SRSLTE_NRE; + } + srslte_zc_sequence_lut_init_nr(&q->r_uv_1prb, 1, 0, alphas_1prb, SRSLTE_NRE); + + // Initialise BPSK modulation table + if (srslte_modem_table_lte(&q->bpsk, SRSLTE_MOD_BPSK) < SRSLTE_SUCCESS) { + ERROR("Initiating modem table\n"); + return SRSLTE_ERROR; + } + + // Initialise QPSK modulation table + if (srslte_modem_table_lte(&q->qpsk, SRSLTE_MOD_QPSK) < SRSLTE_SUCCESS) { + ERROR("Initiating modem table\n"); + return SRSLTE_ERROR; + } + + for (uint32_t n_pucch = 1; n_pucch <= SRSLTE_PUCCH_NR_FORMAT1_N_MAX; n_pucch++) { + for (uint32_t i = 0; i < SRSLTE_PUCCH_NR_FORMAT1_N_MAX; i++) { + for (uint32_t m = 0; m < SRSLTE_PUCCH_NR_FORMAT1_N_MAX; m++) { + uint32_t rho = pucch_nr_format1_rho[i][n_pucch - 1][m]; + q->format1_w_i_m[i][n_pucch - 1][m] = cexpf(I * 2.0f * (float)M_PI * (float)rho / n_pucch); + } + } + } + + return SRSLTE_SUCCESS; +} + +void srslte_pucch_nr_free(srslte_pucch_nr_t* q) +{ + if (q == NULL) { + return; + } + + srslte_zc_sequence_lut_free(&q->r_uv_1prb); + + srslte_modem_table_free(&q->bpsk); + srslte_modem_table_free(&q->qpsk); + + 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) +{ + if (carrier == NULL || cfg == NULL || slot == NULL || resource == NULL || slot_symbols == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (srslte_pucch_nr_format0_resource_valid(resource) < SRSLTE_SUCCESS) { + ERROR("Invalid PUCCH format 0 resource\n"); + return SRSLTE_SUCCESS; + } + + uint32_t u = 0; + uint32_t v = 0; + if (pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) { + ERROR("Error getting group sequence\n"); + return SRSLTE_ERROR; + } + + 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); + + // 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 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]; + + // Copy sequence in grid + srslte_vec_cf_copy(slot_symbols_ptr, r_uv, SRSLTE_NRE); + } + + return SRSLTE_SUCCESS; +} + +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) +{ + if (carrier == NULL || cfg == NULL || slot == NULL || resource == NULL || slot_symbols == NULL || measure == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (srslte_pucch_nr_format0_resource_valid(resource) < SRSLTE_SUCCESS) { + ERROR("Invalid PUCCH format 0 resource\n"); + return SRSLTE_SUCCESS; + } + + uint32_t u = 0; + uint32_t v = 0; + if (pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) { + ERROR("Error getting group sequence\n"); + return SRSLTE_ERROR; + } + + uint32_t l_prime = resource->start_symbol_idx; + float epre = 0.0f; + 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); + + // 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 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]; + + // Measure EPRE and average + epre += srslte_vec_avg_power_cf(slot_symbols_ptr, SRSLTE_NRE) / resource->nof_symbols; + + // Do correlation + cf_t corr = srslte_vec_dot_prod_conj_ccc(r_uv, slot_symbols_ptr, SRSLTE_NRE); + corr /= SRSLTE_NRE; + + // Measure RSRP and average + rsrp += (__real__ corr * __real__ corr + __imag__ corr * __imag__ corr) / resource->nof_symbols; + } + + // Save measurement + measure->rsrp = rsrp; + measure->rsrp_dBfs = srslte_convert_power_to_dB(rsrp); + measure->epre = epre; + measure->epre_dBfs = srslte_convert_power_to_dB(epre); + if (isnormal(epre)) { + measure->norm_corr = rsrp / epre; + } else { + measure->norm_corr = 0.0f; + } + + return SRSLTE_SUCCESS; +} + +// 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) +{ + if (resource->intra_slot_hopping) { + if (m_prime == 0) { + return resource->nof_symbols / 4; + } + + return resource->nof_symbols / 2 - resource->nof_symbols / 4; + } + + if (m_prime == 1) { + return 0; + } + + 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) +{ + if (n_pucch < 1 || n_pucch > SRSLTE_PUCCH_NR_FORMAT1_N_MAX) { + ERROR("Invalid n_pucch\n"); + return NAN; + } + if (i >= SRSLTE_PUCCH_NR_FORMAT1_N_MAX) { + ERROR("Invalid i\n"); + return NAN; + } + if (m >= SRSLTE_PUCCH_NR_FORMAT1_N_MAX) { + ERROR("Invalid m\n"); + return NAN; + } + + // Get value + 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) +{ + 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; + } + + // Modulate d + cf_t d = 0; + if (nof_bits == 1) { + srslte_mod_modulate(&q->bpsk, b, &d, nof_bits); + } else { + srslte_mod_modulate(&q->qpsk, b, &d, nof_bits); + } + + // Get group sequence + uint32_t u = 0; + uint32_t v = 0; + if (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]; + + // Get Alpha index + uint32_t alpha_idx = pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs); + + // 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; + } + + // Compute y = d(0) * r_uv + cf_t y[SRSLTE_NRE]; + 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); + + // Compute z(n) = w(i) * y(n) + cf_t z[SRSLTE_NRE]; + srslte_vec_sc_prod_ccc(y, w_i_m, z, SRSLTE_NRE); + + // Put z in the grid + srslte_vec_cf_copy(slot_symbols_ptr, z, SRSLTE_NRE); + } + + return SRSLTE_SUCCESS; +} \ No newline at end of file diff --git a/lib/src/phy/phch/test/CMakeLists.txt b/lib/src/phy/phch/test/CMakeLists.txt index 646b637b2..49b1e355a 100644 --- a/lib/src/phy/phch/test/CMakeLists.txt +++ b/lib/src/phy/phch/test/CMakeLists.txt @@ -614,6 +614,10 @@ endif(RF_FOUND) # NR ######################################################################## +add_executable(pucch_nr_test pucch_nr_test.c) +target_link_libraries(pucch_nr_test srslte_phy) +add_test(pucch_nr_test pucch_nr_test) + add_executable(sch_nr_test sch_nr_test.c) target_link_libraries(sch_nr_test srslte_phy) add_test(sch_nr_test sch_nr_test -m 0 -p 1) diff --git a/lib/src/phy/phch/test/pucch_nr_test.c b/lib/src/phy/phch/test/pucch_nr_test.c new file mode 100644 index 000000000..44e505220 --- /dev/null +++ b/lib/src/phy/phch/test/pucch_nr_test.c @@ -0,0 +1,149 @@ +/** + * + * \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/common/test_common.h" +#include "srslte/phy/phch/pucch_nr.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" +#include +#include +#include +#include +#include +#include + +static srslte_carrier_nr_t carrier = { + 0, // cell_id + 0, // numerology + 6, // nof_prb + 0, // start + 1 // max_mimo_layers +}; + +static uint32_t starting_prb_stride = 4; +static uint32_t starting_symbol_stride = 4; + +static int test_pucch_format0(srslte_pucch_nr_t* pucch, const srslte_pucch_nr_common_cfg_t* cfg, cf_t* slot_symbols) +{ + srslte_dl_slot_cfg_t slot = {}; + srslte_pucch_nr_resource_format0_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 = 1; resource.nof_symbols <= 2; 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.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) == + SRSLTE_SUCCESS); + + // Measure PUCCH format 0 for all possible values of m_cs + for (uint32_t m_cs_test = 0; m_cs_test <= 6; m_cs_test += 2) { + srslte_pucch_nr_measure_t measure = {}; + TESTASSERT(srslte_pucch_nr_format0_measure( + pucch, &carrier, cfg, &slot, &resource, m_cs_test, slot_symbols, &measure) == + SRSLTE_SUCCESS); + + if (m_cs == m_cs_test) { + TESTASSERT(fabsf(measure.epre - 1) < 0.001); + TESTASSERT(fabsf(measure.rsrp - 1) < 0.001); + TESTASSERT(fabsf(measure.norm_corr - 1) < 0.001); + } else { + TESTASSERT(fabsf(measure.epre - 1) < 0.001); + TESTASSERT(fabsf(measure.rsrp - 0) < 0.1); + TESTASSERT(fabsf(measure.norm_corr - 0) < 0.1); + } + } + } + } + } + } + } + } + + return SRSLTE_SUCCESS; +} + +static void usage(char* prog) +{ + printf("Usage: %s [csNnv]\n", prog); + printf("\t-c cell id [Default %d]\n", carrier.id); + printf("\t-n nof_prb [Default %d]\n", carrier.nof_prb); + 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, "cnv")) != -1) { + switch (opt) { + case 'c': + carrier.id = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'n': + carrier.nof_prb = (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) +{ + 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 = {}; + + if (slot_symb == NULL) { + ERROR("Alloc\n"); + goto clean_exit; + } + + if (srslte_pucch_nr_init(&pucch) < SRSLTE_SUCCESS) { + ERROR("PUCCH init\n"); + goto clean_exit; + } + + srslte_pucch_nr_common_cfg_t common_cfg = {}; + if (test_pucch_format0(&pucch, &common_cfg, slot_symb) < SRSLTE_SUCCESS) { + ERROR("Failed PUCCH format 0\n"); + goto clean_exit; + } + + ret = SRSLTE_SUCCESS; +clean_exit: + if (slot_symb) { + free(slot_symb); + } + + srslte_pucch_nr_free(&pucch); + + if (ret == SRSLTE_SUCCESS) { + printf("Test passed!\n"); + } else { + printf("Test failed!\n"); + } + + return ret; +} \ No newline at end of file diff --git a/lib/src/phy/phch/uci_nr.c b/lib/src/phy/phch/uci_nr.c index 8b6ed9a90..093ada5f7 100644 --- a/lib/src/phy/phch/uci_nr.c +++ b/lib/src/phy/phch/uci_nr.c @@ -412,7 +412,7 @@ int uci_nr_encode(srslte_uci_nr_t* q, } // Implements TS 38.212 Table 6.3.1.4-1: Total rate matching output sequence length Etot -static int uci_nr_pucch_E_tot(const srslte_pucch_nr_cfg_t* pucch_cfg, const srslte_uci_cfg_nr_t* uci_cfg) +static int uci_nr_pucch_E_tot(const srslte_pucch_nr_resource_t* pucch_cfg, const srslte_uci_cfg_nr_t* uci_cfg) { if (pucch_cfg == NULL || uci_cfg == NULL) { return SRSLTE_ERROR_INVALID_INPUTS; @@ -453,7 +453,7 @@ static int uci_nr_pucch_E_tot(const srslte_pucch_nr_cfg_t* pucch_cfg, const srsl // Implements TS 38.212 Table 6.3.1.4.1-1: Rate matching output sequence length E UCI static int -uci_nr_pucch_E_uci(const srslte_pucch_nr_cfg_t* pucch_cfg, const srslte_uci_cfg_nr_t* uci_cfg, uint32_t E_tot) +uci_nr_pucch_E_uci(const srslte_pucch_nr_resource_t* pucch_cfg, const srslte_uci_cfg_nr_t* uci_cfg, uint32_t E_tot) { if (uci_cfg->o_csi1 != 0 && uci_cfg->o_csi2) { ERROR("Simultaneous CSI part 1 and CSI part 2 is not implemented\n"); @@ -463,19 +463,19 @@ uci_nr_pucch_E_uci(const srslte_pucch_nr_cfg_t* pucch_cfg, const srslte_uci_cfg_ return E_tot; } -int srslte_uci_nr_encode_pucch(srslte_uci_nr_t* q, - const srslte_pucch_nr_cfg_t* pucch_cfg, - const srslte_uci_cfg_nr_t* uci_cfg, - const srslte_uci_value_nr_t* value, - uint8_t* o) +int srslte_uci_nr_encode_pucch(srslte_uci_nr_t* q, + const srslte_pucch_nr_resource_t* pucch_resource_cfg, + const srslte_uci_cfg_nr_t* uci_cfg, + const srslte_uci_value_nr_t* value, + uint8_t* o) { - int E_tot = uci_nr_pucch_E_tot(pucch_cfg, uci_cfg); + int E_tot = uci_nr_pucch_E_tot(pucch_resource_cfg, uci_cfg); if (E_tot < SRSLTE_SUCCESS) { return SRSLTE_ERROR; } - int E_uci = uci_nr_pucch_E_uci(pucch_cfg, uci_cfg, E_tot); + int E_uci = uci_nr_pucch_E_uci(pucch_resource_cfg, uci_cfg, E_tot); if (E_uci < SRSLTE_SUCCESS) { return SRSLTE_ERROR; }