From dc2542901adaf363db41ea505d81d263fceb0c82 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Thu, 4 Mar 2021 12:12:16 +0100 Subject: [PATCH] Verified HARQ-ACK in PUSCH --- .../srslte/phy/ch_estimation/dmrs_sch.h | 6 + lib/include/srslte/phy/phch/pusch_nr.h | 2 + lib/include/srslte/phy/phch/uci_cfg_nr.h | 15 +- lib/include/srslte/phy/phch/uci_nr.h | 13 + lib/include/srslte/phy/utils/vector.h | 3 + lib/src/phy/ch_estimation/dmrs_sch.c | 3 +- lib/src/phy/fec/cbsegm.c | 9 +- lib/src/phy/phch/pusch_nr.c | 232 +++++++++++------- lib/src/phy/phch/ra_nr.c | 17 +- lib/src/phy/phch/sch_nr.c | 3 +- lib/src/phy/phch/test/CMakeLists.txt | 2 + lib/src/phy/phch/test/pusch_nr_test.c | 29 ++- lib/src/phy/phch/uci_nr.c | 76 ++++-- 13 files changed, 269 insertions(+), 141 deletions(-) diff --git a/lib/include/srslte/phy/ch_estimation/dmrs_sch.h b/lib/include/srslte/phy/ch_estimation/dmrs_sch.h index 6b7829a9b..3db0fc834 100644 --- a/lib/include/srslte/phy/ch_estimation/dmrs_sch.h +++ b/lib/include/srslte/phy/ch_estimation/dmrs_sch.h @@ -20,6 +20,12 @@ #define SRSLTE_DMRS_SCH_MAX_SYMBOLS 4 +/** + * @brief Helper macro for counting the number of subcarriers taken by DMRS in a PRB. + */ +#define SRSLTE_DMRS_SCH_SC(CDM_GROUPS, DMRS_TYPE) \ + (SRSLTE_MIN(SRSLTE_NRE, (CDM_GROUPS) * ((DMRS_TYPE) == srslte_dmrs_sch_type_1 ? 6 : 4))) + /** * @brief PDSCH DMRS estimator object * diff --git a/lib/include/srslte/phy/phch/pusch_nr.h b/lib/include/srslte/phy/phch/pusch_nr.h index 629e6464d..6f37ee646 100644 --- a/lib/include/srslte/phy/phch/pusch_nr.h +++ b/lib/include/srslte/phy/phch/pusch_nr.h @@ -50,6 +50,7 @@ typedef struct SRSLTE_API { srslte_evm_buffer_t* evm_buffer; bool meas_time_en; uint32_t meas_time_us; + srslte_uci_cfg_nr_t uci_cfg; ///< Internal UCI bits configuration uint8_t* g_ulsch; ///< Temporal Encoded UL-SCH data uint8_t* g_ack; ///< Temporal Encoded HARQ-ACK bits uint8_t* g_csi1; ///< Temporal Encoded CSI part 1 bits @@ -61,6 +62,7 @@ typedef struct SRSLTE_API { uint32_t G_ack; ///< Number of encoded HARQ-ACK bits uint32_t G_csi1; ///< Number of encoded CSI part 1 bits uint32_t G_csi2; ///< Number of encoded CSI part 2 bits + uint32_t G_ulsch; ///< Number of encoded shared channel } srslte_pusch_nr_t; /** diff --git a/lib/include/srslte/phy/phch/uci_cfg_nr.h b/lib/include/srslte/phy/phch/uci_cfg_nr.h index 95f2e856c..453ba6457 100644 --- a/lib/include/srslte/phy/phch/uci_cfg_nr.h +++ b/lib/include/srslte/phy/phch/uci_cfg_nr.h @@ -53,14 +53,13 @@ typedef struct { typedef struct { uint32_t l0; ///< First OFDM symbol that does not carry DMRS of the PUSCH, after the first DMRS symbol(s) uint32_t l1; ///< OFDM symbol index of the first OFDM symbol that does not carry DMRS - uint32_t M_pusch_sc[SRSLTE_NSYMB_PER_SLOT_NR]; ///< Number of potential RE for PUSCH transmission - uint32_t M_pusch_sc_acc[SRSLTE_NSYMB_PER_SLOT_NR]; ///< Number of potential RE for PUSCH before the symbol - uint32_t M_uci_sc[SRSLTE_NSYMB_PER_SLOT_NR]; ///< Number of potential RE for UCI transmission - uint32_t K_sum; ///< Sum of UL-SCH code block sizes, set to zero if no UL-SCH - srslte_mod_t modulation; ///< Modulation for the PUSCH - uint32_t nof_layers; ///< Number of layers for PUSCH - float R; ///< Code rate of the PUSCH - float alpha; ///< Higher layer parameter scaling + uint32_t M_pusch_sc[SRSLTE_NSYMB_PER_SLOT_NR]; ///< Number of potential RE for PUSCH transmission + uint32_t M_uci_sc[SRSLTE_NSYMB_PER_SLOT_NR]; ///< Number of potential RE for UCI transmission + uint32_t K_sum; ///< Sum of UL-SCH code block sizes, set to zero if no UL-SCH + srslte_mod_t modulation; ///< Modulation for the PUSCH + uint32_t nof_layers; ///< Number of layers for PUSCH + float R; ///< Code rate of the PUSCH + float alpha; ///< Higher layer parameter scaling float beta_harq_ack_offset; float beta_csi_part1_offset; uint32_t nof_re; diff --git a/lib/include/srslte/phy/phch/uci_nr.h b/lib/include/srslte/phy/phch/uci_nr.h index f3f93d796..523d5b8b1 100644 --- a/lib/include/srslte/phy/phch/uci_nr.h +++ b/lib/include/srslte/phy/phch/uci_nr.h @@ -155,6 +155,19 @@ SRSLTE_API int srslte_uci_nr_encode_pusch_ack(srslte_uci_nr_t* q, const srslte_uci_value_nr_t* value, uint8_t* o_ack); +/** + * @brief Decodes HARQ-ACK bits for PUSCH transmission + * @param[in,out] q NR-UCI object + * @param[in] cfg UCI configuration + * @param[in] llr Provides softbits LLR + * @param[out] value UCI value + * @return SRSLTE_SUCCESS if the decoding process was successful, SRSLTE_ERROR code otherwise + */ +SRSLTE_API int srslte_uci_nr_decode_pusch_ack(srslte_uci_nr_t* q, + const srslte_uci_cfg_nr_t* cfg, + int8_t* llr, + srslte_uci_value_nr_t* value); + /** * @brief Calculates the total number of UCI bits * @param uci_cfg UCI configuration diff --git a/lib/include/srslte/phy/utils/vector.h b/lib/include/srslte/phy/utils/vector.h index a2a4a9c92..bf5276b09 100644 --- a/lib/include/srslte/phy/utils/vector.h +++ b/lib/include/srslte/phy/utils/vector.h @@ -40,6 +40,9 @@ extern "C" { #define SRSLTE_MAX(a, b) ((a) > (b) ? (a) : (b)) #define SRSLTE_MIN(a, b) ((a) < (b) ? (a) : (b)) +#define SRSLTE_CEIL(NUM, DEN) (((NUM) + ((DEN)-1)) / (DEN)) +#define SRSLTE_FLOOR(NUM, DEN) ((NUM) / (DEN)) +#define SRSLTE_ROUND(NUM, DEN) ((uint32_t)round((double)(NUM) / (double)(DEN))) // Cumulative moving average #define SRSLTE_VEC_CMA(data, average, n) ((average) + ((data) - (average)) / ((n) + 1)) diff --git a/lib/src/phy/ch_estimation/dmrs_sch.c b/lib/src/phy/ch_estimation/dmrs_sch.c index 1415739cb..4cdc35ba0 100644 --- a/lib/src/phy/ch_estimation/dmrs_sch.c +++ b/lib/src/phy/ch_estimation/dmrs_sch.c @@ -430,8 +430,7 @@ int srslte_dmrs_sch_get_N_prb(const srslte_dmrs_sch_cfg_t* dmrs_cfg, const srslt } // Get number of frequency domain resource elements used for DMRS - int nof_sc = SRSLTE_MIN(SRSLTE_NRE, - grant->nof_dmrs_cdm_groups_without_data * (dmrs_cfg->type == srslte_dmrs_sch_type_1 ? 6 : 4)); + int nof_sc = SRSLTE_DMRS_SCH_SC(grant->nof_dmrs_cdm_groups_without_data, dmrs_cfg->type); // Get number of symbols used for DMRS uint32_t symbols[SRSLTE_DMRS_SCH_MAX_SYMBOLS] = {}; diff --git a/lib/src/phy/fec/cbsegm.c b/lib/src/phy/fec/cbsegm.c index 39029aacd..71740f5c0 100644 --- a/lib/src/phy/fec/cbsegm.c +++ b/lib/src/phy/fec/cbsegm.c @@ -14,6 +14,7 @@ #include "srslte/phy/fec/ldpc/base_graph.h" #include "srslte/phy/fec/turbo/turbodecoder_gen.h" #include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" #include /** @@ -31,8 +32,6 @@ const uint32_t tc_cb_sizes[SRSLTE_NOF_TC_CB_SIZES] = { 3904, 3968, 4032, 4096, 4160, 4224, 4288, 4352, 4416, 4480, 4544, 4608, 4672, 4736, 4800, 4864, 4928, 4992, 5056, 5120, 5184, 5248, 5312, 5376, 5440, 5504, 5568, 5632, 5696, 5760, 5824, 5888, 5952, 6016, 6080, 6144}; -#define CEIL(NUM, DEN) (((NUM) + ((DEN)-1)) / (DEN)) - /** * @brief Calculates the number of code blocks and the total size * @param[in] B Transport block size including TB CRC @@ -46,7 +45,7 @@ static void cbsegm_cb_size(uint32_t B, uint32_t Z, uint32_t* C, uint32_t* B_prim *C = 1; *B_prime = B; } else { - *C = CEIL(B, (Z - 24U)); + *C = SRSLTE_CEIL(B, (Z - 24U)); *B_prime = B + 24U * (*C); } } @@ -151,12 +150,12 @@ bool srslte_cbsegm_cbsize_isvalid(uint32_t size) static int cbsegm_ldpc_select_ls(uint32_t Kp, uint32_t K_b, uint32_t* Z_c, uint8_t* i_ls) { // Early return if the minimum required lift size is too high - if (CEIL(Kp, K_b) > MAX_LIFTSIZE) { + if (SRSLTE_CEIL(Kp, K_b) > MAX_LIFTSIZE) { return SRSLTE_ERROR; } // Iterate from the minimum required lift size to the maximum value - for (uint16_t Z = CEIL(Kp, K_b); Z <= MAX_LIFTSIZE; Z++) { + for (uint16_t Z = SRSLTE_CEIL(Kp, K_b); Z <= MAX_LIFTSIZE; Z++) { // Get index for a selected lifting size uint8_t i = get_ls_index(Z); diff --git a/lib/src/phy/phch/pusch_nr.c b/lib/src/phy/phch/pusch_nr.c index 130fd8fc2..e74e1e07b 100644 --- a/lib/src/phy/phch/pusch_nr.c +++ b/lib/src/phy/phch/pusch_nr.c @@ -206,6 +206,7 @@ void srslte_pusch_nr_free(srslte_pusch_nr_t* q) } srslte_sch_nr_free(&q->sch); + srslte_uci_nr_free(&q->uci); for (uint32_t i = 0; i < SRSLTE_MAX_LAYERS_NR; i++) { if (q->x[i]) { @@ -466,14 +467,13 @@ pusch_nr_cinit(const srslte_carrier_nr_t* carrier, const srslte_sch_cfg_nr_t* cf return cinit; } -static inline int -pusch_nr_fill_uci_cfg(srslte_pusch_nr_t* q, const srslte_sch_cfg_nr_t* cfg, srslte_uci_cfg_nr_t* uci_cfg) +static inline int pusch_nr_fill_uci_cfg(srslte_pusch_nr_t* q, const srslte_sch_cfg_nr_t* cfg) { // Initially, copy all fields - *uci_cfg = cfg->uci; + q->uci_cfg = cfg->uci; // Reset UCI PUSCH configuration - SRSLTE_MEM_ZERO(&uci_cfg->pusch, srslte_uci_nr_pusch_cfg_t, 1); + SRSLTE_MEM_ZERO(&q->uci_cfg.pusch, srslte_uci_nr_pusch_cfg_t, 1); // Get DMRS symbol indexes uint32_t nof_dmrs_l = 0; @@ -490,7 +490,7 @@ pusch_nr_fill_uci_cfg(srslte_pusch_nr_t* q, const srslte_sch_cfg_nr_t* cfg, srsl // Check if it is not carrying DMRS... if (l != dmrs_l[dmrs_l_idx]) { // Set value and stop iterating - uci_cfg->pusch.l0 = l; + q->uci_cfg.pusch.l0 = l; break; } @@ -505,7 +505,7 @@ pusch_nr_fill_uci_cfg(srslte_pusch_nr_t* q, const srslte_sch_cfg_nr_t* cfg, srsl for (uint32_t l = cfg->grant.S, dmrs_l_idx = 0; l < cfg->grant.S + cfg->grant.L; l++) { // Check if it is not carrying DMRS... if (l != dmrs_l[dmrs_l_idx]) { - uci_cfg->pusch.l1 = l; + q->uci_cfg.pusch.l1 = l; break; } @@ -516,33 +516,24 @@ pusch_nr_fill_uci_cfg(srslte_pusch_nr_t* q, const srslte_sch_cfg_nr_t* cfg, srsl } // Number of DMRS per PRB - int n_prb_dmrs = srslte_dmrs_sch_get_N_prb(&cfg->dmrs, &cfg->grant); - if (n_prb_dmrs < SRSLTE_SUCCESS) { - ERROR("Error calculating number of DMRS per PRB"); - return SRSLTE_ERROR; - } - - // Accumulative Resource Element shall start in zero - uci_cfg->pusch.M_pusch_sc_acc[0] = 0; + uint32_t n_sc_dmrs = SRSLTE_DMRS_SCH_SC(cfg->grant.nof_dmrs_cdm_groups_without_data, cfg->dmrs.type); // Set UCI RE number of candidates per OFDM symbol according to TS 38.312 6.3.2.4.2.1 for (uint32_t l = 0, dmrs_l_idx = 0; l < SRSLTE_NSYMB_PER_SLOT_NR; l++) { // Skip if OFDM symbol is outside of the PUSCH transmission - if (l < cfg->grant.S || l >= (cfg->grant.S + cfg->grant.L) || l == dmrs_l[dmrs_l_idx]) { - uci_cfg->pusch.M_pusch_sc[l] = 0; - uci_cfg->pusch.M_pusch_sc_acc[l + 1] = uci_cfg->pusch.M_pusch_sc_acc[l] + uci_cfg->pusch.M_pusch_sc[l]; - uci_cfg->pusch.M_uci_sc[l] = 0; + if (l < cfg->grant.S || l >= (cfg->grant.S + cfg->grant.L)) { + q->uci_cfg.pusch.M_pusch_sc[l] = 0; + q->uci_cfg.pusch.M_uci_sc[l] = 0; continue; } // OFDM symbol carries DMRS if (l == dmrs_l[dmrs_l_idx]) { // Calculate PUSCH RE candidates - uci_cfg->pusch.M_pusch_sc[l] = cfg->grant.nof_prb * (SRSLTE_NRE - n_prb_dmrs); - uci_cfg->pusch.M_pusch_sc_acc[l + 1] = uci_cfg->pusch.M_pusch_sc_acc[l] + uci_cfg->pusch.M_pusch_sc[l]; + q->uci_cfg.pusch.M_pusch_sc[l] = cfg->grant.nof_prb * (SRSLTE_NRE - n_sc_dmrs); // The Number of RE candidates for UCI are 0 - uci_cfg->pusch.M_uci_sc[l] = 0; + q->uci_cfg.pusch.M_uci_sc[l] = 0; // Advance DMRS symbol index dmrs_l_idx++; @@ -555,10 +546,10 @@ pusch_nr_fill_uci_cfg(srslte_pusch_nr_t* q, const srslte_sch_cfg_nr_t* cfg, srsl uint32_t M_ptrs_sc = 0; // Not implemented yet // Number of RE given by the grant - uci_cfg->pusch.M_pusch_sc[l] = cfg->grant.nof_prb * SRSLTE_NRE; + q->uci_cfg.pusch.M_pusch_sc[l] = cfg->grant.nof_prb * SRSLTE_NRE; // Calculate the number of UCI candidates - uci_cfg->pusch.M_uci_sc[l] = uci_cfg->pusch.M_pusch_sc[l] - M_ptrs_sc; + q->uci_cfg.pusch.M_uci_sc[l] = q->uci_cfg.pusch.M_pusch_sc[l] - M_ptrs_sc; } // Generate SCH Transport block information @@ -571,23 +562,21 @@ pusch_nr_fill_uci_cfg(srslte_pusch_nr_t* q, const srslte_sch_cfg_nr_t* cfg, srsl // Calculate the sum of codeblock sizes for (uint32_t i = 0; i < sch_tb_info.C; i++) { // Accumulate codeblock size if mask is enabled - uci_cfg->pusch.K_sum += (sch_tb_info.mask[i]) ? sch_tb_info.Kr : 0; + q->uci_cfg.pusch.K_sum += (sch_tb_info.mask[i]) ? sch_tb_info.Kr : 0; } // Set other PUSCH parameters - uci_cfg->pusch.modulation = cfg->grant.tb[0].mod; - uci_cfg->pusch.nof_layers = cfg->grant.nof_layers; - uci_cfg->pusch.R = (float)cfg->grant.tb[0].R; - uci_cfg->pusch.alpha = cfg->scaling; - uci_cfg->pusch.beta_harq_ack_offset = cfg->beta_harq_ack_offset; - uci_cfg->pusch.beta_csi_part1_offset = cfg->beta_csi_part1_offset; - uci_cfg->pusch.nof_re = cfg->grant.tb[0].nof_re; + q->uci_cfg.pusch.modulation = cfg->grant.tb[0].mod; + q->uci_cfg.pusch.nof_layers = cfg->grant.nof_layers; + q->uci_cfg.pusch.R = (float)cfg->grant.tb[0].R; + q->uci_cfg.pusch.alpha = cfg->scaling; + q->uci_cfg.pusch.beta_harq_ack_offset = cfg->beta_harq_ack_offset; + q->uci_cfg.pusch.beta_csi_part1_offset = cfg->beta_csi_part1_offset; + q->uci_cfg.pusch.nof_re = cfg->grant.tb[0].nof_re; return SRSLTE_SUCCESS; } -#define CEIL(NUM, DEN) (((NUM) + ((DEN)-1)) / (DEN)) - // Implements TS 38.212 6.2.7 Data and control multiplexing (for NR-PUSCH) static int pusch_nr_gen_mux_uci(srslte_pusch_nr_t* q, const srslte_uci_cfg_nr_t* cfg) { @@ -646,21 +635,21 @@ static int pusch_nr_gen_mux_uci(srslte_pusch_nr_t* q, const srslte_uci_cfg_nr_t* // Compute HARQ-ACK bits multiplexing uint32_t ack_d = 0; uint32_t ack_m_re_count = 0; - if (l >= l1 && m_ack_count < G_ack_rvd) { - if (cfg->o_ack <= 2) { + if (l >= l1) { + if (cfg->o_ack <= 2 && m_ack_count < G_ack_rvd) { ack_d = 1; ack_m_re_count = M_ulsch_sc; if (G_ack_rvd - m_ack_count < M_uci_sc * Nl * Qm) { ack_d = (M_uci_sc * Nl * Qm) / (G_ack_rvd - m_ack_count); - ack_m_re_count = CEIL(G_ack_rvd - m_ack_count, Nl * Qm); + ack_m_re_count = SRSLTE_CEIL(G_ack_rvd - m_ack_count, Nl * Qm); } M_uci_rvd = ack_m_re_count; - } else { + } else if (m_ack_count < G_ack) { ack_d = 1; ack_m_re_count = M_ulsch_sc; if (G_ack - m_ack_count < M_uci_sc * Nl * Qm) { - ack_d = (M_uci_sc * Nl * Qm) / (G_ack_rvd - m_ack_count); - ack_m_re_count = M_ulsch_sc; + ack_d = (M_uci_sc * Nl * Qm) / (G_ack - m_ack_count); + ack_m_re_count = SRSLTE_CEIL(G_ack - m_ack_count, Nl * Qm); } M_uci_sc -= ack_m_re_count; } @@ -674,7 +663,7 @@ static int pusch_nr_gen_mux_uci(srslte_pusch_nr_t* q, const srslte_uci_cfg_nr_t* csi1_m_re_count = M_uci_sc - M_uci_rvd; if (G_csi1 - m_csi1_count < (M_uci_sc - M_uci_rvd) * Nl * Qm) { csi1_d = ((M_uci_sc - M_uci_rvd) * Nl * Qm) / (G_csi1 - m_csi1_count); - csi1_m_re_count = CEIL(G_csi1 - m_csi1_count, Nl * Qm); + csi1_m_re_count = SRSLTE_CEIL(G_csi1 - m_csi1_count, Nl * Qm); } M_uci_sc -= csi1_m_re_count; } @@ -687,7 +676,7 @@ static int pusch_nr_gen_mux_uci(srslte_pusch_nr_t* q, const srslte_uci_cfg_nr_t* csi2_m_re_count = M_uci_sc - M_uci_rvd; if (G_csi2 - m_csi2_count < (M_uci_sc - M_uci_rvd) * Nl * Qm) { csi2_d = ((M_uci_sc - M_uci_rvd) * Nl * Qm) / (G_csi2 - m_csi2_count); - csi2_m_re_count = CEIL(G_csi2 - m_csi2_count, Nl * Qm); + csi2_m_re_count = SRSLTE_CEIL(G_csi2 - m_csi2_count, Nl * Qm); } M_uci_sc -= csi2_m_re_count; } @@ -695,7 +684,7 @@ static int pusch_nr_gen_mux_uci(srslte_pusch_nr_t* q, const srslte_uci_cfg_nr_t* // Leave the rest for UL-SCH uint32_t ulsch_m_re_count = M_uci_sc; - for (uint32_t i = 0, csi1_i = 0, csi2_i = 0; i < cfg->pusch.M_pusch_sc[l] * Qm * Nl; i++) { + for (uint32_t i = 0, csi1_i = 0, csi2_i = 0; i < cfg->pusch.M_pusch_sc[l]; i++) { if (ack_m_re_count != 0 && i % ack_d == 0 && m_ack_count < G_ack) { for (uint32_t j = 0; j < Nl * Qm; j++) { pos_ack[m_ack_count++] = m_all_count++; @@ -724,7 +713,7 @@ static int pusch_nr_gen_mux_uci(srslte_pusch_nr_t* q, const srslte_uci_cfg_nr_t* } // Set reserved bits - if (i % ack_d == 0 && m_ack_count < G_ack_rvd) { + if (ack_m_re_count != 0 && i % ack_d == 0 && m_ack_count < G_ack_rvd) { for (uint32_t j = 0; j < Nl * Qm; j++) { pos_ack[m_ack_count++] = m_all_count++; } @@ -732,7 +721,7 @@ static int pusch_nr_gen_mux_uci(srslte_pusch_nr_t* q, const srslte_uci_cfg_nr_t* } } - // Checks that all RE are allocated as planned + // Assert that all RE have been allocated if (ack_m_re_count != 0) { ERROR("ack_m_re_count=%d", ack_m_re_count); } @@ -747,6 +736,10 @@ static int pusch_nr_gen_mux_uci(srslte_pusch_nr_t* q, const srslte_uci_cfg_nr_t* } } + // Update UL-SCH number of encoded bits + q->G_ulsch = m_ulsch_count; + + // Assert Number of bits if (G_ack_rvd != 0 && G_ack_rvd != m_ack_count) { ERROR("Not matched %d!=%d", G_ack_rvd, m_ack_count); } @@ -760,6 +753,26 @@ static int pusch_nr_gen_mux_uci(srslte_pusch_nr_t* q, const srslte_uci_cfg_nr_t* ERROR("Not matched %d!=%d", G_csi2, m_csi2_count); } + // Print debug information if configured for ity + if (SRSLTE_DEBUG_ENABLED && srslte_verbose >= SRSLTE_VERBOSE_DEBUG && !handler_registered) { + if (m_ulsch_count != 0) { + DEBUG("UL-SCH bit positions:"); + srslte_vec_fprint_i(stdout, (int*)pos_ulsch, m_ulsch_count); + } + if (m_ack_count != 0) { + DEBUG("HARQ-ACK bit positions [%d]:", m_ack_count); + srslte_vec_fprint_i(stdout, (int*)pos_ack, m_ack_count); + } + if (m_csi1_count != 0) { + DEBUG("CSI part 1 bit positions [%d]:", m_csi1_count); + srslte_vec_fprint_i(stdout, (int*)pos_csi1, m_csi1_count); + } + if (m_csi2_count != 0) { + DEBUG("CSI part 2 bit positions [%d]:", m_csi2_count); + srslte_vec_fprint_i(stdout, (int*)pos_csi2, m_csi2_count); + } + } + return SRSLTE_SUCCESS; } @@ -787,25 +800,32 @@ static inline int pusch_nr_encode_codeword(srslte_pusch_nr_t* q, return SRSLTE_ERROR_OUT_OF_BOUNDS; } + // Encode HARQ-ACK bits + int E_uci_ack = srslte_uci_nr_encode_pusch_ack(&q->uci, &q->uci_cfg, uci, q->g_ack); + if (E_uci_ack < SRSLTE_SUCCESS) { + ERROR("Error encoding HARQ-ACK bits"); + return SRSLTE_ERROR; + } + q->G_ack = E_uci_ack; + q->G_csi1 = 0; + q->G_csi2 = 0; + + // Generate PUSCH UCI/UL-SCH multiplexing + if (pusch_nr_gen_mux_uci(q, &q->uci_cfg) < SRSLTE_SUCCESS) { + ERROR("Error generating PUSCH mux tables"); + return SRSLTE_ERROR; + } + // Encode SCH if (srslte_ulsch_nr_encode(&q->sch, &cfg->sch_cfg, tb, data, q->g_ulsch) < SRSLTE_SUCCESS) { ERROR("Error in SCH encoding"); return SRSLTE_ERROR; } - if (SRSLTE_DEBUG_ENABLED && srslte_verbose >= SRSLTE_VERBOSE_DEBUG && !handler_registered) { - DEBUG("b="); - srslte_vec_fprint_b(stdout, q->b[tb->cw_idx], tb->nof_bits); - } - // Multiplex UL-SCH - for (uint32_t i = 0; i < tb->nof_bits; i++) { + for (uint32_t i = 0; i < q->G_ulsch; i++) { q->b[tb->cw_idx][q->pos_ulsch[i]] = q->g_ulsch[i]; } - if (SRSLTE_DEBUG_ENABLED && srslte_verbose >= SRSLTE_VERBOSE_DEBUG && !handler_registered) { - DEBUG("UL-SCH bit positions:"); - srslte_vec_fprint_i(stdout, (int*)q->pos_ulsch, tb->nof_bits); - } // Multiplex CSI part 1 for (uint32_t i = 0; i < q->G_csi1; i++) { @@ -822,6 +842,11 @@ static inline int pusch_nr_encode_codeword(srslte_pusch_nr_t* q, q->b[tb->cw_idx][q->pos_ack[i]] = q->g_ack[i]; } + if (SRSLTE_DEBUG_ENABLED && srslte_verbose >= SRSLTE_VERBOSE_DEBUG && !handler_registered) { + DEBUG("b="); + srslte_vec_fprint_b(stdout, q->b[tb->cw_idx], tb->nof_bits); + } + // 7.3.1.1 Scrambling uint32_t cinit = pusch_nr_cinit(&q->carrier, cfg, rnti, tb->cw_idx); srslte_sequence_apply_bit(q->b[tb->cw_idx], q->b[tb->cw_idx], tb->nof_bits, cinit); @@ -860,26 +885,11 @@ int srslte_pusch_nr_encode(srslte_pusch_nr_t* q, } // Fill UCI configuration for PUSCH configuration - srslte_uci_cfg_nr_t uci_cfg = {}; - if (pusch_nr_fill_uci_cfg(q, cfg, &uci_cfg) < SRSLTE_SUCCESS) { + if (pusch_nr_fill_uci_cfg(q, cfg) < SRSLTE_SUCCESS) { ERROR("Error filling UCI configuration for PUSCH"); return SRSLTE_ERROR; } - // Encode HARQ-ACK bits - int E_uci_ack = srslte_uci_nr_encode_pusch_ack(&q->uci, &uci_cfg, &data[0].uci, q->g_ack); - if (E_uci_ack < SRSLTE_SUCCESS) { - ERROR("Error encoding HARQ-ACK bits"); - return SRSLTE_ERROR; - } - q->G_ack = E_uci_ack; - - // Generate PUSCH UCI/UL-SCH multiplexing - if (pusch_nr_gen_mux_uci(q, &uci_cfg) < SRSLTE_SUCCESS) { - ERROR("Error generating PUSCH mux tables"); - return SRSLTE_ERROR; - } - // 7.3.1.1 and 7.3.1.2 uint32_t nof_cw = 0; for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { @@ -954,6 +964,22 @@ static inline int pusch_nr_decode_codeword(srslte_pusch_nr_t* q, srslte_vec_fprint_c(stdout, q->d[tb->cw_idx], tb->nof_re); } + // Calculate UCI bits + int n = srslte_uci_nr_pusch_ack_nof_bits(&q->uci_cfg.pusch, q->uci_cfg.o_ack); + if (n < SRSLTE_SUCCESS) { + ERROR("Calculating G_ack"); + return SRSLTE_ERROR; + } + q->G_ack = (uint32_t)n; + q->G_csi1 = 0; + q->G_csi2 = 0; + + // Generate PUSCH UCI/UL-SCH multiplexing + if (pusch_nr_gen_mux_uci(q, &q->uci_cfg) < SRSLTE_SUCCESS) { + ERROR("Error generating PUSCH mux tables"); + return SRSLTE_ERROR; + } + // Demodulation int8_t* llr = (int8_t*)q->b[tb->cw_idx]; if (srslte_demod_soft_demodulate_b(tb->mod, q->d[tb->cw_idx], llr, tb->nof_re)) { @@ -965,28 +991,55 @@ static inline int pusch_nr_decode_codeword(srslte_pusch_nr_t* q, res->evm = srslte_evm_run_b(q->evm_buffer, &q->modem_tables[tb->mod], q->d[tb->cw_idx], llr, tb->nof_bits); } + // Descrambling + srslte_sequence_apply_c(llr, llr, tb->nof_bits, pusch_nr_cinit(&q->carrier, cfg, rnti, tb->cw_idx)); + + if (SRSLTE_DEBUG_ENABLED && srslte_verbose >= SRSLTE_VERBOSE_DEBUG && !handler_registered) { + DEBUG("b="); + srslte_vec_fprint_bs(stdout, llr, tb->nof_bits); + } + // Demultiplex UL-SCH, change sign - int8_t* g_ulsch_llr = (int8_t*)q->g_ulsch; - for (uint32_t i = 0; i < tb->nof_bits; i++) { - g_ulsch_llr[i] = -llr[q->pos_ulsch[i]]; + int8_t* g_ulsch = (int8_t*)q->g_ulsch; + for (uint32_t i = 0; i < q->G_ulsch; i++) { + g_ulsch[i] = -llr[q->pos_ulsch[i]]; } - if (SRSLTE_DEBUG_ENABLED && srslte_verbose >= SRSLTE_VERBOSE_DEBUG && !handler_registered) { - DEBUG("UL-SCH bit positions:"); - srslte_vec_fprint_i(stdout, (int*)q->pos_ulsch, tb->nof_bits); + for (uint32_t i = q->G_ulsch; i < tb->nof_bits; i++) { + g_ulsch[i] = 0; } - // Descrambling - srslte_sequence_apply_c(g_ulsch_llr, g_ulsch_llr, tb->nof_bits, pusch_nr_cinit(&q->carrier, cfg, rnti, tb->cw_idx)); + // Demultiplex HARQ-ACK + int8_t* g_ack = (int8_t*)q->g_ack; + for (uint32_t i = 0; i < q->G_ack; i++) { + g_ack[i] = llr[q->pos_ack[i]]; + } - if (SRSLTE_DEBUG_ENABLED && srslte_verbose >= SRSLTE_VERBOSE_DEBUG && !handler_registered) { - DEBUG("b="); - srslte_vec_fprint_bs(stdout, g_ulsch_llr, tb->nof_bits); + // Demultiplex CSI part 1 + int8_t* g_csi1 = (int8_t*)q->g_csi1; + for (uint32_t i = 0; i < q->G_csi1; i++) { + g_csi1[i] = llr[q->pos_csi1[i]]; } - // Decode SCH - if (srslte_ulsch_nr_decode(&q->sch, &cfg->sch_cfg, tb, g_ulsch_llr, res->payload, &res->crc) < SRSLTE_SUCCESS) { - ERROR("Error in SCH decoding"); - return SRSLTE_ERROR; + // Demultiplex CSI part 2 + int8_t* g_csi2 = (int8_t*)q->g_csi2; + for (uint32_t i = 0; i < q->G_csi2; i++) { + g_csi2[i] = llr[q->pos_csi2[i]]; + } + + // Decode Ul-SCH + if (q->G_ulsch != 0) { + if (srslte_ulsch_nr_decode(&q->sch, &cfg->sch_cfg, tb, g_ulsch, res->payload, &res->crc) < SRSLTE_SUCCESS) { + ERROR("Error in SCH decoding"); + return SRSLTE_ERROR; + } + } + + // Decode HARQ-ACK + if (q->G_ack) { + if (srslte_uci_nr_decode_pusch_ack(&q->uci, &q->uci_cfg, g_ack, &res->uci)) { + ERROR("Error in UCI decoding"); + return SRSLTE_ERROR; + } } return SRSLTE_SUCCESS; @@ -1016,18 +1069,11 @@ int srslte_pusch_nr_decode(srslte_pusch_nr_t* q, } // Fill UCI configuration for PUSCH configuration - srslte_uci_cfg_nr_t uci_cfg = {}; - if (pusch_nr_fill_uci_cfg(q, cfg, &uci_cfg) < SRSLTE_SUCCESS) { + if (pusch_nr_fill_uci_cfg(q, cfg) < SRSLTE_SUCCESS) { ERROR("Error filling UCI configuration for PUSCH"); return SRSLTE_ERROR; } - // Generate PUSCH UCI/UL-SCH multiplexing - if (pusch_nr_gen_mux_uci(q, &uci_cfg) < SRSLTE_SUCCESS) { - ERROR("Error generating PUSCH mux tables"); - return SRSLTE_ERROR; - } - uint32_t nof_cw = 0; for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { nof_cw += grant->tb[tb].enabled ? 1 : 0; diff --git a/lib/src/phy/phch/ra_nr.c b/lib/src/phy/phch/ra_nr.c index d340fae32..399c881da 100644 --- a/lib/src/phy/phch/ra_nr.c +++ b/lib/src/phy/phch/ra_nr.c @@ -310,16 +310,13 @@ int srslte_ra_dl_nr_slot_nof_re(const srslte_sch_cfg_nr_t* pdsch_cfg, const srsl return SRSLTE_MIN(SRSLTE_MAX_NRE_NR, n_re_prime) * n_prb; } -#define CEIL(NUM, DEN) (((NUM) + ((DEN)-1)) / (DEN)) -#define FLOOR(NUM, DEN) ((NUM) / (DEN)) -#define ROUND(NUM, DEN) ((uint32_t)round((NUM) / (DEN))) #define POW2(N) (1U << (N)) static uint32_t ra_nr_tbs_from_n_info3(uint32_t n_info) { // quantized intermediate number of information bits uint32_t n = (uint32_t)SRSLTE_MAX(3.0, floor(log2(n_info)) - 6.0); - uint32_t n_info_prime = SRSLTE_MAX(ra_nr_tbs_table[0], POW2(n) * FLOOR(n_info, POW2(n))); + uint32_t n_info_prime = SRSLTE_MAX(ra_nr_tbs_table[0], POW2(n) * SRSLTE_FLOOR(n_info, POW2(n))); // use Table 5.1.3.2-1 find the closest TBS that is not less than n_info_prime for (uint32_t i = 0; i < RA_NR_TBS_SIZE_TABLE; i++) { @@ -335,19 +332,19 @@ static uint32_t ra_nr_tbs_from_n_info4(uint32_t n_info, double R) { // quantized intermediate number of information bits uint32_t n = (uint32_t)(floor(log2(n_info - 24.0)) - 5.0); - uint32_t n_info_prime = SRSLTE_MAX(3840, POW2(n) * ROUND(n_info - 24.0, POW2(n))); + uint32_t n_info_prime = SRSLTE_MAX(3840, POW2(n) * SRSLTE_ROUND(n_info - 24.0, POW2(n))); if (R <= 0.25) { - uint32_t C = CEIL(n_info_prime + 24U, 3816U); - return 8U * C * CEIL(n_info_prime + 24U, 8U * C) - 24U; + uint32_t C = SRSLTE_CEIL(n_info_prime + 24U, 3816U); + return 8U * C * SRSLTE_CEIL(n_info_prime + 24U, 8U * C) - 24U; } if (n_info_prime > 8424) { - uint32_t C = CEIL(n_info_prime + 24U, 8424U); - return 8U * C * CEIL(n_info_prime + 24U, 8U * C) - 24U; + uint32_t C = SRSLTE_CEIL(n_info_prime + 24U, 8424U); + return 8U * C * SRSLTE_CEIL(n_info_prime + 24U, 8U * C) - 24U; } - return 8U * CEIL(n_info_prime + 24U, 8U) - 24U; + return 8U * SRSLTE_CEIL(n_info_prime + 24U, 8U) - 24U; } /** diff --git a/lib/src/phy/phch/sch_nr.c b/lib/src/phy/phch/sch_nr.c index becba1cd5..174f76e1d 100644 --- a/lib/src/phy/phch/sch_nr.c +++ b/lib/src/phy/phch/sch_nr.c @@ -131,7 +131,6 @@ int srslte_sch_nr_fill_tb_info(const srslte_carrier_nr_t* carrier, return SRSLTE_SUCCESS; } -#define CEIL(NUM, DEN) (((NUM) + ((DEN)-1)) / (DEN)) #define MOD(NUM, DEN) ((NUM) % (DEN)) static inline uint32_t sch_nr_get_E(const srslte_sch_nr_tb_info_t* cfg, uint32_t j) @@ -144,7 +143,7 @@ static inline uint32_t sch_nr_get_E(const srslte_sch_nr_tb_info_t* cfg, uint32_t if (j <= (cfg->Cp - MOD(cfg->G / (cfg->Nl * cfg->Qm), cfg->Cp) - 1)) { return cfg->Nl * cfg->Qm * (cfg->G / (cfg->Nl * cfg->Qm * cfg->Cp)); } - return cfg->Nl * cfg->Qm * CEIL(cfg->G, cfg->Nl * cfg->Qm * cfg->Cp); + return cfg->Nl * cfg->Qm * SRSLTE_CEIL(cfg->G, cfg->Nl * cfg->Qm * cfg->Cp); } static inline int sch_nr_init_common(srslte_sch_nr_t* q) diff --git a/lib/src/phy/phch/test/CMakeLists.txt b/lib/src/phy/phch/test/CMakeLists.txt index ae8febb8f..869d33aef 100644 --- a/lib/src/phy/phch/test/CMakeLists.txt +++ b/lib/src/phy/phch/test/CMakeLists.txt @@ -631,6 +631,8 @@ add_nr_test(pdsch_nr_test pdsch_nr_test -p 6 -m 20) add_executable(pusch_nr_test pusch_nr_test.c) target_link_libraries(pusch_nr_test srslte_phy) add_nr_test(pusch_nr_test pusch_nr_test -p 6 -m 20) +add_nr_test(pusch_nr_ack_4_test pusch_nr_test -p 50 -m 20 -A 4) +add_nr_test(pusch_nr_ack_20_test pusch_nr_test -p 50 -m 20 -A 20) add_executable(pdcch_nr_test pdcch_nr_test.c) target_link_libraries(pdcch_nr_test srslte_phy) diff --git a/lib/src/phy/phch/test/pusch_nr_test.c b/lib/src/phy/phch/test/pusch_nr_test.c index a123612f1..acedcdd50 100644 --- a/lib/src/phy/phch/test/pusch_nr_test.c +++ b/lib/src/phy/phch/test/pusch_nr_test.c @@ -187,9 +187,9 @@ int main(int argc, char** argv) mcs_end = SRSLTE_MIN(mcs + 1, mcs_end); } - pusch_cfg.scaling = 0.650f; - pusch_cfg.beta_harq_ack_offset = 5.000f; - pusch_cfg.beta_csi_part1_offset = 5.000f; + pusch_cfg.scaling = 0.5f; + pusch_cfg.beta_harq_ack_offset = 1.500f; + pusch_cfg.beta_csi_part1_offset = 1.500f; if (srslte_chest_dl_res_init(&chest, carrier.nof_prb) < SRSLTE_SUCCESS) { ERROR("Initiating chest"); @@ -201,6 +201,7 @@ int main(int argc, char** argv) for (uint32_t n = 0; n < SRSLTE_MAX_PRB_NR; n++) { pusch_cfg.grant.prb_idx[n] = (n < n_prb); } + pusch_cfg.grant.nof_prb = n_prb; pusch_cfg.grant.dci_format = srslte_dci_format_nr_0_0; if (srslte_ra_nr_fill_tb(&pusch_cfg, &pusch_cfg.grant, mcs, &pusch_cfg.grant.tb[0]) < SRSLTE_SUCCESS) { @@ -288,11 +289,13 @@ int main(int argc, char** argv) goto clean_exit; } + // Validate UL-SCH CRC check if (!data_rx[0].crc) { ERROR("Failed to match CRC; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, pusch_cfg.grant.tb[0].tbs); goto clean_exit; } + // Validate UL-SCH payload if (memcmp(data_tx[0].payload, data_rx[0].payload, pusch_cfg.grant.tb[0].tbs / 8) != 0) { ERROR("Failed to match Tx/Rx data; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, pusch_cfg.grant.tb[0].tbs); printf("Tx data: "); @@ -302,6 +305,26 @@ int main(int argc, char** argv) goto clean_exit; } + // Validate UCI is decoded successfully + if (nof_ack_bits > 0 || nof_csi_bits > 0) { + if (!data_rx[0].uci.valid) { + ERROR("UCI data was not decoded ok"); + goto clean_exit; + } + } + + // Validate HARQ-ACK is decoded successfully + if (nof_ack_bits > 0) { + if (memcmp(data_tx[0].uci.ack, data_rx[0].uci.ack, nof_ack_bits) != 0) { + ERROR("UCI HARQ-ACK bits are unmatched"); + printf("Tx data: "); + srslte_vec_fprint_byte(stdout, data_tx[0].uci.ack, nof_ack_bits); + printf("Rx data: "); + srslte_vec_fprint_byte(stdout, data_rx[0].uci.ack, nof_ack_bits); + goto clean_exit; + } + } + printf("n_prb=%d; mcs=%d; TBS=%d; EVM=%f; PASSED!\n", n_prb, mcs, pusch_cfg.grant.tb[0].tbs, data_rx[0].evm); } } diff --git a/lib/src/phy/phch/uci_nr.c b/lib/src/phy/phch/uci_nr.c index 1e4ce8dc2..5dd7b3100 100644 --- a/lib/src/phy/phch/uci_nr.c +++ b/lib/src/phy/phch/uci_nr.c @@ -467,8 +467,6 @@ static int uci_nr_decode_3_11_bit(srslte_uci_nr_t* q, return SRSLTE_SUCCESS; } -#define CEIL(NUM, DEN) (((NUM) + ((DEN)-1)) / (DEN)) - static int uci_nr_encode_11_1706_bit(srslte_uci_nr_t* q, const srslte_uci_cfg_nr_t* cfg, uint32_t A, uint8_t* o, uint32_t E_uci) { @@ -487,7 +485,7 @@ uci_nr_encode_11_1706_bit(srslte_uci_nr_t* q, const srslte_uci_cfg_nr_t* cfg, ui if (I_seg == 1) { C = 2; } - uint32_t A_prime = CEIL(A, C) * C; + uint32_t A_prime = SRSLTE_CEIL(A, C) * C; // Get polar code uint32_t K_r = A_prime / C + L; @@ -570,7 +568,7 @@ static int uci_nr_decode_11_1706_bit(srslte_uci_nr_t* q, if (I_seg == 1) { C = 2; } - uint32_t A_prime = CEIL(A, C) * C; + uint32_t A_prime = SRSLTE_CEIL(A, C) * C; // Get polar code uint32_t K_r = A_prime / C + L; @@ -664,13 +662,10 @@ static int uci_nr_encode(srslte_uci_nr_t* q, const srslte_uci_cfg_nr_t* uci_cfg, return SRSLTE_ERROR; } -static int uci_nr_decode(srslte_uci_nr_t* q, - const srslte_uci_cfg_nr_t* uci_cfg, - int8_t* llr, - uint32_t E_uci, - srslte_uci_value_nr_t* uci_value) +static int +uci_nr_decode(srslte_uci_nr_t* q, const srslte_uci_cfg_nr_t* uci_cfg, int8_t* llr, uint32_t E_uci, bool* valid) { - if (q == NULL || uci_cfg == NULL || uci_value == NULL || llr == NULL) { + if (q == NULL || uci_cfg == NULL || valid == NULL || llr == NULL) { return SRSLTE_ERROR_INVALID_INPUTS; } @@ -687,22 +682,17 @@ static int uci_nr_decode(srslte_uci_nr_t* q, } else if (A == 2) { ERROR("Not implemented"); } else if (A <= 11) { - if (uci_nr_decode_3_11_bit(q, uci_cfg, A, llr, E_uci, &uci_value->valid) < SRSLTE_SUCCESS) { + if (uci_nr_decode_3_11_bit(q, uci_cfg, A, llr, E_uci, valid) < SRSLTE_SUCCESS) { return SRSLTE_ERROR; } } 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) { + if (uci_nr_decode_11_1706_bit(q, uci_cfg, A, llr, E_uci, valid) < SRSLTE_SUCCESS) { return SRSLTE_ERROR; } } else { ERROR("Invalid number of bits (A=%d)", A); } - // Unpack bits - if (uci_nr_unpack_pucch(uci_cfg, q->bit_sequence, uci_value) < SRSLTE_SUCCESS) { - return SRSLTE_ERROR; - } - return SRSLTE_SUCCESS; } @@ -788,10 +778,22 @@ int srslte_uci_nr_decode_pucch(srslte_uci_nr_t* q, int E_uci = uci_nr_pucch_E_uci(pucch_resource_cfg, uci_cfg, E_tot); if (E_uci < SRSLTE_SUCCESS) { + ERROR("Error calculating number of encoded PUCCH UCI bits"); return SRSLTE_ERROR; } - return uci_nr_decode(q, uci_cfg, llr, E_uci, value); + if (uci_nr_decode(q, uci_cfg, llr, E_uci, &value->valid) < SRSLTE_SUCCESS) { + ERROR("Error decoding UCI bits"); + return SRSLTE_ERROR; + } + + // Unpack bits + if (uci_nr_unpack_pucch(uci_cfg, q->bit_sequence, value) < SRSLTE_SUCCESS) { + ERROR("Error unpacking PUCCH UCI bits"); + return SRSLTE_ERROR; + } + + return SRSLTE_SUCCESS; } uint32_t srslte_uci_nr_total_bits(const srslte_uci_cfg_nr_t* uci_cfg) @@ -904,4 +906,42 @@ int srslte_uci_nr_encode_pusch_ack(srslte_uci_nr_t* q, } return uci_nr_encode(q, cfg, A, o, E_uci); +} + +int srslte_uci_nr_decode_pusch_ack(srslte_uci_nr_t* q, + const srslte_uci_cfg_nr_t* cfg, + int8_t* llr, + srslte_uci_value_nr_t* value) +{ + int A = cfg->o_ack; + + // Check inputs + if (q == NULL || cfg == NULL || llr == NULL || value == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + // 6.3.2.1 UCI bit sequence generation + // 6.3.2.1.1 HARQ-ACK + bool has_csi_part2 = srslte_csi_has_part2(cfg->csi, cfg->nof_csi); + if (cfg->pusch.K_sum == 0 && cfg->nof_csi > 1 && !has_csi_part2 && cfg->o_ack < 2) { + A = 2; + } + + // Compute total of encoded bits according to 6.3.2.4 Rate matching + int E_uci = srslte_uci_nr_pusch_ack_nof_bits(&cfg->pusch, A); + if (E_uci < SRSLTE_SUCCESS) { + ERROR("Error calculating number of encoded bits"); + return SRSLTE_ERROR; + } + + // Decode + if (uci_nr_decode(q, cfg, llr, E_uci, &value->valid) < SRSLTE_SUCCESS) { + ERROR("Error decoding UCI"); + return SRSLTE_ERROR; + } + + // Unpack + srslte_vec_u8_copy(value->ack, q->bit_sequence, A); + + return SRSLTE_SUCCESS; } \ No newline at end of file