/* * Copyright 2013-2019 Software Radio Systems Limited * * This file is part of srsLTE. * * srsLTE is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * srsLTE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * A copy of the GNU Affero General Public License can be found in * the LICENSE file in the top-level directory of this distribution * and at http://www.gnu.org/licenses/. * */ #include "srslte/srslte.h" #include "srsue/hdr/phy/cc_worker.h" #include "srslte/interfaces/ue_interfaces.h" #define Error(fmt, ...) \ if (SRSLTE_DEBUG_ENABLED) \ log_h->error(fmt, ##__VA_ARGS__) #define Warning(fmt, ...) \ if (SRSLTE_DEBUG_ENABLED) \ log_h->warning(fmt, ##__VA_ARGS__) #define Info(fmt, ...) \ if (SRSLTE_DEBUG_ENABLED) \ log_h->info(fmt, ##__VA_ARGS__) #define Debug(fmt, ...) \ if (SRSLTE_DEBUG_ENABLED) \ log_h->debug(fmt, ##__VA_ARGS__) #define CURRENT_TTI (sf_cfg_dl.tti) #define CURRENT_SFIDX (sf_cfg_dl.tti % 10) #define CURRENT_TTI_TX (sf_cfg_ul.tti) using namespace asn1::rrc; namespace srsue { /************ * * Common Functions * */ cc_worker::cc_worker(uint32_t cc_idx, uint32_t max_prb, srsue::phy_common* phy, srslte::log* log_h) { ZERO_OBJECT(signal_buffer_rx); ZERO_OBJECT(signal_buffer_tx); ZERO_OBJECT(pending_dl_grant); ZERO_OBJECT(cell); ZERO_OBJECT(sf_cfg_dl); ZERO_OBJECT(sf_cfg_ul); ZERO_OBJECT(ue_dl); ZERO_OBJECT(ue_dl_cfg); ZERO_OBJECT(ue_dl_cfg.cfg.pdsch); ZERO_OBJECT(pmch_cfg); ZERO_OBJECT(chest_mbsfn_cfg); ZERO_OBJECT(chest_default_cfg); ZERO_OBJECT(ue_ul); ZERO_OBJECT(ue_ul_cfg); ZERO_OBJECT(dl_metrics); ZERO_OBJECT(ul_metrics); cell_initiated = false; this->cc_idx = cc_idx; this->phy = phy; this->log_h = log_h; for (uint32_t i = 0; i < phy->args->nof_rx_ant; i++) { signal_buffer_rx[i] = (cf_t*)srslte_vec_malloc(3 * sizeof(cf_t) * SRSLTE_SF_LEN_PRB(max_prb)); if (!signal_buffer_rx[i]) { Error("Allocating memory\n"); return; } signal_buffer_tx[i] = (cf_t*)srslte_vec_malloc(3 * sizeof(cf_t) * SRSLTE_SF_LEN_PRB(max_prb)); if (!signal_buffer_tx[i]) { Error("Allocating memory\n"); return; } } if (srslte_ue_dl_init(&ue_dl, signal_buffer_rx, max_prb, phy->args->nof_rx_ant)) { Error("Initiating UE DL\n"); return; } if (srslte_ue_ul_init(&ue_ul, signal_buffer_tx[0], max_prb)) { Error("Initiating UE UL\n"); return; } phy->set_ue_dl_cfg(&ue_dl_cfg); phy->set_ue_ul_cfg(&ue_ul_cfg); phy->set_pdsch_cfg(&ue_dl_cfg.cfg.pdsch); phy->set_pdsch_cfg(&pmch_cfg.pdsch_cfg); // set same config in PMCH decoder // Define MBSFN subframes channel estimation and save default one chest_mbsfn_cfg.filter_type = SRSLTE_CHEST_FILTER_TRIANGLE; chest_mbsfn_cfg.filter_coef[0] = 0.1; chest_mbsfn_cfg.interpolate_subframe = true; chest_mbsfn_cfg.noise_alg = SRSLTE_NOISE_ALG_PSS; chest_default_cfg = ue_dl_cfg.chest_cfg; if (phy->args->pdsch_8bit_decoder) { ue_dl.pdsch.llr_is_8bit = true; ue_dl.pdsch.dl_sch.llr_is_8bit = true; } } cc_worker::~cc_worker() { for (uint32_t i = 0; i < phy->args->nof_rx_ant; i++) { if (signal_buffer_tx[i]) { free(signal_buffer_tx[i]); } if (signal_buffer_rx[i]) { free(signal_buffer_rx[i]); } } srslte_ue_dl_free(&ue_dl); srslte_ue_ul_free(&ue_ul); } void cc_worker::reset() { bzero(&dl_metrics, sizeof(dl_metrics_t)); bzero(&ul_metrics, sizeof(ul_metrics_t)); phy_interface_rrc::phy_cfg_t empty_cfg = {}; // defaults empty_cfg.common.pucch_cnfg.delta_pucch_shift.value = pucch_cfg_common_s::delta_pucch_shift_opts::ds1; empty_cfg.common.ul_pwr_ctrl.alpha.value = alpha_r12_opts::al0; empty_cfg.common.ul_pwr_ctrl.delta_flist_pucch.delta_f_pucch_format1.value = delta_flist_pucch_s::delta_f_pucch_format1_opts::delta_f0; empty_cfg.common.ul_pwr_ctrl.delta_flist_pucch.delta_f_pucch_format1b.value = delta_flist_pucch_s::delta_f_pucch_format1b_opts::delta_f1; empty_cfg.common.ul_pwr_ctrl.delta_flist_pucch.delta_f_pucch_format2.value = delta_flist_pucch_s::delta_f_pucch_format2_opts::delta_f0; empty_cfg.common.ul_pwr_ctrl.delta_flist_pucch.delta_f_pucch_format2a.value = delta_flist_pucch_s::delta_f_pucch_format2a_opts::delta_f0; empty_cfg.common.ul_pwr_ctrl.delta_flist_pucch.delta_f_pucch_format2b.value = delta_flist_pucch_s::delta_f_pucch_format2b_opts::delta_f0; set_pcell_config(&empty_cfg); } bool cc_worker::set_cell(srslte_cell_t cell) { if (this->cell.id != cell.id || !cell_initiated) { this->cell = cell; if (srslte_ue_dl_set_cell(&ue_dl, cell)) { Error("Initiating UE DL\n"); return false; } if (srslte_ue_dl_set_mbsfn_area_id(&ue_dl, 1)) { Error("Setting mbsfn id\n"); } if (srslte_ue_ul_set_cell(&ue_ul, cell)) { Error("Initiating UE UL\n"); return false; } if (cell.frame_type == SRSLTE_TDD && !ue_dl_cfg.chest_cfg.interpolate_subframe) { ue_dl_cfg.chest_cfg.interpolate_subframe = true; log_h->console("Enabling subframe interpolation for TDD cells (recommended setting)\n"); } cell_initiated = true; } return true; } cf_t* cc_worker::get_rx_buffer(uint32_t antenna_idx) { return signal_buffer_rx[antenna_idx]; } cf_t* cc_worker::get_tx_buffer(uint32_t antenna_idx) { return signal_buffer_tx[antenna_idx]; } void cc_worker::set_tti(uint32_t tti) { sf_cfg_dl.tti = tti; sf_cfg_ul.tti = TTI_TX(tti); sf_cfg_ul.shortened = false; } void cc_worker::set_cfo(float cfo) { ue_ul_cfg.cfo_value = cfo; } float cc_worker::get_ref_cfo() { return ue_dl.chest_res.cfo; } void cc_worker::set_crnti(uint16_t rnti) { srslte_ue_dl_set_rnti(&ue_dl, rnti); srslte_ue_ul_set_rnti(&ue_ul, rnti); } void cc_worker::set_tdd_config(srslte_tdd_config_t config) { sf_cfg_dl.tdd_config = config; sf_cfg_ul.tdd_config = config; } void cc_worker::enable_pregen_signals(bool enabled) { this->pregen_enabled = enabled; } void cc_worker::fill_dci_cfg(srslte_dci_cfg_t* cfg, bool rel10) { bzero(cfg, sizeof(srslte_dci_cfg_t)); if (rel10 && phy->cif_enabled) { cfg->cif_enabled = phy->cif_enabled; } cfg->multiple_csi_request_enabled = phy->multiple_csi_request_enabled; cfg->srs_request_enabled = phy->srs_request_enabled; } void cc_worker::set_dl_pending_grant(uint32_t cc_idx, srslte_dci_dl_t* dl_dci) { if (!pending_dl_grant[cc_idx].enable) { pending_dl_grant[cc_idx].dl_dci = *dl_dci; pending_dl_grant[cc_idx].enable = true; } else { Warning("set_dl_pending_grant: cc=%d already exists\n", cc_idx); } } bool cc_worker::get_dl_pending_grant(uint32_t cc_idx, srslte_dci_dl_t* dl_dci) { if (pending_dl_grant[cc_idx].enable) { *dl_dci = pending_dl_grant[cc_idx].dl_dci; pending_dl_grant[cc_idx].enable = false; return true; } else { return false; } } /************ * * Downlink Functions * */ bool cc_worker::work_dl_regular() { bool dl_ack[SRSLTE_MAX_CODEWORDS]; mac_interface_phy::tb_action_dl_t dl_action; bool found_dl_grant = false; sf_cfg_dl.sf_type = SRSLTE_SF_NORM; // Set default channel estimation ue_dl_cfg.chest_cfg = chest_default_cfg; /* For TDD, when searching for SIB1, the ul/dl configuration is unknown and need to do blind search over * the possible mi values */ uint32_t mi_set_len; if (cell.frame_type == SRSLTE_TDD && !sf_cfg_dl.tdd_config.configured) { mi_set_len = 3; } else { mi_set_len = 1; } // Blind search PHICH mi value for (uint32_t i = 0; i < mi_set_len && !found_dl_grant; i++) { if (mi_set_len == 1) { srslte_ue_dl_set_mi_auto(&ue_dl); } else { srslte_ue_dl_set_mi_manual(&ue_dl, i); } /* Do FFT and extract PDCCH LLR, or quit if no actions are required in this subframe */ if (srslte_ue_dl_decode_fft_estimate(&ue_dl, &sf_cfg_dl, &ue_dl_cfg) < 0) { Error("Getting PDCCH FFT estimate\n"); return false; } /* Look for DL and UL dci(s) if this is PCell, or no cross-carrier scheduling is enabled */ if ((cc_idx == 0) || (!phy->cif_enabled)) { found_dl_grant = decode_pdcch_dl() > 0; decode_pdcch_ul(); } } srslte_dci_dl_t dci_dl; bool has_dl_grant = get_dl_pending_grant(cc_idx, &dci_dl); // If found a dci for this carrier, generate a grant, pass it to MAC and decode the associated PDSCH if (has_dl_grant) { // Read last TB from last retx for this pid for (uint32_t i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { ue_dl_cfg.cfg.pdsch.grant.last_tbs[i] = phy->last_dl_tbs[dci_dl.pid][cc_idx][i]; } // Generate PHY grant if (srslte_ue_dl_dci_to_pdsch_grant(&ue_dl, &sf_cfg_dl, &ue_dl_cfg, &dci_dl, &ue_dl_cfg.cfg.pdsch.grant)) { Error("Converting DCI message to DL dci\n"); return -1; } // Save TB for next retx for (uint32_t i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { phy->last_dl_tbs[dci_dl.pid][cc_idx][i] = ue_dl_cfg.cfg.pdsch.grant.last_tbs[i]; } // Set RNTI ue_dl_cfg.cfg.pdsch.rnti = dci_dl.rnti; // Generate MAC grant mac_interface_phy::mac_grant_dl_t mac_grant; dl_phy_to_mac_grant(&ue_dl_cfg.cfg.pdsch.grant, &dci_dl, &mac_grant); // Save ACK resource configuration srslte_pdsch_ack_resource_t ack_resource = {dci_dl.dai, dci_dl.location.ncce}; // Send grant to MAC and get action for this TB, then call tb_decoded to unlock MAC phy->mac->new_grant_dl(cc_idx, mac_grant, &dl_action); decode_pdsch(ack_resource, &dl_action, dl_ack); phy->mac->tb_decoded(cc_idx, mac_grant, dl_ack); } /* Decode PHICH */ decode_phich(); return true; } bool cc_worker::work_dl_mbsfn(srslte_mbsfn_cfg_t mbsfn_cfg) { mac_interface_phy::tb_action_dl_t dl_action; // Configure MBSFN settings srslte_ue_dl_set_mbsfn_area_id(&ue_dl, mbsfn_cfg.mbsfn_area_id); srslte_ue_dl_set_non_mbsfn_region(&ue_dl, mbsfn_cfg.non_mbsfn_region_length); sf_cfg_dl.sf_type = SRSLTE_SF_MBSFN; // Set MBSFN channel estimation chest_mbsfn_cfg.mbsfn_area_id = mbsfn_cfg.mbsfn_area_id; ue_dl_cfg.chest_cfg = chest_mbsfn_cfg; /* Do FFT and extract PDCCH LLR, or quit if no actions are required in this subframe */ if (srslte_ue_dl_decode_fft_estimate(&ue_dl, &sf_cfg_dl, &ue_dl_cfg) < 0) { Error("Getting PDCCH FFT estimate\n"); return false; } decode_pdcch_ul(); if (mbsfn_cfg.enable) { srslte_configure_pmch(&pmch_cfg, &cell, &mbsfn_cfg); srslte_ra_dl_compute_nof_re(&cell, &sf_cfg_dl, &pmch_cfg.pdsch_cfg.grant); // Send grant to MAC and get action for this TB, then call tb_decoded to unlock MAC phy->mac->new_mch_dl(pmch_cfg.pdsch_cfg.grant, &dl_action); bool mch_decoded = true; if (!decode_pmch(&dl_action, &mbsfn_cfg)) { mch_decoded = false; } phy->mac->mch_decoded((uint32_t)pmch_cfg.pdsch_cfg.grant.tb[0].tbs / 8, mch_decoded); } else if (mbsfn_cfg.is_mcch) { // release lock in phy_common phy->set_mch_period_stop(0); } /* Decode PHICH */ decode_phich(); return true; } void cc_worker::dl_phy_to_mac_grant(srslte_pdsch_grant_t* phy_grant, srslte_dci_dl_t* dl_dci, srsue::mac_interface_phy::mac_grant_dl_t* mac_grant) { /* Fill MAC dci structure */ mac_grant->pid = dl_dci->pid; mac_grant->rnti = dl_dci->rnti; for (int i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { mac_grant->tb[i].ndi = dl_dci->tb[i].ndi; mac_grant->tb[i].ndi_present = (dl_dci->tb[i].mcs_idx <= 28); mac_grant->tb[i].tbs = phy_grant->tb[i].enabled ? (phy_grant->tb[i].tbs / (uint32_t)8) : 0; mac_grant->tb[i].rv = phy_grant->tb[i].rv; } // If SIB dci, use PID to signal TTI to obtain RV from higher layers if (mac_grant->rnti == SRSLTE_SIRNTI) { mac_grant->pid = CURRENT_TTI; } } int cc_worker::decode_pdcch_dl() { int nof_grants = 0; srslte_dci_dl_t dci[SRSLTE_MAX_CARRIERS]; ZERO_OBJECT(dci); uint16_t dl_rnti = phy->mac->get_dl_sched_rnti(CURRENT_TTI); if (dl_rnti) { /* Blind search first without cross scheduling then with it if enabled */ for (int i = 0; i < (phy->cif_enabled ? 2 : 1) && !nof_grants; i++) { fill_dci_cfg(&ue_dl_cfg.dci_cfg, i > 0); Debug("PDCCH looking for rnti=0x%x\n", dl_rnti); nof_grants = srslte_ue_dl_find_dl_dci(&ue_dl, &sf_cfg_dl, &ue_dl_cfg, dl_rnti, dci); if (nof_grants < 0) { Error("Looking for DL grants\n"); return -1; } } // If RAR dci, save TTI if (nof_grants > 0 && SRSLTE_RNTI_ISRAR(dl_rnti)) { phy->set_rar_grant_tti(CURRENT_TTI); } for (int k = 0; k < nof_grants; k++) { // Save dci to CC index set_dl_pending_grant(dci[k].cif_present ? dci[k].cif : cc_idx, &dci[k]); // Logging char str[512]; srslte_dci_dl_info(&dci[k], str, 512); Info("PDCCH: cc=%d, %s, snr=%.1f dB\n", cc_idx, str, ue_dl.chest_res.snr_db); } } return nof_grants; } int cc_worker::decode_pdsch(srslte_pdsch_ack_resource_t ack_resource, mac_interface_phy::tb_action_dl_t* action, bool mac_acks[SRSLTE_MAX_CODEWORDS]) { srslte_pdsch_res_t pdsch_dec[SRSLTE_MAX_CODEWORDS]; ZERO_OBJECT(pdsch_dec); // See if at least 1 codeword needs to be decoded. If not need to be decode, resend ACK bool decode_enable = false; bool tb_enable[SRSLTE_MAX_CODEWORDS]; for (uint32_t tb = 0; tb < SRSLTE_MAX_CODEWORDS; tb++) { tb_enable[tb] = ue_dl_cfg.cfg.pdsch.grant.tb[tb].enabled; if (action->tb[tb].enabled) { decode_enable = true; // Prepare I/O based on action pdsch_dec[tb].payload = action->tb[tb].payload; ue_dl_cfg.cfg.pdsch.softbuffers.rx[tb] = action->tb[tb].softbuffer.rx; } else { // If this TB is duplicate, indicate PDSCH to skip it ue_dl_cfg.cfg.pdsch.grant.tb[tb].enabled = false; } } // Run PDSCH decoder if (decode_enable) { if (srslte_ue_dl_decode_pdsch(&ue_dl, &sf_cfg_dl, &ue_dl_cfg.cfg.pdsch, pdsch_dec)) { Error("ERROR: Decoding PDSCH\n"); } } // Generate ACKs for MAC and PUCCH uint8_t pending_acks[SRSLTE_MAX_CODEWORDS]; for (uint32_t tb = 0; tb < SRSLTE_MAX_CODEWORDS; tb++) { // For MAC, set to true if it's a duplicate mac_acks[tb] = action->tb[tb].enabled ? pdsch_dec[tb].crc : true; // For PUCCH feedback, need to send even if duplicate, but only those CW that were enabled before disabling in th // grant pending_acks[tb] = tb_enable[tb] ? mac_acks[tb] : 2; } if (action->generate_ack && ue_dl_cfg.cfg.pdsch.grant.nof_tb > 0) { phy->set_dl_pending_ack(&sf_cfg_dl, cc_idx, pending_acks, ack_resource); } if (decode_enable) { // Metrics dl_metrics.mcs = ue_dl_cfg.cfg.pdsch.grant.tb[0].mcs_idx; dl_metrics.turbo_iters = pdsch_dec->avg_iterations_block / 2; // Logging char str[512]; srslte_pdsch_rx_info(&ue_dl_cfg.cfg.pdsch, pdsch_dec, str, 512); Info("PDSCH: cc=%d, %s, snr=%.1f dB\n", cc_idx, str, ue_dl.chest_res.snr_db); } return SRSLTE_SUCCESS; } int cc_worker::decode_pmch(mac_interface_phy::tb_action_dl_t* action, srslte_mbsfn_cfg_t* mbsfn_cfg) { srslte_pdsch_res_t pmch_dec; pmch_cfg.area_id = mbsfn_cfg->mbsfn_area_id; pmch_cfg.pdsch_cfg.softbuffers.rx[0] = action->tb[0].softbuffer.rx; pmch_dec.payload = action->tb[0].payload; if (action->tb[0].enabled) { srslte_softbuffer_rx_reset_tbs(pmch_cfg.pdsch_cfg.softbuffers.rx[0], pmch_cfg.pdsch_cfg.grant.tb[0].tbs); if (srslte_ue_dl_decode_pmch(&ue_dl, &sf_cfg_dl, &pmch_cfg, &pmch_dec)) { Error("Decoding PMCH\n"); return -1; } // Store metrics dl_metrics.mcs = pmch_cfg.pdsch_cfg.grant.tb[0].mcs_idx; Info("PMCH: l_crb=%2d, tbs=%d, mcs=%d, crc=%s, snr=%.1f dB, n_iter=%.1f\n", pmch_cfg.pdsch_cfg.grant.nof_prb, pmch_cfg.pdsch_cfg.grant.tb[0].tbs / 8, pmch_cfg.pdsch_cfg.grant.tb[0].mcs_idx, pmch_dec.crc ? "OK" : "KO", ue_dl.chest_res.snr_db, pmch_dec.avg_iterations_block); if (pmch_dec.crc) { return 1; } } else { Warning("Received dci for TBS=0\n"); } return 0; } void cc_worker::decode_phich() { srslte_dci_ul_t dci_ul = {}; srslte_phich_grant_t phich_grant = {}; srslte_phich_res_t phich_res = {}; // Receive PHICH, in TDD might be more than one for (uint32_t I_phich = 0; I_phich < 2; I_phich++) { phich_grant.I_phich = I_phich; if (phy->get_ul_pending_ack(&sf_cfg_dl, cc_idx, &phich_grant, &dci_ul)) { if (srslte_ue_dl_decode_phich(&ue_dl, &sf_cfg_dl, &ue_dl_cfg, &phich_grant, &phich_res)) { Error("Decoding PHICH\n"); } phy->set_ul_received_ack(&sf_cfg_dl, cc_idx, phich_res.ack_value, I_phich, &dci_ul); Info("PHICH: hi=%d, corr=%.1f, I_lowest=%d, n_dmrs=%d, I_phich=%d\n", phich_res.ack_value, phich_res.distance, phich_grant.n_prb_lowest, phich_grant.n_dmrs, I_phich); } } } void cc_worker::update_measurements() { float snr_ema_coeff = phy->args->snr_ema_coeff; // In TDD, ignore special subframes without PDSCH if (srslte_sfidx_tdd_type(sf_cfg_dl.tdd_config, CURRENT_SFIDX) == SRSLTE_TDD_SF_S && srslte_sfidx_tdd_nof_dw(sf_cfg_dl.tdd_config) < 4) { return; } // Average RSRQ over DEFAULT_MEAS_PERIOD_MS then sent to RRC float rsrq_db = ue_dl.chest_res.rsrq_db; if (std::isnormal(rsrq_db)) { if (!(CURRENT_TTI % phy->pcell_report_period) || !phy->avg_rsrq_db) { phy->avg_rsrq_db = rsrq_db; } else { phy->avg_rsrq_db = SRSLTE_VEC_CMA(rsrq_db, phy->avg_rsrq_db, CURRENT_TTI % phy->pcell_report_period); } } // Average RSRP taken from CRS float rsrp_lin = ue_dl.chest_res.rsrp; if (std::isnormal(rsrp_lin)) { if (!phy->avg_rsrp[cc_idx] && !std::isnan(phy->avg_rsrp[cc_idx])) { phy->avg_rsrp[cc_idx] = SRSLTE_VEC_EMA(rsrp_lin, phy->avg_rsrp[cc_idx], snr_ema_coeff); } else { phy->avg_rsrp[cc_idx] = rsrp_lin; } } /* Correct absolute power measurements by RX gain offset */ float rsrp_dbm = ue_dl.chest_res.rsrp_dbm - phy->rx_gain_offset; // Serving cell RSRP measurements are averaged over DEFAULT_MEAS_PERIOD_MS then sent to RRC if (std::isnormal(rsrp_dbm)) { if (!(CURRENT_TTI % phy->pcell_report_period) || !phy->avg_rsrp_dbm) { phy->avg_rsrp_dbm[cc_idx] = rsrp_dbm; } else { phy->avg_rsrp_dbm[cc_idx] = SRSLTE_VEC_CMA(rsrp_dbm, phy->avg_rsrp_dbm[cc_idx], CURRENT_TTI % phy->pcell_report_period); } } // Compute PL float tx_crs_power = ue_dl_cfg.cfg.pdsch.rs_power; phy->pathloss[cc_idx] = tx_crs_power - phy->avg_rsrp_dbm[cc_idx]; // Average noise float cur_noise = ue_dl.chest_res.noise_estimate; if (std::isnormal(cur_noise)) { if (!phy->avg_noise) { phy->avg_noise[cc_idx] = cur_noise; } else { phy->avg_noise[cc_idx] = SRSLTE_VEC_EMA(cur_noise, phy->avg_noise[cc_idx], snr_ema_coeff); } } // Average snr in the log domain if (std::isnormal(ue_dl.chest_res.snr_db)) { if (!phy->avg_noise) { phy->avg_snr_db_cqi[cc_idx] = ue_dl.chest_res.snr_db; } else { phy->avg_snr_db_cqi[cc_idx] = SRSLTE_VEC_EMA(ue_dl.chest_res.snr_db, phy->avg_snr_db_cqi[cc_idx], snr_ema_coeff); } } // Store metrics dl_metrics.n = phy->avg_noise[cc_idx]; dl_metrics.rsrp = phy->avg_rsrp_dbm[cc_idx]; dl_metrics.rsrq = phy->avg_rsrq_db; dl_metrics.rssi = phy->avg_rssi_dbm; dl_metrics.pathloss = phy->pathloss[cc_idx]; dl_metrics.sinr = phy->avg_snr_db_cqi[cc_idx]; phy->set_dl_metrics(dl_metrics, cc_idx); phy->set_ul_metrics(ul_metrics, cc_idx); } /************ * * Uplink Functions * */ bool cc_worker::work_ul(srslte_uci_data_t* uci_data) { bool signal_ready; srslte_dci_ul_t dci_ul = {}; mac_interface_phy::mac_grant_ul_t ul_mac_grant = {}; mac_interface_phy::tb_action_ul_t ul_action = {}; uint32_t pid = 0; bool ul_grant_available = phy->get_ul_pending_grant(&sf_cfg_ul, cc_idx, &pid, &dci_ul); ul_mac_grant.phich_available = phy->get_ul_received_ack(&sf_cfg_ul, cc_idx, &ul_mac_grant.hi_value, ul_grant_available ? NULL : &dci_ul); // If there is no grant, pid is from current TX TTI if (!ul_grant_available) { pid = phy->ul_pidof(CURRENT_TTI_TX, &sf_cfg_ul.tdd_config); } /* Generate CQI reports if required, note that in case both aperiodic * and periodic ones present, only aperiodic is sent (36.213 section 7.2) */ if (ul_grant_available && dci_ul.cqi_request) { set_uci_aperiodic_cqi(uci_data); } else { /* Check PCell and enabled secondary cells */ if (cc_idx == 0 || phy->scell_enable[cc_idx]) { set_uci_periodic_cqi(uci_data); } } /* Send UL dci or HARQ information (from PHICH) to MAC and receive actions*/ if (ul_grant_available || ul_mac_grant.phich_available) { // Read last TB info from last retx for this PID ue_ul_cfg.ul_cfg.pusch.grant.last_tb = phy->last_ul_tb[pid][cc_idx]; // Generate PHY grant if (srslte_ue_ul_dci_to_pusch_grant(&ue_ul, &sf_cfg_ul, &ue_ul_cfg, &dci_ul, &ue_ul_cfg.ul_cfg.pusch.grant)) { Error("Converting DCI message to UL dci\n"); } // Save TBS info for next retx phy->last_ul_tb[pid][cc_idx] = ue_ul_cfg.ul_cfg.pusch.grant.tb; // Fill MAC dci ul_phy_to_mac_grant(&ue_ul_cfg.ul_cfg.pusch.grant, &dci_ul, pid, ul_grant_available, &ul_mac_grant); phy->mac->new_grant_ul(cc_idx, ul_mac_grant, &ul_action); // Calculate PUSCH Hopping procedure ue_ul_cfg.ul_cfg.hopping.current_tx_nb = ul_action.current_tx_nb; srslte_ue_ul_pusch_hopping(&ue_ul, &sf_cfg_ul, &ue_ul_cfg, &ue_ul_cfg.ul_cfg.pusch.grant); } // Set UL RNTI if (ul_grant_available || ul_mac_grant.phich_available) { ue_ul_cfg.ul_cfg.pusch.rnti = dci_ul.rnti; } else { ue_ul_cfg.ul_cfg.pucch.rnti = phy->mac->get_ul_sched_rnti(CURRENT_TTI_TX); } // PCell sends SR and ACK if (cc_idx == 0) { set_uci_sr(uci_data); // This must be called after set_uci_sr() and set_uci_*_cqi set_uci_ack(uci_data, ul_grant_available, dci_ul.dai, ul_action.tb.enabled); } // Generate uplink signal, include uci data on only PCell signal_ready = encode_uplink(&ul_action, (cc_idx == 0) ? uci_data : NULL); // Prepare to receive ACK through PHICH if (ul_action.expect_ack) { srslte_phich_grant_t phich_grant = {}; phich_grant.I_phich = 0; if (cell.frame_type == SRSLTE_TDD && sf_cfg_ul.tdd_config.sf_config == 0) { if ((sf_cfg_ul.tti % 10) == 4 || (sf_cfg_ul.tti % 10) == 9) { phich_grant.I_phich = 1; } } phich_grant.n_prb_lowest = ue_ul_cfg.ul_cfg.pusch.grant.n_prb_tilde[0]; phich_grant.n_dmrs = ue_ul_cfg.ul_cfg.pusch.grant.n_dmrs; phy->set_ul_pending_ack(&sf_cfg_ul, cc_idx, phich_grant, &dci_ul); } return signal_ready; } void cc_worker::ul_phy_to_mac_grant(srslte_pusch_grant_t* phy_grant, srslte_dci_ul_t* dci_ul, uint32_t pid, bool ul_grant_available, srsue::mac_interface_phy::mac_grant_ul_t* mac_grant) { if (mac_grant->phich_available && !dci_ul->rnti) { mac_grant->rnti = phy->mac->get_ul_sched_rnti(CURRENT_TTI); } else { mac_grant->rnti = dci_ul->rnti; } mac_grant->tb.ndi = dci_ul->tb.ndi; mac_grant->tb.ndi_present = ul_grant_available; mac_grant->tb.tbs = phy_grant->tb.tbs / (uint32_t)8; mac_grant->tb.rv = phy_grant->tb.rv; mac_grant->pid = pid; } int cc_worker::decode_pdcch_ul() { int nof_grants = 0; srslte_dci_ul_t dci[SRSLTE_MAX_CARRIERS]; ZERO_OBJECT(dci); uint16_t ul_rnti = phy->mac->get_ul_sched_rnti(CURRENT_TTI); if (ul_rnti) { /* Blind search first without cross scheduling then with it if enabled */ for (int i = 0; i < (phy->cif_enabled ? 2 : 1) && !nof_grants; i++) { fill_dci_cfg(&ue_dl_cfg.dci_cfg, i > 0); nof_grants = srslte_ue_dl_find_ul_dci(&ue_dl, &sf_cfg_dl, &ue_dl_cfg, ul_rnti, dci); if (nof_grants < 0) { Error("Looking for UL grants\n"); return -1; } } /* Convert every DCI message to UL dci */ for (int k = 0; k < nof_grants; k++) { // If the DCI does not have Carrier Indicator Field then indicate in which carrier the dci was found uint32_t cc_idx_grant = dci[k].cif_present ? dci[k].cif : cc_idx; // Save DCI phy->set_ul_pending_grant(&sf_cfg_dl, cc_idx_grant, &dci[k]); // Logging char str[512]; srslte_dci_ul_info(&dci[k], str, 512); Info("PDCCH: cc=%d, %s, snr=%.1f dB\n", cc_idx_grant, str, ue_dl.chest_res.snr_db); } } return nof_grants; } bool cc_worker::encode_uplink(mac_interface_phy::tb_action_ul_t* action, srslte_uci_data_t* uci_data) { srslte_pusch_data_t data = {}; ue_ul_cfg.cc_idx = cc_idx; // Setup input data if (action) { data.ptr = action->tb.payload; ue_ul_cfg.ul_cfg.pusch.softbuffers.tx = action->tb.softbuffer.tx; } // Set UCI data and configuration if (uci_data) { data.uci = uci_data->value; ue_ul_cfg.ul_cfg.pusch.uci_cfg = uci_data->cfg; ue_ul_cfg.ul_cfg.pucch.uci_cfg = uci_data->cfg; } else { ZERO_OBJECT(ue_ul_cfg.ul_cfg.pusch.uci_cfg); ZERO_OBJECT(ue_ul_cfg.ul_cfg.pucch.uci_cfg); } // Use RV from higher layers ue_ul_cfg.ul_cfg.pusch.grant.tb.rv = action->tb.rv; // Setup PUSCH grant ue_ul_cfg.grant_available = action->tb.enabled; // Set UL RNTI ue_ul_cfg.ul_cfg.pucch.rnti = phy->mac->get_ul_sched_rnti(CURRENT_TTI_TX); // Encode signal int ret = srslte_ue_ul_encode(&ue_ul, &sf_cfg_ul, &ue_ul_cfg, &data); if (ret < 0) { Error("Encoding UL cc=%d\n", cc_idx); } // Store metrics if (action->tb.enabled) { ul_metrics.mcs = ue_ul_cfg.ul_cfg.pusch.grant.tb.mcs_idx; } // Logging char str[512]; if (srslte_ue_ul_info(&ue_ul_cfg, &sf_cfg_ul, &data.uci, str, 512)) { Info("%s\n", str); } return ret > 0; } void cc_worker::set_uci_sr(srslte_uci_data_t* uci_data) { if (srslte_ue_ul_gen_sr(&ue_ul_cfg, &sf_cfg_ul, uci_data, phy->sr_enabled)) { if (phy->sr_enabled) { phy->sr_last_tx_tti = CURRENT_TTI_TX; phy->sr_enabled = false; } } } uint32_t cc_worker::get_wideband_cqi() { int cqi_fixed = phy->args->cqi_fixed; int cqi_max = phy->args->cqi_max; uint32_t wb_cqi_value = srslte_cqi_from_snr(phy->avg_snr_db_cqi[cc_idx] + ue_dl_cfg.snr_to_cqi_offset); if (cqi_fixed >= 0) { wb_cqi_value = cqi_fixed; } else if (cqi_max >= 0 && wb_cqi_value > (uint32_t)cqi_max) { wb_cqi_value = cqi_max; } return wb_cqi_value; } void cc_worker::set_uci_periodic_cqi(srslte_uci_data_t* uci_data) { srslte_ue_dl_gen_cqi_periodic(&ue_dl, &ue_dl_cfg, get_wideband_cqi(), CURRENT_TTI_TX, uci_data); } void cc_worker::set_uci_aperiodic_cqi(srslte_uci_data_t* uci_data) { if (ue_dl_cfg.cfg.cqi_report.aperiodic_configured) { srslte_ue_dl_gen_cqi_aperiodic(&ue_dl, &ue_dl_cfg, get_wideband_cqi(), uci_data); } else { Warning("Received CQI request but aperiodic mode is not configured\n"); } } void cc_worker::set_uci_ack(srslte_uci_data_t* uci_data, bool is_grant_available, uint32_t V_dai_ul, bool is_pusch_available) { srslte_pdsch_ack_t ack_info = {}; uint32_t nof_configured_carriers = 0; // Only PCell generates ACK for all SCell for (uint32_t cc_idx = 0; cc_idx < phy->args->nof_carriers; cc_idx++) { if (cc_idx == 0 || phy->scell_configured[cc_idx]) { phy->get_dl_pending_ack(&sf_cfg_ul, cc_idx, &ack_info.cc[cc_idx]); nof_configured_carriers++; } } // Set ACK length for CA (default value is set to DTX) if (ue_ul_cfg.ul_cfg.pucch.ack_nack_feedback_mode != SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_NORMAL) { if (ue_dl_cfg.cfg.tm > SRSLTE_TM2) { /* TM3, TM4 */ uci_data->cfg.ack.nof_acks = nof_configured_carriers * SRSLTE_MAX_CODEWORDS; } else { /* TM1, TM2 */ uci_data->cfg.ack.nof_acks = nof_configured_carriers; } } // Configure ACK parameters ack_info.is_grant_available = is_grant_available; ack_info.is_pusch_available = is_pusch_available; ack_info.V_dai_ul = V_dai_ul; ack_info.tdd_ack_bundle = ue_ul_cfg.ul_cfg.pucch.tdd_ack_bundle; ack_info.simul_cqi_ack = ue_ul_cfg.ul_cfg.pucch.simul_cqi_ack; ack_info.ack_nack_feedback_mode = ue_ul_cfg.ul_cfg.pucch.ack_nack_feedback_mode; ack_info.nof_cc = nof_configured_carriers; ack_info.transmission_mode = ue_dl_cfg.cfg.tm; // Generate ACK/NACK bits srslte_ue_dl_gen_ack(&ue_dl, &sf_cfg_dl, &ack_info, uci_data); } float cc_worker::set_power(float tx_power) { float gain = 0; /* Check if UL power control is enabled */ if (phy->args->ul_pwr_ctrl_en) { /* Adjust maximum power if it changes significantly */ if (tx_power < phy->cur_radio_power - 5 || tx_power > phy->cur_radio_power + 5) { phy->cur_radio_power = tx_power; float radio_tx_power = phy->cur_radio_power; gain = phy->get_radio()->set_tx_power(radio_tx_power); } } return gain; } /************ * * Configuration Functions * */ srslte_cqi_report_mode_t cc_worker::aperiodic_mode(cqi_report_mode_aperiodic_e mode) { switch (mode) { case cqi_report_mode_aperiodic_e::rm12: return SRSLTE_CQI_MODE_12; case cqi_report_mode_aperiodic_e::rm20: return SRSLTE_CQI_MODE_20; case cqi_report_mode_aperiodic_e::rm22: return SRSLTE_CQI_MODE_22; case cqi_report_mode_aperiodic_e::rm30: return SRSLTE_CQI_MODE_30; case cqi_report_mode_aperiodic_e::rm31: return SRSLTE_CQI_MODE_31; case cqi_report_mode_aperiodic_e::rm10_v1310: case cqi_report_mode_aperiodic_e::rm11_v1310: case cqi_report_mode_aperiodic_e::rm32_v1250: fprintf(stderr, "Aperiodic mode %s not handled\n", mode.to_string().c_str()); default: return SRSLTE_CQI_MODE_NA; } } void cc_worker::parse_antenna_info(phys_cfg_ded_s* dedicated) { if (dedicated->ant_info_r10_present) { // Parse Release 10 ant_info_ded_r10_s::tx_mode_r10_e_::options tx_mode = dedicated->ant_info_r10->explicit_value_r10().tx_mode_r10.value; if ((srslte_tm_t)tx_mode < SRSLTE_TMINV) { ue_dl_cfg.cfg.tm = (srslte_tm_t)tx_mode; } else { fprintf(stderr, "Transmission mode (R10) %s is not supported\n", dedicated->ant_info_r10->explicit_value_r10().tx_mode_r10.to_string().c_str()); } } else if (dedicated->ant_info_present && dedicated->ant_info.type() == phys_cfg_ded_s::ant_info_c_::types::explicit_value) { // Parse Release 8 ant_info_ded_s::tx_mode_e_::options tx_mode = dedicated->ant_info.explicit_value().tx_mode.value; if ((srslte_tm_t)tx_mode < SRSLTE_TMINV) { ue_dl_cfg.cfg.tm = (srslte_tm_t)tx_mode; } else { fprintf(stderr, "Transmission mode (R8) %s is not supported\n", dedicated->ant_info.explicit_value().tx_mode.to_string().c_str()); } } else { if (cell.nof_ports == 1) { // No antenna info provided ue_dl_cfg.cfg.tm = SRSLTE_TM1; } else { // No antenna info provided ue_dl_cfg.cfg.tm = SRSLTE_TM2; } } } void cc_worker::parse_pucch_config(phy_interface_rrc::phy_cfg_t* phy_cfg) { phy_interface_rrc::phy_cfg_common_t* common = &phy_cfg->common; phys_cfg_ded_s* dedicated = &phy_cfg->dedicated; /* PUCCH configuration */ bzero(&ue_ul_cfg.ul_cfg.pucch, sizeof(srslte_pucch_cfg_t)); ue_ul_cfg.ul_cfg.pucch.delta_pucch_shift = common->pucch_cnfg.delta_pucch_shift.to_number(); ue_ul_cfg.ul_cfg.pucch.N_cs = common->pucch_cnfg.n_cs_an; ue_ul_cfg.ul_cfg.pucch.n_rb_2 = common->pucch_cnfg.n_rb_cqi; /* PUCCH Scheduling configuration */ ue_ul_cfg.ul_cfg.pucch.n_pucch_1[0] = 0; // TODO: n_pucch_1 for SPS ue_ul_cfg.ul_cfg.pucch.n_pucch_1[1] = 0; ue_ul_cfg.ul_cfg.pucch.n_pucch_1[2] = 0; ue_ul_cfg.ul_cfg.pucch.n_pucch_1[3] = 0; ue_ul_cfg.ul_cfg.pucch.N_pucch_1 = common->pucch_cnfg.n1_pucch_an; if (dedicated->cqi_report_cfg.cqi_report_periodic_present and dedicated->cqi_report_cfg.cqi_report_periodic.type().value == setup_e::setup) { ue_ul_cfg.ul_cfg.pucch.n_pucch_2 = dedicated->cqi_report_cfg.cqi_report_periodic.setup().cqi_pucch_res_idx; ue_ul_cfg.ul_cfg.pucch.simul_cqi_ack = dedicated->cqi_report_cfg.cqi_report_periodic.setup().simul_ack_nack_and_cqi; } else { // FIXME: put is_pucch_configured flag here? ue_ul_cfg.ul_cfg.pucch.n_pucch_2 = 0; ue_ul_cfg.ul_cfg.pucch.simul_cqi_ack = false; } /* SR configuration */ if (dedicated->sched_request_cfg_present and dedicated->sched_request_cfg.type() == setup_e::setup) { ue_ul_cfg.ul_cfg.pucch.I_sr = dedicated->sched_request_cfg.setup().sr_cfg_idx; ue_ul_cfg.ul_cfg.pucch.n_pucch_sr = dedicated->sched_request_cfg.setup().sr_pucch_res_idx; ue_ul_cfg.ul_cfg.pucch.sr_configured = true; } else { ue_ul_cfg.ul_cfg.pucch.I_sr = 0; ue_ul_cfg.ul_cfg.pucch.n_pucch_sr = 0; ue_ul_cfg.ul_cfg.pucch.sr_configured = false; } if (dedicated->pucch_cfg_ded.tdd_ack_nack_feedback_mode_present) { ue_ul_cfg.ul_cfg.pucch.tdd_ack_bundle = dedicated->pucch_cfg_ded.tdd_ack_nack_feedback_mode == pucch_cfg_ded_s::tdd_ack_nack_feedback_mode_e_::bundling; } else { ue_ul_cfg.ul_cfg.pucch.tdd_ack_bundle = false; } if (dedicated->pucch_cfg_ded_v1020_present) { pucch_cfg_ded_v1020_s* pucch_cfg_ded = dedicated->pucch_cfg_ded_v1020.get(); if (pucch_cfg_ded->pucch_format_r10_present) { typedef pucch_cfg_ded_v1020_s::pucch_format_r10_c_ pucch_format_r10_t; pucch_format_r10_t* pucch_format_r10 = &pucch_cfg_ded->pucch_format_r10; if (pucch_format_r10->type() == pucch_format_r10_t::types::format3_r10) { // Select feedback mode ue_ul_cfg.ul_cfg.pucch.ack_nack_feedback_mode = SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_PUCCH3; pucch_format3_conf_r13_s* format3_r13 = &pucch_format_r10->format3_r10(); for (uint32_t n = 0; n < SRSLTE_MIN(format3_r13->n3_pucch_an_list_r13.size(), SRSLTE_PUCCH_SIZE_AN_CS); n++) { ue_ul_cfg.ul_cfg.pucch.n3_pucch_an_list[n] = format3_r13->n3_pucch_an_list_r13[n]; } if (format3_r13->two_ant_port_activ_pucch_format3_r13_present) { if (format3_r13->two_ant_port_activ_pucch_format3_r13.type() == setup_e::setup) { // TODO: UL MIMO Configure PUCCH two antenna port } else { // TODO: UL MIMO Disable two antenna port } } } else if (pucch_format_r10->type() == pucch_cfg_ded_v1020_s::pucch_format_r10_c_::types::ch_sel_r10) { typedef pucch_format_r10_t::ch_sel_r10_s_ ch_sel_r10_t; ch_sel_r10_t* ch_sel_r10 = &pucch_format_r10->ch_sel_r10(); if (ch_sel_r10->n1_pucch_an_cs_r10_present) { typedef ch_sel_r10_t::n1_pucch_an_cs_r10_c_ n1_pucch_an_cs_r10_t; n1_pucch_an_cs_r10_t* n1_pucch_an_cs_r10 = &ch_sel_r10->n1_pucch_an_cs_r10; if (n1_pucch_an_cs_r10->type() == setup_e::setup) { // Select feedback mode ue_ul_cfg.ul_cfg.pucch.ack_nack_feedback_mode = SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_CS; typedef n1_pucch_an_cs_r10_t::setup_s_::n1_pucch_an_cs_list_r10_l_ n1_pucch_an_cs_list_r10_t; n1_pucch_an_cs_list_r10_t n1_pucch_an_cs_list = ch_sel_r10->n1_pucch_an_cs_r10.setup().n1_pucch_an_cs_list_r10; for (uint32_t i = 0; i < SRSLTE_MIN(n1_pucch_an_cs_list.size(), SRSLTE_PUCCH_NOF_AN_CS); i++) { n1_pucch_an_cs_r10_l n1_pucch_an_cs = n1_pucch_an_cs_list[i]; for (uint32_t j = 0; j < SRSLTE_PUCCH_SIZE_AN_CS; j++) { ue_ul_cfg.ul_cfg.pucch.n1_pucch_an_cs[j][i] = n1_pucch_an_cs[j]; } } } else { ue_ul_cfg.ul_cfg.pucch.ack_nack_feedback_mode = SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_NORMAL; } } } else { // Do nothing } } } } /* Translates RRC structs into PHY structs */ void cc_worker::set_pcell_config(phy_interface_rrc::phy_cfg_t* phy_cfg) { phy_interface_rrc::phy_cfg_common_t* common = &phy_cfg->common; phys_cfg_ded_s* dedicated = &phy_cfg->dedicated; // Configure PDSCH if (dedicated->pdsch_cfg_ded_present && common->pdsch_cnfg.p_b < 4) { ue_dl_cfg.cfg.pdsch.p_a = dedicated->pdsch_cfg_ded.p_a.to_number(); ue_dl_cfg.cfg.pdsch.p_b = common->pdsch_cnfg.p_b; ue_dl_cfg.cfg.pdsch.power_scale = true; } else { ue_dl_cfg.cfg.pdsch.power_scale = false; } ue_dl_cfg.cfg.pdsch.rs_power = (float)common->pdsch_cnfg.ref_sig_pwr; parse_antenna_info(dedicated); // Configure PUSCH ue_ul_cfg.ul_cfg.pusch.enable_64qam = phy->args->ue_category >= 5 && phy_cfg->common.pusch_cnfg.pusch_cfg_basic.enable64_qam; /* PUSCH DMRS signal configuration */ bzero(&ue_ul_cfg.ul_cfg.dmrs, sizeof(srslte_refsignal_dmrs_pusch_cfg_t)); ue_ul_cfg.ul_cfg.dmrs.group_hopping_en = common->pusch_cnfg.ul_ref_sigs_pusch.group_hop_enabled; ue_ul_cfg.ul_cfg.dmrs.sequence_hopping_en = common->pusch_cnfg.ul_ref_sigs_pusch.seq_hop_enabled; ue_ul_cfg.ul_cfg.dmrs.cyclic_shift = common->pusch_cnfg.ul_ref_sigs_pusch.cyclic_shift; ue_ul_cfg.ul_cfg.dmrs.delta_ss = common->pusch_cnfg.ul_ref_sigs_pusch.group_assign_pusch; /* PUSCH Hopping configuration */ bzero(&ue_ul_cfg.ul_cfg.hopping, sizeof(srslte_pusch_hopping_cfg_t)); ue_ul_cfg.ul_cfg.hopping.n_sb = common->pusch_cnfg.pusch_cfg_basic.n_sb; ue_ul_cfg.ul_cfg.hopping.hop_mode = common->pusch_cnfg.pusch_cfg_basic.hop_mode.value == pusch_cfg_common_s::pusch_cfg_basic_s_::hop_mode_e_::intra_and_inter_sub_frame ? ue_ul_cfg.ul_cfg.hopping.SRSLTE_PUSCH_HOP_MODE_INTRA_SF : ue_ul_cfg.ul_cfg.hopping.SRSLTE_PUSCH_HOP_MODE_INTER_SF; ue_ul_cfg.ul_cfg.hopping.hopping_offset = common->pusch_cnfg.pusch_cfg_basic.pusch_hop_offset; /* PUSCH UCI configuration */ bzero(&ue_ul_cfg.ul_cfg.pusch.uci_offset, sizeof(srslte_uci_offset_cfg_t)); ue_ul_cfg.ul_cfg.pusch.uci_offset.I_offset_ack = dedicated->pusch_cfg_ded.beta_offset_ack_idx; ue_ul_cfg.ul_cfg.pusch.uci_offset.I_offset_cqi = dedicated->pusch_cfg_ded.beta_offset_cqi_idx; ue_ul_cfg.ul_cfg.pusch.uci_offset.I_offset_ri = dedicated->pusch_cfg_ded.beta_offset_ri_idx; parse_pucch_config(phy_cfg); /* SRS Configuration */ bzero(&ue_ul_cfg.ul_cfg.srs, sizeof(srslte_refsignal_srs_cfg_t)); ue_ul_cfg.ul_cfg.srs.configured = dedicated->srs_ul_cfg_ded_present and dedicated->srs_ul_cfg_ded.type() == setup_e::setup and common->srs_ul_cnfg.type() == setup_e::setup; if (ue_ul_cfg.ul_cfg.srs.configured) { ue_ul_cfg.ul_cfg.srs.I_srs = dedicated->srs_ul_cfg_ded.setup().srs_cfg_idx; ue_ul_cfg.ul_cfg.srs.B = dedicated->srs_ul_cfg_ded.setup().srs_bw; ue_ul_cfg.ul_cfg.srs.b_hop = dedicated->srs_ul_cfg_ded.setup().srs_hop_bw; ue_ul_cfg.ul_cfg.srs.n_rrc = dedicated->srs_ul_cfg_ded.setup().freq_domain_position; ue_ul_cfg.ul_cfg.srs.k_tc = dedicated->srs_ul_cfg_ded.setup().tx_comb; ue_ul_cfg.ul_cfg.srs.n_srs = dedicated->srs_ul_cfg_ded.setup().cyclic_shift; ue_ul_cfg.ul_cfg.srs.simul_ack = common->srs_ul_cnfg.setup().ack_nack_srs_simul_tx; ue_ul_cfg.ul_cfg.srs.bw_cfg = common->srs_ul_cnfg.setup().srs_bw_cfg.to_number(); ue_ul_cfg.ul_cfg.srs.subframe_config = common->srs_ul_cnfg.setup().srs_sf_cfg.to_number(); } /* UL power control configuration */ bzero(&ue_ul_cfg.ul_cfg.power_ctrl, sizeof(srslte_ue_ul_powerctrl_t)); ue_ul_cfg.ul_cfg.power_ctrl.p0_nominal_pusch = common->ul_pwr_ctrl.p0_nominal_pusch; ue_ul_cfg.ul_cfg.power_ctrl.alpha = common->ul_pwr_ctrl.alpha.to_number(); ue_ul_cfg.ul_cfg.power_ctrl.p0_nominal_pucch = common->ul_pwr_ctrl.p0_nominal_pucch; ue_ul_cfg.ul_cfg.power_ctrl.delta_f_pucch[0] = common->ul_pwr_ctrl.delta_flist_pucch.delta_f_pucch_format1.to_number(); ue_ul_cfg.ul_cfg.power_ctrl.delta_f_pucch[1] = common->ul_pwr_ctrl.delta_flist_pucch.delta_f_pucch_format1b.to_number(); ue_ul_cfg.ul_cfg.power_ctrl.delta_f_pucch[2] = common->ul_pwr_ctrl.delta_flist_pucch.delta_f_pucch_format2.to_number(); ue_ul_cfg.ul_cfg.power_ctrl.delta_f_pucch[3] = common->ul_pwr_ctrl.delta_flist_pucch.delta_f_pucch_format2a.to_number(); ue_ul_cfg.ul_cfg.power_ctrl.delta_f_pucch[4] = common->ul_pwr_ctrl.delta_flist_pucch.delta_f_pucch_format2b.to_number(); ue_ul_cfg.ul_cfg.power_ctrl.delta_preamble_msg3 = common->ul_pwr_ctrl.delta_preamb_msg3; ue_ul_cfg.ul_cfg.power_ctrl.p0_ue_pusch = dedicated->ul_pwr_ctrl_ded.p0_ue_pusch; ue_ul_cfg.ul_cfg.power_ctrl.delta_mcs_based = dedicated->ul_pwr_ctrl_ded.delta_mcs_enabled == ul_pwr_ctrl_ded_s::delta_mcs_enabled_e_::en0; ue_ul_cfg.ul_cfg.power_ctrl.acc_enabled = dedicated->ul_pwr_ctrl_ded.accumulation_enabled; ue_ul_cfg.ul_cfg.power_ctrl.p0_ue_pucch = dedicated->ul_pwr_ctrl_ded.p0_ue_pucch; ue_ul_cfg.ul_cfg.power_ctrl.p_srs_offset = dedicated->ul_pwr_ctrl_ded.p_srs_offset; /* CQI configuration */ bzero(&ue_dl_cfg.cfg.cqi_report, sizeof(srslte_cqi_report_cfg_t)); ue_dl_cfg.cfg.cqi_report.periodic_configured = dedicated->cqi_report_cfg.cqi_report_periodic_present and dedicated->cqi_report_cfg.cqi_report_periodic.type() == setup_e::setup; if (ue_dl_cfg.cfg.cqi_report.periodic_configured) { ue_dl_cfg.cfg.cqi_report.pmi_idx = dedicated->cqi_report_cfg.cqi_report_periodic.setup().cqi_pmi_cfg_idx; ue_dl_cfg.cfg.cqi_report.format_is_subband = dedicated->cqi_report_cfg.cqi_report_periodic.setup().cqi_format_ind_periodic.type().value == cqi_report_periodic_c::setup_s_::cqi_format_ind_periodic_c_::types::subband_cqi; if (ue_dl_cfg.cfg.cqi_report.format_is_subband) { ue_dl_cfg.cfg.cqi_report.subband_size = dedicated->cqi_report_cfg.cqi_report_periodic.setup().cqi_format_ind_periodic.subband_cqi().k; } if (dedicated->cqi_report_cfg.cqi_report_periodic.setup().ri_cfg_idx_present) { if (cell.nof_ports > 1) { log_h->error("Warning: Received Rank Indication report configuration but only 1 antenna is available\n"); } ue_dl_cfg.cfg.cqi_report.ri_idx = dedicated->cqi_report_cfg.cqi_report_periodic.setup().ri_cfg_idx; ue_dl_cfg.cfg.cqi_report.ri_idx_present = true; } else { ue_dl_cfg.cfg.cqi_report.ri_idx_present = false; } } if (dedicated->cqi_report_cfg.cqi_report_mode_aperiodic_present) { ue_dl_cfg.cfg.cqi_report.aperiodic_configured = true; ue_dl_cfg.cfg.cqi_report.aperiodic_mode = aperiodic_mode(dedicated->cqi_report_cfg.cqi_report_mode_aperiodic); } if (pregen_enabled) { Info("Pre-generating UL signals...\n"); srslte_ue_ul_pregen_signals(&ue_ul, &ue_ul_cfg); Info("Done pre-generating signals worker...\n"); } } void cc_worker::set_scell_config(asn1::rrc::scell_to_add_mod_r10_s* phy_cfg) { if (phy_cfg->rr_cfg_common_scell_r10_present) { rr_cfg_common_scell_r10_s* rr_cfg_common_scell_r10 = &phy_cfg->rr_cfg_common_scell_r10; if (rr_cfg_common_scell_r10->ul_cfg_r10_present) { typedef rr_cfg_common_scell_r10_s::ul_cfg_r10_s_ ul_cfg_r10_t; ul_cfg_r10_t* ul_cfg_r10 = &rr_cfg_common_scell_r10->ul_cfg_r10; // Parse Power control ul_pwr_ctrl_common_scell_r10_s* ul_pwr_ctrl_common_scell_r10 = &ul_cfg_r10->ul_pwr_ctrl_common_scell_r10; bzero(&ue_ul_cfg.ul_cfg.power_ctrl, sizeof(srslte_ue_ul_powerctrl_t)); ue_ul_cfg.ul_cfg.power_ctrl.p0_nominal_pusch = ul_pwr_ctrl_common_scell_r10->p0_nominal_pusch_r10; ue_ul_cfg.ul_cfg.power_ctrl.alpha = ul_pwr_ctrl_common_scell_r10->alpha_r10.to_number(); // Parse SRS typedef srs_ul_cfg_common_c::setup_s_ srs_ul_cfg_common_t; if (ul_cfg_r10->srs_ul_cfg_common_r10.type() == setup_e::setup) { srs_ul_cfg_common_t* srs_ul_cfg_common = &ul_cfg_r10->srs_ul_cfg_common_r10.setup(); ue_ul_cfg.ul_cfg.srs.configured = true; ue_ul_cfg.ul_cfg.srs.simul_ack = srs_ul_cfg_common->ack_nack_srs_simul_tx; ue_ul_cfg.ul_cfg.srs.bw_cfg = srs_ul_cfg_common->srs_bw_cfg.to_number(); ue_ul_cfg.ul_cfg.srs.subframe_config = srs_ul_cfg_common->srs_sf_cfg.to_number(); } else { ue_ul_cfg.ul_cfg.srs.configured = false; } // Parse PUSCH pusch_cfg_common_s* pusch_cfg_common = &ul_cfg_r10->pusch_cfg_common_r10; bzero(&ue_ul_cfg.ul_cfg.hopping, sizeof(srslte_pusch_hopping_cfg_t)); ue_ul_cfg.ul_cfg.hopping.n_sb = pusch_cfg_common->pusch_cfg_basic.n_sb; ue_ul_cfg.ul_cfg.hopping.hop_mode = pusch_cfg_common->pusch_cfg_basic.hop_mode.value == pusch_cfg_common_s::pusch_cfg_basic_s_::hop_mode_e_::intra_and_inter_sub_frame ? ue_ul_cfg.ul_cfg.hopping.SRSLTE_PUSCH_HOP_MODE_INTRA_SF : ue_ul_cfg.ul_cfg.hopping.SRSLTE_PUSCH_HOP_MODE_INTER_SF; ue_ul_cfg.ul_cfg.hopping.hopping_offset = pusch_cfg_common->pusch_cfg_basic.pusch_hop_offset; } } if (phy_cfg->rr_cfg_ded_scell_r10_present) { rr_cfg_ded_scell_r10_s* rr_cfg_ded_scell_r10 = &phy_cfg->rr_cfg_ded_scell_r10; if (rr_cfg_ded_scell_r10->phys_cfg_ded_scell_r10_present) { phys_cfg_ded_scell_r10_s* phys_cfg_ded_scell_r10 = &rr_cfg_ded_scell_r10->phys_cfg_ded_scell_r10; // Parse nonUL Configuration if (phys_cfg_ded_scell_r10->non_ul_cfg_r10_present) { typedef phys_cfg_ded_scell_r10_s::non_ul_cfg_r10_s_ non_ul_cfg_t; non_ul_cfg_t* non_ul_cfg = &phys_cfg_ded_scell_r10->non_ul_cfg_r10; // Parse Transmission mode if (non_ul_cfg->ant_info_r10_present) { ant_info_ded_r10_s::tx_mode_r10_e_::options tx_mode = non_ul_cfg->ant_info_r10.tx_mode_r10.value; if ((srslte_tm_t)tx_mode < SRSLTE_TMINV) { ue_dl_cfg.cfg.tm = (srslte_tm_t)tx_mode; } else { fprintf(stderr, "Transmission mode (R10) %s is not supported\n", non_ul_cfg->ant_info_r10.tx_mode_r10.to_string().c_str()); } } // Parse Cross carrier scheduling if (non_ul_cfg->cross_carrier_sched_cfg_r10_present) { typedef cross_carrier_sched_cfg_r10_s::sched_cell_info_r10_c_ sched_info_t; typedef sched_info_t::types cross_carrier_type_e; sched_info_t* sched_info = &non_ul_cfg->cross_carrier_sched_cfg_r10.sched_cell_info_r10; cross_carrier_type_e cross_carrier_type = sched_info->type(); if (cross_carrier_type == cross_carrier_type_e::own_r10) { ue_dl_cfg.dci_cfg.cif_enabled = sched_info->own_r10().cif_presence_r10; } else { ue_dl_cfg.dci_cfg.cif_enabled = false; // This CC does not have Carrier Indicator Field // ue_dl_cfg.blablabla = sched_info->other_r10().pdsch_start_r10; // ue_dl_cfg.blablabla = sched_info->other_r10().sched_cell_id_r10; } } // Parse pdsch config dedicated if (non_ul_cfg->pdsch_cfg_ded_r10_present) { ue_dl_cfg.cfg.pdsch.p_b = phy_cfg->rr_cfg_common_scell_r10.non_ul_cfg_r10.pdsch_cfg_common_r10.p_b; ue_dl_cfg.cfg.pdsch.p_a = non_ul_cfg->pdsch_cfg_ded_r10.p_a.to_number(); ue_dl_cfg.cfg.pdsch.power_scale = true; } } // Parse UL Configuration if (phys_cfg_ded_scell_r10->ul_cfg_r10_present) { typedef phys_cfg_ded_scell_r10_s::ul_cfg_r10_s_ ul_cfg_t; ul_cfg_t* ul_cfg = &phys_cfg_ded_scell_r10->ul_cfg_r10; // Parse CQI param if (ul_cfg->cqi_report_cfg_scell_r10_present) { cqi_report_cfg_scell_r10_s* cqi_report_cfg = &ul_cfg->cqi_report_cfg_scell_r10; // Aperiodic report if (cqi_report_cfg->cqi_report_mode_aperiodic_r10_present) { ue_dl_cfg.cfg.cqi_report.aperiodic_configured = true; ue_dl_cfg.cfg.cqi_report.aperiodic_mode = aperiodic_mode(cqi_report_cfg->cqi_report_mode_aperiodic_r10); } // Periodic report if (cqi_report_cfg->cqi_report_periodic_scell_r10_present) { if (cqi_report_cfg->cqi_report_periodic_scell_r10.type() == setup_e::setup) { typedef cqi_report_periodic_r10_c::setup_s_ cqi_cfg_t; cqi_cfg_t cqi_cfg = cqi_report_cfg->cqi_report_periodic_scell_r10.setup(); ue_dl_cfg.cfg.cqi_report.periodic_configured = true; ue_dl_cfg.cfg.cqi_report.pmi_idx = cqi_cfg.cqi_pmi_cfg_idx; ue_dl_cfg.cfg.cqi_report.format_is_subband = cqi_cfg.cqi_format_ind_periodic_r10.type().value == cqi_cfg_t::cqi_format_ind_periodic_r10_c_::types::subband_cqi_r10; if (ue_dl_cfg.cfg.cqi_report.format_is_subband) { ue_dl_cfg.cfg.cqi_report.subband_size = cqi_cfg.cqi_format_ind_periodic_r10.subband_cqi_r10().k; } if (cqi_cfg.ri_cfg_idx_present) { ue_dl_cfg.cfg.cqi_report.ri_idx = cqi_cfg.ri_cfg_idx; ue_dl_cfg.cfg.cqi_report.ri_idx_present = true; } else { ue_dl_cfg.cfg.cqi_report.ri_idx_present = false; } } else { // Release, disable periodic reporting ue_dl_cfg.cfg.cqi_report.periodic_configured = false; } } } if (ul_cfg->srs_ul_cfg_ded_r10_present) { // Sounding reference signals if (ul_cfg->srs_ul_cfg_ded_r10.type() == setup_e::setup) { srs_ul_cfg_ded_c::setup_s_* srs_ul_cfg_ded_r10 = &ul_cfg->srs_ul_cfg_ded_r10.setup(); ue_ul_cfg.ul_cfg.srs.bw_cfg = srs_ul_cfg_ded_r10->srs_bw.to_number(); ue_ul_cfg.ul_cfg.srs.I_srs = srs_ul_cfg_ded_r10->srs_cfg_idx; ue_ul_cfg.ul_cfg.srs.B = srs_ul_cfg_ded_r10->srs_bw; ue_ul_cfg.ul_cfg.srs.b_hop = srs_ul_cfg_ded_r10->srs_hop_bw; ue_ul_cfg.ul_cfg.srs.n_rrc = srs_ul_cfg_ded_r10->freq_domain_position; ue_ul_cfg.ul_cfg.srs.k_tc = srs_ul_cfg_ded_r10->tx_comb; ue_ul_cfg.ul_cfg.srs.n_srs = srs_ul_cfg_ded_r10->cyclic_shift; ue_ul_cfg.ul_cfg.srs.bw_cfg = 0; } else { ue_ul_cfg.ul_cfg.srs.configured = false; } } } } } } int cc_worker::read_ce_abs(float* ce_abs, uint32_t tx_antenna, uint32_t rx_antenna) { uint32_t i = 0; int sz = srslte_symbol_sz(cell.nof_prb); bzero(ce_abs, sizeof(float) * sz); int g = (sz - 12 * cell.nof_prb) / 2; for (i = 0; i < 12 * cell.nof_prb; i++) { ce_abs[g + i] = 20 * log10f(std::abs(std::complex(ue_dl.chest_res.ce[tx_antenna][rx_antenna][i]))); if (std::isinf(ce_abs[g + i])) { ce_abs[g + i] = -80; } } return sz; } int cc_worker::read_pdsch_d(cf_t* pdsch_d) { memcpy(pdsch_d, ue_dl.pdsch.d[0], ue_dl_cfg.cfg.pdsch.grant.nof_re * sizeof(cf_t)); return ue_dl_cfg.cfg.pdsch.grant.nof_re; } } // namespace srsue