Add PUCCH SNR measurement (#2175)

* Add PUCCH SNR measurement

* Fix PUCCH SNR estimation

* Reverted PUCCH noise estimation

* use fpclassify instead of iszero

Co-authored-by: Xavier Arteaga <xavier@softwareradiosystems.com>
master
Ismael Gomez 4 years ago committed by GitHub
parent 0780f3caea
commit d5f0634975
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -123,6 +123,8 @@ public:
*/ */
virtual int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t cqi_value) = 0; virtual int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t cqi_value) = 0;
typedef enum { PUSCH = 0, PUCCH, SRS } ul_channel_t;
/** /**
* PHY callback for giving MAC the SNR in dB of an UL transmission for a given RNTI at a given carrier * PHY callback for giving MAC the SNR in dB of an UL transmission for a given RNTI at a given carrier
* *
@ -130,9 +132,10 @@ public:
* @param rnti The UE identifier in the eNb * @param rnti The UE identifier in the eNb
* @param cc_idx The eNb Cell/Carrier where the UL transmission was received * @param cc_idx The eNb Cell/Carrier where the UL transmission was received
* @param snr_db The actual SNR of the received signal * @param snr_db The actual SNR of the received signal
* @param ch Indicates uplink channel (PUSCH, PUCCH or SRS)
* @return SRSLTE_SUCCESS if no error occurs, SRSLTE_ERROR* if an error occurs * @return SRSLTE_SUCCESS if no error occurs, SRSLTE_ERROR* if an error occurs
*/ */
virtual int snr_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, float snr_db) = 0; virtual int snr_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, float snr_db, ul_channel_t ch) = 0;
/** /**
* PHY callback for giving MAC the Time Aligment information in microseconds of a given RNTI during a TTI processing * PHY callback for giving MAC the Time Aligment information in microseconds of a given RNTI during a TTI processing

@ -41,6 +41,10 @@ typedef struct SRSLTE_API {
uint32_t nof_re; uint32_t nof_re;
float noise_estimate; float noise_estimate;
float noise_estimate_dbm; float noise_estimate_dbm;
float rsrp;
float rsrp_dBfs;
float epre;
float epre_dBfs;
float snr; float snr;
float snr_db; float snr_db;
float cfo; float cfo;

@ -83,6 +83,7 @@ typedef struct SRSLTE_API {
typedef struct SRSLTE_API { typedef struct SRSLTE_API {
srslte_uci_value_t uci_data; srslte_uci_value_t uci_data;
float dmrs_correlation; float dmrs_correlation;
float snr_db;
float correlation; float correlation;
bool detected; bool detected;

@ -11,6 +11,7 @@
*/ */
#include <complex.h> #include <complex.h>
#include <float.h>
#include <math.h> #include <math.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -392,23 +393,46 @@ int srslte_chest_ul_estimate_pusch(srslte_chest_ul_t* q,
return 0; return 0;
} }
static float
estimate_noise_pilots_pucch(srslte_chest_ul_t* q, cf_t* ce, uint32_t n_rs, uint32_t n_prb[SRSLTE_NOF_SLOTS_PER_SF])
{
float power = 0;
for (int ns = 0; ns < SRSLTE_NOF_SLOTS_PER_SF; ns++) {
for (int i = 0; i < n_rs; i++) {
// All CE are the same, so pick the first symbol of the first slot always and compare with the noisy estimates
power += srslte_chest_estimate_noise_pilots(
&q->pilot_estimates[(i + ns * n_rs) * SRSLTE_NRE],
&ce[SRSLTE_RE_IDX(q->cell.nof_prb, ns * SRSLTE_CP_NSYMB(q->cell.cp), n_prb[ns] * SRSLTE_NRE)],
q->tmp_noise,
SRSLTE_NRE);
}
}
power /= (SRSLTE_NOF_SLOTS_PER_SF * n_rs);
if (q->smooth_filter_len == 3) {
// Calibrated for filter length 3
float w = q->smooth_filter[0];
float a = 7.419 * w * w + 0.1117 * w - 0.005387;
return (power / (a * 0.8));
} else {
return power;
}
}
int srslte_chest_ul_estimate_pucch(srslte_chest_ul_t* q, int srslte_chest_ul_estimate_pucch(srslte_chest_ul_t* q,
srslte_ul_sf_cfg_t* sf, srslte_ul_sf_cfg_t* sf,
srslte_pucch_cfg_t* cfg, srslte_pucch_cfg_t* cfg,
cf_t* input, cf_t* input,
srslte_chest_ul_res_t* res) srslte_chest_ul_res_t* res)
{ {
if (!q->dmrs_signal_configured) {
ERROR("Error must call srslte_chest_ul_set_cfg() before using the UL estimator\n");
return SRSLTE_ERROR;
}
int n_rs = srslte_refsignal_dmrs_N_rs(cfg->format, q->cell.cp); int n_rs = srslte_refsignal_dmrs_N_rs(cfg->format, q->cell.cp);
if (!n_rs) { if (!n_rs) {
ERROR("Error computing N_rs\n"); ERROR("Error computing N_rs\n");
return SRSLTE_ERROR; return SRSLTE_ERROR;
} }
int nrefs_sf = SRSLTE_NRE * n_rs * 2; int nrefs_sf = SRSLTE_NRE * n_rs * SRSLTE_NOF_SLOTS_PER_SF;
/* Get references from the input signal */ /* Get references from the input signal */
srslte_refsignal_dmrs_pucch_get(&q->dmrs_signal, cfg, input, q->pilot_recv_signal); srslte_refsignal_dmrs_pucch_get(&q->dmrs_signal, cfg, input, q->pilot_recv_signal);
@ -446,8 +470,29 @@ int srslte_chest_ul_estimate_pucch(srslte_chest_ul_t* q,
srslte_vec_prod_conj_ccc(q->pilot_recv_signal, q->pilot_known_signal, q->pilot_estimates, nrefs_sf); srslte_vec_prod_conj_ccc(q->pilot_recv_signal, q->pilot_known_signal, q->pilot_estimates, nrefs_sf);
} }
if (cfg->meas_ta_en && n_rs > 0) { // Measure power
float ta_err = 0.0; float rsrp_avg = 0.0f;
for (int ns = 0; ns < SRSLTE_NOF_SLOTS_PER_SF; ns++) {
for (int i = 0; i < n_rs; i++) {
cf_t corr = srslte_vec_acc_cc(q->pilot_estimates, SRSLTE_NOF_SLOTS_PER_SF * SRSLTE_NRE * n_rs) / (SRSLTE_NRE);
rsrp_avg += __real__ corr * __real__ corr + __imag__ corr * __imag__ corr;
}
}
rsrp_avg /= SRSLTE_NOF_SLOTS_PER_SF * n_rs;
float epre = srslte_vec_avg_power_cf(q->pilot_estimates, SRSLTE_NOF_SLOTS_PER_SF * SRSLTE_NRE * n_rs);
// RSRP shall not be greater than EPRE
rsrp_avg = SRSLTE_MIN(rsrp_avg, epre);
// Set EPRE and RSRP
res->epre = epre;
res->epre_dBfs = srslte_convert_power_to_dB(res->epre);
res->rsrp = rsrp_avg;
res->rsrp_dBfs = srslte_convert_power_to_dB(res->rsrp);
// Estimate time alignment
if (cfg->meas_ta_en) {
float ta_err = 0.0f;
for (int ns = 0; ns < SRSLTE_NOF_SLOTS_PER_SF; ns++) { for (int ns = 0; ns < SRSLTE_NOF_SLOTS_PER_SF; ns++) {
for (int i = 0; i < n_rs; i++) { for (int i = 0; i < n_rs; i++) {
ta_err += srslte_vec_estimate_frequency(&q->pilot_estimates[(i + ns * n_rs) * SRSLTE_NRE], SRSLTE_NRE) / ta_err += srslte_vec_estimate_frequency(&q->pilot_estimates[(i + ns * n_rs) * SRSLTE_NRE], SRSLTE_NRE) /
@ -467,6 +512,8 @@ int srslte_chest_ul_estimate_pucch(srslte_chest_ul_t* q,
} }
if (res->ce != NULL) { if (res->ce != NULL) {
uint32_t n_prb[2] = {};
/* TODO: Currently averaging entire slot, performance good enough? */ /* TODO: Currently averaging entire slot, performance good enough? */
for (int ns = 0; ns < 2; ns++) { for (int ns = 0; ns < 2; ns++) {
// Average all slot // Average all slot
@ -490,14 +537,31 @@ int srslte_chest_ul_estimate_pucch(srslte_chest_ul_t* q,
q->smooth_filter_len); q->smooth_filter_len);
// Determine n_prb // Determine n_prb
uint32_t n_prb = srslte_pucch_n_prb(&q->cell, cfg, ns); n_prb[ns] = srslte_pucch_n_prb(&q->cell, cfg, ns);
// copy estimates to slot // copy estimates to slot
for (int i = 0; i < SRSLTE_CP_NSYMB(q->cell.cp); i++) { for (int i = 0; i < SRSLTE_CP_NSYMB(q->cell.cp); i++) {
memcpy(&res->ce[SRSLTE_RE_IDX(q->cell.nof_prb, i + ns * SRSLTE_CP_NSYMB(q->cell.cp), n_prb * SRSLTE_NRE)], srslte_vec_cf_copy(
&res->ce[SRSLTE_RE_IDX(q->cell.nof_prb, i + ns * SRSLTE_CP_NSYMB(q->cell.cp), n_prb[ns] * SRSLTE_NRE)],
&q->pilot_recv_signal[ns * n_rs * SRSLTE_NRE], &q->pilot_recv_signal[ns * n_rs * SRSLTE_NRE],
sizeof(cf_t) * SRSLTE_NRE); SRSLTE_NRE);
}
}
// Estimate noise/interference
res->noise_estimate = estimate_noise_pilots_pucch(q, res->ce, n_rs, n_prb);
if (fpclassify(res->noise_estimate) == FP_ZERO) {
res->noise_estimate = FLT_MIN;
} }
res->noise_estimate_dbm = srslte_convert_power_to_dBm(res->noise_estimate);
// Estimate SINR
if (isnormal(res->noise_estimate)) {
res->snr = res->rsrp / res->noise_estimate;
res->snr_db = srslte_convert_power_to_dB(res->snr);
} else {
res->snr = NAN;
res->snr_db = NAN;
} }
} }

@ -204,6 +204,7 @@ static int get_pucch(srslte_enb_ul_t* q, srslte_ul_sf_cfg_t* ul_sf, srslte_pucch
ERROR("Error estimating PUCCH DMRS\n"); ERROR("Error estimating PUCCH DMRS\n");
return SRSLTE_ERROR; return SRSLTE_ERROR;
} }
pucch_res.snr_db = q->chest_res.snr_db;
ret = srslte_pucch_decode(&q->pucch, ul_sf, cfg, &q->chest_res, q->sf_symbols, &pucch_res); ret = srslte_pucch_decode(&q->pucch, ul_sf, cfg, &q->chest_res, q->sf_symbols, &pucch_res);
if (ret < SRSLTE_SUCCESS) { if (ret < SRSLTE_SUCCESS) {

@ -1380,7 +1380,8 @@ void srslte_pucch_rx_info(srslte_pucch_cfg_t* cfg, srslte_pucch_res_t* pucch_res
if (pucch_res) { if (pucch_res) {
if (isnormal(cfg->threshold_dmrs_detection)) { if (isnormal(cfg->threshold_dmrs_detection)) {
n = srslte_print_check(str, str_len, n, ", dmrs_corr=%.3f", pucch_res->dmrs_correlation); n = srslte_print_check(
str, str_len, n, ", dmrs_corr=%.3f, snr=%.1f dB", pucch_res->dmrs_correlation, pucch_res->snr_db);
} }
n = srslte_print_check(str, str_len, n, ", corr=%.3f", pucch_res->correlation); n = srslte_print_check(str, str_len, n, ", corr=%.3f", pucch_res->correlation);

@ -19,7 +19,7 @@
#include "srslte/srslte.h" #include "srslte/srslte.h"
srslte_cell_t cell = { static srslte_cell_t cell = {
25, // nof_prb 25, // nof_prb
1, // nof_ports 1, // nof_ports
1, // cell_id 1, // cell_id
@ -30,23 +30,25 @@ srslte_cell_t cell = {
}; };
uint32_t subframe = 0; static uint32_t subframe = 0;
bool test_cqi_only = false; static bool test_cqi_only = false;
static float snr_db = 20.0f;
void usage(char* prog) static void usage(char* prog)
{ {
printf("Usage: %s [csNnv]\n", prog); printf("Usage: %s [csNnv]\n", prog);
printf("\t-c cell id [Default %d]\n", cell.id); printf("\t-c cell id [Default %d]\n", cell.id);
printf("\t-s subframe [Default %d]\n", subframe); printf("\t-s subframe [Default %d]\n", subframe);
printf("\t-n nof_prb [Default %d]\n", cell.nof_prb); printf("\t-n nof_prb [Default %d]\n", cell.nof_prb);
printf("\t-q Test CQI encoding/decoding only [Default %s].\n", test_cqi_only ? "yes" : "no"); printf("\t-q Test CQI encoding/decoding only [Default %s].\n", test_cqi_only ? "yes" : "no");
printf("\t-S Signal to Noise Ratio in dB [Default %.2f].\n", snr_db);
printf("\t-v [set verbose to debug, default none]\n"); printf("\t-v [set verbose to debug, default none]\n");
} }
void parse_args(int argc, char** argv) void parse_args(int argc, char** argv)
{ {
int opt; int opt;
while ((opt = getopt(argc, argv, "csNnqv")) != -1) { while ((opt = getopt(argc, argv, "csNnqSv")) != -1) {
switch (opt) { switch (opt) {
case 's': case 's':
subframe = (uint32_t)strtol(argv[optind], NULL, 10); subframe = (uint32_t)strtol(argv[optind], NULL, 10);
@ -60,6 +62,9 @@ void parse_args(int argc, char** argv)
case 'q': case 'q':
test_cqi_only = true; test_cqi_only = true;
break; break;
case 'S':
snr_db = strtof(argv[optind], NULL);
break;
case 'v': case 'v':
srslte_verbose++; srslte_verbose++;
break; break;
@ -126,12 +131,16 @@ int test_uci_cqi_pucch(void)
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
srslte_pucch_t pucch; srslte_pucch_t pucch_ue = {};
srslte_pucch_cfg_t pucch_cfg; srslte_pucch_t pucch_enb = {};
srslte_pucch_cfg_t pucch_cfg = {};
srslte_refsignal_ul_t dmrs; srslte_refsignal_ul_t dmrs;
cf_t* sf_symbols = NULL; cf_t* sf_symbols = NULL;
cf_t pucch_dmrs[2 * SRSLTE_NRE * 3]; cf_t pucch_dmrs[2 * SRSLTE_NRE * 3];
int ret = -1; int ret = -1;
srslte_chest_ul_t chest = {};
srslte_chest_ul_res_t chest_res = {};
srslte_channel_awgn_t awgn = {};
parse_args(argc, argv); parse_args(argc, argv);
@ -139,14 +148,30 @@ int main(int argc, char** argv)
return test_uci_cqi_pucch(); return test_uci_cqi_pucch();
} }
if (srslte_pucch_init_ue(&pucch)) { if (srslte_pucch_init_ue(&pucch_ue)) {
ERROR("Error creating PDSCH object\n");
exit(-1);
}
if (srslte_pucch_set_cell(&pucch_ue, cell)) {
ERROR("Error creating PDSCH object\n");
exit(-1);
}
if (srslte_pucch_set_rnti(&pucch_ue, 11)) {
ERROR("Error setting C-RNTI\n");
goto quit;
}
if (srslte_pucch_init_enb(&pucch_enb)) {
ERROR("Error creating PDSCH object\n"); ERROR("Error creating PDSCH object\n");
exit(-1); exit(-1);
} }
if (srslte_pucch_set_cell(&pucch, cell)) { if (srslte_pucch_set_cell(&pucch_enb, cell)) {
ERROR("Error creating PDSCH object\n"); ERROR("Error creating PDSCH object\n");
exit(-1); exit(-1);
} }
if (srslte_pucch_set_rnti(&pucch_enb, 11)) {
ERROR("Error setting C-RNTI\n");
goto quit;
}
if (srslte_refsignal_ul_init(&dmrs, cell.nof_prb)) { if (srslte_refsignal_ul_init(&dmrs, cell.nof_prb)) {
ERROR("Error creating PDSCH object\n"); ERROR("Error creating PDSCH object\n");
exit(-1); exit(-1);
@ -156,10 +181,27 @@ int main(int argc, char** argv)
exit(-1); exit(-1);
} }
bzero(&pucch_cfg, sizeof(srslte_pucch_cfg_t)); if (srslte_chest_ul_init(&chest, cell.nof_prb) < SRSLTE_SUCCESS) {
ERROR("Error initiating channel estimator\n");
goto quit;
}
if (srslte_pucch_set_rnti(&pucch, 11)) { if (srslte_chest_ul_res_init(&chest_res, cell.nof_prb) < SRSLTE_SUCCESS) {
ERROR("Error setting C-RNTI\n"); ERROR("Error initiating channel estimator result\n");
goto quit;
}
if (srslte_chest_ul_set_cell(&chest, cell) < SRSLTE_SUCCESS) {
ERROR("Error setting channel estimator cell\n");
goto quit;
}
if (srslte_channel_awgn_init(&awgn, 0x1234) < SRSLTE_SUCCESS) {
ERROR("Error initiating AWGN\n");
goto quit;
}
if (srslte_channel_awgn_set_n0(&awgn, -snr_db) < SRSLTE_SUCCESS) {
ERROR("Error setting AWGN\n");
goto quit; goto quit;
} }
@ -172,7 +214,7 @@ int main(int argc, char** argv)
ZERO_OBJECT(ul_sf); ZERO_OBJECT(ul_sf);
srslte_pucch_format_t format; srslte_pucch_format_t format;
for (format = 0; format <= SRSLTE_PUCCH_FORMAT_3; format++) { for (format = 0; format < SRSLTE_PUCCH_FORMAT_ERROR; format++) {
for (uint32_t d = 1; d <= 3; d++) { for (uint32_t d = 1; d <= 3; d++) {
for (uint32_t ncs = 0; ncs < 8; ncs += d) { for (uint32_t ncs = 0; ncs < 8; ncs += d) {
for (uint32_t n_pucch = 1; n_pucch < 130; n_pucch += 50) { for (uint32_t n_pucch = 1; n_pucch < 130; n_pucch += 50) {
@ -215,8 +257,9 @@ int main(int argc, char** argv)
uci_data.cfg.cqi.data_enable = true; uci_data.cfg.cqi.data_enable = true;
} }
// Encode PUCCH signals
gettimeofday(&t[1], NULL); gettimeofday(&t[1], NULL);
if (srslte_pucch_encode(&pucch, &ul_sf, &pucch_cfg, &uci_data.value, sf_symbols)) { if (srslte_pucch_encode(&pucch_ue, &ul_sf, &pucch_cfg, &uci_data.value, sf_symbols)) {
ERROR("Error encoding PUCCH\n"); ERROR("Error encoding PUCCH\n");
goto quit; goto quit;
} }
@ -231,7 +274,48 @@ int main(int argc, char** argv)
} }
gettimeofday(&t[2], NULL); gettimeofday(&t[2], NULL);
get_time_interval(t); get_time_interval(t);
INFO("format %d, n_pucch: %d, ncs: %d, d: %d, t_exec=%ld us\n", format, n_pucch, ncs, d, t[0].tv_usec); uint64_t t_enc = t[0].tv_usec + t[0].tv_sec * 1000000UL;
// Run AWGN channel
srslte_channel_awgn_run_c(&awgn, sf_symbols, sf_symbols, SRSLTE_NOF_RE(cell));
// Decode PUCCH signals
gettimeofday(&t[1], NULL);
if (srslte_chest_ul_estimate_pucch(&chest, &ul_sf, &pucch_cfg, sf_symbols, &chest_res) < SRSLTE_SUCCESS) {
ERROR("Error estimating PUCCH channel\n");
goto quit;
}
srslte_pucch_res_t res = {};
if (srslte_pucch_decode(&pucch_enb, &ul_sf, &pucch_cfg, &chest_res, sf_symbols, &res) < SRSLTE_SUCCESS) {
ERROR("Error decoding PUCCH\n");
goto quit;
}
gettimeofday(&t[2], NULL);
get_time_interval(t);
uint64_t t_dec = t[0].tv_usec + t[0].tv_sec * 1000000UL;
// Check EPRE and RSRP are +/- 1 dB and SNR measurements are +/- 3dB
if (fabsf(chest_res.epre_dBfs) > 1.0 || fabsf(chest_res.rsrp_dBfs) > 1.0 ||
fabsf(chest_res.snr_db - snr_db) > 3.0) {
ERROR("Invalid EPRE (%+.2f), RSRP (%+.2f) or SNR (%+.2f)\n",
chest_res.epre_dBfs,
chest_res.rsrp_dBfs,
chest_res.snr_db);
goto quit;
}
INFO("format %d, n_pucch: %d, ncs: %d, d: %d, t_encode=%ld us, t_decode=%ld us, EPRE=%+.1f dBfs, RSRP=%+.1f "
"dBfs, SNR=%+.1f dBfs\n",
format,
n_pucch,
ncs,
d,
t_enc,
t_dec,
chest_res.epre_dBfs,
chest_res.rsrp_dBfs,
chest_res.snr_db);
} }
} }
} }
@ -239,9 +323,12 @@ int main(int argc, char** argv)
ret = 0; ret = 0;
quit: quit:
srslte_pucch_free(&pucch); srslte_pucch_free(&pucch_ue);
srslte_pucch_free(&pucch_enb);
srslte_refsignal_ul_free(&dmrs); srslte_refsignal_ul_free(&dmrs);
srslte_chest_ul_free(&chest);
srslte_chest_ul_res_free(&chest_res);
srslte_channel_awgn_free(&awgn);
if (sf_symbols) { if (sf_symbols) {
free(sf_symbols); free(sf_symbols);
} }

@ -100,6 +100,7 @@ private:
void metrics_read(phy_metrics_t* metrics); void metrics_read(phy_metrics_t* metrics);
void metrics_dl(uint32_t mcs); void metrics_dl(uint32_t mcs);
void metrics_ul(uint32_t mcs, float rssi, float sinr, float turbo_iters); void metrics_ul(uint32_t mcs, float rssi, float sinr, float turbo_iters);
void metrics_ul_pucch(float sinr);
uint32_t get_rnti() const { return rnti; } uint32_t get_rnti() const { return rnti; }
private: private:

@ -19,11 +19,13 @@ namespace srsenb {
struct ul_metrics_t { struct ul_metrics_t {
float n; float n;
float sinr; float pusch_sinr;
float pucch_sinr;
float rssi; float rssi;
float turbo_iters; float turbo_iters;
float mcs; float mcs;
int n_samples; int n_samples;
int n_samples_pucch;
}; };
struct dl_metrics_t { struct dl_metrics_t {

@ -67,9 +67,9 @@ public:
{ {
return mac.cqi_info(tti, rnti, cc_idx, cqi_value); return mac.cqi_info(tti, rnti, cc_idx, cqi_value);
} }
int snr_info(uint32_t tti_rx, uint16_t rnti, uint32_t cc_idx, float snr_db) final int snr_info(uint32_t tti_rx, uint16_t rnti, uint32_t cc_idx, float snr_db, ul_channel_t ch) final
{ {
return mac.snr_info(tti_rx, rnti, cc_idx, snr_db); return mac.snr_info(tti_rx, rnti, cc_idx, snr_db, ch);
} }
int ta_info(uint32_t tti, uint16_t rnti, float ta_us) override { return mac.ta_info(tti, rnti, ta_us); } int ta_info(uint32_t tti, uint16_t rnti, float ta_us) override { return mac.ta_info(tti, rnti, ta_us); }
int ack_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t tb_idx, bool ack) final int ack_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t tb_idx, bool ack) final

@ -52,7 +52,7 @@ public:
int ri_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t ri_value) override; int ri_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t ri_value) override;
int pmi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t pmi_value) override; int pmi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t pmi_value) override;
int cqi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t cqi_value) override; int cqi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t cqi_value) override;
int snr_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, float snr) override; int snr_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, float snr, ul_channel_t ch) override;
int ta_info(uint32_t tti, uint16_t rnti, float ta_us) override; int ta_info(uint32_t tti, uint16_t rnti, float ta_us) override;
int ack_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t tb_idx, bool ack) override; int ack_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t tb_idx, bool ack) override;
int crc_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t nof_bytes, bool crc_res) override; int crc_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t nof_bytes, bool crc_res) override;

@ -195,9 +195,9 @@ void parse_args(all_args_t* args, int argc, char* argv[])
("expert.equalizer_mode", bpo::value<string>(&args->phy.equalizer_mode)->default_value("mmse"), "Equalizer mode") ("expert.equalizer_mode", bpo::value<string>(&args->phy.equalizer_mode)->default_value("mmse"), "Equalizer mode")
("expert.estimator_fil_w", bpo::value<float>(&args->phy.estimator_fil_w)->default_value(0.1), "Chooses the coefficients for the 3-tap channel estimator centered filter.") ("expert.estimator_fil_w", bpo::value<float>(&args->phy.estimator_fil_w)->default_value(0.1), "Chooses the coefficients for the 3-tap channel estimator centered filter.")
("expert.lte_sample_rates", bpo::value<bool>(&use_standard_lte_rates)->default_value(false), "Whether to use default LTE sample rates instead of shorter variants.") ("expert.lte_sample_rates", bpo::value<bool>(&use_standard_lte_rates)->default_value(false), "Whether to use default LTE sample rates instead of shorter variants.")
("expert.report_json_enable", bpo::value<bool>(&args->general.report_json_enable)->default_value(true), "Write eNB report to JSON file") ("expert.report_json_enable", bpo::value<bool>(&args->general.report_json_enable)->default_value(false), "Write eNB report to JSON file")
("expert.report_json_filename", bpo::value<string>(&args->general.report_json_filename)->default_value("/tmp/enb_report.json"), "Report JSON filename") ("expert.report_json_filename", bpo::value<string>(&args->general.report_json_filename)->default_value("/tmp/enb_report.json"), "Report JSON filename")
("expert.alarms_log_enable", bpo::value<bool>(&args->general.alarms_log_enable)->default_value(true), "Log alarms") ("expert.alarms_log_enable", bpo::value<bool>(&args->general.alarms_log_enable)->default_value(false), "Log alarms")
("expert.alarms_filename", bpo::value<string>(&args->general.alarms_filename)->default_value("/tmp/enb_alarms.log"), "Alarms filename") ("expert.alarms_filename", bpo::value<string>(&args->general.alarms_filename)->default_value("/tmp/enb_alarms.log"), "Alarms filename")
("expert.rrc_inactivity_timer", bpo::value<uint32_t>(&args->general.rrc_inactivity_timer)->default_value(30000), "Inactivity timer in ms.") ("expert.rrc_inactivity_timer", bpo::value<uint32_t>(&args->general.rrc_inactivity_timer)->default_value(30000), "Inactivity timer in ms.")
("expert.print_buffer_state", bpo::value<bool>(&args->general.print_buffer_state)->default_value(false), "Prints on the console the buffer state every 10 seconds") ("expert.print_buffer_state", bpo::value<bool>(&args->general.print_buffer_state)->default_value(false), "Prints on the console the buffer state every 10 seconds")

@ -100,8 +100,8 @@ static void fill_ue_metrics(mset_ue_container& ue, const enb_metrics_t& m, unsig
if (m.stack.mac.ues[i].tx_pkts > 0 && m.stack.mac.ues[i].tx_errors > 0) { if (m.stack.mac.ues[i].tx_pkts > 0 && m.stack.mac.ues[i].tx_errors > 0) {
ue.write<metric_dl_bler>(std::max(0.1f, (float)100 * m.stack.mac.ues[i].tx_errors / m.stack.mac.ues[i].tx_pkts)); ue.write<metric_dl_bler>(std::max(0.1f, (float)100 * m.stack.mac.ues[i].tx_errors / m.stack.mac.ues[i].tx_pkts));
} }
if (!std::isnan(m.phy[i].ul.sinr)) { if (!std::isnan(m.phy[i].ul.pusch_sinr)) {
ue.write<metric_ul_snr>(std::max(0.1f, m.phy[i].ul.sinr)); ue.write<metric_ul_snr>(std::max(0.1f, m.phy[i].ul.pusch_sinr));
} }
if (!std::isnan(m.phy[i].ul.mcs)) { if (!std::isnan(m.phy[i].ul.mcs)) {
ue.write<metric_ul_mcs>(std::max(0.1f, m.phy[i].ul.mcs)); ue.write<metric_ul_mcs>(std::max(0.1f, m.phy[i].ul.mcs));

@ -83,8 +83,8 @@ void metrics_stdout::set_metrics(const enb_metrics_t& metrics, const uint32_t pe
if (++n_reports > 10) { if (++n_reports > 10) {
n_reports = 0; n_reports = 0;
cout << endl; cout << endl;
cout << "------DL--------------------------------UL------------------------------------" << endl; cout << "------DL-------------------------------UL--------------------------------------------" << endl;
cout << "rnti cqi ri mcs brate ok nok (%) snr phr mcs brate ok nok (%) bsr" << endl; cout << "rnti cqi ri mcs brate ok nok (%) pusch pucch phr mcs brate ok nok (%) bsr" << endl;
} }
for (size_t i = 0; i < metrics.stack.rrc.ues.size(); i++) { for (size_t i = 0; i < metrics.stack.rrc.ues.size(); i++) {
@ -124,12 +124,19 @@ void metrics_stdout::set_metrics(const enb_metrics_t& metrics, const uint32_t pe
} }
cout << " "; cout << " ";
if (not isnan(metrics.phy[i].ul.sinr)) { if (not isnan(metrics.phy[i].ul.pusch_sinr)) {
cout << float_to_string(SRSLTE_MAX(0.1, metrics.phy[i].ul.sinr), 2, 4); cout << float_to_string(SRSLTE_MAX(0.1, metrics.phy[i].ul.pusch_sinr), 2, 5);
} else { } else {
cout << float_to_string(0, 1, 4); cout << float_to_string(0, 2, 5);
} }
if (not isnan(metrics.phy[i].ul.pucch_sinr)) {
cout << float_to_string(SRSLTE_MAX(0.1, metrics.phy[i].ul.pucch_sinr), 2, 5);
} else {
cout << float_to_string(0, 2, 5);
}
cout << " ";
cout << float_to_string(metrics.stack.mac.ues[i].phr, 2, 5); cout << float_to_string(metrics.stack.mac.ues[i].phr, 2, 5);
if (not isnan(metrics.phy[i].ul.mcs)) { if (not isnan(metrics.phy[i].ul.mcs)) {
cout << float_to_string(SRSLTE_MAX(0.1, metrics.phy[i].ul.mcs), 1, 4); cout << float_to_string(SRSLTE_MAX(0.1, metrics.phy[i].ul.mcs), 1, 4);

@ -335,7 +335,7 @@ void cc_worker::decode_pusch_rnti(stack_interface_phy_lte::ul_sched_grant_t& ul_
// Notify MAC of RL status // Notify MAC of RL status
if (snr_db >= PUSCH_RL_SNR_DB_TH) { if (snr_db >= PUSCH_RL_SNR_DB_TH) {
// Notify MAC UL channel quality // Notify MAC UL channel quality
phy->stack->snr_info(ul_sf.tti, rnti, cc_idx, snr_db); phy->stack->snr_info(ul_sf.tti, rnti, cc_idx, snr_db, mac_interface_phy_lte::PUSCH);
// Notify MAC of Time Alignment only if it enabled and valid measurement, ignore value otherwise // Notify MAC of Time Alignment only if it enabled and valid measurement, ignore value otherwise
if (ul_cfg.pusch.meas_ta_en and not std::isnan(enb_ul.chest_res.ta_us) and not std::isinf(enb_ul.chest_res.ta_us)) { if (ul_cfg.pusch.meas_ta_en and not std::isnan(enb_ul.chest_res.ta_us) and not std::isinf(enb_ul.chest_res.ta_us)) {
@ -409,6 +409,7 @@ int cc_worker::decode_pucch()
if (pucch_res.detected and pucch_res.ta_valid) { if (pucch_res.detected and pucch_res.ta_valid) {
phy->stack->ta_info(tti_rx, rnti, pucch_res.ta_us); phy->stack->ta_info(tti_rx, rnti, pucch_res.ta_us);
phy->stack->snr_info(tti_rx, rnti, cc_idx, pucch_res.snr_db, mac_interface_phy_lte::PUCCH);
} }
// Logging // Logging
@ -417,6 +418,9 @@ int cc_worker::decode_pucch()
srslte_pucch_rx_info(&ul_cfg.pucch, &pucch_res, str, sizeof(str)); srslte_pucch_rx_info(&ul_cfg.pucch, &pucch_res, str, sizeof(str));
log_h->info("PUCCH: cc=%d; %s\n", cc_idx, str); log_h->info("PUCCH: cc=%d; %s\n", cc_idx, str);
} }
// Save metrics
ue_db[rnti]->metrics_ul_pucch(pucch_res.snr_db);
} }
} }
} }
@ -615,12 +619,18 @@ void cc_worker::ue::metrics_dl(uint32_t mcs)
void cc_worker::ue::metrics_ul(uint32_t mcs, float rssi, float sinr, float turbo_iters) void cc_worker::ue::metrics_ul(uint32_t mcs, float rssi, float sinr, float turbo_iters)
{ {
metrics.ul.mcs = SRSLTE_VEC_CMA((float)mcs, metrics.ul.mcs, metrics.ul.n_samples); metrics.ul.mcs = SRSLTE_VEC_CMA((float)mcs, metrics.ul.mcs, metrics.ul.n_samples);
metrics.ul.sinr = SRSLTE_VEC_CMA((float)sinr, metrics.ul.sinr, metrics.ul.n_samples); metrics.ul.pusch_sinr = SRSLTE_VEC_CMA((float)sinr, metrics.ul.pusch_sinr, metrics.ul.n_samples);
metrics.ul.rssi = SRSLTE_VEC_CMA((float)rssi, metrics.ul.rssi, metrics.ul.n_samples); metrics.ul.rssi = SRSLTE_VEC_CMA((float)rssi, metrics.ul.rssi, metrics.ul.n_samples);
metrics.ul.turbo_iters = SRSLTE_VEC_CMA((float)turbo_iters, metrics.ul.turbo_iters, metrics.ul.n_samples); metrics.ul.turbo_iters = SRSLTE_VEC_CMA((float)turbo_iters, metrics.ul.turbo_iters, metrics.ul.n_samples);
metrics.ul.n_samples++; metrics.ul.n_samples++;
} }
void cc_worker::ue::metrics_ul_pucch(float sinr)
{
metrics.ul.pucch_sinr = SRSLTE_VEC_CMA((float)sinr, metrics.ul.pucch_sinr, metrics.ul.n_samples_pucch);
metrics.ul.n_samples_pucch++;
}
int cc_worker::read_ce_abs(float* ce_abs) int cc_worker::read_ce_abs(float* ce_abs)
{ {
int sz = srslte_symbol_sz(phy->get_nof_prb(cc_idx)); int sz = srslte_symbol_sz(phy->get_nof_prb(cc_idx));

@ -285,11 +285,14 @@ uint32_t sf_worker::get_metrics(std::vector<phy_metrics_t>& metrics)
m->dl.mcs = SRSLTE_VEC_PMA(m->dl.mcs, m->dl.n_samples, m_->dl.mcs, m_->dl.n_samples); m->dl.mcs = SRSLTE_VEC_PMA(m->dl.mcs, m->dl.n_samples, m_->dl.mcs, m_->dl.n_samples);
m->dl.n_samples += m_->dl.n_samples; m->dl.n_samples += m_->dl.n_samples;
m->ul.n = SRSLTE_VEC_PMA(m->ul.n, m->ul.n_samples, m_->ul.n, m_->ul.n_samples); m->ul.n = SRSLTE_VEC_PMA(m->ul.n, m->ul.n_samples, m_->ul.n, m_->ul.n_samples);
m->ul.sinr = SRSLTE_VEC_PMA(m->ul.sinr, m->ul.n_samples, m_->ul.sinr, m_->ul.n_samples); m->ul.pusch_sinr = SRSLTE_VEC_PMA(m->ul.pusch_sinr, m->ul.n_samples, m_->ul.pusch_sinr, m_->ul.n_samples);
m->ul.pucch_sinr =
SRSLTE_VEC_PMA(m->ul.pucch_sinr, m->ul.n_samples_pucch, m_->ul.pucch_sinr, m_->ul.n_samples_pucch);
m->ul.mcs = SRSLTE_VEC_PMA(m->ul.mcs, m->ul.n_samples, m_->ul.mcs, m_->ul.n_samples); m->ul.mcs = SRSLTE_VEC_PMA(m->ul.mcs, m->ul.n_samples, m_->ul.mcs, m_->ul.n_samples);
m->ul.rssi = SRSLTE_VEC_PMA(m->ul.rssi, m->ul.n_samples, m_->ul.rssi, m_->ul.n_samples); m->ul.rssi = SRSLTE_VEC_PMA(m->ul.rssi, m->ul.n_samples, m_->ul.rssi, m_->ul.n_samples);
m->ul.turbo_iters = SRSLTE_VEC_PMA(m->ul.turbo_iters, m->ul.n_samples, m_->ul.turbo_iters, m_->ul.n_samples); m->ul.turbo_iters = SRSLTE_VEC_PMA(m->ul.turbo_iters, m->ul.n_samples, m_->ul.turbo_iters, m_->ul.n_samples);
m->ul.n_samples += m_->ul.n_samples; m->ul.n_samples += m_->ul.n_samples;
m->ul.n_samples_pucch += m_->ul.n_samples_pucch;
} }
} }
return cnt; return cnt;

@ -215,10 +215,12 @@ void phy::get_metrics(std::vector<phy_metrics_t>& metrics)
metrics[j].dl.mcs += metrics_tmp[j].dl.n_samples * metrics_tmp[j].dl.mcs; metrics[j].dl.mcs += metrics_tmp[j].dl.n_samples * metrics_tmp[j].dl.mcs;
metrics[j].ul.n_samples += metrics_tmp[j].ul.n_samples; metrics[j].ul.n_samples += metrics_tmp[j].ul.n_samples;
metrics[j].ul.n_samples_pucch += metrics_tmp[j].ul.n_samples_pucch;
metrics[j].ul.mcs += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.mcs; metrics[j].ul.mcs += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.mcs;
metrics[j].ul.n += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.n; metrics[j].ul.n += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.n;
metrics[j].ul.rssi += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.rssi; metrics[j].ul.rssi += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.rssi;
metrics[j].ul.sinr += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.sinr; metrics[j].ul.pusch_sinr += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.pusch_sinr;
metrics[j].ul.pucch_sinr += metrics_tmp[j].ul.n_samples_pucch * metrics_tmp[j].ul.pucch_sinr;
metrics[j].ul.turbo_iters += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.turbo_iters; metrics[j].ul.turbo_iters += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.turbo_iters;
} }
} }
@ -227,7 +229,8 @@ void phy::get_metrics(std::vector<phy_metrics_t>& metrics)
metrics[j].ul.mcs /= metrics[j].ul.n_samples; metrics[j].ul.mcs /= metrics[j].ul.n_samples;
metrics[j].ul.n /= metrics[j].ul.n_samples; metrics[j].ul.n /= metrics[j].ul.n_samples;
metrics[j].ul.rssi /= metrics[j].ul.n_samples; metrics[j].ul.rssi /= metrics[j].ul.n_samples;
metrics[j].ul.sinr /= metrics[j].ul.n_samples; metrics[j].ul.pusch_sinr /= metrics[j].ul.n_samples;
metrics[j].ul.pucch_sinr /= metrics[j].ul.n_samples_pucch;
metrics[j].ul.turbo_iters /= metrics[j].ul.n_samples; metrics[j].ul.turbo_iters /= metrics[j].ul.n_samples;
} }
} }

@ -388,7 +388,7 @@ int mac::cqi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t cqi
return SRSLTE_SUCCESS; return SRSLTE_SUCCESS;
} }
int mac::snr_info(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, float snr) int mac::snr_info(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, float snr, ul_channel_t ch)
{ {
log_h->step(tti_rx); log_h->step(tti_rx);
srslte::rwlock_read_guard lock(rwlock); srslte::rwlock_read_guard lock(rwlock);
@ -397,7 +397,7 @@ int mac::snr_info(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, float snr
return SRSLTE_ERROR; return SRSLTE_ERROR;
} }
return scheduler.ul_snr_info(tti_rx, rnti, enb_cc_idx, snr, 0); return scheduler.ul_snr_info(tti_rx, rnti, enb_cc_idx, snr, (uint32_t)ch);
} }
int mac::ta_info(uint32_t tti, uint16_t rnti, float ta_us) int mac::ta_info(uint32_t tti, uint16_t rnti, float ta_us)

@ -55,7 +55,8 @@ public:
metrics[0].phy.resize(1); metrics[0].phy.resize(1);
metrics[0].phy[0].dl.mcs = 28.0; metrics[0].phy[0].dl.mcs = 28.0;
metrics[0].phy[0].ul.mcs = 20.2; metrics[0].phy[0].ul.mcs = 20.2;
metrics[0].phy[0].ul.sinr = 14.2; metrics[0].phy[0].ul.pucch_sinr = 14.2;
metrics[0].phy[0].ul.pusch_sinr = 14.2;
// second // second
metrics[1].rf.rf_o = 10; metrics[1].rf.rf_o = 10;
@ -77,7 +78,8 @@ public:
metrics[1].phy.resize(1); metrics[1].phy.resize(1);
metrics[1].phy[0].dl.mcs = 6.2; metrics[1].phy[0].dl.mcs = 6.2;
metrics[1].phy[0].ul.mcs = 28.0; metrics[1].phy[0].ul.mcs = 28.0;
metrics[1].phy[0].ul.sinr = 22.2; metrics[1].phy[0].ul.pucch_sinr = 22.2;
metrics[1].phy[0].ul.pusch_sinr = 22.2;
// third entry // third entry
metrics[2].rf.rf_o = 10; metrics[2].rf.rf_o = 10;
@ -99,7 +101,8 @@ public:
metrics[2].phy.resize(1); metrics[2].phy.resize(1);
metrics[2].phy[0].dl.mcs = 28.0; metrics[2].phy[0].dl.mcs = 28.0;
metrics[2].phy[0].ul.mcs = 20.2; metrics[2].phy[0].ul.mcs = 20.2;
metrics[2].phy[0].ul.sinr = 14.2; metrics[2].phy[0].ul.pusch_sinr = 14.2;
metrics[2].phy[0].ul.pucch_sinr = 14.2;
// fourth entry with incomple PHY and MAC stats // fourth entry with incomple PHY and MAC stats
metrics[3].rf.rf_o = 10; metrics[3].rf.rf_o = 10;

@ -454,7 +454,7 @@ public:
return SRSLTE_SUCCESS; return SRSLTE_SUCCESS;
} }
int snr_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, float snr_db) override int snr_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, float snr_db, ul_channel_t ch) override
{ {
notify_snr_info(); notify_snr_info();
return 0; return 0;

Loading…
Cancel
Save