Integrated SR decoder in gNb

This commit is contained in:
Xavier Arteaga 2021-07-19 15:46:16 +02:00 committed by Xavier Arteaga
parent 6a9baef11d
commit bb4f469794
12 changed files with 276 additions and 65 deletions

View File

@ -206,9 +206,11 @@ public:
class mac_interface_phy_nr class mac_interface_phy_nr
{ {
public: public:
const static int MAX_SSB = 4; const static int MAX_SSB = 4;
const static int MAX_GRANTS = 64; const static int MAX_GRANTS = 64;
const static int MAX_NZP_CSI_RS = 4; const static int MAX_PUCCH_MSG = 64;
const static int MAX_PUCCH_CANDIDATES = 2;
const static int MAX_NZP_CSI_RS = 4;
struct pdcch_dl_t { struct pdcch_dl_t {
srsran_dci_cfg_nr_t dci_cfg = {}; srsran_dci_cfg_nr_t dci_cfg = {};
@ -255,8 +257,8 @@ public:
}; };
struct pucch_t { struct pucch_t {
srsran_pucch_nr_common_cfg_t pucch_cfg; ///< UE dedicated PUCCH configuration srsran_pucch_nr_common_cfg_t pucch_cfg; ///< UE dedicated PUCCH configuration
srsran::bounded_vector<pucch_candidate_t, MAX_GRANTS> candidates; ///< PUCCH candidates to decode srsran::bounded_vector<pucch_candidate_t, MAX_PUCCH_CANDIDATES> candidates; ///< PUCCH candidates to decode
}; };
struct ul_sched_t { struct ul_sched_t {
@ -265,8 +267,8 @@ public:
}; };
struct pucch_info_t { struct pucch_info_t {
srsran_uci_data_nr_t uci_data; ///< RNTI is available under cfg->pucch->rnti srsran_uci_data_nr_t uci_data; ///< RNTI is available under cfg->pucch->rnti
// ... add signal measurements here srsran_csi_trs_measurements_t csi; ///< DMRS based signal Channel State Information (CSI)
}; };
struct pusch_info_t { struct pusch_info_t {

View File

@ -57,7 +57,8 @@ SRSRAN_API int srsran_gnb_ul_get_pucch(srsran_gnb_ul_t* q,
const srsran_pucch_nr_common_cfg_t* cfg, const srsran_pucch_nr_common_cfg_t* cfg,
const srsran_pucch_nr_resource_t* resource, const srsran_pucch_nr_resource_t* resource,
const srsran_uci_cfg_nr_t* uci_cfg, const srsran_uci_cfg_nr_t* uci_cfg,
srsran_uci_value_nr_t* uci_value); srsran_uci_value_nr_t* uci_value,
srsran_csi_trs_measurements_t* meas);
SRSRAN_API uint32_t srsran_gnb_ul_pucch_info(srsran_gnb_ul_t* q, SRSRAN_API uint32_t srsran_gnb_ul_pucch_info(srsran_gnb_ul_t* q,
const srsran_pucch_nr_resource_t* resource, const srsran_pucch_nr_resource_t* resource,

View File

@ -290,7 +290,18 @@ bool phy_cfg_nr_t::get_uci_cfg(const srsran_slot_cfg_t& slot_cfg,
} }
// Generate configuration for SR // Generate configuration for SR
// ... uint32_t sr_resource_id[SRSRAN_PUCCH_MAX_NOF_SR_RESOURCES] = {};
int n = srsran_ue_ul_nr_sr_send_slot(pucch.sr_resources, slot_cfg.idx, sr_resource_id);
if (n < SRSRAN_SUCCESS) {
ERROR("Calculating SR opportunities");
return false;
}
if (n > 0) {
uci_cfg.pucch.sr_resource_id = sr_resource_id[0];
uci_cfg.o_sr = srsran_ra_ul_nr_nof_sr_bits((uint32_t)n);
uci_cfg.sr_positive_present = true;
}
// Generate configuration for CSI reports // Generate configuration for CSI reports
// ... // ...

View File

@ -210,6 +210,11 @@ static int gnb_ul_decode_pucch_format1(srsran_gnb_ul_t* q,
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
// As format 1 with positive SR is not encoded with any payload, set SR to 1
if (uci_cfg->sr_positive_present) {
uci_value->sr = 1;
}
// Take valid decision // Take valid decision
uci_value->valid = (norm_corr > 0.5f); uci_value->valid = (norm_corr > 0.5f);
@ -248,7 +253,8 @@ int srsran_gnb_ul_get_pucch(srsran_gnb_ul_t* q,
const srsran_pucch_nr_common_cfg_t* cfg, const srsran_pucch_nr_common_cfg_t* cfg,
const srsran_pucch_nr_resource_t* resource, const srsran_pucch_nr_resource_t* resource,
const srsran_uci_cfg_nr_t* uci_cfg, const srsran_uci_cfg_nr_t* uci_cfg,
srsran_uci_value_nr_t* uci_value) srsran_uci_value_nr_t* uci_value,
srsran_csi_trs_measurements_t* meas)
{ {
if (q == NULL || slot_cfg == NULL || cfg == NULL || resource == NULL || uci_cfg == NULL || uci_value == NULL) { if (q == NULL || slot_cfg == NULL || cfg == NULL || resource == NULL || uci_cfg == NULL || uci_value == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS; return SRSRAN_ERROR_INVALID_INPUTS;
@ -257,17 +263,39 @@ int srsran_gnb_ul_get_pucch(srsran_gnb_ul_t* q,
// Estimate channel // Estimate channel
switch (resource->format) { switch (resource->format) {
case SRSRAN_PUCCH_NR_FORMAT_1: case SRSRAN_PUCCH_NR_FORMAT_1:
return gnb_ul_decode_pucch_format1(q, slot_cfg, cfg, resource, uci_cfg, uci_value); if (gnb_ul_decode_pucch_format1(q, slot_cfg, cfg, resource, uci_cfg, uci_value) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
break;
case SRSRAN_PUCCH_NR_FORMAT_2: case SRSRAN_PUCCH_NR_FORMAT_2:
return gnb_ul_decode_pucch_format2(q, slot_cfg, cfg, resource, uci_cfg, uci_value); if (gnb_ul_decode_pucch_format2(q, slot_cfg, cfg, resource, uci_cfg, uci_value) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
break;
case SRSRAN_PUCCH_NR_FORMAT_0: case SRSRAN_PUCCH_NR_FORMAT_0:
case SRSRAN_PUCCH_NR_FORMAT_3: case SRSRAN_PUCCH_NR_FORMAT_3:
case SRSRAN_PUCCH_NR_FORMAT_4: case SRSRAN_PUCCH_NR_FORMAT_4:
case SRSRAN_PUCCH_NR_FORMAT_ERROR: case SRSRAN_PUCCH_NR_FORMAT_ERROR:
ERROR("Invalid or not implemented PUCCH-NR format %d", (int)resource->format); ERROR("Invalid or not implemented PUCCH-NR format %d", (int)resource->format);
return SRSRAN_ERROR;
} }
return SRSRAN_ERROR; // Copy DMRS measurements
if (meas != NULL) {
meas->rsrp = q->chest_pucch.rsrp;
meas->rsrp_dB = q->chest_pucch.rsrp_dBfs;
meas->epre = q->chest_pucch.epre;
meas->epre_dB = q->chest_pucch.epre_dBfs;
meas->n0 = q->chest_pucch.noise_estimate;
meas->n0_dB = q->chest_pucch.noise_estimate_dbm;
meas->snr_dB = q->chest_pucch.snr_db;
meas->cfo_hz = q->chest_pucch.cfo_hz;
meas->cfo_hz_max = NAN; // Unavailable
meas->delay_us = q->chest_pucch.ta_us;
meas->nof_re = 0; // Unavailable
}
return SRSRAN_SUCCESS;
} }
uint32_t srsran_gnb_ul_pucch_info(srsran_gnb_ul_t* q, uint32_t srsran_gnb_ul_pucch_info(srsran_gnb_ul_t* q,

View File

@ -478,17 +478,21 @@ static int ra_ul_nr_pucch_resource_default(uint32_t r_pucch, srsran_pucch_nr_res
} }
static int ra_ul_nr_pucch_resource_hl(const srsran_pucch_nr_hl_cfg_t* cfg, static int ra_ul_nr_pucch_resource_hl(const srsran_pucch_nr_hl_cfg_t* cfg,
uint32_t O_uci, const srsran_uci_cfg_nr_t* uci_cfg,
uint32_t pucch_resource_id, uint32_t pucch_resource_id,
srsran_pucch_nr_resource_t* resource) srsran_pucch_nr_resource_t* resource)
{ {
uint32_t N2 = cfg->sets[1].max_payload_size > 0 ? cfg->sets[1].max_payload_size : SRSRAN_UCI_NR_MAX_NOF_BITS; uint32_t O_uci = srsran_uci_nr_total_bits(uci_cfg);
uint32_t N3 = cfg->sets[2].max_payload_size > 0 ? cfg->sets[2].max_payload_size : SRSRAN_UCI_NR_MAX_NOF_BITS; uint32_t N2 = cfg->sets[1].max_payload_size > 0 ? cfg->sets[1].max_payload_size : SRSRAN_UCI_NR_MAX_NOF_BITS;
uint32_t N3 = cfg->sets[2].max_payload_size > 0 ? cfg->sets[2].max_payload_size : SRSRAN_UCI_NR_MAX_NOF_BITS;
// If the UE transmits O UCI UCI information bits, that include HARQ-ACK information bits, the UE determines a PUCCH // If the UE transmits O UCI UCI information bits, that include HARQ-ACK information bits, the UE determines a PUCCH
// resource set to be... // resource set to be...
uint32_t resource_set_id = 3; uint32_t resource_set_id = 3;
if (O_uci <= 2 && cfg->sets[0].nof_resources > 0) { if (uci_cfg->nof_csi == 0 && uci_cfg->ack.count <= 2) {
// a first set of PUCCH resources with pucch-ResourceSetId = 0 if O_UCI ≤ 2 including 1 or 2 HARQ-ACK
// information bits and a positive or negative SR on one SR transmission occasion if transmission of HARQ-ACK
// information and SR occurs simultaneously, or
resource_set_id = 0; resource_set_id = 0;
} else if (O_uci <= N2 && cfg->sets[1].nof_resources > 0) { } else if (O_uci <= N2 && cfg->sets[1].nof_resources > 0) {
resource_set_id = 1; resource_set_id = 1;
@ -524,8 +528,6 @@ int srsran_ra_ul_nr_pucch_resource(const srsran_pucch_nr_hl_cfg_t* pucch_cfg,
return SRSRAN_ERROR_INVALID_INPUTS; return SRSRAN_ERROR_INVALID_INPUTS;
} }
uint32_t O_uci = srsran_uci_nr_total_bits(uci_cfg);
// Use SR PUCCH resource // Use SR PUCCH resource
// - At least one positive SR // - At least one positive SR
// - No HARQ-ACK // - No HARQ-ACK
@ -553,7 +555,7 @@ int srsran_ra_ul_nr_pucch_resource(const srsran_pucch_nr_hl_cfg_t* pucch_cfg,
// - At least one positive SR // - At least one positive SR
// - up to 2 HARQ-ACK // - up to 2 HARQ-ACK
// - No CSI report // - No CSI report
if (uci_cfg->sr_positive_present > 0 && uci_cfg->ack.count <= SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS && if (uci_cfg->sr_positive_present && uci_cfg->ack.count <= SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS &&
uci_cfg->nof_csi == 0) { uci_cfg->nof_csi == 0) {
uint32_t sr_resource_id = uci_cfg->pucch.sr_resource_id; uint32_t sr_resource_id = uci_cfg->pucch.sr_resource_id;
if (sr_resource_id >= SRSRAN_PUCCH_MAX_NOF_SR_RESOURCES) { if (sr_resource_id >= SRSRAN_PUCCH_MAX_NOF_SR_RESOURCES) {
@ -571,7 +573,8 @@ int srsran_ra_ul_nr_pucch_resource(const srsran_pucch_nr_hl_cfg_t* pucch_cfg,
// Select PUCCH resource for HARQ-ACK // Select PUCCH resource for HARQ-ACK
srsran_pucch_nr_resource_t resource_harq = {}; srsran_pucch_nr_resource_t resource_harq = {};
if (ra_ul_nr_pucch_resource_hl(pucch_cfg, O_uci, uci_cfg->pucch.resource_id, &resource_harq) < SRSRAN_SUCCESS) { if (ra_ul_nr_pucch_resource_hl(pucch_cfg, uci_cfg, uci_cfg->pucch.resource_id, &resource_harq) < SRSRAN_SUCCESS) {
ERROR("Error selecting HARQ-ACK resource");
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
@ -595,6 +598,7 @@ int srsran_ra_ul_nr_pucch_resource(const srsran_pucch_nr_hl_cfg_t* pucch_cfg,
} }
// The impossible happened... // The impossible happened...
ERROR("The impossible happened...");
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
@ -603,7 +607,7 @@ int srsran_ra_ul_nr_pucch_resource(const srsran_pucch_nr_hl_cfg_t* pucch_cfg,
// - More than 2 HARQ-ACK // - More than 2 HARQ-ACK
// - No CSI report // - No CSI report
if (uci_cfg->o_sr > 0 && uci_cfg->ack.count > SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS && uci_cfg->nof_csi == 0) { if (uci_cfg->o_sr > 0 && uci_cfg->ack.count > SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS && uci_cfg->nof_csi == 0) {
return ra_ul_nr_pucch_resource_hl(pucch_cfg, O_uci, uci_cfg->pucch.resource_id, resource); return ra_ul_nr_pucch_resource_hl(pucch_cfg, uci_cfg, uci_cfg->pucch.resource_id, resource);
} }
// Use format 2, 3 or 4 CSI report resource from higher layers // Use format 2, 3 or 4 CSI report resource from higher layers
@ -622,7 +626,7 @@ int srsran_ra_ul_nr_pucch_resource(const srsran_pucch_nr_hl_cfg_t* pucch_cfg,
uint32_t r_pucch = (2 * uci_cfg->pucch.n_cce_0) + 2 * uci_cfg->pucch.resource_id; uint32_t r_pucch = (2 * uci_cfg->pucch.n_cce_0) + 2 * uci_cfg->pucch.resource_id;
return ra_ul_nr_pucch_resource_default(r_pucch, resource); return ra_ul_nr_pucch_resource_default(r_pucch, resource);
} }
return ra_ul_nr_pucch_resource_hl(pucch_cfg, O_uci, uci_cfg->pucch.resource_id, resource); return ra_ul_nr_pucch_resource_hl(pucch_cfg, uci_cfg, uci_cfg->pucch.resource_id, resource);
} }
uint32_t srsran_ra_ul_nr_nof_sr_bits(uint32_t K) uint32_t srsran_ra_ul_nr_nof_sr_bits(uint32_t K)

View File

@ -148,22 +148,37 @@ bool slot_worker::work_ul()
// For each PUCCH... // For each PUCCH...
for (stack_interface_phy_nr::pucch_t& pucch : ul_sched.pucch) { for (stack_interface_phy_nr::pucch_t& pucch : ul_sched.pucch) {
stack_interface_phy_nr::pucch_info_t pucch_info = {}; srsran::bounded_vector<stack_interface_phy_nr::pucch_info_t, stack_interface_phy_nr::MAX_PUCCH_CANDIDATES>
pucch_info.uci_data.cfg = pucch.candidates[0].uci_cfg; pucch_info(pucch.candidates.size());
// Decode PUCCH // For each candidate decode PUCCH
if (srsran_gnb_ul_get_pucch(&gnb_ul, for (uint32_t i = 0; i < (uint32_t)pucch.candidates.size(); i++) {
&ul_slot_cfg, pucch_info[i].uci_data.cfg = pucch.candidates[i].uci_cfg;
&pucch.pucch_cfg,
&pucch.candidates[0].resource, // Decode PUCCH
&pucch_info.uci_data.cfg, if (srsran_gnb_ul_get_pucch(&gnb_ul,
&pucch_info.uci_data.value) < SRSRAN_SUCCESS) { &ul_slot_cfg,
logger.error("Error getting PUCCH"); &pucch.pucch_cfg,
return false; &pucch.candidates[i].resource,
&pucch_info[i].uci_data.cfg,
&pucch_info[i].uci_data.value,
&pucch_info[i].csi) < SRSRAN_SUCCESS) {
logger.error("Error getting PUCCH");
return false;
}
}
// Find most suitable PUCCH candidate
uint32_t best_candidate = 0;
for (uint32_t i = 1; i < (uint32_t)pucch_info.size(); i++) {
// Select candidate if exceeds the previous best candidate SNR
if (pucch_info[i].csi.snr_dB > pucch_info[best_candidate].csi.snr_dB) {
best_candidate = i;
}
} }
// Inform stack // Inform stack
if (stack.pucch_info(ul_slot_cfg, pucch_info) < SRSRAN_SUCCESS) { if (stack.pucch_info(ul_slot_cfg, pucch_info[best_candidate]) < SRSRAN_SUCCESS) {
logger.error("Error pushing PUCCH information to stack"); logger.error("Error pushing PUCCH information to stack");
return false; return false;
} }
@ -171,8 +186,11 @@ bool slot_worker::work_ul()
// Log PUCCH decoding // Log PUCCH decoding
if (logger.info.enabled()) { if (logger.info.enabled()) {
std::array<char, 512> str; std::array<char, 512> str;
srsran_gnb_ul_pucch_info( srsran_gnb_ul_pucch_info(&gnb_ul,
&gnb_ul, &pucch.candidates[0].resource, &pucch_info.uci_data, str.data(), (uint32_t)str.size()); &pucch.candidates[0].resource,
&pucch_info[best_candidate].uci_data,
str.data(),
(uint32_t)str.size());
logger.info("PUCCH: %s", str.data()); logger.info("PUCCH: %s", str.data());
} }

View File

@ -322,9 +322,28 @@ bool sched_worker_manager::save_sched_result(tti_point pdcch_tti, uint32_t cc, d
// Put UCI configuration in PUCCH config // Put UCI configuration in PUCCH config
ul_res.pucch.emplace_back(); ul_res.pucch.emplace_back();
pucch_t& pucch = ul_res.pucch.back(); pucch_t& pucch = ul_res.pucch.back();
pucch.uci_cfg = uci_cfg; pucch.candidates.emplace_back();
bool ret = phy_cfg->get_pucch_uci_cfg(slot_cfg, pucch.uci_cfg, pucch.pucch_cfg, pucch.resource); pucch.candidates.back().uci_cfg = uci_cfg;
srsran_assert(ret, "Error getting PUCCH UCI cfg"); srsran_assert(phy_cfg->get_pucch_uci_cfg(
slot_cfg, pucch.candidates.back().uci_cfg, pucch.pucch_cfg, pucch.candidates.back().resource),
"Error getting PUCCH UCI cfg");
// If this slot has a SR opportunity and the selected PUCCH format is 1, consider positive SR.
if (uci_cfg.sr_positive_present and uci_cfg.ack.count > 0 and
pucch.candidates.back().resource.format == SRSRAN_PUCCH_NR_FORMAT_1) {
// Set SR negative
if (uci_cfg.o_sr > 0) {
uci_cfg.sr_positive_present = false;
}
// Append new resource
pucch.candidates.emplace_back();
pucch.candidates.back().uci_cfg = uci_cfg;
srsran_assert(
phy_cfg->get_pucch_uci_cfg(
slot_cfg, pucch.candidates.back().uci_cfg, pucch.pucch_cfg, pucch.candidates.back().resource),
"Error getting PUCCH UCI cfg");
}
} }
} }
} }

View File

@ -110,7 +110,7 @@ void sf_worker::work_imp()
// Perform UL processing // Perform UL processing
for (auto& w : cc_workers) { for (auto& w : cc_workers) {
w->work_ul(); w.get()->work_ul();
} }
// Set Tx buffers // Set Tx buffers

View File

@ -27,14 +27,17 @@ if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB)
add_nr_test(nr_phy_test_10MHz_dl_only nr_phy_test add_nr_test(nr_phy_test_10MHz_dl_only nr_phy_test
--duration=100 --duration=100
--gnb.stack.pdsch.slots=\"0,1,2,3,4,5\" --gnb.stack.pdsch.slots=\"0,1,2,3,4,5\"
--gnb.stack.pusch.slots=\"\" --gnb.stack.pdsch.start=0 # Start at RB 0
--gnb.stack.pdsch.length=52 # Full 10 MHz BW
--gnb.stack.pdsch.mcs=28 # Maximum MCS
--gnb.stack.pusch.slots=none
--gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS} --gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS}
--ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS} --ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS}
) )
add_nr_test(nr_phy_test_10MHz_ul_only nr_phy_test add_nr_test(nr_phy_test_10MHz_ul_only nr_phy_test
--duration=100 # 100 slots --duration=100 # 100 slots
--gnb.stack.pdsch.slots=6 # No PDSCH --gnb.stack.pdsch.slots=none
--gnb.stack.pusch.slots=6,7,8,9 # All possible UL slots --gnb.stack.pusch.slots=6,7,8,9 # All possible UL slots
--gnb.stack.pusch.start=0 # Start at RB 0 --gnb.stack.pusch.start=0 # Start at RB 0
--gnb.stack.pusch.length=52 # Full 10 MHz BW --gnb.stack.pusch.length=52 # Full 10 MHz BW
@ -59,11 +62,32 @@ if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB)
add_nr_test(nr_phy_test_10MHz_prach nr_phy_test add_nr_test(nr_phy_test_10MHz_prach nr_phy_test
--duration=1000 # 100 slots --duration=1000 # 100 slots
--gnb.stack.pdsch.slots=6 # No PDSCH --gnb.stack.pdsch.slots=none # No PDSCH
--gnb.stack.pusch.slots=0 # No PUSCH --gnb.stack.pusch.slots=none # No PUSCH
--gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS} --gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS}
--ue.stack.prach.period=30 # Transmit PRACH every 30 radio frames --ue.stack.prach.period=30 # Transmit PRACH every 30 radio frames
--ue.stack.prach.preamble=10 # Use preamble 10 --ue.stack.prach.preamble=10 # Use preamble 10
--ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS} --ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS}
) )
add_nr_test(nr_phy_test_10MHz_sr nr_phy_test
--duration=1000 # 100 slots
--gnb.stack.pdsch.slots=none # No PDSCH
--gnb.stack.pusch.slots=none # No PUSCH
--gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS}
--ue.stack.sr.period=1 # Transmit SR every candidate
--ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS}
)
add_nr_test(nr_phy_test_10MHz_dl_sr nr_phy_test
--duration=100
--gnb.stack.pdsch.slots=\"0,1,2,3,4,5\"
--gnb.stack.pdsch.start=0 # Start at RB 0
--gnb.stack.pdsch.length=2 # Full 10 MHz BW
--gnb.stack.pdsch.mcs=1 # Minimum MCS
--gnb.stack.pusch.slots=none
--gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS}
--ue.stack.sr.period=1 # Transmit SR every candidate
--ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS}
)
endif () endif ()

View File

@ -33,10 +33,27 @@ public:
uint32_t count; uint32_t count;
float avg_ta; float avg_ta;
}; };
struct pucch_metrics_t {
float epre_db_avg = 0.0f;
float epre_db_min = +INFINITY;
float epre_db_max = -INFINITY;
float rsrp_db_avg = 0.0f;
float rsrp_db_min = +INFINITY;
float rsrp_db_max = -INFINITY;
float snr_db_avg = 0.0f;
float snr_db_min = +INFINITY;
float snr_db_max = -INFINITY;
float ta_us_avg = 0.0f;
float ta_us_min = +INFINITY;
float ta_us_max = -INFINITY;
uint32_t count = 0;
};
struct metrics_t { struct metrics_t {
std::map<uint32_t, prach_metrics_t> prach = {}; ///< PRACH metrics indexed with premable index std::map<uint32_t, prach_metrics_t> prach = {}; ///< PRACH metrics indexed with premable index
srsenb::mac_ue_metrics_t mac = {}; ///< MAC metrics srsenb::mac_ue_metrics_t mac = {}; ///< MAC metrics
uint32_t sr_count = 0; ///< SR counter
pucch_metrics_t pucch = {};
}; };
private: private:
@ -44,10 +61,10 @@ private:
bool use_dummy_sched = true; bool use_dummy_sched = true;
const uint16_t rnti = 0x1234; const uint16_t rnti = 0x1234;
struct { struct {
srsran::circular_array<srsran_dci_location_t, SRSRAN_NOF_SF_X_FRAME> dci_location; srsran::circular_array<srsran_dci_location_t, SRSRAN_NOF_SF_X_FRAME> dci_location = {};
uint32_t mcs; uint32_t mcs = 0;
uint32_t freq_res = 0; uint32_t freq_res = 0;
std::set<uint32_t> slots; std::set<uint32_t> slots = {};
} dl, ul; } dl, ul;
srsran::circular_array<uint32_t, SRSRAN_NOF_SF_X_FRAME> dl_data_to_ul_ack; srsran::circular_array<uint32_t, SRSRAN_NOF_SF_X_FRAME> dl_data_to_ul_ack;
uint32_t ss_id = 0; uint32_t ss_id = 0;
@ -259,6 +276,7 @@ private:
{ {
std::unique_lock<std::mutex> lock(metrics_mutex); std::unique_lock<std::mutex> lock(metrics_mutex);
// Process HARQ-ACK
for (uint32_t i = 0; i < cfg.ack.count; i++) { for (uint32_t i = 0; i < cfg.ack.count; i++) {
const srsran_harq_ack_bit_t* ack_bit = &cfg.ack.bits[i]; const srsran_harq_ack_bit_t* ack_bit = &cfg.ack.bits[i];
bool is_ok = (value.ack[i] == 1) and value.valid; bool is_ok = (value.ack[i] == 1) and value.valid;
@ -271,6 +289,11 @@ private:
} }
} }
// Process SR
if (value.valid and value.sr > 0) {
metrics.sr_count++;
}
return true; return true;
} }
@ -314,8 +337,12 @@ public:
dl.mcs = args.pdsch.mcs; dl.mcs = args.pdsch.mcs;
ul.mcs = args.pusch.mcs; ul.mcs = args.pusch.mcs;
srsran::string_parse_list(args.pdsch.slots, ',', dl.slots); if (args.pdsch.slots != "none" and not args.pdsch.slots.empty()) {
srsran::string_parse_list(args.pusch.slots, ',', ul.slots); srsran::string_parse_list(args.pdsch.slots, ',', dl.slots);
}
if (args.pusch.slots != "none" and not args.pusch.slots.empty()) {
srsran::string_parse_list(args.pusch.slots, ',', ul.slots);
}
// Select DCI locations // Select DCI locations
for (uint32_t slot = 0; slot < SRSRAN_NOF_SF_X_FRAME; slot++) { for (uint32_t slot = 0; slot < SRSRAN_NOF_SF_X_FRAME; slot++) {
@ -474,14 +501,35 @@ public:
// If any UCI information is triggered, schedule PUCCH // If any UCI information is triggered, schedule PUCCH
if (uci_cfg.ack.count > 0 || uci_cfg.nof_csi > 0 || uci_cfg.o_sr > 0) { if (uci_cfg.ack.count > 0 || uci_cfg.nof_csi > 0 || uci_cfg.o_sr > 0) {
mac_interface_phy_nr::pucch_t pucch = {}; ul_sched.pucch.emplace_back();
pucch.candidates[0].uci_cfg = uci_cfg;
if (not phy_cfg.get_pucch_uci_cfg(slot_cfg, uci_cfg, pucch.pucch_cfg, pucch.candidates[0].resource)) { uci_cfg.pucch.rnti = rnti;
mac_interface_phy_nr::pucch_t& pucch = ul_sched.pucch.back();
pucch.candidates.emplace_back();
pucch.candidates.back().uci_cfg = uci_cfg;
if (not phy_cfg.get_pucch_uci_cfg(slot_cfg, uci_cfg, pucch.pucch_cfg, pucch.candidates.back().resource)) {
logger.error("Error getting UCI CFG"); logger.error("Error getting UCI CFG");
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
ul_sched.pucch.push_back(pucch); // If this slot has a SR opportunity and the selected PUCCH format is 1, consider positive SR.
if (uci_cfg.o_sr > 0 and uci_cfg.ack.count > 0 and
pucch.candidates.back().resource.format == SRSRAN_PUCCH_NR_FORMAT_1) {
// Set SR negative
if (uci_cfg.o_sr > 0) {
uci_cfg.sr_positive_present = false;
}
// Append new resource
pucch.candidates.emplace_back();
pucch.candidates.back().uci_cfg = uci_cfg;
if (not phy_cfg.get_pucch_uci_cfg(slot_cfg, uci_cfg, pucch.pucch_cfg, pucch.candidates.back().resource)) {
logger.error("Error getting UCI CFG");
return SRSRAN_ERROR;
}
}
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
@ -498,7 +546,19 @@ public:
} }
// Handle PHY metrics // Handle PHY metrics
// ... metrics.pucch.epre_db_avg = SRSRAN_VEC_CMA(pucch_info.csi.epre_dB, metrics.pucch.epre_db_avg, metrics.pucch.count);
metrics.pucch.epre_db_min = SRSRAN_MIN(metrics.pucch.epre_db_min, pucch_info.csi.epre_dB);
metrics.pucch.epre_db_max = SRSRAN_MAX(metrics.pucch.epre_db_max, pucch_info.csi.epre_dB);
metrics.pucch.rsrp_db_avg = SRSRAN_VEC_CMA(pucch_info.csi.rsrp_dB, metrics.pucch.rsrp_db_avg, metrics.pucch.count);
metrics.pucch.rsrp_db_min = SRSRAN_MIN(metrics.pucch.rsrp_db_min, pucch_info.csi.rsrp_dB);
metrics.pucch.rsrp_db_max = SRSRAN_MAX(metrics.pucch.rsrp_db_max, pucch_info.csi.rsrp_dB);
metrics.pucch.snr_db_avg = SRSRAN_VEC_CMA(pucch_info.csi.snr_dB, metrics.pucch.snr_db_avg, metrics.pucch.count);
metrics.pucch.snr_db_min = SRSRAN_MIN(metrics.pucch.snr_db_min, pucch_info.csi.snr_dB);
metrics.pucch.snr_db_max = SRSRAN_MAX(metrics.pucch.snr_db_max, pucch_info.csi.snr_dB);
metrics.pucch.ta_us_avg = SRSRAN_VEC_CMA(pucch_info.csi.delay_us, metrics.pucch.ta_us_avg, metrics.pucch.count);
metrics.pucch.ta_us_min = SRSRAN_MIN(metrics.pucch.ta_us_min, pucch_info.csi.delay_us);
metrics.pucch.ta_us_max = SRSRAN_MAX(metrics.pucch.ta_us_max, pucch_info.csi.delay_us);
metrics.pucch.count++;
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }

View File

@ -23,7 +23,8 @@ public:
}; };
struct metrics_t { struct metrics_t {
std::map<uint32_t, prach_metrics_t> prach = {}; ///< PRACH metrics indexed with premable index std::map<uint32_t, prach_metrics_t> prach = {}; ///< PRACH metrics indexed with premable index
uint32_t sr_count = 0; ///< Counts number of transmitted SR
}; };
private: private:
@ -95,11 +96,14 @@ public:
return false; return false;
} }
bool ret = (sr_count % sr_period == 0); if (sr_count >= (sr_period - 1) and not ul_sch_tx) {
metrics.sr_count++;
sr_count = 0;
return true;
}
sr_count++; sr_count++;
return false;
return ret;
} }
bool is_valid() const { return valid; } bool is_valid() const { return valid; }

View File

@ -225,14 +225,54 @@ int main(int argc, char** argv)
TESTASSERT(metrics.ue_stack.prach.count(p.first) > 0); TESTASSERT(metrics.ue_stack.prach.count(p.first) > 0);
} }
srsran::console(" +------------+------------+------------+\n\n"); srsran::console(" +------------+------------+------------+\n\n");
}
// Print PUCCH
if (metrics.gnb_stack.pucch.count > 0) {
srsran::console("PUCCH DMRS Receiver metrics:\n");
srsran::console(" +------------+------------+------------+------------+\n");
srsran::console(" | %10s | %10s | %10s | %10s |\n", "Measure", "Average", "Min", "Max");
srsran::console(" +------------+------------+------------+------------+\n");
srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n",
"EPRE (dB)",
metrics.gnb_stack.pucch.epre_db_avg,
metrics.gnb_stack.pucch.epre_db_min,
metrics.gnb_stack.pucch.epre_db_min);
srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n",
"RSRP (dB)",
metrics.gnb_stack.pucch.rsrp_db_avg,
metrics.gnb_stack.pucch.rsrp_db_min,
metrics.gnb_stack.pucch.rsrp_db_max);
srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n",
"SINR (dB)",
metrics.gnb_stack.pucch.snr_db_avg,
metrics.gnb_stack.pucch.snr_db_min,
metrics.gnb_stack.pucch.snr_db_max);
srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n",
"TA (us)",
metrics.gnb_stack.pucch.ta_us_avg,
metrics.gnb_stack.pucch.ta_us_min,
metrics.gnb_stack.pucch.ta_us_max);
srsran::console(" +------------+------------+------------+------------+\n");
} else { } else {
// In this case no PRACH should // In this case the gNb should not have detected any
TESTASSERT(metrics.gnb_stack.prach.empty()); TESTASSERT(metrics.gnb_stack.prach.empty());
} }
// Print SR
if (metrics.ue_stack.sr_count > 0) {
srsran::console("SR:\n");
srsran::console(" +------------+------------+\n");
srsran::console(" | %10s | %10s |\n", "Transmit'd", "Received");
srsran::console(" +------------+------------+\n");
srsran::console(" | %10d | %10d |\n", metrics.ue_stack.sr_count, metrics.gnb_stack.sr_count);
srsran::console(" +------------+------------+\n");
}
// Assert metrics // Assert metrics
TESTASSERT(metrics.gnb_stack.mac.tx_errors == 0); TESTASSERT(metrics.gnb_stack.mac.tx_errors == 0);
TESTASSERT(metrics.gnb_stack.mac.rx_errors == 0); TESTASSERT(metrics.gnb_stack.mac.rx_errors == 0);
TESTASSERT(metrics.ue_stack.sr_count == metrics.gnb_stack.sr_count);
// If reached here, the test is successful // If reached here, the test is successful
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;