static uint32_t Q_prime_ri_ack(srslte_pusch_cfg_t *cfg, uint32_t O, uint32_t O_cqi, float beta) { if (beta < 0) { ERROR("Error beta is reserved\n"); return -1; } uint32_t K = cfg->K_segm; // If not carrying UL-SCH, get Q_prime according to 5.2.4.1 if (K == 0) { if (O_cqi <= 11) { K = O_cqi; } else { K = O_cqi + 8; } } uint32_t x = (uint32_t)ceilf((float)O * cfg->grant.L_prb * SRSLTE_NRE * cfg->grant.nof_symb * beta / K); uint32_t Q_prime = SRSLTE_MIN(x, 4 * cfg->grant.L_prb * SRSLTE_NRE); return Q_prime; }
static uint32_t Q_prime_ri_ack(srslte_pusch_cfg_t *cfg, uint32_t O, uint32_t O_cqi, float beta) { if (beta < 0) { fprintf(stderr, "Error beta is reserved\n"); return -1; } uint32_t K = cfg->cb_segm.C1*cfg->cb_segm.K1 + cfg->cb_segm.C2*cfg->cb_segm.K2; // If not carrying UL-SCH, get Q_prime according to 5.2.4.1 if (K == 0) { if (O_cqi <= 11) { K = O_cqi; } else { K = O_cqi+8; } } uint32_t x = (uint32_t) ceilf((float) O*cfg->grant.M_sc_init*cfg->nbits.nof_symb*beta/K); uint32_t Q_prime = SRSLTE_MIN(x, 4*cfg->grant.M_sc); return Q_prime; }
int srslte_ue_dl_find_ul_dci(srslte_ue_dl_t* q, srslte_dl_sf_cfg_t* sf, srslte_ue_dl_cfg_t* cfg, uint16_t rnti, srslte_dci_ul_t dci_ul[SRSLTE_MAX_DCI_MSG]) { srslte_dci_msg_t dci_msg[SRSLTE_MAX_DCI_MSG]; uint32_t nof_msg = 0; if (rnti) { /* Do not search if an UL DCI is already pending */ if (q->pending_ul_dci_count) { nof_msg = SRSLTE_MIN(SRSLTE_MAX_DCI_MSG, q->pending_ul_dci_count); q->pending_ul_dci_count = 0; memcpy(dci_msg, q->pending_ul_dci_msg, sizeof(srslte_dci_msg_t) * nof_msg); } else { uint32_t sf_idx = sf->tti % 10; uint32_t cfi = sf->cfi; set_mi_value(q, sf, cfg); // Configure and run DCI blind search dci_blind_search_t search_space; search_space.nof_locations = 0; dci_blind_search_t* current_ss = &search_space; if (q->pregen_rnti == rnti) { current_ss = &q->current_ss_ue[MI_IDX(sf_idx)][cfi - 1][sf_idx]; } else { // If locations are not pre-generated, generate them now current_ss->nof_locations = srslte_pdcch_ue_locations(&q->pdcch, sf, current_ss->loc, MAX_CANDIDATES_UE, rnti); } current_ss->format = SRSLTE_DCI_FORMAT0; INFO("Searching UL C-RNTI in %d ue locations\n", search_space.nof_locations); nof_msg = dci_blind_search(q, sf, rnti, current_ss, &cfg->dci_cfg, dci_msg); } // Unpack DCI messages for (uint32_t i = 0; i < nof_msg; i++) { if (srslte_dci_msg_unpack_pusch(&q->cell, sf, &cfg->dci_cfg, &dci_msg[i], &dci_ul[i])) { ERROR("Unpacking UL DCI\n"); return SRSLTE_ERROR; } } return nof_msg; } else { return 0; } }
static uint32_t Q_prime_cqi(srslte_pusch_cfg_t* cfg, uint32_t O, float beta, uint32_t Q_prime_ri) { uint32_t K = cfg->K_segm; uint32_t Q_prime = 0; uint32_t L = (O < 11) ? 0 : 8; uint32_t x = 999999; if (K > 0) { x = (uint32_t)ceilf((float)(O + L) * cfg->grant.L_prb * SRSLTE_NRE * cfg->grant.nof_symb * beta / K); } Q_prime = SRSLTE_MIN(x, cfg->grant.L_prb * SRSLTE_NRE * cfg->grant.nof_symb - Q_prime_ri); return Q_prime; }
static int select_ri_pmi(srslte_ue_dl_t* q, uint32_t* ri, uint32_t* pmi, float* sinr_db) { float best_sinr_db = -INFINITY; uint32_t best_pmi = 0, best_ri = 0; uint32_t max_ri = SRSLTE_MIN(q->nof_rx_antennas, q->cell.nof_ports); if (q->cell.nof_ports < 2) { /* Do nothing */ return SRSLTE_SUCCESS; } else { /* Select the best Rank indicator (RI) and Precoding Matrix Indicator (PMI) */ for (uint32_t this_ri = 0; this_ri < max_ri; this_ri++) { uint32_t this_pmi = 0; float this_sinr_db = 0.0f; if (select_pmi(q, this_ri, &this_pmi, &this_sinr_db)) { DEBUG("SINR calculation error"); return SRSLTE_ERROR; } /* Find best SINR, force maximum number of layers if SNR is higher than 30 dB */ if (this_sinr_db > best_sinr_db + 0.1 || this_sinr_db > 20.0) { best_sinr_db = this_sinr_db; best_pmi = this_pmi; best_ri = this_ri; } } } /* Set RI */ if (ri != NULL) { *ri = best_ri; } /* Set PMI */ if (pmi != NULL) { *pmi = best_pmi; } /* Set SINR */ if (sinr_db != NULL) { *sinr_db = best_sinr_db; } return SRSLTE_SUCCESS; }
static uint32_t Q_prime_cqi(srslte_pusch_cfg_t *cfg, uint32_t O, float beta, uint32_t Q_prime_ri) { uint32_t K = cfg->cb_segm.C1*cfg->cb_segm.K1 + cfg->cb_segm.C2*cfg->cb_segm.K2; uint32_t Q_prime = 0; uint32_t L = (O<11)?0:8; uint32_t x = 999999; if (K > 0) { x = (uint32_t) ceilf((float) (O+L)*cfg->grant.M_sc_init*cfg->nbits.nof_symb*beta/K); } Q_prime = SRSLTE_MIN(x, cfg->grant.M_sc * cfg->nbits.nof_symb - Q_prime_ri); return Q_prime; }
// For decoding the block-encoded CQI we use ML decoding int decode_cqi_short(srslte_uci_cqi_pusch_t *q, int16_t *q_bits, uint32_t Q, uint8_t *data, uint32_t nof_bits) { if (nof_bits <= 11 && nof_bits > 0 && q != NULL && data != NULL && q_bits != NULL) { // Accumulate all copies of the 32-length sequence if (Q>32) { int i=1; for (;i<Q/32;i++) { srslte_vec_sum_sss(&q_bits[i*32], q_bits, q_bits, 32); } srslte_vec_sum_sss(&q_bits[i*32], q_bits, q_bits, Q%32); } uint32_t max_w = 0; int32_t max_corr = INT32_MIN; for (uint32_t w=0;w<(1<<nof_bits);w++) { // Calculate correlation with pregenerated word and select maximum int32_t corr = srslte_vec_dot_prod_sss(&q->cqi_table_s[nof_bits-1][w*32], q_bits, SRSLTE_MIN(32, Q)); if (corr > max_corr) { max_corr = corr; max_w = w; } } // Convert word to bits again uint8_t *ptr = data; srslte_bit_unpack(max_w, &ptr, nof_bits); INFO("Decoded CQI: w=%d, corr=%d\n", max_w, max_corr); return SRSLTE_SUCCESS; } else { return SRSLTE_ERROR_INVALID_INPUTS; } }
/* UE downlink procedure for reporting ACK/NACK, Section 7.3 36.213 */ void srslte_ue_dl_gen_ack(srslte_ue_dl_t* q, srslte_dl_sf_cfg_t* sf, srslte_pdsch_ack_t* ack_info, srslte_uci_data_t* uci_data) { uint32_t V_dai_dl = 0; bool is_tdd_mode16 = sf->tdd_config.sf_config >= 1 && sf->tdd_config.sf_config <= 6; uint32_t nof_tb = 1; if (ack_info->transmission_mode > SRSLTE_TM2) { nof_tb = SRSLTE_MAX_CODEWORDS; } // Implementation 3GPP 36.213 V10.13.0. Section 7.3. 2nd Clause. if (q->cell.frame_type == SRSLTE_FDD) { if (ack_info->ack_nack_feedback_mode == SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_CS && uci_data->value.scheduling_request && !ack_info->is_pusch_available) { for (uint32_t cc_idx = 0; cc_idx < ack_info->nof_cc; cc_idx++) { uint32_t dtx_count = 0; bool bundle_spatial = true; for (uint32_t tb = 0; tb < nof_tb; tb++) { if (ack_info->cc[cc_idx].m[0].present && ack_info->cc[cc_idx].m[0].value[tb] != 2) { if (ack_info->cc[cc_idx].m[0].value[tb] != 1) { bundle_spatial = false; } if (cc_idx != 0) { uci_data->cfg.ack.has_scell_ack = true; } } else { dtx_count++; } } uci_data->value.ack.ack_value[cc_idx] = (uint8_t)((bundle_spatial && dtx_count != nof_tb) ? 1 : 0); } uci_data->cfg.ack.nof_acks = ack_info->nof_cc; uci_data->cfg.ack.tdd_ack_M = 1; return; } else if ((ack_info->ack_nack_feedback_mode == SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_CS && ack_info->is_pusch_available) || ack_info->ack_nack_feedback_mode == SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_PUCCH3) { uint32_t tb_count = 0; uint32_t n = 0; for (uint32_t cc_idx = 0; cc_idx < ack_info->nof_cc; cc_idx++) { for (uint32_t tb = 0; tb < nof_tb; tb++, n++) { uci_data->value.ack.ack_value[n] = ack_info->cc[cc_idx].m[0].value[tb]; if (ack_info->cc[cc_idx].m[0].present && ack_info->cc[cc_idx].m[0].value[tb] != 2) { tb_count++; } } } uci_data->cfg.ack.nof_acks = (tb_count != 0) ? n : 0; return; } } // Calculate U_dai and count number of ACK for this subframe by spatial bundling across codewords uint32_t nof_pos_acks = 0; uint32_t nof_total_acks = 0; uint32_t U_dai = 0; for (uint32_t cc_idx = 0; cc_idx < ack_info->nof_cc; cc_idx++) { for (uint32_t i = 0; i < ack_info->cc[cc_idx].M; i++) { bool bundle_spatial = false; bool first_bundle = true; for (uint32_t j = 0; j < nof_tb; j++) { if (ack_info->cc[cc_idx].m[i].present) { if (first_bundle) { bundle_spatial = ack_info->cc[cc_idx].m[i].value[j] == 1; U_dai++; first_bundle = false; } else { bundle_spatial &= ack_info->cc[cc_idx].m[i].value[j] == 1; } if (bundle_spatial) { nof_pos_acks++; } if (ack_info->cc[cc_idx].m[i].value[j] != 2) { nof_total_acks++; } } } } } for (uint32_t cc_idx = 0; cc_idx < ack_info->nof_cc; cc_idx++) { // Arrange bits for FDD or TDD Bundling or Multiplexing srslte_pdsch_ack_cc_t* ack_value = &ack_info->cc[cc_idx]; uint32_t min_k = 10; if (ack_info->cc[cc_idx].M > 0) { uci_data->cfg.ack.tdd_ack_M = ack_value->M; // ACK/NACK bundling or multiplexing and M=1 if (ack_info->tdd_ack_bundle || ack_value->M == 1) { for (uint32_t tb = 0; tb < nof_tb; tb++) { bool first_in_bundle = true; for (uint32_t k = 0; k < ack_value->M; k++) { if (ack_value->m[k].present && ack_value->m[k].value[tb] != 2) { uci_data->cfg.ack.has_scell_ack |= (cc_idx != 0); // If a grant is detected in an scell // Bundle on time domain if (first_in_bundle) { uci_data->value.ack.ack_value[nof_tb * cc_idx + tb] = ack_value->m[k].value[tb]; first_in_bundle = false; } else { uci_data->value.ack.ack_value[nof_tb * cc_idx + tb] = (uint8_t)( ((uci_data->value.ack.ack_value[nof_tb * cc_idx + tb] == 1) & (ack_value->m[k].value[tb])) ? 1 : 0); } // V_dai_dl is for the one with lowest k value if (ack_value->m[k].k < min_k || q->cell.frame_type == SRSLTE_FDD) { min_k = ack_value->m[k].k; V_dai_dl = ack_value->m[k].resource.v_dai_dl + 1; // Table 7.3-X if (cc_idx == 0) { uci_data->cfg.ack.ncce[0] = ack_info->cc[cc_idx].m[k].resource.n_cce; uci_data->cfg.ack.tdd_ack_m = k; } } } } } // ACK/NACK multiplexing and M > 1 } else { for (uint32_t k = 0; k < ack_value->M; k++) { // Bundle spatial domain bool spatial_ack = true; for (uint32_t i = 0; i < nof_tb; i++) { if (ack_value->m[k].value[i] != 2) { spatial_ack &= (ack_value->m[k].value[i] == 1); } } // In multiplexing for pusch, sort them accordingly if (ack_value->m[k].present) { uint32_t p = k; if (q->cell.frame_type == SRSLTE_TDD && ack_info->is_pusch_available && ack_info->is_grant_available) { p = ack_value->m[k].resource.v_dai_dl; } uci_data->value.ack.ack_value[ack_value->M * cc_idx + p] = (uint8_t)(spatial_ack ? 1 : 0); uci_data->cfg.ack.ncce[ack_value->M * cc_idx + p] = ack_info->cc[cc_idx].m[k].resource.n_cce; } } } } } bool missing_ack = false; // For TDD PUSCH if (q->cell.frame_type == SRSLTE_TDD && is_tdd_mode16) { ack_info->V_dai_ul++; // Table 7.3-x uci_data->cfg.ack.tdd_is_bundling = ack_info->tdd_ack_bundle; // Bundling or multiplexing and M=1 if (ack_info->tdd_ack_bundle || ack_info->cc[0].M == 1) { // 1 or 2 ACK/NACK bits uci_data->cfg.ack.nof_acks = nof_tb; // Determine if there is any missing ACK/NACK in the set and N_bundle value // Case not transmitting on PUSCH if (!ack_info->is_pusch_available) { if ((V_dai_dl != (U_dai - 1) % 4 + 1 && U_dai > 0) || U_dai == 0) { // In ul procedure 10.2, skip ACK/NACK in bundling PUCCH uci_data->cfg.ack.nof_acks = 0; if (U_dai > 0) { missing_ack = true; } } // Transmitting on PUSCH and based on detected PDCCH } else if (ack_info->is_grant_available) { if (ack_info->V_dai_ul != (U_dai - 1) % 4 + 1) { bzero(uci_data->value.ack.ack_value, nof_tb); uci_data->cfg.ack.N_bundle = ack_info->V_dai_ul + 2; } else { uci_data->cfg.ack.N_bundle = ack_info->V_dai_ul; } // do not transmit case if (ack_info->V_dai_ul == 4 && U_dai == 0) { uci_data->cfg.ack.nof_acks = 0; } // Transmitting on PUSCH not based on grant } else { if (V_dai_dl != (U_dai - 1) % 4 + 1 && U_dai > 0) { bzero(uci_data->value.ack.ack_value, nof_tb); } uci_data->cfg.ack.N_bundle = U_dai; // do not transmit case if (U_dai == 0) { uci_data->cfg.ack.nof_acks = 0; } } // In PUSCH and MIMO, nack 2nd codeword if not received, in PUCCH do not transmit if (nof_tb == 2 && uci_data->value.ack.ack_value[1] == 2 && uci_data->cfg.ack.nof_acks == 2) { if (!ack_info->is_pusch_available) { uci_data->cfg.ack.nof_acks = 1; } else { uci_data->value.ack.ack_value[1] = 0; } } // Multiplexing and M>1 } else { if (ack_info->is_pusch_available) { if (ack_info->is_grant_available) { // Do not transmit if... if (!(ack_info->V_dai_ul == 4 && U_dai == 0)) { uci_data->cfg.ack.nof_acks = ack_info->V_dai_ul; } } else { uci_data->cfg.ack.nof_acks = ack_info->cc[0].M; } // Set DTX bits to NACKs uint32_t count_acks = 0; for (uint32_t i = 0; i < uci_data->cfg.ack.nof_acks; i++) { if (uci_data->value.ack.ack_value[i] == 2) { uci_data->value.ack.ack_value[i] = 0; } else { count_acks++; } } if (!count_acks) { uci_data->cfg.ack.nof_acks = 0; } } else { uci_data->cfg.ack.nof_acks = ack_info->cc[0].M; } } } else { if (q->cell.frame_type == SRSLTE_TDD) { // And subframe config 0 uci_data->cfg.ack.N_bundle = 1; } uci_data->cfg.ack.nof_acks = nof_total_acks; } // If no pending ACK/NACK if (uci_data->cfg.ack.nof_acks == 0) { return; } // Multiple ACK/NACK responses with SR and CQI if (q->cell.frame_type == SRSLTE_TDD && uci_data->cfg.ack.nof_acks && !ack_info->is_pusch_available && (uci_data->value.scheduling_request || ((uci_data->cfg.cqi.data_enable || uci_data->cfg.cqi.ri_len) && ack_info->simul_cqi_ack))) { if (missing_ack) { uci_data->value.ack.ack_value[0] = 0; uci_data->value.ack.ack_value[1] = 0; } else { nof_pos_acks = SRSLTE_MIN(9, nof_pos_acks); uci_data->value.ack.ack_value[0] = multiple_acknack[nof_pos_acks][0]; uci_data->value.ack.ack_value[1] = multiple_acknack[nof_pos_acks][1]; } uci_data->cfg.ack.nof_acks = 2; } }