From 35f4e5d69a3c02440f4cc10710c9e21fbf4bf6b7 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Fri, 31 Jan 2020 19:42:56 +0100 Subject: [PATCH] Initial PUCCH 1B with channel selection --- lib/include/srslte/phy/enb/enb_ul.h | 2 - lib/include/srslte/phy/phch/pucch.h | 56 ++++++ lib/include/srslte/phy/phch/pucch_cfg.h | 3 +- lib/src/phy/enb/enb_ul.c | 112 +++++++++-- lib/src/phy/phch/pucch.c | 237 ++++++++++++++++++++++-- lib/src/phy/phch/uci.c | 5 +- lib/src/phy/ue/ue_ul.c | 103 +++------- lib/test/phy/CMakeLists.txt | 5 + lib/test/phy/pucch_cs_test.c | 166 +++++++++++++++++ srsenb/hdr/stack/mac/scheduler_ue.h | 1 - srsenb/src/stack/mac/scheduler_ue.cc | 48 ----- 11 files changed, 575 insertions(+), 163 deletions(-) create mode 100644 lib/test/phy/pucch_cs_test.c diff --git a/lib/include/srslte/phy/enb/enb_ul.h b/lib/include/srslte/phy/enb/enb_ul.h index bc296d6f4..fd07a2f57 100644 --- a/lib/include/srslte/phy/enb/enb_ul.h +++ b/lib/include/srslte/phy/enb/enb_ul.h @@ -86,6 +86,4 @@ SRSLTE_API int srslte_enb_ul_get_pusch(srslte_enb_ul_t* q, srslte_pusch_cfg_t* cfg, srslte_pusch_res_t* res); -SRSLTE_API uint32_t srslte_enb_ul_get_pucch_prb_idx(srslte_cell_t* cell, srslte_pucch_cfg_t* cfg, uint32_t ns); - #endif // SRSLTE_ENB_UL_H diff --git a/lib/include/srslte/phy/phch/pucch.h b/lib/include/srslte/phy/phch/pucch.h index b3605619b..46fb77a44 100644 --- a/lib/include/srslte/phy/phch/pucch.h +++ b/lib/include/srslte/phy/phch/pucch.h @@ -41,10 +41,16 @@ #define SRSLTE_PUCCH_N_SEQ 12 #define SRSLTE_PUCCH2_NOF_BITS SRSLTE_UCI_CQI_CODED_PUCCH_B +#define SRSLTE_PUCCH_1A_2A_NOF_ACK (1) +#define SRSLTE_PUCCH_1B_2B_NOF_ACK (2) #define SRSLTE_PUCCH3_NOF_BITS (4 * SRSLTE_NRE) #define SRSLTE_PUCCH_MAX_BITS SRSLTE_CQI_MAX_BITS #define SRSLTE_PUCCH_MAX_SYMBOLS 128 +// PUCCH Format 1B Channel selection +#define SRSLTE_PUCCH_CS_MAX_ACK 4 +#define SRSLTE_PUCCH_CS_MAX_NOF_ALLOC 4 + typedef struct { srslte_sequence_t seq_f2[SRSLTE_NOF_SF_X_FRAME]; uint32_t cell_id; @@ -138,6 +144,13 @@ SRSLTE_API char* srslte_pucch_format_text(srslte_pucch_format_t format); SRSLTE_API char* srslte_pucch_format_text_short(srslte_pucch_format_t format); +/** + * Returns the number of ACK bits supported by a given PUCCH format + * @param format PUCCH format + * @return Returns the number of bits supported by the format + */ +SRSLTE_API uint32_t srslte_pucch_nof_ack_format(srslte_pucch_format_t format); + SRSLTE_API void srslte_pucch_tx_info(srslte_pucch_cfg_t* cfg, srslte_uci_value_t* uci_data, char* str, uint32_t str_len); @@ -146,4 +159,47 @@ srslte_pucch_rx_info(srslte_pucch_cfg_t* cfg, srslte_uci_value_t* uci_data, char SRSLTE_API bool srslte_pucch_cfg_isvalid(srslte_pucch_cfg_t* cfg, uint32_t nof_prb); +/** + * Implements 3GPP 36.213 R10 10.1 PUCCH format selection + * @param cfg PUCCH configuration struct + * @param uci_cfg uplink control information configuration + * @param uci_value uplink control information, set NULL for eNb + * @param cp Cyclic prefix + * @return Returns the number of entries in the table or negative value indicating error + */ +SRSLTE_API srslte_pucch_format_t srslte_pucch_select_format(srslte_pucch_cfg_t* cfg, + srslte_uci_cfg_t* uci_cfg, + srslte_cp_t cp); + +/** + * Implements 3GPP 36.213 R10 10.1.2.2.1 PUCCH format 1b with channel selection HARQ-ACK procedure + * resource list + * @param cfg PUCCH configuration struct + * @param uci_cfg uplink control information configuration + * @param n_pucch_i table with the PUCCH format 1b possible resources + * @return Returns the number of entries in the table or negative value indicating error + */ +SRSLTE_API int srslte_pucch_cs_resources(srslte_pucch_cfg_t* cfg, + srslte_uci_cfg_t* uci_cfg, + uint32_t n_pucch_i[SRSLTE_PUCCH_CS_MAX_NOF_ALLOC]); + +/** + * Decodes the HARQ ACK bits from a selected resource (j) and received bits (b) + * 3GPP 36.213 R10 10.1.2.2.1 PUCCH format 1b with channel selection HARQ-ACK procedure + * tables: + * - Table 10.1.2.2.1-3: Transmission of Format 1b HARQ-ACK channel selection for A = 2 + * - Table 10.1.2.2.1-4: Transmission of Format 1b HARQ-ACK channel selection for A = 3 + * - Table 10.1.2.2.1-5: Transmission of Format 1b HARQ-ACK channel selection for A = 4 + * @param cfg PUCCH configuration struct + * @param uci_cfg uplink control information configuration + * @param j selected channel + * @param b received bits + * @return Returns SRSLTE_SUCCESS if it can decode it succesfully, SRSLTE_ERROR code otherwise + */ +SRSLTE_API int srslte_pucch_cs_get_ack(srslte_pucch_cfg_t* cfg, + srslte_uci_cfg_t* uci_cfg, + uint32_t j, + uint8_t b[SRSLTE_PUCCH_1B_2B_NOF_ACK], + srslte_uci_value_t* uci_value); + #endif // SRSLTE_PUCCH_H diff --git a/lib/include/srslte/phy/phch/pucch_cfg.h b/lib/include/srslte/phy/phch/pucch_cfg.h index 443edd89a..dcb81df32 100644 --- a/lib/include/srslte/phy/phch/pucch_cfg.h +++ b/lib/include/srslte/phy/phch/pucch_cfg.h @@ -27,6 +27,7 @@ #include "srslte/phy/phch/uci_cfg.h" #define SRSLTE_PUCCH_SIZE_AN_CS 4 +#define SRSLTE_PUCCH_SIZE_AN_N3 4 #define SRSLTE_PUCCH_NOF_AN_CS 2 #define SRSLTE_PUCCH2_MAX_DMRS_BITS 16 @@ -68,7 +69,7 @@ typedef struct SRSLTE_API { // Release 10 CA specific srslte_ack_nack_feedback_mode_t ack_nack_feedback_mode; uint32_t n1_pucch_an_cs[SRSLTE_PUCCH_SIZE_AN_CS][SRSLTE_PUCCH_NOF_AN_CS]; - uint32_t n3_pucch_an_list[SRSLTE_PUCCH_SIZE_AN_CS]; + uint32_t n3_pucch_an_list[SRSLTE_PUCCH_SIZE_AN_N3]; // Other configuration float threshold_format1; diff --git a/lib/src/phy/enb/enb_ul.c b/lib/src/phy/enb/enb_ul.c index e5830bee5..4fb9aba8d 100644 --- a/lib/src/phy/enb/enb_ul.c +++ b/lib/src/phy/enb/enb_ul.c @@ -170,32 +170,108 @@ void srslte_enb_ul_fft(srslte_enb_ul_t* q) srslte_ofdm_rx_sf(&q->fft); } -static int get_pucch(srslte_enb_ul_t* q, srslte_ul_sf_cfg_t* ul_sf, srslte_pucch_cfg_t* cfg, srslte_pucch_res_t* res) +static int pucch_resource_selection(srslte_pucch_cfg_t* cfg, + srslte_uci_cfg_t* uci_cfg, + srslte_cell_t* cell, + uint32_t n_pucch_i[SRSLTE_PUCCH_CS_MAX_ACK]) { - srslte_uci_value_t uci_value_default = {}; - srslte_ue_ul_pucch_resource_selection(&q->cell, cfg, &cfg->uci_cfg, &uci_value_default); + int ret = 1; - // Prepare configuration - if (srslte_chest_ul_estimate_pucch(&q->chest, ul_sf, cfg, q->sf_symbols, &q->chest_res)) { - ERROR("Error estimating PUCCH DMRS\n"); - return SRSLTE_ERROR; + if (!cfg || !cell || !uci_cfg || !n_pucch_i) { + ERROR("get_npucch(): Invalid parameters\n"); + ret = SRSLTE_ERROR_INVALID_INPUTS; + + } else if (uci_cfg->is_scheduling_request_tti) { + n_pucch_i[0] = cfg->n_pucch_sr; + + } else if (cfg->format < SRSLTE_PUCCH_FORMAT_2) { + if (cfg->sps_enabled) { + n_pucch_i[0] = cfg->n_pucch_1[uci_cfg->ack[0].tpc_for_pucch % 4]; + + } else { + if (cell->frame_type == SRSLTE_FDD) { + switch (cfg->ack_nack_feedback_mode) { + case SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_PUCCH3: + n_pucch_i[0] = cfg->n3_pucch_an_list[uci_cfg->ack[0].tpc_for_pucch % SRSLTE_PUCCH_SIZE_AN_N3]; + break; + case SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_CS: + ret = srslte_pucch_cs_resources(cfg, uci_cfg, n_pucch_i); + break; + default: + n_pucch_i[0] = uci_cfg->ack[0].ncce[0] + cfg->N_pucch_1; + break; + } + } else { + ERROR("TDD not supported\n"); + ret = SRSLTE_ERROR; + } + } + + } else { + n_pucch_i[0] = cfg->n_pucch_2; } - int ret_val = srslte_pucch_decode(&q->pucch, ul_sf, cfg, &q->chest_res, q->sf_symbols, res); - if (ret_val < 0) { - ERROR("Error decoding PUCCH\n"); - return SRSLTE_ERROR; - } - return ret_val; + return ret; } -uint32_t srslte_enb_ul_get_pucch_prb_idx(srslte_cell_t* cell, srslte_pucch_cfg_t* cfg, uint32_t ns) +static int get_pucch(srslte_enb_ul_t* q, srslte_ul_sf_cfg_t* ul_sf, srslte_pucch_cfg_t* cfg, srslte_pucch_res_t* res) { - // compute Format and n_pucch - srslte_ue_ul_pucch_resource_selection(cell, cfg, &cfg->uci_cfg, NULL); + int ret = SRSLTE_SUCCESS; + uint32_t n_pucch_i[SRSLTE_PUCCH_CS_MAX_ACK]; + srslte_pucch_res_t pucch_res; - // compute prb_idx - return srslte_pucch_n_prb(cell, cfg, ns); + // Drop CQI if there is collision with ACK + if (!cfg->simul_cqi_ack && srslte_uci_cfg_total_ack(&cfg->uci_cfg) > 0 && cfg->uci_cfg.cqi.data_enable) { + cfg->uci_cfg.cqi.data_enable = false; + } + + // Select format + cfg->format = srslte_pucch_select_format(cfg, &cfg->uci_cfg, q->cell.cp); + + // Get possible resources + int nof_resources = pucch_resource_selection(cfg, &cfg->uci_cfg, &q->cell, n_pucch_i); + if (nof_resources < SRSLTE_SUCCESS || nof_resources > SRSLTE_PUCCH_CS_MAX_ACK) { + ERROR("No PUCCH resource could be calculated\n"); + return SRSLTE_ERROR; + } + + // Initialise minimum correlation + res->correlation = -INFINITY; + + // Iterate possible resources and select the one with higher correlation + for (int i = 0; i < nof_resources && ret == SRSLTE_SUCCESS; i++) { + // Configure resource + cfg->n_pucch = n_pucch_i[i]; + + // Prepare configuration + if (srslte_chest_ul_estimate_pucch(&q->chest, ul_sf, cfg, q->sf_symbols, &q->chest_res)) { + ERROR("Error estimating PUCCH DMRS\n"); + return SRSLTE_ERROR; + } + + ret = srslte_pucch_decode(&q->pucch, ul_sf, cfg, &q->chest_res, q->sf_symbols, &pucch_res); + if (ret < SRSLTE_SUCCESS) { + ERROR("Error decoding PUCCH\n"); + } else { + + // If channel selection enabled + if (cfg->ack_nack_feedback_mode == SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_CS) { + uint8_t b[2] = {pucch_res.uci_data.ack.ack_value[0], pucch_res.uci_data.ack.ack_value[1]}; + srslte_pucch_cs_get_ack(cfg, &cfg->uci_cfg, i, b, &pucch_res.uci_data); + } + + char txt[256]; + srslte_pucch_rx_info(cfg, &pucch_res.uci_data, txt, sizeof(txt)); + INFO("[ENB_UL/PUCCH] Decoded %s, corr=%.3f\n", txt, pucch_res.correlation); + + // Check correlation value, keep maximum + if (pucch_res.correlation > res->correlation) { + *res = pucch_res; + } + } + } + + return ret; } int srslte_enb_ul_get_pucch(srslte_enb_ul_t* q, diff --git a/lib/src/phy/phch/pucch.c b/lib/src/phy/phch/pucch.c index b8db9af06..d7053dfdf 100644 --- a/lib/src/phy/phch/pucch.c +++ b/lib/src/phy/phch/pucch.c @@ -742,32 +742,32 @@ static bool decode_signal(srslte_pucch_t* q, return detected; } -static void decode_bits(srslte_uci_cfg_t* uci_cfg, +static void decode_bits(srslte_pucch_cfg_t* cfg, bool pucch_found, uint8_t pucch_bits[SRSLTE_PUCCH_MAX_BITS], - uint8_t pucch_dmrs_bits[SRSLTE_PUCCH2_MAX_DMRS_BITS], + uint8_t pucch2_bits[SRSLTE_PUCCH_MAX_BITS], srslte_uci_value_t* uci_data) { // If was looking for scheduling request, update value - if (uci_cfg->is_scheduling_request_tti) { + if (cfg->uci_cfg.is_scheduling_request_tti) { uci_data->scheduling_request = pucch_found; } // Save ACK bits - for (uint32_t a = 0; a < srslte_uci_cfg_total_ack(uci_cfg); a++) { - if (uci_cfg->cqi.data_enable || uci_cfg->cqi.ri_len) { - uci_data->ack.ack_value[a] = pucch_dmrs_bits[a]; + for (uint32_t a = 0; a < srslte_pucch_nof_ack_format(cfg->format); a++) { + if (cfg->uci_cfg.cqi.data_enable || cfg->uci_cfg.cqi.ri_len) { + uci_data->ack.ack_value[a] = pucch2_bits[a]; } else { uci_data->ack.ack_value[a] = pucch_bits[a]; } } // PUCCH2 CQI bits are already decoded - if (uci_cfg->cqi.data_enable) { - srslte_cqi_value_unpack(&uci_cfg->cqi, pucch_bits, &uci_data->cqi); + if (cfg->uci_cfg.cqi.data_enable) { + srslte_cqi_value_unpack(&cfg->uci_cfg.cqi, pucch_bits, &uci_data->cqi); } - if (uci_cfg->cqi.ri_len) { + if (cfg->uci_cfg.cqi.ri_len) { uci_data->ri = pucch_bits[0]; /* Assume only one bit of RI */ } } @@ -837,7 +837,7 @@ int srslte_pucch_decode(srslte_pucch_t* q, bool pucch_found = decode_signal(q, sf, cfg, pucch_bits, nof_re, nof_uci_bits, &data->correlation); // Convert bits to UCI data - decode_bits(&cfg->uci_cfg, pucch_found, pucch_bits, cfg->pucch2_drs_bits, &data->uci_data); + decode_bits(cfg, pucch_found, pucch_bits, cfg->pucch2_drs_bits, &data->uci_data); data->detected = pucch_found; @@ -893,8 +893,8 @@ char* srslte_pucch_format_text(srslte_pucch_format_t format) ret = "Format 3"; break; case SRSLTE_PUCCH_FORMAT_ERROR: + default: ret = "Format Error"; - break; } return ret; @@ -928,6 +928,7 @@ char* srslte_pucch_format_text_short(srslte_pucch_format_t format) ret = "3"; break; case SRSLTE_PUCCH_FORMAT_ERROR: + default: ret = "Err"; break; } @@ -935,6 +936,28 @@ char* srslte_pucch_format_text_short(srslte_pucch_format_t format) return ret; } +uint32_t srslte_pucch_nof_ack_format(srslte_pucch_format_t format) +{ + uint32_t ret = 0; + + switch (format) { + + case SRSLTE_PUCCH_FORMAT_1A: + case SRSLTE_PUCCH_FORMAT_2A: + ret = 1; + break; + case SRSLTE_PUCCH_FORMAT_1B: + case SRSLTE_PUCCH_FORMAT_2B: + ret = 2; + break; + default: + // Keep default + break; + } + + return ret; +} + /* Verify PUCCH configuration as given in Section 5.4 36.211 */ bool srslte_pucch_cfg_isvalid(srslte_pucch_cfg_t* cfg, uint32_t nof_prb) { @@ -1154,3 +1177,195 @@ void srslte_pucch_rx_info(srslte_pucch_cfg_t* cfg, srslte_uci_value_t* uci_data, srslte_uci_data_info(&cfg->uci_cfg, uci_data, &str[n], str_len - n); } } + +srslte_pucch_format_t srslte_pucch_select_format(srslte_pucch_cfg_t* cfg, srslte_uci_cfg_t* uci_cfg, srslte_cp_t cp) +{ + srslte_pucch_format_t format = SRSLTE_PUCCH_FORMAT_ERROR; + // No CQI data + if (!uci_cfg->cqi.data_enable && uci_cfg->cqi.ri_len == 0) { + // PUCCH Format 3 condition specified in: + // 3GPP 36.213 10.1.2.2.2 PUCCH format 3 HARQ-ACK procedure + if (cfg->ack_nack_feedback_mode == SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_PUCCH3 && + srslte_uci_cfg_total_ack(uci_cfg) > 1) { + format = SRSLTE_PUCCH_FORMAT_3; + } + // 1-bit ACK + optional SR + else if (srslte_uci_cfg_total_ack(uci_cfg) == 1) { + format = SRSLTE_PUCCH_FORMAT_1A; + } + // 2-bit ACK + optional SR + else if (srslte_uci_cfg_total_ack(uci_cfg) >= 2 && srslte_uci_cfg_total_ack(uci_cfg) <= 4) { + format = SRSLTE_PUCCH_FORMAT_1B; // with channel selection if > 2 + } + // If UCI value is provided, use SR signal only, otherwise SR request opportunity + else if (uci_cfg->is_scheduling_request_tti) { + format = SRSLTE_PUCCH_FORMAT_1; + } else { + ERROR("Error selecting PUCCH format: Unsupported number of ACK bits %d\n", srslte_uci_cfg_total_ack(uci_cfg)); + } + } + // CQI data + else { + // CQI and no ack + if (srslte_uci_cfg_total_ack(uci_cfg) == 0) { + format = SRSLTE_PUCCH_FORMAT_2; + } + // CQI + 1-bit ACK + else if (srslte_uci_cfg_total_ack(uci_cfg) == 1 && SRSLTE_CP_ISNORM(cp)) { + format = SRSLTE_PUCCH_FORMAT_2A; + } + // CQI + 2-bit ACK + else if (srslte_uci_cfg_total_ack(uci_cfg) == 2) { + format = SRSLTE_PUCCH_FORMAT_2B; + } + // CQI + 2-bit ACK + cyclic prefix + else if (srslte_uci_cfg_total_ack(uci_cfg) == 1 && SRSLTE_CP_ISEXT(cp)) { + format = SRSLTE_PUCCH_FORMAT_2B; + } + } + return format; +} + +int srslte_pucch_cs_resources(srslte_pucch_cfg_t* cfg, srslte_uci_cfg_t* uci_cfg, uint32_t n_pucch_i[4]) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (cfg && uci_cfg && n_pucch_i) { + // Determine the 4 PUCCH resources n_pucch_j associated with HARQ-ACK(j) + uint32_t k = 0; + for (int i = 0; i < SRSLTE_MAX_CARRIERS && k < SRSLTE_PUCCH_CS_MAX_ACK; i++) { + // If grant has been scheduled in PCell + if (uci_cfg->ack[i].grant_cc_idx == 0) { + for (uint32_t j = 0; j < uci_cfg->ack[i].nof_acks && k < SRSLTE_PUCCH_CS_MAX_ACK; j++) { + n_pucch_i[k++] = uci_cfg->ack[i].ncce[0] + cfg->N_pucch_1 + j; + } + } else { + for (uint32_t j = 0; j < uci_cfg->ack[i].nof_acks; j++) { + if (k < 4) { + n_pucch_i[k++] = cfg->n1_pucch_an_cs[uci_cfg->ack[i].tpc_for_pucch % SRSLTE_PUCCH_SIZE_AN_CS] + [j % SRSLTE_PUCCH_NOF_AN_CS]; + } else { + fprintf(stderr, "get_npucch_cs(): Too many ack bits\n"); + return SRSLTE_ERROR; + } + } + } + } + + ret = (int)k; + } + + return ret; +} + +#define PUCCH_CS_SET_ACK(J, B0, B1, ...) \ + do { \ + if (j == J && b[0] == B0 && b[1] == B1) { \ + uint8_t pos[] = {__VA_ARGS__}; \ + for (uint32_t i = 0; i < sizeof(pos) && pos[i] < SRSLTE_PUCCH_CS_MAX_ACK; i++) { \ + uci_value[pos[i]] = 1; \ + } \ + ret = SRSLTE_SUCCESS; \ + } \ + } while (false) + +static int puccch_cs_get_ack_a2(uint32_t j, const uint8_t b[2], uint8_t uci_value[SRSLTE_UCI_MAX_ACK_BITS]) +{ + int ret = SRSLTE_ERROR; + + PUCCH_CS_SET_ACK(1, 1, 1, 0, 1); + PUCCH_CS_SET_ACK(0, 1, 1, 0); + PUCCH_CS_SET_ACK(1, 0, 0, 1); + PUCCH_CS_SET_ACK(1, 0, 0, SRSLTE_PUCCH_CS_MAX_ACK); + + return ret; +} + +static int puccch_cs_get_ack_a3(uint32_t j, const uint8_t b[2], uint8_t uci_value[SRSLTE_UCI_MAX_ACK_BITS]) +{ + int ret = SRSLTE_ERROR; + + PUCCH_CS_SET_ACK(1, 1, 1, 0, 1, 2); + PUCCH_CS_SET_ACK(1, 1, 0, 0, 2); + PUCCH_CS_SET_ACK(1, 0, 1, 1, 2); + PUCCH_CS_SET_ACK(2, 1, 1, 2); + PUCCH_CS_SET_ACK(0, 1, 1, 0, 1); + PUCCH_CS_SET_ACK(0, 1, 0, 0); + PUCCH_CS_SET_ACK(0, 0, 1, 1); + PUCCH_CS_SET_ACK(1, 0, 0, SRSLTE_PUCCH_CS_MAX_ACK); + + return ret; +} + +static int puccch_cs_get_ack_a4(uint32_t j, const uint8_t b[2], uint8_t uci_value[SRSLTE_UCI_MAX_ACK_BITS]) +{ + int ret = SRSLTE_ERROR; + + PUCCH_CS_SET_ACK(1, 1, 1, 0, 1, 2, 3); + PUCCH_CS_SET_ACK(2, 0, 1, 0, 2, 3); + PUCCH_CS_SET_ACK(1, 0, 1, 1, 2, 3); + PUCCH_CS_SET_ACK(3, 1, 1, 2, 3); + PUCCH_CS_SET_ACK(1, 1, 0, 0, 1, 2); + PUCCH_CS_SET_ACK(2, 0, 0, 0, 2); + PUCCH_CS_SET_ACK(1, 0, 0, 1, 2); + PUCCH_CS_SET_ACK(3, 1, 0, 2); + PUCCH_CS_SET_ACK(2, 1, 1, 0, 1, 3); + PUCCH_CS_SET_ACK(2, 1, 0, 0, 3); + PUCCH_CS_SET_ACK(3, 0, 1, 1, 3); + PUCCH_CS_SET_ACK(3, 0, 0, 3); + PUCCH_CS_SET_ACK(0, 1, 1, 0, 1); + PUCCH_CS_SET_ACK(0, 1, 0, 0); + PUCCH_CS_SET_ACK(0, 0, 1, 1); + PUCCH_CS_SET_ACK(0, 0, 0, SRSLTE_PUCCH_CS_MAX_ACK); + + return ret; +} + +int srslte_pucch_cs_get_ack(srslte_pucch_cfg_t* cfg, + srslte_uci_cfg_t* uci_cfg, + uint32_t j, + uint8_t b[2], + srslte_uci_value_t* uci_value) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (cfg && uci_cfg && uci_value) { + // Set bits to 0 by default + memset(uci_value->ack.ack_value, 0, SRSLTE_UCI_MAX_ACK_BITS); + uci_value->ack.valid = true; + + uint32_t nof_ack = srslte_uci_cfg_total_ack(uci_cfg); + switch (nof_ack) { + case 2: + // A = 2 + ret = puccch_cs_get_ack_a2(j, b, uci_value->ack.ack_value); + break; + case 3: + // A = 3 + ret = puccch_cs_get_ack_a3(j, b, uci_value->ack.ack_value); + break; + case 4: + // A = 4 + ret = puccch_cs_get_ack_a4(j, b, uci_value->ack.ack_value); + break; + default: + // Unhandled case + ERROR("Unexpected number of ACK (%d)\n", nof_ack); + ret = SRSLTE_ERROR; + } + } + + return ret; +} + +int srslte_pucch_pucch3_resources(const srslte_pucch_cfg_t* cfg, const srslte_uci_cfg_t* uci_cfg, uint32_t n_pucch_i[4]) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (cfg && uci_cfg && n_pucch_i) { + n_pucch_i[0] = cfg->n3_pucch_an_list[uci_cfg->ack[0].tpc_for_pucch % SRSLTE_PUCCH_SIZE_AN_CS]; + ret = 1; + } + + return ret; +} diff --git a/lib/src/phy/phch/uci.c b/lib/src/phy/phch/uci.c index 54d9f2d09..c60789dfc 100644 --- a/lib/src/phy/phch/uci.c +++ b/lib/src/phy/phch/uci.c @@ -771,9 +771,10 @@ int srslte_uci_data_info(srslte_uci_cfg_t* uci_cfg, srslte_uci_value_t* uci_data n = srslte_print_check(str, str_len, n, ", sr=%s", uci_data->scheduling_request ? "yes" : "no"); } - if (srslte_uci_cfg_total_ack(uci_cfg)) { + uint32_t nof_acks = srslte_uci_cfg_total_ack(uci_cfg); + if (nof_acks) { n = srslte_print_check(str, str_len, n, ", ack="); - for (uint32_t i = 0; i < srslte_uci_cfg_total_ack(uci_cfg); i++) { + for (uint32_t i = 0; i < nof_acks; i++) { n = srslte_print_check(str, str_len, n, "%d", uci_data->ack.ack_value[i]); } if (uci_cfg->ack[0].N_bundle) { diff --git a/lib/src/phy/ue/ue_ul.c b/lib/src/phy/ue/ue_ul.c index 2b36b2d3a..c0800b37b 100644 --- a/lib/src/phy/ue/ue_ul.c +++ b/lib/src/phy/ue/ue_ul.c @@ -474,58 +474,6 @@ float srs_power(srslte_ue_ul_t* q, srslte_ue_ul_cfg_t* cfg, float PL) return p_srs; } -/* Choose PUCCH format based on pending transmission as described in 10.1 of 36.213 */ -static srslte_pucch_format_t -get_format(srslte_pucch_cfg_t* cfg, srslte_uci_cfg_t* uci_cfg, srslte_uci_value_t* uci_value, srslte_cp_t cp) -{ - srslte_pucch_format_t format = SRSLTE_PUCCH_FORMAT_ERROR; - // No CQI data - if (!uci_cfg->cqi.data_enable && uci_cfg->cqi.ri_len == 0) { - // PUCCH Format 3 condition specified in: - // 3GPP 36.213 10.1.2.2.2 PUCCH format 3 HARQ-ACK procedure - if (cfg->ack_nack_feedback_mode == SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_PUCCH3 && - srslte_uci_cfg_total_ack(uci_cfg) > 1) { - format = SRSLTE_PUCCH_FORMAT_3; - } - // 1-bit ACK + optional SR - else if (srslte_uci_cfg_total_ack(uci_cfg) == 1) { - format = SRSLTE_PUCCH_FORMAT_1A; - } - // 2-bit ACK + optional SR - else if (srslte_uci_cfg_total_ack(uci_cfg) >= 2 && srslte_uci_cfg_total_ack(uci_cfg) <= 4) { - format = SRSLTE_PUCCH_FORMAT_1B; // with channel selection if > 2 - } - // If UCI value is provided, use SR signal only, otherwise SR request opportunity - else if (uci_cfg->is_scheduling_request_tti || (uci_value && uci_value->scheduling_request)) { - format = SRSLTE_PUCCH_FORMAT_1; - } else { - fprintf(stderr, - "Error selecting PUCCH format: Unsupported number of ACK bits %d\n", - srslte_uci_cfg_total_ack(uci_cfg)); - } - } - // CQI data - else { - // CQI and no ack - if (srslte_uci_cfg_total_ack(uci_cfg) == 0) { - format = SRSLTE_PUCCH_FORMAT_2; - } - // CQI + 1-bit ACK - else if (srslte_uci_cfg_total_ack(uci_cfg) == 1 && SRSLTE_CP_ISNORM(cp)) { - format = SRSLTE_PUCCH_FORMAT_2A; - } - // CQI + 2-bit ACK - else if (srslte_uci_cfg_total_ack(uci_cfg) == 2) { - format = SRSLTE_PUCCH_FORMAT_2B; - } - // CQI + 2-bit ACK + cyclic prefix - else if (srslte_uci_cfg_total_ack(uci_cfg) == 1 && SRSLTE_CP_ISEXT(cp)) { - format = SRSLTE_PUCCH_FORMAT_2B; - } - } - return format; -} - // Selection of n_pucch for PUCCH Format 1a and 1b with channel selection for 1 and 2 CC static uint32_t get_npucch_cs(srslte_pucch_cfg_t* cfg, srslte_uci_cfg_t* uci_cfg, srslte_uci_value_t* uci_value) { @@ -533,31 +481,14 @@ static uint32_t get_npucch_cs(srslte_pucch_cfg_t* cfg, srslte_uci_cfg_t* uci_cfg uint8_t* b = uci_value->ack.ack_value; uint32_t n_pucch_i[4] = {}; - // Determine the 4 PUCCH resources n_pucch_j associated with HARQ-ACK(j) - uint32_t k = 0; - for (int i = 0; i < 2; i++) { - // If grant has been scheduled in PCell - if (uci_cfg->ack[i].grant_cc_idx == 0) { - for (uint32_t j = 0; j < uci_cfg->ack[i].nof_acks; j++) { - if (k < 4) { - n_pucch_i[k++] = uci_cfg->ack[i].ncce[0] + cfg->N_pucch_1 + j; - } else { - fprintf(stderr, "get_npucch_cs(): Too many ack bits\n"); - } - } - } else { - for (uint32_t j = 0; j < uci_cfg->ack[i].nof_acks; j++) { - if (k < 4) { - n_pucch_i[k++] = cfg->n1_pucch_an_cs[uci_cfg->ack[i].tpc_for_pucch % 4][j]; - } else { - fprintf(stderr, "get_npucch_cs(): Too many ack bits\n"); - } - } - } + if (srslte_pucch_cs_resources(cfg, uci_cfg, n_pucch_i) < SRSLTE_SUCCESS) { + ERROR("Selecting n_pucch_i resources\n"); + return 0; } // Do resource selection and bit mapping according to tables 10.1.2.2.1-3, 10.1.2.2.1-4 and 10.1.2.2.1-5 - switch (srslte_uci_cfg_total_ack(uci_cfg)) { + uint32_t nof_ack = srslte_uci_cfg_total_ack(uci_cfg); + switch (nof_ack) { case 1: // 1-bit is Format1A always n_pucch = n_pucch_i[0]; @@ -631,10 +562,12 @@ static uint32_t get_npucch_cs(srslte_pucch_cfg_t* cfg, srslte_uci_cfg_t* uci_cfg b[1] = (uint8_t)((b[3] != 1 ? 0 : 1) & ((b[1] != 1 ? 0 : 1) ^ (b[2] != 1 ? 0 : 1))); } else { /* n_pucch1_3 */ - b[0] = (uint8_t)((b[1] != 1 ? 0 : 1) & (b[0] != 1 ? 1 : 0)); - b[1] = (uint8_t)((b[3] != 1 ? 0 : 1) & ((b[1] != 1 ? 0 : 1) ^ (b[2] != 1 ? 0 : 1))); + b[0] = (uint8_t)(b[2] != 1 ? 0 : 1); + b[1] = (uint8_t)(((b[3] == 1) && ((b[1] == 1) != (b[2] == 1))) ? 1 : 0); } break; + default: + ERROR("Too many (%d) ACK for this CS mode\n", srslte_uci_cfg_total_ack(uci_cfg)); } return n_pucch; @@ -891,7 +824,13 @@ void srslte_ue_ul_pucch_resource_selection(srslte_cell_t* cell, uci_cfg->cqi.data_enable = false; } - cfg->format = get_format(cfg, uci_cfg, uci_value, cell->cp); + // Assume that if a scheduling request is carried, it is the right TTI + if (uci_value) { + uci_cfg->is_scheduling_request_tti |= uci_value->scheduling_request; + } + + // Get PUCCH Resources + cfg->format = srslte_pucch_select_format(cfg, uci_cfg, cell->cp); cfg->n_pucch = get_npucch(cfg, uci_cfg, uci_value, cell); if (uci_value) { @@ -925,7 +864,7 @@ pucch_encode(srslte_ue_ul_t* q, srslte_ul_sf_cfg_t* sf, srslte_ue_ul_cfg_t* cfg, int ret = SRSLTE_ERROR_INVALID_INPUTS; if (q != NULL && cfg != NULL) { - + srslte_uci_value_t uci_value2 = *uci_data; ret = SRSLTE_ERROR; if (!srslte_pucch_cfg_isvalid(&cfg->ul_cfg.pucch, q->cell.nof_prb)) { @@ -936,11 +875,11 @@ pucch_encode(srslte_ue_ul_t* q, srslte_ul_sf_cfg_t* sf, srslte_ue_ul_cfg_t* cfg, bzero(q->sf_symbols, sizeof(cf_t) * SRSLTE_NOF_RE(q->cell)); // Prepare configuration - srslte_ue_ul_pucch_resource_selection(&q->cell, &cfg->ul_cfg.pucch, &cfg->ul_cfg.pucch.uci_cfg, uci_data); + srslte_ue_ul_pucch_resource_selection(&q->cell, &cfg->ul_cfg.pucch, &cfg->ul_cfg.pucch.uci_cfg, &uci_value2); srslte_refsignal_srs_pucch_shortened(&q->signals, sf, &cfg->ul_cfg.srs, &cfg->ul_cfg.pucch); - if (srslte_pucch_encode(&q->pucch, sf, &cfg->ul_cfg.pucch, uci_data, q->sf_symbols)) { + if (srslte_pucch_encode(&q->pucch, sf, &cfg->ul_cfg.pucch, &uci_value2, q->sf_symbols)) { ERROR("Error encoding TB\n"); return ret; } @@ -958,6 +897,10 @@ pucch_encode(srslte_ue_ul_t* q, srslte_ul_sf_cfg_t* sf, srslte_ue_ul_cfg_t* cfg, apply_cfo(q, cfg); apply_norm(q, cfg, (float)q->cell.nof_prb / 15 / 10); + char txt[256]; + srslte_pucch_tx_info(&cfg->ul_cfg.pucch, uci_data, txt, sizeof(txt)); + INFO("[PUCCH] Encoded %s\n", txt); + ret = SRSLTE_SUCCESS; } diff --git a/lib/test/phy/CMakeLists.txt b/lib/test/phy/CMakeLists.txt index 19f98780f..14c615101 100644 --- a/lib/test/phy/CMakeLists.txt +++ b/lib/test/phy/CMakeLists.txt @@ -65,3 +65,8 @@ foreach (cell_n_prb 6 15 25 50 75 100) endforeach (ue_dl_tm) endforeach (allow_256 0 1) endforeach (cell_n_prb) + +add_executable(pucch_cs_test pucch_cs_test.c) +target_link_libraries(pucch_cs_test srslte_phy srslte_common srslte_phy ${SEC_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +add_test(pucch_cs_test pucch_cs_test) + diff --git a/lib/test/phy/pucch_cs_test.c b/lib/test/phy/pucch_cs_test.c new file mode 100644 index 000000000..305936a6b --- /dev/null +++ b/lib/test/phy/pucch_cs_test.c @@ -0,0 +1,166 @@ +/* + * Copyright 2013-2019 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 +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + +static int test_pucch_cs(uint32_t nof_prb, uint32_t nof_tb[SRSLTE_MAX_CARRIERS], uint16_t nof_carriers) +{ + srslte_pucch_cfg_t pucch_cfg = {}; + uint16_t rnti = 0x1234; + srslte_cell_t cell = { + nof_prb, // nof_prb + 1, // nof_ports + 1, // cell_id + SRSLTE_CP_NORM, // cyclic prefix + SRSLTE_PHICH_NORM, // PHICH length + SRSLTE_PHICH_R_1_6, // PHICH resources + SRSLTE_FDD, + }; + + cf_t* buffer = NULL; + srslte_refsignal_dmrs_pusch_cfg_t dmrs_pusch_cfg = {}; // Use default + srslte_ue_ul_t ue_ul = {}; + srslte_ue_ul_cfg_t ue_ul_cfg = {}; + srslte_enb_ul_t enb_ul = {}; + srslte_ul_sf_cfg_t ul_sf = {}; + srslte_pucch_res_t pucch_res = {}; + srslte_pusch_data_t pusch_data = {}; + + // Basic default args + pucch_cfg.delta_pucch_shift = 1; // 1, 2, 3 + pucch_cfg.n_rb_2 = 1; // 0, 1, ..., 98 + pucch_cfg.N_cs = 1; // 0, 1, ..., 7 + pucch_cfg.N_pucch_1 = 1; // 0, 1, ..., 2047 + pucch_cfg.ack_nack_feedback_mode = SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_CS; // Normal, CS, PUCCH3 + + // Set Channel Selection resources + for (uint32_t i = 0, k = 6; i < SRSLTE_PUCCH_SIZE_AN_CS; i++) { + for (uint32_t j = 0; j < SRSLTE_PUCCH_NOF_AN_CS; j++, k++) { + pucch_cfg.n1_pucch_an_cs[i][j] = k; + } + } + + uint32_t base_ncce = 1; + for (uint32_t i = 0; i < nof_carriers; base_ncce += nof_tb[i++]) { + pucch_cfg.uci_cfg.ack[i].grant_cc_idx = 0; // 0: PCell Scheduling; 1: SCell Scheduling + pucch_cfg.uci_cfg.ack[i].ncce[0] = base_ncce; // PDCCH Location, 0 is always fine + pucch_cfg.uci_cfg.ack[i].nof_acks = nof_tb[i]; // Number of transport blocks, 1 or 2 + } + + // Set derived parameters + pucch_cfg.rnti = rnti; + + // Init buffers + buffer = srslte_vec_cf_malloc(SRSLTE_SF_LEN_PRB(cell.nof_prb)); + TESTASSERT(buffer); + + // Init UE + TESTASSERT(!srslte_ue_ul_init(&ue_ul, buffer, cell.nof_prb)); + TESTASSERT(!srslte_ue_ul_set_cell(&ue_ul, cell)); + srslte_ue_ul_set_rnti(&ue_ul, rnti); + + // 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_add_rnti(&enb_ul, rnti)); + + // The test itself starts here + for (ul_sf.tti = 0; ul_sf.tti < (1U << (nof_carriers * 2)); ul_sf.tti++) { + + // Generate new data + pusch_data.uci.ack.valid = true; + for (uint32_t i = 0, k = 0; i < nof_carriers; i++) { + for (uint32_t j = 0; j < nof_tb[i]; j++, k++) { + pusch_data.uci.ack.ack_value[k] = (ul_sf.tti >> k) & 0x01; + } + } + + // Copy UL configuration + ue_ul_cfg.ul_cfg.pucch = pucch_cfg; + + // Generate UL Signal + TESTASSERT(srslte_ue_ul_encode(&ue_ul, &ul_sf, &ue_ul_cfg, &pusch_data) >= SRSLTE_SUCCESS); + + // Process UL signal + srslte_enb_ul_fft(&enb_ul); + + TESTASSERT(!srslte_enb_ul_get_pucch(&enb_ul, &ul_sf, &pucch_cfg, &pucch_res)); + + TESTASSERT(pucch_res.detected); + TESTASSERT(pucch_res.uci_data.ack.valid); + + // Check results + for (int i = 0, k = 0; i < nof_carriers; i++) { + for (int j = 0; j < nof_tb[i]; j++, k++) { + INFO("cc=%d; tb=%d; tx_ack=%d; rx_ack=%d;\n", + i, + j, + pusch_data.uci.ack.ack_value[k], + pucch_res.uci_data.ack.ack_value[k]); + TESTASSERT(pusch_data.uci.ack.ack_value[k] == pucch_res.uci_data.ack.ack_value[k]); + } + } + } + + // Free all + srslte_ue_ul_free(&ue_ul); + srslte_enb_ul_free(&enb_ul); + free(buffer); + + return SRSLTE_SUCCESS; +} + +int main(int argc, char** argv) +{ + // Set PHY lib verbose to INFO + srslte_verbose = SRSLTE_VERBOSE_INFO; + + uint32_t nof_tb_1[SRSLTE_MAX_CARRIERS] = {1, 1, 1, 1, 0}; + uint32_t nof_tb_2[SRSLTE_MAX_CARRIERS] = {2, 1, 1, 0, 0}; + uint32_t nof_tb_3[SRSLTE_MAX_CARRIERS] = {2, 2, 0, 0, 0}; + + TESTASSERT(!test_pucch_cs(6, nof_tb_1, 2)); + TESTASSERT(!test_pucch_cs(6, nof_tb_1, 3)); + TESTASSERT(!test_pucch_cs(6, nof_tb_1, 4)); + TESTASSERT(!test_pucch_cs(6, nof_tb_2, 3)); + TESTASSERT(!test_pucch_cs(6, nof_tb_2, 3)); + TESTASSERT(!test_pucch_cs(6, nof_tb_3, 2)); + + TESTASSERT(!test_pucch_cs(100, nof_tb_1, 2)); + TESTASSERT(!test_pucch_cs(100, nof_tb_1, 3)); + TESTASSERT(!test_pucch_cs(100, nof_tb_1, 4)); + TESTASSERT(!test_pucch_cs(100, nof_tb_2, 3)); + TESTASSERT(!test_pucch_cs(100, nof_tb_2, 3)); + TESTASSERT(!test_pucch_cs(100, nof_tb_3, 2)); + + printf("Ok\n"); + + return SRSLTE_SUCCESS; +} diff --git a/srsenb/hdr/stack/mac/scheduler_ue.h b/srsenb/hdr/stack/mac/scheduler_ue.h index 966918ca5..6944679a0 100644 --- a/srsenb/hdr/stack/mac/scheduler_ue.h +++ b/srsenb/hdr/stack/mac/scheduler_ue.h @@ -200,7 +200,6 @@ public: bool needs_cqi(uint32_t tti, uint32_t cc_idx, bool will_send = false); uint32_t get_max_retx(); - bool get_pucch_sched(uint32_t current_tti, uint32_t cc_idx, uint32_t prb_idx[2]); bool pucch_sr_collision(uint32_t current_tti, uint32_t n_cce); static int cqi_to_tbs(uint32_t cqi, diff --git a/srsenb/src/stack/mac/scheduler_ue.cc b/srsenb/src/stack/mac/scheduler_ue.cc index 71a24996c..7b51c3d7e 100644 --- a/srsenb/src/stack/mac/scheduler_ue.cc +++ b/srsenb/src/stack/mac/scheduler_ue.cc @@ -229,54 +229,6 @@ bool sched_ue::pucch_sr_collision(uint32_t current_tti, uint32_t n_cce) return false; } -bool sched_ue::get_pucch_sched(uint32_t current_tti, uint32_t cc_idx, uint32_t prb_idx[2]) -{ - bool ret = false; - - std::lock_guard lock(mutex); - - if (phy_config_dedicated_enabled) { - - // Configure expected UCI for this TTI - ZERO_OBJECT(cfg.pucch_cfg.uci_cfg); - - // SR - cfg.pucch_cfg.uci_cfg.is_scheduling_request_tti = srslte_ue_ul_sr_send_tti(&cfg.pucch_cfg, current_tti); - - ret |= cfg.pucch_cfg.uci_cfg.is_scheduling_request_tti; - - // Pending ACKs - for (auto& h : carriers[cc_idx].dl_harq) { - if (TTI_TX(h.get_tti()) == current_tti) { - cfg.pucch_cfg.uci_cfg.ack[0].ncce[0] = h.get_n_cce(); - cfg.pucch_cfg.uci_cfg.ack[0].nof_acks = 1; - ret = true; - } - } - // Periodic CQI - if (srslte_enb_dl_gen_cqi_periodic(&cell, &cfg.dl_cfg, current_tti, 1, &cfg.pucch_cfg.uci_cfg.cqi)) { - ret = true; - } - - // Compute PRB index - if (prb_idx) { - for (int j = 0; j < 2; j++) { - prb_idx[j] = srslte_enb_ul_get_pucch_prb_idx(&cell, &cfg.pucch_cfg, j); - } - Debug("SCHED: Reserved %s PUCCH for rnti=0x%x, n_prb=%d,%d, n_pucch=%d, ncce=%d, has_sr=%d\n", - srslte_pucch_format_text(cfg.pucch_cfg.format), - rnti, - prb_idx[0], - prb_idx[1], - cfg.pucch_cfg.n_pucch, - cfg.pucch_cfg.uci_cfg.ack[0].ncce[0], - cfg.pucch_cfg.uci_cfg.is_scheduling_request_tti); - } - } - - return ret; -} - int sched_ue::set_ack_info(uint32_t tti, uint32_t cc_idx, uint32_t tb_idx, bool ack) { std::lock_guard lock(mutex);