diff --git a/lib/include/srslte/phy/common/phy_common_nr.h b/lib/include/srslte/phy/common/phy_common_nr.h index 45e4f300d..af1bcad48 100644 --- a/lib/include/srslte/phy/common/phy_common_nr.h +++ b/lib/include/srslte/phy/common/phy_common_nr.h @@ -108,6 +108,11 @@ extern "C" { */ #define SRSLTE_MAX_NOF_DL_ALLOCATION 16 +/** + * @brief Maximum dl-DataToUL-ACK value. This is defined by TS 38.331 v15.10.1 in PUCCH-Config + */ +#define SRSLTE_MAX_NOF_DL_DATA_TO_UL 8 + typedef enum SRSLTE_API { srslte_coreset_mapping_type_non_interleaved = 0, srslte_coreset_mapping_type_interleaved, @@ -210,6 +215,15 @@ typedef enum SRSLTE_API { srslte_xoverhead_18 } srslte_xoverhead_t; +/** + * @brief PDSCH HARQ ACK codebook configuration + * @remark Described in TS 38.331 V15.10.0 PhysicalCellGroupConfig + */ +typedef enum SRSLTE_API { + srslte_pdsch_harq_ack_codebook_none = 0, + srslte_pdsch_harq_ack_codebook_semi_static, + srslte_pdsch_harq_ack_codebook_dynamic, +} srslte_pdsch_harq_ack_codebook_t; /** * @brief NR carrier parameters. It is a combination of fixed cell and bandwidth-part (BWP) */ @@ -281,6 +295,8 @@ typedef struct SRSLTE_API { uint32_t id; uint32_t coreset_id; uint32_t duration; // in slots + uint32_t periodicity; + uint32_t offset; srslte_search_space_type_t type; uint32_t nof_candidates[SRSLTE_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR]; } srslte_search_space_t; diff --git a/lib/include/srslte/phy/phch/pucch_cfg_nr.h b/lib/include/srslte/phy/phch/pucch_cfg_nr.h index 577198577..30079dda3 100644 --- a/lib/include/srslte/phy/phch/pucch_cfg_nr.h +++ b/lib/include/srslte/phy/phch/pucch_cfg_nr.h @@ -67,6 +67,21 @@ */ #define SRSLTE_PUCCH_NR_MAX_CODE_RATE 7 +/** + * Maximum number of NR-PUCCH resources per set (TS 38.331 maxNrofPUCCH-ResourcesPerSet) + */ +#define SRSLTE_PUCCH_NR_MAX_NOF_RESOURCES_PER_SET 32 + +/** + * Maximum number NR-PUCCH resource sets (TS 38.331 maxNrofPUCCH-ResourceSets) + */ +#define SRSLTE_PUCCH_NR_MAX_NOF_SETS 4 + +/** + * Maximum numer of NR-PUCCH CS indexes (TS 38.213 Table 9.2.1-1: PUCCH resource sets...) + */ +#define SRSLTE_PUCCH_NR_MAX_NOF_CS_INDEXES 4 + typedef enum SRSLTE_API { SRSLTE_PUCCH_NR_FORMAT_0 = 0, SRSLTE_PUCCH_NR_FORMAT_1, @@ -126,6 +141,18 @@ typedef struct SRSLTE_API { bool additional_dmrs; ///< UE enables 2 DMRS symbols per hop of a PUCCH Format 3 or 4 } srslte_pucch_nr_resource_t; +typedef struct SRSLTE_API { + srslte_pucch_nr_resource_t resources[SRSLTE_PUCCH_NR_MAX_NOF_RESOURCES_PER_SET]; + uint32_t nof_resources; ///< Set to 0 if it is NOT provided by higher layers + uint32_t max_payload_size; ///< Maximum payload size, set to 0 if not present +} srslte_pucch_nr_resource_set_t; + +typedef struct SRSLTE_API { + srslte_pucch_nr_common_cfg_t common; ///< NR-PUCCH configuration common for all formats and resources + srslte_pucch_nr_resource_set_t sets[SRSLTE_PUCCH_NR_MAX_NOF_SETS]; ///< Resource sets, indexed by pucch-ResourceSetId + bool enabled; ///< Set to true if any set is enabled +} srslte_pucch_nr_hl_cfg_t; + /** * @brief Validates an NR-PUCCH resource configuration provided by upper layers * @param resource Resource configuration to validate diff --git a/lib/include/srslte/phy/phch/pucch_nr.h b/lib/include/srslte/phy/phch/pucch_nr.h index b7a8691ae..2c8584386 100644 --- a/lib/include/srslte/phy/phch/pucch_nr.h +++ b/lib/include/srslte/phy/phch/pucch_nr.h @@ -241,4 +241,9 @@ SRSLTE_API int srslte_pucch_nr_format_2_3_4_decode(srslte_pucch_nr_t* cf_t* slot_symbols, srslte_uci_value_nr_t* uci_value); +SRSLTE_API uint32_t srslte_pucch_nr_tx_info(const srslte_pucch_nr_resource_t* resource, + const srslte_uci_data_nr_t* uci_data, + char* str, + uint32_t str_len); + #endif // SRSLTE_PUCCH_NR_H diff --git a/lib/include/srslte/phy/phch/ra_ul_nr.h b/lib/include/srslte/phy/phch/ra_ul_nr.h index 5e882b7b3..eac5d5aae 100644 --- a/lib/include/srslte/phy/phch/ra_ul_nr.h +++ b/lib/include/srslte/phy/phch/ra_ul_nr.h @@ -99,4 +99,15 @@ SRSLTE_API int srslte_ra_ul_nr_freq(const srslte_carrier_nr_t* carrier, const srslte_dci_ul_nr_t* dci_ul, srslte_sch_grant_nr_t* grant); +/** + * @brief Selects a valid PUCCH resource for transmission + * @param pucch_cfg PUCCH configuration from upper layers + * @param uci_cfg Uplink Control information configuration (and PDCCH context) + * @param[out] resource Selected resource for transmitting PUCCH + * @return SRSLTE_SUCCESS if provided configuration is valid, SRSLTE_ERROR code otherwise + */ +SRSLTE_API int srslte_ra_ul_nr_pucch_resource(const srslte_pucch_nr_hl_cfg_t* pucch_cfg, + const srslte_uci_cfg_nr_t* uci_cfg, + srslte_pucch_nr_resource_t* resource); + #endif // SRSLTE_RA_UL_NR_H diff --git a/lib/include/srslte/phy/phch/uci_cfg_nr.h b/lib/include/srslte/phy/phch/uci_cfg_nr.h index b87a6c8a2..6d0cedb11 100644 --- a/lib/include/srslte/phy/phch/uci_cfg_nr.h +++ b/lib/include/srslte/phy/phch/uci_cfg_nr.h @@ -17,6 +17,12 @@ #include #include +/** + * @brief Maximum number of Uplink Control Bits + * @remark TS 38.212 section 5.2.1 Polar coding: The value of A is no larger than 1706. + */ +#define SRSLTE_UCI_NR_MAX_NOF_BITS 1706U + /** * @brief Maximum number of HARQ ACK feedback bits that can be carried in Uplink Control Information (UCI) message */ @@ -43,12 +49,15 @@ * @brief Uplink Control Information (UCI) message configuration */ typedef struct SRSLTE_API { - uint32_t o_ack; ///< Number of HARQ-ACK bits - uint32_t o_sr; ///< Number of SR bits - uint32_t o_csi1; ///< Number of CSI1 report number of bits - uint32_t o_csi2; ///< Number of CSI2 report number of bits - srslte_mod_t modulation; ///< Modulation (PUSCH only) - uint16_t rnti; ///< RNTI + uint32_t o_ack; ///< Number of HARQ-ACK bits + uint32_t o_sr; ///< Number of SR bits + uint32_t o_csi1; ///< Number of CSI1 report number of bits + uint32_t o_csi2; ///< Number of CSI2 report number of bits + srslte_mod_t modulation; ///< Modulation (PUSCH only) + uint16_t rnti; ///< RNTI + uint32_t pucch_resource_id; ///< PUCCH resource indicator field in the DCI format 1_0 or DCI format 1_1 + uint32_t n_cce_0; ///< index of a first CCE for the PDCCH reception + uint32_t N_cce; ///< number of CCEs in a CORESET of a PDCCH reception with DCI format 1_0 or DCI format 1_1 } srslte_uci_cfg_nr_t; /** @@ -62,4 +71,12 @@ typedef struct SRSLTE_API { bool valid; ///< Indicates whether the message has been decoded successfully, ignored in the transmitter } srslte_uci_value_nr_t; +/** + * @brief Uplink Control Information (UCI) data (configuration + values) + */ +typedef struct SRSLTE_API { + srslte_uci_cfg_nr_t cfg; + srslte_uci_value_nr_t value; +} srslte_uci_data_nr_t; + #endif // SRSLTE_UCI_CFG_NR_H diff --git a/lib/include/srslte/phy/phch/uci_nr.h b/lib/include/srslte/phy/phch/uci_nr.h index df5d68e8f..78ae55554 100644 --- a/lib/include/srslte/phy/phch/uci_nr.h +++ b/lib/include/srslte/phy/phch/uci_nr.h @@ -114,4 +114,15 @@ SRSLTE_API int srslte_uci_nr_decode_pucch(srslte_uci_nr_t* q, int8_t* llr, srslte_uci_value_nr_t* value); +SRSLTE_API uint32_t srslte_uci_nr_total_bits(const srslte_uci_cfg_nr_t* uci_cfg); + +/** + * @brief Converts to string an UCI data structure + * @param uci_data UCO data structure + * @param str Destination string + * @param str_len String length + * @return Resultant string length + */ +SRSLTE_API uint32_t srslte_uci_nr_info(const srslte_uci_data_nr_t* uci_data, char* str, uint32_t str_len); + #endif // SRSLTE_UCI_NR_H diff --git a/lib/include/srslte/phy/ue/ue_dl_nr.h b/lib/include/srslte/phy/ue/ue_dl_nr.h index 9e763941d..dcfc568b3 100644 --- a/lib/include/srslte/phy/ue/ue_dl_nr.h +++ b/lib/include/srslte/phy/ue/ue_dl_nr.h @@ -19,6 +19,7 @@ #include "srslte/phy/phch/dci_nr.h" #include "srslte/phy/phch/pdcch_nr.h" #include "srslte/phy/phch/pdsch_nr.h" +#include /** * Maximum number of CORESET @@ -59,13 +60,16 @@ typedef struct SRSLTE_API { } srslte_ue_dl_nr_pdcch_cfg_t; typedef struct { - uint32_t v_dai_dl; - bool dci_format_1_1; + uint32_t scell_idx; ///< Serving cell index + uint32_t v_dai_dl; ///< Downlink Assigment Index + bool dci_format_1_1; ///< Set to true if the PDSCH transmission is triggered by a PDCCH DCI format 1_1 transmission + uint32_t k1; ///< HARQ feedback timing + uint16_t rnti; + uint32_t pucch_resource_id; } srslte_pdsch_ack_resource_nr_t; typedef struct { srslte_pdsch_ack_resource_nr_t resource; - uint32_t k; uint8_t value[SRSLTE_MAX_CODEWORDS]; // 0/1 or 2 for DTX bool present; // set to true if there is a PDSCH on serving cell c associated with PDCCH in PDCCH monitoring occasion // m, or there is a PDCCH indicating SPS PDSCH release on serving cell c @@ -89,10 +93,13 @@ typedef struct { } srslte_pdsch_ack_nr_t; typedef struct SRSLTE_API { - bool harq_ack_spatial_bundling_pucch; ///< Param harq-ACK-SpatialBundlingPUCCH, set to true if provided - bool harq_ack_spatial_bundling_pusch; ///< Param harq-ACK-SpatialBundlingPUSCH, set to true if provided - bool pdsch_harq_ack_codebook_semi_static; ///< set to true for pdsch-HARQ-ACK-Codebook is set to semi-static + bool harq_ack_spatial_bundling_pucch; ///< Param harq-ACK-SpatialBundlingPUCCH, set to true if provided + bool harq_ack_spatial_bundling_pusch; ///< Param harq-ACK-SpatialBundlingPUSCH, set to true if provided + srslte_pdsch_harq_ack_codebook_t pdsch_harq_ack_codebook; ///< pdsch-HARQ-ACK-Codebook configuration bool max_cw_sched_dci_is_2; ///< Param maxNrofCodeWordsScheduledByDCI, set to true if present and equal to 2 + + uint32_t dl_data_to_ul_ack[SRSLTE_MAX_NOF_DL_DATA_TO_UL]; + uint32_t nof_dl_data_to_ul_ack; } srslte_ue_dl_nr_harq_ack_cfg_t; typedef struct SRSLTE_API { @@ -153,8 +160,12 @@ SRSLTE_API int srslte_ue_dl_nr_pdsch_info(const srslte_ue_dl_nr_t* q, char* str, uint32_t str_len); +SRSLTE_API int srslte_ue_dl_nr_pdsch_ack_resource(const srslte_ue_dl_nr_harq_ack_cfg_t* cfg, + const srslte_dci_dl_nr_t* dci_dl, + srslte_pdsch_ack_resource_nr_t* pdsch_ack_resource); + SRSLTE_API int srslte_ue_dl_nr_gen_ack(const srslte_ue_dl_nr_harq_ack_cfg_t* cfg, const srslte_pdsch_ack_nr_t* ack_info, - uint8_t* uci_data); + srslte_uci_data_nr_t* uci_data); #endif // SRSLTE_UE_DL_NR_H diff --git a/lib/include/srslte/phy/ue/ue_ul_nr.h b/lib/include/srslte/phy/ue/ue_ul_nr.h index 60676eba6..d5f592ed5 100644 --- a/lib/include/srslte/phy/ue/ue_ul_nr.h +++ b/lib/include/srslte/phy/ue/ue_ul_nr.h @@ -27,10 +27,13 @@ #include "srslte/phy/common/phy_common_nr.h" #include "srslte/phy/dft/ofdm.h" #include "srslte/phy/phch/phch_cfg_nr.h" +#include "srslte/phy/phch/pucch_cfg_nr.h" +#include "srslte/phy/phch/pucch_nr.h" #include "srslte/phy/phch/pusch_nr.h" typedef struct SRSLTE_API { srslte_pusch_nr_args_t pusch; + srslte_pucch_nr_args_t pucch; uint32_t nof_max_prb; } srslte_ue_ul_nr_args_t; @@ -43,8 +46,8 @@ typedef struct SRSLTE_API { cf_t* sf_symbols[SRSLTE_MAX_PORTS]; srslte_pusch_nr_t pusch; + srslte_pucch_nr_t pucch; srslte_dmrs_sch_t dmrs; - } srslte_ue_ul_nr_t; SRSLTE_API int srslte_ue_ul_nr_init(srslte_ue_ul_nr_t* q, cf_t* output, const srslte_ue_ul_nr_args_t* args); @@ -56,9 +59,20 @@ SRSLTE_API int srslte_ue_ul_nr_encode_pusch(srslte_ue_ul_nr_t* q, const srslte_sch_cfg_nr_t* pusch_cfg, uint8_t* data_); +SRSLTE_API int srslte_ue_ul_nr_encode_pucch(srslte_ue_ul_nr_t* q, + const srslte_slot_cfg_t* slot_cfg, + const srslte_pucch_nr_common_cfg_t* cfg, + const srslte_pucch_nr_resource_t* resource, + const srslte_uci_data_nr_t* uci_data); + SRSLTE_API void srslte_ue_ul_nr_free(srslte_ue_ul_nr_t* q); SRSLTE_API int srslte_ue_ul_nr_pusch_info(const srslte_ue_ul_nr_t* q, const srslte_sch_cfg_nr_t* cfg, char* str, uint32_t str_len); +SRSLTE_API int srslte_ue_ul_nr_pucch_info(const srslte_pucch_nr_resource_t* resource, + const srslte_uci_data_nr_t* uci_data, + char* str, + uint32_t str_len); + #endif // SRSLTE_UE_UL_DATA_H diff --git a/lib/include/srslte/srslte.h b/lib/include/srslte/srslte.h index ecfeb8e57..3f05b6195 100644 --- a/lib/include/srslte/srslte.h +++ b/lib/include/srslte/srslte.h @@ -98,9 +98,11 @@ extern "C" { #include "srslte/phy/phch/ra_dl_nr.h" #include "srslte/phy/phch/ra_nr.h" #include "srslte/phy/phch/ra_ul.h" +#include "srslte/phy/phch/ra_ul_nr.h" #include "srslte/phy/phch/regs.h" #include "srslte/phy/phch/sch.h" #include "srslte/phy/phch/uci.h" +#include "srslte/phy/phch/uci_nr.h" #include "srslte/phy/ue/ue_cell_search.h" #include "srslte/phy/ue/ue_dl.h" diff --git a/lib/src/phy/phch/pucch_nr.c b/lib/src/phy/phch/pucch_nr.c index 81c1aa987..95c7dba90 100644 --- a/lib/src/phy/phch/pucch_nr.c +++ b/lib/src/phy/phch/pucch_nr.c @@ -707,4 +707,58 @@ int srslte_pucch_nr_format_2_3_4_decode(srslte_pucch_nr_t* q, } return SRSLTE_SUCCESS; -} \ No newline at end of file +} + +static uint32_t pucch_nr_resource_info(const srslte_pucch_nr_resource_t* r, char* str, uint32_t str_len) +{ + uint32_t len = 0; + + uint32_t nof_prb = 1; + if (r->format == SRSLTE_PUCCH_NR_FORMAT_2 || r->format == SRSLTE_PUCCH_NR_FORMAT_3) { + nof_prb = r->nof_prb; + } + + len = srslte_print_check(str, + str_len, + len, + "f=%d, prb=%d:%d, symb=%d:%d", + (int)r->format, + r->starting_prb, + nof_prb, + r->start_symbol_idx, + r->nof_symbols); + + if (r->intra_slot_hopping) { + len = srslte_print_check(str, str_len, len, ", hop=%d", r->second_hop_prb); + } + + if (r->format == SRSLTE_PUCCH_NR_FORMAT_0 || r->format == SRSLTE_PUCCH_NR_FORMAT_1) { + len = srslte_print_check(str, str_len, len, ", cs=%d", r->initial_cyclic_shift); + } + + if (r->format == SRSLTE_PUCCH_NR_FORMAT_1) { + len = srslte_print_check(str, str_len, len, ", occ=%d", r->time_domain_occ); + } + + if (r->format == SRSLTE_PUCCH_NR_FORMAT_4) { + len = srslte_print_check(str, str_len, len, ", occ=%d:%d", r->occ_index, r->occ_lenth); + } + + return len; +} + +uint32_t srslte_pucch_nr_tx_info(const srslte_pucch_nr_resource_t* resource, + const srslte_uci_data_nr_t* uci_data, + char* str, + uint32_t str_len) +{ + uint32_t len = 0; + + len += pucch_nr_resource_info(resource, &str[len], str_len - len); + + len = srslte_print_check(str, str_len, len, ", "); + + len += srslte_uci_nr_info(uci_data, &str[len], str_len - len); + + return len; +} diff --git a/lib/src/phy/phch/ra_ul_nr.c b/lib/src/phy/phch/ra_ul_nr.c index df06e34be..fad5f0272 100644 --- a/lib/src/phy/phch/ra_ul_nr.c +++ b/lib/src/phy/phch/ra_ul_nr.c @@ -395,3 +395,69 @@ int srslte_ra_ul_nr_freq(const srslte_carrier_nr_t* carrier, ERROR("Only DCI Format 0_0 is supported"); return SRSLTE_ERROR; } + +// Implements TS 38.213 Table 9.2.1-1: PUCCH resource sets before dedicated PUCCH resource configuration +static int ra_ul_nr_pucch_resource_default(uint32_t r_pucch, srslte_pucch_nr_resource_t* resource) +{ + ERROR("Not implemented"); + return SRSLTE_ERROR; +} + +static int ra_ul_nr_pucch_resource_hl(const srslte_pucch_nr_hl_cfg_t* cfg, + uint32_t O_uci, + uint32_t pucch_resource_id, + srslte_pucch_nr_resource_t* resource) +{ + uint32_t N2 = cfg->sets[1].max_payload_size > 0 ? cfg->sets[1].max_payload_size : SRSLTE_UCI_NR_MAX_NOF_BITS; + uint32_t N3 = cfg->sets[2].max_payload_size > 0 ? cfg->sets[2].max_payload_size : SRSLTE_UCI_NR_MAX_NOF_BITS; + + // If the UE transmits O UCI UCI information bits, that include HARQ-ACK information bits, the UE determines a PUCCH + // resource set to be... + uint32_t resource_set_id = 3; + if (O_uci <= 2 && cfg->sets[0].nof_resources > 0) { + resource_set_id = 0; + } else if (O_uci <= N2 && cfg->sets[1].nof_resources > 0) { + resource_set_id = 1; + } else if (O_uci <= N3 && cfg->sets[2].nof_resources > 0) { + resource_set_id = 2; + } else if (cfg->sets[3].nof_resources == 0) { + ERROR("Invalid PUCCH resource configuration, N3=%d, O_uci=%d", N3, O_uci); + return SRSLTE_ERROR; + } else if (O_uci > SRSLTE_UCI_NR_MAX_NOF_BITS) { + ERROR("The number of UCI bits (%d), exceeds the maximum (%d)", O_uci, SRSLTE_UCI_NR_MAX_NOF_BITS); + return SRSLTE_ERROR; + } + + // Select resource from the set + if (pucch_resource_id >= SRSLTE_PUCCH_NR_MAX_NOF_RESOURCES_PER_SET || + pucch_resource_id >= cfg->sets[resource_set_id].nof_resources) { + ERROR("The PUCCH resource identifier (%d) exceeds the number of configured resources (%d) for set identifier %d", + pucch_resource_id, + cfg->sets[resource_set_id].nof_resources, + resource_set_id); + return SRSLTE_ERROR; + } + *resource = cfg->sets[resource_set_id].resources[pucch_resource_id]; + + return SRSLTE_SUCCESS; +} + +int srslte_ra_ul_nr_pucch_resource(const srslte_pucch_nr_hl_cfg_t* pucch_cfg, + const srslte_uci_cfg_nr_t* uci_cfg, + srslte_pucch_nr_resource_t* resource) +{ + if (pucch_cfg == NULL || uci_cfg == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + uint32_t O_uci = srslte_uci_nr_total_bits(uci_cfg); + + // If a UE does not have dedicated PUCCH resource configuration, provided by PUCCH-ResourceSet in PUCCH-Config, + // a PUCCH resource set is provided by pucch-ResourceCommon through an index to a row of Table 9.2.1-1 for size + // transmission of HARQ-ACK information on PUCCH in an initial UL BWP of N BWP PRBs. + if (!pucch_cfg->enabled) { + uint32_t r_pucch = (2 * uci_cfg->n_cce_0) + 2 * uci_cfg->pucch_resource_id; + return ra_ul_nr_pucch_resource_default(r_pucch, resource); + } + return ra_ul_nr_pucch_resource_hl(pucch_cfg, O_uci, uci_cfg->pucch_resource_id, resource); +} \ No newline at end of file diff --git a/lib/src/phy/phch/sch_nr.c b/lib/src/phy/phch/sch_nr.c index e07ee205e..3163fcbfa 100644 --- a/lib/src/phy/phch/sch_nr.c +++ b/lib/src/phy/phch/sch_nr.c @@ -706,10 +706,10 @@ int srslte_sch_nr_tb_info(const srslte_sch_tb_t* tb, char* str, uint32_t str_len len += srslte_print_check(str, str_len, len, - "tb={mod=%s,Nl=%d,TBS=%d,R=%.3f,rv=%d,Nre=%d,Nbit=%d,cw=%d}", + "tb={mod=%s,Nl=%d,tbs=%d,R=%.3f,rv=%d,Nre=%d,Nbit=%d,cw=%d}", srslte_mod_string(tb->mod), tb->N_L, - tb->tbs, + tb->tbs / 8, tb->R, tb->rv, tb->nof_re, diff --git a/lib/src/phy/phch/uci_nr.c b/lib/src/phy/phch/uci_nr.c index dfaae96dc..a374b77ae 100644 --- a/lib/src/phy/phch/uci_nr.c +++ b/lib/src/phy/phch/uci_nr.c @@ -20,8 +20,6 @@ #define UCI_NR_INFO_TX(...) INFO("UCI-NR Tx: " __VA_ARGS__) #define UCI_NR_INFO_RX(...) INFO("UCI-NR Rx: " __VA_ARGS__) -// TS 38.212 section 5.2.1 Polar coding: The value of A is no larger than 1706. -#define UCI_NR_MAX_A 1706U #define UCI_NR_MAX_L 11U #define UCI_NR_POLAR_MAX 2048U #define UCI_NR_POLAR_RM_IBIL 0 @@ -84,14 +82,14 @@ int srslte_uci_nr_init(srslte_uci_nr_t* q, const srslte_uci_nr_args_t* args) } // Allocate bit sequence with space for the CRC - q->bit_sequence = srslte_vec_u8_malloc(UCI_NR_MAX_A); + q->bit_sequence = srslte_vec_u8_malloc(SRSLTE_UCI_NR_MAX_NOF_BITS); if (q->bit_sequence == NULL) { ERROR("Error malloc"); return SRSLTE_ERROR; } // Allocate c with space for a and the CRC - q->c = srslte_vec_u8_malloc(UCI_NR_MAX_A + UCI_NR_MAX_L); + q->c = srslte_vec_u8_malloc(SRSLTE_UCI_NR_MAX_NOF_BITS + UCI_NR_MAX_L); if (q->c == NULL) { ERROR("Error malloc"); return SRSLTE_ERROR; @@ -656,7 +654,7 @@ static int uci_nr_encode(srslte_uci_nr_t* q, } // Encoding of other sizes up to 1906 - if (A < UCI_NR_MAX_A) { + if (A < SRSLTE_UCI_NR_MAX_NOF_BITS) { return uci_nr_encode_11_1706_bit(q, uci_cfg, A, o, E_uci); } @@ -689,7 +687,7 @@ static int uci_nr_decode(srslte_uci_nr_t* q, if (uci_nr_decode_3_11_bit(q, uci_cfg, A, llr, E_uci, &uci_value->valid) < SRSLTE_SUCCESS) { return SRSLTE_ERROR; } - } else if (A < UCI_NR_MAX_A) { + } else if (A < SRSLTE_UCI_NR_MAX_NOF_BITS) { if (uci_nr_decode_11_1706_bit(q, uci_cfg, A, llr, E_uci, &uci_value->valid) < SRSLTE_SUCCESS) { return SRSLTE_ERROR; } @@ -785,3 +783,45 @@ int srslte_uci_nr_decode_pucch(srslte_uci_nr_t* q, return uci_nr_decode(q, uci_cfg, llr, E_uci, value); } + +uint32_t srslte_uci_nr_total_bits(const srslte_uci_cfg_nr_t* uci_cfg) +{ + if (uci_cfg == NULL) { + return 0; + } + + return uci_cfg->o_ack + uci_cfg->o_csi1 + uci_cfg->o_csi2 + uci_cfg->o_sr; +} + +uint32_t srslte_uci_nr_info(const srslte_uci_data_nr_t* uci_data, char* str, uint32_t str_len) +{ + uint32_t len = 0; + + len = srslte_print_check(str, str_len, len, "rnti=0x%x", uci_data->cfg.rnti); + + if (uci_data->cfg.o_ack > 0) { + char str2[10]; + srslte_vec_sprint_bin(str2, 10, uci_data->value.ack, uci_data->cfg.o_ack); + len = srslte_print_check(str, str_len, len, ", ack=%s", str2); + } + + if (uci_data->cfg.o_csi1 > 0) { + char str2[10]; + srslte_vec_sprint_bin(str2, 10, uci_data->value.csi1, uci_data->cfg.o_csi1); + len = srslte_print_check(str, str_len, len, ", csi1=%s", str2); + } + + if (uci_data->cfg.o_csi2 > 0) { + char str2[10]; + srslte_vec_sprint_bin(str2, 10, uci_data->value.csi2, uci_data->cfg.o_csi2); + len = srslte_print_check(str, str_len, len, ", csi2=%s", str2); + } + + if (uci_data->cfg.o_sr > 0) { + char str2[10]; + srslte_vec_sprint_bin(str2, 10, uci_data->value.sr, uci_data->cfg.o_sr); + len = srslte_print_check(str, str_len, len, ", sr=%s", str2); + } + + return len; +} \ No newline at end of file diff --git a/lib/src/phy/ue/ue_dl_nr.c b/lib/src/phy/ue/ue_dl_nr.c index 9708401dd..3b76d4978 100644 --- a/lib/src/phy/ue/ue_dl_nr.c +++ b/lib/src/phy/ue/ue_dl_nr.c @@ -517,11 +517,13 @@ static uint32_t ue_dl_nr_V_DL_DAI(uint32_t dai) return dai + 1; } -static int -ue_dl_nr_gen_ack_type2(const srslte_ue_dl_nr_harq_ack_cfg_t* cfg, const srslte_pdsch_ack_nr_t* ack_info, uint8_t* o_ack) +static int ue_dl_nr_gen_ack_type2(const srslte_ue_dl_nr_harq_ack_cfg_t* cfg, + const srslte_pdsch_ack_nr_t* ack_info, + srslte_uci_data_nr_t* uci_data) { bool harq_ack_spatial_bundling = ack_info->use_pusch ? cfg->harq_ack_spatial_bundling_pusch : cfg->harq_ack_spatial_bundling_pucch; + uint8_t* o_ack = uci_data->value.ack; uint32_t m = 0; // PDCCH with DCI format 1_0 or DCI format 1_1 monitoring occasion index: lower index corresponds to // earlier PDCCH with DCI format 1_0 or DCI format 1_1 monitoring occasion @@ -529,11 +531,10 @@ ue_dl_nr_gen_ack_type2(const srslte_ue_dl_nr_harq_ack_cfg_t* cfg, const srslte_p uint32_t V_temp = 0; uint32_t V_temp2 = 0; - uint32_t N_DL_cells = ack_info->nof_cc; // number of serving cells configured by higher layers for the UE - uint32_t M = ack_info->cc[0].M; // Set M to the number of PDCCH monitoring occasion(s) + uint32_t N_DL_cells = ack_info->nof_cc; // number of serving cells configured by higher layers for the UE // The following code follows the exact pseudo-code provided in TS 38.213 9.1.3.1 Type-2 HARQ-ACK codebook ... - while (m < M) { + while (m < SRSLTE_UCI_NR_MAX_M) { uint32_t c = 0; // serving cell index: lower indexes correspond to lower RRC indexes of corresponding cell while (c < N_DL_cells) { // Get ACK information of serving cell c for the PDCH monitoring occasion m @@ -563,6 +564,10 @@ ue_dl_nr_gen_ack_type2(const srslte_ue_dl_nr_harq_ack_cfg_t* cfg, const srslte_p c = c + 1; } else { if (ack->present) { + // Load ACK resource data into UCI info + uci_data->cfg.pucch_resource_id = ack_info->cc[c].m[m].resource.pucch_resource_id; + uci_data->cfg.rnti = ack_info->cc[c].m[m].resource.rnti; + if (V_DL_CDAI <= V_temp) { j = j + 1; } @@ -605,20 +610,63 @@ ue_dl_nr_gen_ack_type2(const srslte_ue_dl_nr_harq_ack_cfg_t* cfg, const srslte_p // if harq-ACK-SpatialBundlingPUCCH is not provided to the UE and the UE is configured by // maxNrofCodeWordsScheduledByDCI with reception of two transport blocks for at least one configured DL BWP of a // serving cell, - uint32_t O_ack = 4 * j + V_temp2; if (!harq_ack_spatial_bundling && cfg->max_cw_sched_dci_is_2) { - O_ack = 2 * (4 * j + V_temp2); + uci_data->cfg.o_ack = 2 * (4 * j + V_temp2); + } else { + uci_data->cfg.o_ack = 4 * j + V_temp2; } // Implement here SPS PDSCH reception // ... - return (int)O_ack; + return SRSLTE_SUCCESS; +} + +int ue_dl_nr_pdsch_k1(const srslte_ue_dl_nr_harq_ack_cfg_t* cfg, const srslte_dci_dl_nr_t* dci_dl) +{ + // For DCI format 1_0, the PDSCH-to-HARQ_feedback timing indicator field values map to {1, 2, 3, 4, 5, 6, 7, 8} + if (dci_dl->format == srslte_dci_format_nr_1_0) { + return (int)dci_dl->harq_feedback + 1; + } + + // For DCI format 1_1, if present, the PDSCH-to-HARQ_feedback timing indicator field values map to values for a set of + // number of slots provided by dl-DataToUL-ACK as defined in Table 9.2.3-1. + if (dci_dl->harq_feedback >= SRSLTE_MAX_NOF_DL_DATA_TO_UL || dci_dl->harq_feedback >= cfg->nof_dl_data_to_ul_ack) { + ERROR("Out-of-range PDSCH-to-HARQ feedback index (%d, max %d)", dci_dl->harq_feedback, cfg->nof_dl_data_to_ul_ack); + return SRSLTE_ERROR; + } + + return cfg->dl_data_to_ul_ack[dci_dl->harq_feedback]; +} + +int srslte_ue_dl_nr_pdsch_ack_resource(const srslte_ue_dl_nr_harq_ack_cfg_t* cfg, + const srslte_dci_dl_nr_t* dci_dl, + srslte_pdsch_ack_resource_nr_t* pdsch_ack_resource) +{ + if (cfg == NULL || dci_dl == NULL || pdsch_ack_resource == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + // Calculate Data to UL ACK timing k1 + int k1 = ue_dl_nr_pdsch_k1(cfg, dci_dl); + if (k1 < SRSLTE_ERROR) { + ERROR("Error calculating K1"); + return SRSLTE_ERROR; + } + + // Fill PDSCH resource + pdsch_ack_resource->dci_format_1_1 = (dci_dl->format == srslte_dci_format_nr_1_1); + pdsch_ack_resource->k1 = k1; + pdsch_ack_resource->v_dai_dl = dci_dl->dai; + pdsch_ack_resource->rnti = dci_dl->rnti; + pdsch_ack_resource->pucch_resource_id = dci_dl->pucch_resource; + + return SRSLTE_SUCCESS; } int srslte_ue_dl_nr_gen_ack(const srslte_ue_dl_nr_harq_ack_cfg_t* cfg, const srslte_pdsch_ack_nr_t* ack_info, - uint8_t* uci_data) + srslte_uci_data_nr_t* uci_data) { // Check inputs if (cfg == NULL || ack_info == NULL || uci_data == NULL) { @@ -626,7 +674,7 @@ int srslte_ue_dl_nr_gen_ack(const srslte_ue_dl_nr_harq_ack_cfg_t* cfg, } // According TS 38.213 9.1.2 Type-1 HARQ-ACK codebook determination - if (cfg->pdsch_harq_ack_codebook_semi_static) { + if (cfg->pdsch_harq_ack_codebook == srslte_pdsch_harq_ack_codebook_semi_static) { // This clause applies if the UE is configured with pdsch-HARQ-ACK-Codebook = semi-static. ERROR("Type-1 HARQ-ACK codebook determination is NOT implemented"); return SRSLTE_ERROR; @@ -634,5 +682,10 @@ int srslte_ue_dl_nr_gen_ack(const srslte_ue_dl_nr_harq_ack_cfg_t* cfg, // According TS 38.213 9.1.3 Type-2 HARQ-ACK codebook determination // This clause applies if the UE is configured with pdsch-HARQ-ACK-Codebook = dynamic. - return ue_dl_nr_gen_ack_type2(cfg, ack_info, uci_data); + if (cfg->pdsch_harq_ack_codebook == srslte_pdsch_harq_ack_codebook_dynamic) { + return ue_dl_nr_gen_ack_type2(cfg, ack_info, uci_data); + } + + ERROR("No HARQ-ACK codebook determination is NOT implemented"); + return SRSLTE_ERROR; } diff --git a/lib/src/phy/ue/ue_ul_nr.c b/lib/src/phy/ue/ue_ul_nr.c index 100d3bda4..d3cfa72f3 100644 --- a/lib/src/phy/ue/ue_ul_nr.c +++ b/lib/src/phy/ue/ue_ul_nr.c @@ -51,6 +51,11 @@ int srslte_ue_ul_nr_init(srslte_ue_ul_nr_t* q, cf_t* output, const srslte_ue_ul_ return SRSLTE_ERROR; } + if (srslte_pucch_nr_init(&q->pucch, &args->pucch) < SRSLTE_SUCCESS) { + ERROR("Error UCI\n"); + return SRSLTE_ERROR; + } + return SRSLTE_SUCCESS; } @@ -126,6 +131,58 @@ int srslte_ue_ul_nr_encode_pusch(srslte_ue_ul_nr_t* q, return SRSLTE_SUCCESS; } +static int ue_ul_nr_encode_pucch_format0(srslte_ue_ul_nr_t* q, + const srslte_pucch_nr_resource_t* resource, + const srslte_uci_data_nr_t* uci_data) +{ + ERROR("Not implemented"); + return SRSLTE_ERROR; +} + +static int ue_ul_nr_encode_pucch_format1(srslte_ue_ul_nr_t* q, + const srslte_slot_cfg_t* slot, + const srslte_pucch_nr_common_cfg_t* cfg, + const srslte_pucch_nr_resource_t* resource, + const srslte_uci_data_nr_t* uci_data) +{ + uint8_t b[SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS] = {}; + b[0] = uci_data->value.ack[0]; + uint32_t nof_bits = 1; + + return srslte_pucch_nr_format1_encode(&q->pucch, &q->carrier, cfg, slot, resource, b, nof_bits, q->sf_symbols[0]); +} + +int srslte_ue_ul_nr_encode_pucch(srslte_ue_ul_nr_t* q, + const srslte_slot_cfg_t* slot_cfg, + const srslte_pucch_nr_common_cfg_t* cfg, + const srslte_pucch_nr_resource_t* resource, + const srslte_uci_data_nr_t* uci_data) +{ + // Check inputs + if (q == NULL || slot_cfg == NULL || resource == NULL || uci_data == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + // Actual PUCCH encoding + switch (resource->format) { + case SRSLTE_PUCCH_NR_FORMAT_0: + return ue_ul_nr_encode_pucch_format0(q, resource, uci_data); + case SRSLTE_PUCCH_NR_FORMAT_1: + return ue_ul_nr_encode_pucch_format1(q, slot_cfg, cfg, resource, uci_data); + case SRSLTE_PUCCH_NR_FORMAT_2: + case SRSLTE_PUCCH_NR_FORMAT_3: + case SRSLTE_PUCCH_NR_FORMAT_4: + return srslte_pucch_nr_format_2_3_4_encode( + &q->pucch, &q->carrier, cfg, slot_cfg, resource, &uci_data->cfg, &uci_data->value, q->sf_symbols[0]); + case SRSLTE_PUCCH_NR_FORMAT_ERROR: + default: + ERROR("Invalid case"); + break; + } + + return SRSLTE_ERROR; +} + void srslte_ue_ul_nr_free(srslte_ue_ul_nr_t* q) { if (q == NULL) { @@ -137,6 +194,8 @@ void srslte_ue_ul_nr_free(srslte_ue_ul_nr_t* q) } srslte_pusch_nr_free(&q->pusch); srslte_dmrs_sch_free(&q->dmrs); + + SRSLTE_MEM_ZERO(q, srslte_ue_ul_nr_t, 1); } int srslte_ue_ul_nr_pusch_info(const srslte_ue_ul_nr_t* q, const srslte_sch_cfg_nr_t* cfg, char* str, uint32_t str_len) @@ -146,5 +205,18 @@ int srslte_ue_ul_nr_pusch_info(const srslte_ue_ul_nr_t* q, const srslte_sch_cfg_ // Append PDSCH info len += srslte_pusch_nr_tx_info(&q->pusch, cfg, &cfg->grant, &str[len], str_len - len); + return len; +} + +int srslte_ue_ul_nr_pucch_info(const srslte_pucch_nr_resource_t* resource, + const srslte_uci_data_nr_t* uci_data, + char* str, + uint32_t str_len) +{ + int len = 0; + + // Append PDSCH info + len += srslte_pucch_nr_tx_info(resource, uci_data, &str[len], str_len - len); + return len; } \ No newline at end of file diff --git a/srsue/hdr/phy/nr/cc_worker.h b/srsue/hdr/phy/nr/cc_worker.h index 912439646..f785d11c1 100644 --- a/srsue/hdr/phy/nr/cc_worker.h +++ b/srsue/hdr/phy/nr/cc_worker.h @@ -54,6 +54,10 @@ private: srslte_softbuffer_tx_t softbuffer_tx = {}; srslte_softbuffer_rx_t softbuffer_rx = {}; std::vector tx_data; + + // Methods for DL... + void decode_pdcch_ul(); + void decode_pdcch_dl(); }; } // namespace nr diff --git a/srsue/hdr/phy/nr/state.h b/srsue/hdr/phy/nr/state.h index 5ef572e04..4d051796c 100644 --- a/srsue/hdr/phy/nr/state.h +++ b/srsue/hdr/phy/nr/state.h @@ -33,6 +33,7 @@ typedef struct { typedef struct { srslte_sch_hl_cfg_nr_t pdsch; srslte_sch_hl_cfg_nr_t pusch; + srslte_pucch_nr_hl_cfg_t pucch; srslte_prach_cfg_t prach; srslte_ue_dl_nr_pdcch_cfg_t pdcch; srslte_ue_dl_nr_harq_ack_cfg_t harq_ack; @@ -41,13 +42,25 @@ typedef struct { class state { private: - struct pending_grant_t { + struct pending_ul_grant_t { bool enable; uint32_t pid; srslte_sch_cfg_nr_t sch_cfg; }; - srslte::circular_array pending_ul_grant = {}; - mutable std::mutex pending_ul_grant_mutex; + srslte::circular_array pending_ul_grant = {}; + mutable std::mutex pending_ul_grant_mutex; + + struct pending_dl_grant_t { + bool enable; + uint32_t pid; + srslte_sch_cfg_nr_t sch_cfg; + srslte_pdsch_ack_resource_nr_t ack_resource; + }; + srslte::circular_array pending_dl_grant = {}; + mutable std::mutex pending_dl_grant_mutex; + + srslte::circular_array pending_ack = {}; + mutable std::mutex pending_ack_mutex; public: mac_interface_phy_nr* stack = nullptr; @@ -87,7 +100,7 @@ public: // physicalCellGroupConfig // pdsch-HARQ-ACK-Codebook: dynamic (1) - cfg.harq_ack.pdsch_harq_ack_codebook_semi_static = false; + cfg.harq_ack.pdsch_harq_ack_codebook = srslte_pdsch_harq_ack_codebook_dynamic; // commonControlResourceSet // controlResourceSetId: 1 @@ -222,6 +235,377 @@ public: // betaOffsetCSI-Part2-Index1: 6 // betaOffsetCSI-Part2-Index2: 6 // scaling: f1 (3) + + // pucch-Config: setup (1) + // setup + // resourceSetToAddModList: 2 items + cfg.pucch.enabled = true; + // Item 0 + // PUCCH-ResourceSet + // pucch-ResourceSetId: 0 + // resourceList: 8 items + // Item 0 + // PUCCH-ResourceId: 0 + // Item 1 + // PUCCH-ResourceId: 1 + // Item 2 + // PUCCH-ResourceId: 2 + // Item 3 + // PUCCH-ResourceId: 3 + // Item 4 + // PUCCH-ResourceId: 4 + // Item 5 + // PUCCH-ResourceId: 5 + // Item 6 + // PUCCH-ResourceId: 6 + // Item 7 + // PUCCH-ResourceId: 7 + cfg.pucch.sets[0].nof_resources = 8; + + // Item 1 + // PUCCH-ResourceSet + // pucch-ResourceSetId: 1 + // resourceList: 8 items + // Item 0 + // PUCCH-ResourceId: 8 + // Item 1 + // PUCCH-ResourceId: 9 + // Item 2 + // PUCCH-ResourceId: 10 + // Item 3 + // PUCCH-ResourceId: 11 + // Item 4 + // PUCCH-ResourceId: 12 + // Item 5 + // PUCCH-ResourceId: 13 + // Item 6 + // PUCCH-ResourceId: 14 + // Item 7 + // PUCCH-ResourceId: 15 + cfg.pucch.sets[1].nof_resources = 8; + + // resourceToAddModList: 18 items + // Item 0 + // PUCCH-Resource + // pucch-ResourceId: 0 + // startingPRB: 0 + // format: format1 (1) + // format1 + // initialCyclicShift: 0 + // nrofSymbols: 14 + // startingSymbolIndex: 0 + // timeDomainOCC: 0 + cfg.pucch.sets[0].resources[0].format = SRSLTE_PUCCH_NR_FORMAT_1; + cfg.pucch.sets[0].resources[0].starting_prb = 0; + cfg.pucch.sets[0].resources[0].initial_cyclic_shift = 0; + cfg.pucch.sets[0].resources[0].nof_symbols = 14; + cfg.pucch.sets[0].resources[0].start_symbol_idx = 0; + cfg.pucch.sets[0].resources[0].time_domain_occ = 0; + + // Item 1 + // PUCCH-Resource + // pucch-ResourceId: 1 + // startingPRB: 0 + // format: format1 (1) + // format1 + // initialCyclicShift: 4 + // nrofSymbols: 14 + // startingSymbolIndex: 0 + // timeDomainOCC: 0 + cfg.pucch.sets[0].resources[1].format = SRSLTE_PUCCH_NR_FORMAT_1; + cfg.pucch.sets[0].resources[1].starting_prb = 0; + cfg.pucch.sets[0].resources[1].initial_cyclic_shift = 4; + cfg.pucch.sets[0].resources[1].nof_symbols = 14; + cfg.pucch.sets[0].resources[1].start_symbol_idx = 0; + cfg.pucch.sets[0].resources[1].time_domain_occ = 0; + + // Item 2 + // PUCCH-Resource + // pucch-ResourceId: 2 + // startingPRB: 0 + // format: format1 (1) + // format1 + // initialCyclicShift: 8 + // nrofSymbols: 14 + // startingSymbolIndex: 0 + // timeDomainOCC: 0 + cfg.pucch.sets[0].resources[2].format = SRSLTE_PUCCH_NR_FORMAT_1; + cfg.pucch.sets[0].resources[2].starting_prb = 0; + cfg.pucch.sets[0].resources[2].initial_cyclic_shift = 8; + cfg.pucch.sets[0].resources[2].nof_symbols = 14; + cfg.pucch.sets[0].resources[2].start_symbol_idx = 0; + cfg.pucch.sets[0].resources[2].time_domain_occ = 0; + + // Item 3 + // PUCCH-Resource + // pucch-ResourceId: 3 + // startingPRB: 0 + // format: format1 (1) + // format1 + // initialCyclicShift: 0 + // nrofSymbols: 14 + // startingSymbolIndex: 0 + // timeDomainOCC: 1 + cfg.pucch.sets[0].resources[3].format = SRSLTE_PUCCH_NR_FORMAT_1; + cfg.pucch.sets[0].resources[3].starting_prb = 0; + cfg.pucch.sets[0].resources[3].initial_cyclic_shift = 0; + cfg.pucch.sets[0].resources[3].nof_symbols = 14; + cfg.pucch.sets[0].resources[3].start_symbol_idx = 0; + cfg.pucch.sets[0].resources[3].time_domain_occ = 1; + + // Item 4 + // PUCCH-Resource + // pucch-ResourceId: 4 + // startingPRB: 0 + // format: format1 (1) + // format1 + // initialCyclicShift: 4 + // nrofSymbols: 14 + // startingSymbolIndex: 0 + // timeDomainOCC: 1 + cfg.pucch.sets[0].resources[4].format = SRSLTE_PUCCH_NR_FORMAT_1; + cfg.pucch.sets[0].resources[4].starting_prb = 0; + cfg.pucch.sets[0].resources[4].initial_cyclic_shift = 4; + cfg.pucch.sets[0].resources[4].nof_symbols = 14; + cfg.pucch.sets[0].resources[4].start_symbol_idx = 0; + cfg.pucch.sets[0].resources[4].time_domain_occ = 1; + + // Item 5 + // PUCCH-Resource + // pucch-ResourceId: 5 + // startingPRB: 0 + // format: format1 (1) + // format1 + // initialCyclicShift: 8 + // nrofSymbols: 14 + // startingSymbolIndex: 0 + // timeDomainOCC: 1 + cfg.pucch.sets[0].resources[5].format = SRSLTE_PUCCH_NR_FORMAT_1; + cfg.pucch.sets[0].resources[5].starting_prb = 0; + cfg.pucch.sets[0].resources[5].initial_cyclic_shift = 8; + cfg.pucch.sets[0].resources[5].nof_symbols = 14; + cfg.pucch.sets[0].resources[5].start_symbol_idx = 0; + cfg.pucch.sets[0].resources[5].time_domain_occ = 1; + + // Item 6 + // PUCCH-Resource + // pucch-ResourceId: 6 + // startingPRB: 0 + // format: format1 (1) + // format1 + // initialCyclicShift: 0 + // nrofSymbols: 14 + // startingSymbolIndex: 0 + // timeDomainOCC: 2 + cfg.pucch.sets[0].resources[6].format = SRSLTE_PUCCH_NR_FORMAT_1; + cfg.pucch.sets[0].resources[6].starting_prb = 0; + cfg.pucch.sets[0].resources[6].initial_cyclic_shift = 0; + cfg.pucch.sets[0].resources[6].nof_symbols = 14; + cfg.pucch.sets[0].resources[6].start_symbol_idx = 0; + cfg.pucch.sets[0].resources[6].time_domain_occ = 2; + + // Item 7 + // PUCCH-Resource + // pucch-ResourceId: 7 + // startingPRB: 0 + // format: format1 (1) + // format1 + // initialCyclicShift: 4 + // nrofSymbols: 14 + // startingSymbolIndex: 0 + // timeDomainOCC: 2 + cfg.pucch.sets[0].resources[7].format = SRSLTE_PUCCH_NR_FORMAT_1; + cfg.pucch.sets[0].resources[7].starting_prb = 0; + cfg.pucch.sets[0].resources[7].initial_cyclic_shift = 0; + cfg.pucch.sets[0].resources[7].nof_symbols = 14; + cfg.pucch.sets[0].resources[7].start_symbol_idx = 0; + cfg.pucch.sets[0].resources[7].time_domain_occ = 2; + + // Item 8 + // PUCCH-Resource + // pucch-ResourceId: 8 + // startingPRB: 51 + // format: format2 (2) + // format2 + // nrofPRBs: 1 + // nrofSymbols: 2 + // startingSymbolIndex: 0 + cfg.pucch.sets[1].resources[0].format = SRSLTE_PUCCH_NR_FORMAT_2; + cfg.pucch.sets[1].resources[0].starting_prb = 51; + cfg.pucch.sets[1].resources[0].nof_prb = 1; + cfg.pucch.sets[1].resources[0].nof_symbols = 2; + cfg.pucch.sets[1].resources[0].start_symbol_idx = 0; + + // Item 9 + // PUCCH-Resource + // pucch-ResourceId: 9 + // startingPRB: 51 + // format: format2 (2) + // format2 + // nrofPRBs: 1 + // nrofSymbols: 2 + // startingSymbolIndex: 2 + cfg.pucch.sets[1].resources[1].format = SRSLTE_PUCCH_NR_FORMAT_2; + cfg.pucch.sets[1].resources[1].starting_prb = 51; + cfg.pucch.sets[1].resources[1].nof_prb = 1; + cfg.pucch.sets[1].resources[1].nof_symbols = 2; + cfg.pucch.sets[1].resources[1].start_symbol_idx = 2; + + // Item 10 + // PUCCH-Resource + // pucch-ResourceId: 10 + // startingPRB: 51 + // format: format2 (2) + // format2 + // nrofPRBs: 1 + // nrofSymbols: 2 + // startingSymbolIndex: 4 + cfg.pucch.sets[1].resources[2].format = SRSLTE_PUCCH_NR_FORMAT_2; + cfg.pucch.sets[1].resources[2].starting_prb = 51; + cfg.pucch.sets[1].resources[2].nof_prb = 1; + cfg.pucch.sets[1].resources[2].nof_symbols = 2; + cfg.pucch.sets[1].resources[2].start_symbol_idx = 4; + + // Item 11 + // PUCCH-Resource + // pucch-ResourceId: 11 + // startingPRB: 51 + // format: format2 (2) + // format2 + // nrofPRBs: 1 + // nrofSymbols: 2 + // startingSymbolIndex: 6 + cfg.pucch.sets[1].resources[3].format = SRSLTE_PUCCH_NR_FORMAT_2; + cfg.pucch.sets[1].resources[3].starting_prb = 51; + cfg.pucch.sets[1].resources[3].nof_prb = 1; + cfg.pucch.sets[1].resources[3].nof_symbols = 2; + cfg.pucch.sets[1].resources[3].start_symbol_idx = 6; + + // Item 12 + // PUCCH-Resource + // pucch-ResourceId: 12 + // startingPRB: 51 + // format: format2 (2) + // format2 + // nrofPRBs: 1 + // nrofSymbols: 2 + // startingSymbolIndex: 8 + cfg.pucch.sets[1].resources[4].format = SRSLTE_PUCCH_NR_FORMAT_2; + cfg.pucch.sets[1].resources[4].starting_prb = 51; + cfg.pucch.sets[1].resources[4].nof_prb = 1; + cfg.pucch.sets[1].resources[4].nof_symbols = 2; + cfg.pucch.sets[1].resources[4].start_symbol_idx = 8; + + // Item 13 + // PUCCH-Resource + // pucch-ResourceId: 13 + // startingPRB: 51 + // format: format2 (2) + // format2 + // nrofPRBs: 1 + // nrofSymbols: 2 + // startingSymbolIndex: 10 + cfg.pucch.sets[1].resources[5].format = SRSLTE_PUCCH_NR_FORMAT_2; + cfg.pucch.sets[1].resources[5].starting_prb = 51; + cfg.pucch.sets[1].resources[5].nof_prb = 1; + cfg.pucch.sets[1].resources[5].nof_symbols = 2; + cfg.pucch.sets[1].resources[5].start_symbol_idx = 10; + + // Item 14 + // PUCCH-Resource + // pucch-ResourceId: 14 + // startingPRB: 51 + // format: format2 (2) + // format2 + // nrofPRBs: 1 + // nrofSymbols: 2 + // startingSymbolIndex: 12 + cfg.pucch.sets[1].resources[6].format = SRSLTE_PUCCH_NR_FORMAT_2; + cfg.pucch.sets[1].resources[6].starting_prb = 51; + cfg.pucch.sets[1].resources[6].nof_prb = 1; + cfg.pucch.sets[1].resources[6].nof_symbols = 2; + cfg.pucch.sets[1].resources[6].start_symbol_idx = 12; + + // Item 15 + // PUCCH-Resource + // pucch-ResourceId: 15 + // startingPRB: 1 + // format: format2 (2) + // format2 + // nrofPRBs: 1 + // nrofSymbols: 2 + // startingSymbolIndex: 0 + cfg.pucch.sets[1].resources[7].format = SRSLTE_PUCCH_NR_FORMAT_2; + cfg.pucch.sets[1].resources[7].starting_prb = 51; + cfg.pucch.sets[1].resources[7].nof_prb = 1; + cfg.pucch.sets[1].resources[7].nof_symbols = 2; + cfg.pucch.sets[1].resources[7].start_symbol_idx = 2; + + // Item 16 + // PUCCH-Resource + // pucch-ResourceId: 16 + // startingPRB: 0 + // format: format1 (1) + // format1 + // initialCyclicShift: 8 + // nrofSymbols: 14 + // startingSymbolIndex: 0 + // timeDomainOCC: 2 + // Item 17 + // PUCCH-Resource + // pucch-ResourceId: 17 + // startingPRB: 1 + // format: format2 (2) + // format2 + // nrofPRBs: 1 + // nrofSymbols: 2 + // startingSymbolIndex: 2 + // format1: setup (1) + // setup + // format2: setup (1) + // setup + // maxCodeRate: zeroDot25 (2) + for (uint32_t i = 0; i < SRSLTE_PUCCH_NR_MAX_NOF_SETS; i++) { + srslte_pucch_nr_resource_set_t* set = &cfg.pucch.sets[i]; + for (uint32_t j = 0; j < set->nof_resources; j++) { + if (set->resources[j].format == SRSLTE_PUCCH_NR_FORMAT_2) { + set->resources[j].max_code_rate = 2; // 0.25 + } + } + } + + // schedulingRequestResourceToAddModList: 1 item + // Item 0 + // SchedulingRequestResourceConfig + // schedulingRequestResourceId: 1 + // schedulingRequestID: 0 + // periodicityAndOffset: sl40 (10) + // sl40: 8 + // resource: 16 + + // dl-DataToUL-ACK: 7 items + // Item 0 + // dl-DataToUL-ACK item: 8 + // Item 1 + // dl-DataToUL-ACK item: 7 + // Item 2 + // dl-DataToUL-ACK item: 6 + // Item 3 + // dl-DataToUL-ACK item: 5 + // Item 4 + // dl-DataToUL-ACK item: 4 + // Item 5 + // dl-DataToUL-ACK item: 12 + // Item 6 + // dl-DataToUL-ACK item: 11 + cfg.harq_ack.dl_data_to_ul_ack[0] = 8; + cfg.harq_ack.dl_data_to_ul_ack[1] = 7; + cfg.harq_ack.dl_data_to_ul_ack[2] = 6; + cfg.harq_ack.dl_data_to_ul_ack[3] = 5; + cfg.harq_ack.dl_data_to_ul_ack[4] = 4; + cfg.harq_ack.dl_data_to_ul_ack[5] = 12; + cfg.harq_ack.dl_data_to_ul_ack[6] = 11; + cfg.harq_ack.nof_dl_data_to_ul_ack = 7; } /** @@ -245,10 +629,10 @@ public: std::lock_guard lock(pending_ul_grant_mutex); // Save entry - pending_grant_t& pending_grant = pending_ul_grant[tti_tx]; - pending_grant.sch_cfg = pusch_cfg; - pending_grant.pid = dci_ul.pid; - pending_grant.enable = true; + pending_ul_grant_t& pending_grant = pending_ul_grant[tti_tx]; + pending_grant.sch_cfg = pusch_cfg; + pending_grant.pid = dci_ul.pid; + pending_grant.enable = true; } /** @@ -264,7 +648,7 @@ public: std::lock_guard lock(pending_ul_grant_mutex); // Select entry - pending_grant_t& pending_grant = pending_ul_grant[tti_tx]; + pending_ul_grant_t& pending_grant = pending_ul_grant[tti_tx]; // If the entry is not active, just return if (!pending_grant.enable) { @@ -279,6 +663,126 @@ public: return true; } + + /** + * @brief Stores a received DL DCI into the pending DL grant list + * @param tti_rx The TTI in which the grant was received + * @param dci_dl The DL DCI message to store + */ + void set_dl_pending_grant(uint32_t tti_rx, const srslte_dci_dl_nr_t& dci_dl) + { + // Convert DL DCI to grant + srslte_sch_cfg_nr_t pdsch_cfg = {}; + if (srslte_ra_dl_dci_to_grant_nr(&carrier, &cfg.pdsch, &dci_dl, &pdsch_cfg, &pdsch_cfg.grant)) { + ERROR("Computing UL grant"); + return; + } + + // Calculate DL DCI to PDSCH ACK resource + srslte_pdsch_ack_resource_nr_t ack_resource = {}; + if (srslte_ue_dl_nr_pdsch_ack_resource(&cfg.harq_ack, &dci_dl, &ack_resource) < SRSLTE_SUCCESS) { + ERROR("Computing UL ACK resource"); + return; + } + + // Calculate Receive TTI + tti_rx = TTI_ADD(tti_rx, pdsch_cfg.grant.k); + + // Scope mutex to protect read/write the list + std::lock_guard lock(pending_dl_grant_mutex); + + // Save entry + pending_dl_grant_t& pending_grant = pending_dl_grant[tti_rx]; + pending_grant.sch_cfg = pdsch_cfg; + pending_grant.ack_resource = ack_resource; + pending_grant.pid = dci_dl.pid; + pending_grant.enable = true; + } + + /** + * @brief Checks the DL pending grant list if there is any grant to receive for the given receive TTI + * @param tti_rx Current receive TTI + * @param sch_cfg Provides the Shared Channel configuration for the PDSCH transmission + * @param ack_resource Provides the UL ACK resource + * @param pid Provides the HARQ process identifier + * @return true if there is a pending grant for the given TX tti, false otherwise + */ + bool get_dl_pending_grant(uint32_t tti_rx, + srslte_sch_cfg_nr_t& pdsch_cfg, + srslte_pdsch_ack_resource_nr_t& ack_resource, + uint32_t& pid) + { + // Scope mutex to protect read/write the list + std::lock_guard lock(pending_dl_grant_mutex); + + // Select entry + pending_dl_grant_t& pending_grant = pending_dl_grant[tti_rx]; + + // If the entry is not active, just return + if (!pending_grant.enable) { + return false; + } + + // Load shared channel configuration and resource + pdsch_cfg = pending_grant.sch_cfg; + ack_resource = pending_grant.ack_resource; + pid = pending_grant.pid; + + // Reset entry + pending_grant.enable = false; + + return true; + } + + /** + * @brief Stores a pending PDSCH ACK into the pending ACK list + * @param tti_rx The TTI in which the PDSCH transmission was received + * @param dci_dl The DL DCI message to store + */ + void set_pending_ack(const uint32_t& tti_rx, const srslte_pdsch_ack_resource_nr_t& ack_resource, const bool& crc_ok) + { + // Calculate Receive TTI + uint32_t tti_tx = TTI_ADD(tti_rx, ack_resource.k1); + + // Scope mutex to protect read/write the list + std::lock_guard lock(pending_ack_mutex); + + // Select UL transmission time resource + srslte_pdsch_ack_nr_t& ack = pending_ack[tti_tx]; + ack.nof_cc = 1; + + // Select serving cell + srslte_pdsch_ack_cc_nr_t& ack_cc = ack.cc[ack_resource.scell_idx]; + srslte_pdsch_ack_m_nr_t& ack_m = ack_cc.m[ack_cc.M]; + ack_cc.M++; + + // Set PDSCH transmission information + ack_m.resource = ack_resource; + ack_m.value[0] = crc_ok ? 1 : 0; + ack_m.present = true; + } + + bool get_pending_ack(const uint32_t& tti_tx, srslte_pdsch_ack_nr_t& pdsch_ack) + { + // Scope mutex to protect read/write the list + std::lock_guard lock(pending_ack_mutex); + + // Select UL transmission time resource + srslte_pdsch_ack_nr_t& ack = pending_ack[tti_tx]; + + // No pending grant was set + if (ack.nof_cc == 0) { + return false; + } + + // Copy data + pdsch_ack = ack; + + // Reset list entry + ack = {}; + + return true; + } }; } // namespace nr } // namespace srsue diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index fd8accae5..19917990b 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -126,30 +126,10 @@ uint32_t cc_worker::get_buffer_len() return buffer_sz; } -bool cc_worker::work_dl() +void cc_worker::decode_pdcch_dl() { - // Run FFT - srslte_ue_dl_nr_estimate_fft(&ue_dl, &dl_slot_cfg); - - // Initialise grants - std::array dci_dl_rx = {}; - std::array dci_ul_rx = {}; - uint32_t nof_found_dci_dl = 0; - uint32_t nof_found_dci_ul = 0; - - // Search for RA DCI - if (phy->cfg.pdcch.ra_search_space_present) { - int n_ra = srslte_ue_dl_nr_find_dl_dci(&ue_dl, - &dl_slot_cfg, - phy->cfg.pdcch.ra_rnti, - &dci_dl_rx[nof_found_dci_dl], - (uint32_t)dci_dl_rx.size() - nof_found_dci_dl); - if (n_ra < SRSLTE_SUCCESS) { - ERROR("Error decoding"); - return false; - } - nof_found_dci_dl += n_ra; - } + std::array dci_rx = {}; + uint32_t nof_found_dci = 0; // Search for test RNTI if (phy->test_rnti > 0) { @@ -157,60 +137,90 @@ bool cc_worker::work_dl() int n_dl = srslte_ue_dl_nr_find_dl_dci(&ue_dl, &dl_slot_cfg, (uint16_t)phy->test_rnti, - &dci_dl_rx[nof_found_dci_dl], - (uint32_t)dci_dl_rx.size() - nof_found_dci_dl); + &dci_rx[nof_found_dci], + (uint32_t)dci_rx.size() - nof_found_dci); if (n_dl < SRSLTE_SUCCESS) { - ERROR("Error decoding"); - return false; + logger.error("Error decoding DL NR-PDCCH for test RNTI"); + return; } - nof_found_dci_dl += n_dl; + nof_found_dci += n_dl; + } - // Search for test UL grants - int n_ul = srslte_ue_dl_nr_find_ul_dci(&ue_dl, - &dl_slot_cfg, - (uint16_t)phy->test_rnti, - &dci_ul_rx[nof_found_dci_ul], - (uint32_t)dci_ul_rx.size() - nof_found_dci_ul); - if (n_ul < SRSLTE_SUCCESS) { - ERROR("Error decoding"); - return false; + // Search for RA DCI + if (phy->cfg.pdcch.ra_search_space_present) { + int n_ra = srslte_ue_dl_nr_find_dl_dci( + &ue_dl, &dl_slot_cfg, phy->cfg.pdcch.ra_rnti, &dci_rx[nof_found_dci], (uint32_t)dci_rx.size() - nof_found_dci); + if (n_ra < SRSLTE_SUCCESS) { + logger.error("Error decoding DL NR-PDCCH for RA-RNTI"); + return; } - nof_found_dci_ul += n_ul; + nof_found_dci += n_ra; } - // Iterate over all UL received grants - for (uint32_t i = 0; i < nof_found_dci_ul; i++) { + // Iterate over all DL received grants + for (uint32_t i = 0; i < nof_found_dci; i++) { // Log found DCI if (logger.info.enabled()) { std::array str; - srslte_dci_ul_nr_to_str(&dci_ul_rx[i], str.data(), str.size()); + srslte_dci_dl_nr_to_str(&dci_rx[i], str.data(), str.size()); logger.info("PDCCH: cc=%d, %s", cc_idx, str.data()); } // Enqueue UL grants - phy->set_ul_pending_grant(dl_slot_cfg.idx, dci_ul_rx[i]); + phy->set_dl_pending_grant(dl_slot_cfg.idx, dci_rx[i]); } +} - // Iterate over all DL received grants - for (uint32_t i = 0; i < nof_found_dci_dl; i++) { - // Notify MAC about PDCCH found grant - // ... At the moment reset softbuffer locally - srslte_softbuffer_rx_reset(&softbuffer_rx); +void cc_worker::decode_pdcch_ul() +{ + std::array dci_rx = {}; + uint32_t nof_found_dci = 0; + + // Search for test RNTI + if (phy->test_rnti > 0) { + // Search for test DL grants + int n_dl = srslte_ue_dl_nr_find_ul_dci(&ue_dl, + &dl_slot_cfg, + (uint16_t)phy->test_rnti, + &dci_rx[nof_found_dci], + (uint32_t)dci_rx.size() - nof_found_dci); + if (n_dl < SRSLTE_SUCCESS) { + logger.error("Error decoding DL NR-PDCCH for test RNTI"); + return; + } + nof_found_dci += n_dl; + } + // Iterate over all UL received grants + for (uint32_t i = 0; i < nof_found_dci; i++) { // Log found DCI if (logger.info.enabled()) { std::array str; - srslte_dci_dl_nr_to_str(&dci_dl_rx[i], str.data(), str.size()); + srslte_dci_ul_nr_to_str(&dci_rx[i], str.data(), str.size()); logger.info("PDCCH: cc=%d, %s", cc_idx, str.data()); } - // Compute DL grant - srslte_sch_cfg_nr_t pdsch_cfg = {}; - if (srslte_ra_dl_dci_to_grant_nr(&ue_dl.carrier, &phy->cfg.pdsch, &dci_dl_rx[i], &pdsch_cfg, &pdsch_cfg.grant)) { - ERROR("Computing DL grant"); - return false; - } + // Enqueue UL grants + phy->set_ul_pending_grant(dl_slot_cfg.idx, dci_rx[i]); + } +} +bool cc_worker::work_dl() +{ + // Run FFT + srslte_ue_dl_nr_estimate_fft(&ue_dl, &dl_slot_cfg); + + // Decode PDCCH DL first + decode_pdcch_dl(); + + // Decode PDCCH UL after + decode_pdcch_ul(); + + // Get DL grant for this TTI, if available + uint32_t pid = 0; + srslte_sch_cfg_nr_t pdsch_cfg = {}; + srslte_pdsch_ack_resource_nr_t ack_resource = {}; + if (phy->get_dl_pending_grant(dl_slot_cfg.idx, pdsch_cfg, ack_resource, pid)) { // Get data buffer srslte::unique_byte_buffer_t data = srslte::make_byte_buffer(); data->N_bytes = pdsch_cfg.grant.tb[0].tbs / 8U; @@ -233,13 +243,16 @@ bool cc_worker::work_dl() logger.info(pdsch_res[0].payload, pdsch_cfg.grant.tb[0].tbs / 8, "PDSCH: cc=%d, %s", cc_idx, str.data()); } + // Enqueue PDSCH ACK information + phy->set_pending_ack(dl_slot_cfg.idx, ack_resource, pdsch_res[0].crc); + // Notify MAC about PDSCH decoding result if (pdsch_res[0].crc) { // Prepare grant mac_interface_phy_nr::mac_nr_grant_dl_t mac_nr_grant = {}; mac_nr_grant.tb[0] = std::move(data); - mac_nr_grant.pid = dci_dl_rx[i].pid; - mac_nr_grant.rnti = dci_dl_rx[i].rnti; + mac_nr_grant.pid = pid; + mac_nr_grant.rnti = pdsch_cfg.grant.rnti; mac_nr_grant.tti = dl_slot_cfg.idx; // Send data to MAC @@ -252,36 +265,70 @@ bool cc_worker::work_dl() bool cc_worker::work_ul() { - srslte_sch_cfg_nr_t pusch_cfg = {}; - uint32_t pid = 0; + srslte_uci_data_nr_t uci_data = {}; + uint32_t pid = 0; + + // Gather PDSCH ACK information + srslte_pdsch_ack_nr_t pdsch_ack = {}; + bool has_ul_ack = phy->get_pending_ack(ul_slot_cfg.idx, pdsch_ack); // Request grant to PHY state for this transmit TTI - if (not phy->get_ul_pending_grant(ul_slot_cfg.idx, pusch_cfg, pid)) { - // If no grant, return earlier - return true; + srslte_sch_cfg_nr_t pusch_cfg = {}; + bool has_pusch_grant = phy->get_ul_pending_grant(ul_slot_cfg.idx, pusch_cfg, pid); + + // If PDSCH UL AKC is available, load into UCI + if (has_ul_ack) { + pdsch_ack.use_pusch = has_pusch_grant; + if (srslte_ue_dl_nr_gen_ack(&phy->cfg.harq_ack, &pdsch_ack, &uci_data) < SRSLTE_SUCCESS) { + ERROR("Filling UCI ACK bits"); + return false; + } } - // Notify MAC about PUSCH found grant - // ... - srslte_softbuffer_tx_reset(&softbuffer_tx); - pusch_cfg.grant.tb[0].softbuffer.tx = &softbuffer_tx; + if (has_pusch_grant) { + // Notify MAC about PUSCH found grant + // ... + srslte_softbuffer_tx_reset(&softbuffer_tx); + pusch_cfg.grant.tb[0].softbuffer.tx = &softbuffer_tx; - // Encode PUSCH transmission - if (srslte_ue_ul_nr_encode_pusch(&ue_ul, &ul_slot_cfg, &pusch_cfg, tx_data.data()) < SRSLTE_SUCCESS) { - ERROR("Encoding PUSCH"); - return false; - } + // Encode PUSCH transmission + if (srslte_ue_ul_nr_encode_pusch(&ue_ul, &ul_slot_cfg, &pusch_cfg, tx_data.data()) < SRSLTE_SUCCESS) { + ERROR("Encoding PUSCH"); + return false; + } + + // PUSCH Logging + if (logger.info.enabled()) { + std::array str; + srslte_ue_ul_nr_pusch_info(&ue_ul, &pusch_cfg, str.data(), str.size()); + logger.info(tx_data.data(), + pusch_cfg.grant.tb[0].tbs / 8, + "PUSCH: cc=%d, %s, tti_tx=%d", + cc_idx, + str.data(), + ul_slot_cfg.idx); + } + } else if (srslte_uci_nr_total_bits(&uci_data.cfg) > 0) { + // Get PUCCH resource + srslte_pucch_nr_resource_t resource = {}; + if (srslte_ra_ul_nr_pucch_resource(&phy->cfg.pucch, &uci_data.cfg, &resource) < SRSLTE_SUCCESS) { + ERROR("Selecting PUCCH resource"); + return false; + } + + // Encode PUCCH message + if (srslte_ue_ul_nr_encode_pucch(&ue_ul, &ul_slot_cfg, &phy->cfg.pucch.common, &resource, &uci_data) < + SRSLTE_SUCCESS) { + ERROR("Encoding PUCCH"); + return false; + } - // Logging - if (logger.info.enabled()) { - std::array str; - srslte_ue_ul_nr_pusch_info(&ue_ul, &pusch_cfg, str.data(), str.size()); - logger.info(tx_data.data(), - pusch_cfg.grant.tb[0].tbs / 8, - "PUSCH: cc=%d, %s, tti_tx=%d", - cc_idx, - str.data(), - ul_slot_cfg.idx); + // PUCCH Logging + if (logger.info.enabled()) { + std::array str; + srslte_ue_ul_nr_pucch_info(&resource, &uci_data, str.data(), str.size()); + logger.info("PUCCH: cc=%d, %s, tti_tx=%d", cc_idx, str.data(), ul_slot_cfg.idx); + } } return true;