From 8f6dd184be68042a3012e0681d3286fe954bf6a8 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Wed, 22 Jan 2020 16:07:14 +0100 Subject: [PATCH] SRSUE: neighbour cell measurement test defined --- lib/src/phy/sync/refsignal_dl_sync.c | 328 +++++++++++++++++++++++---- srsue/test/phy/scell_search_test.cc | 159 +++++++------ 2 files changed, 374 insertions(+), 113 deletions(-) diff --git a/lib/src/phy/sync/refsignal_dl_sync.c b/lib/src/phy/sync/refsignal_dl_sync.c index 823834a9f..c9503182e 100644 --- a/lib/src/phy/sync/refsignal_dl_sync.c +++ b/lib/src/phy/sync/refsignal_dl_sync.c @@ -26,38 +26,154 @@ #include #include +/* + * Constants + * -------------- + * These constants have been optimized for passing scell_search_test for a number of scenarios. + * - 6 PRB. 6 cells distributed uniformly 10ms delay + * srsue/test/phy/scell_search_test --duration=5 --cell.nof_prb=6 --active_cell_list=all + * --simulation_cell_list=1,2,3,4,5,6 --channel_period_s=30 --channel.hst.fd=750 --channel.delay_max=1000 + * + * - 6 PRB. 6 cells distributed uniformly 10ms delay + * srsue/test/phy/scell_search_test --duration=30 --cell.nof_prb=6 --active_cell_list=2,3,4,5,6 + * --simulation_cell_list=1,2,3,4,5,6 --channel_period_s=30 --channel.hst.fd=750 --channel.delay_max=10000 + * + * - 6 PRB. 6 cell PSS, SSS overlapped: + * srsue/test/phy/scell_search_test --duration=30 --cell.nof_prb=6 --active_cell_list=all + * --simulation_cell_list=1,2,3,4,5,6 --channel_period_s=30 --channel.hst.fd=750 --channel.delay_max=0 + * + * - 100 PRB, 6 cell, distributed around 1ms + * srsue/test/phy/scell_search_test --duration=30 --cell.nof_prb=100 --active_cell_list=2,3,4,5,6 + * --simulation_cell_list=1,2,3,4,5,6 --channel_period_s=30 --channel.hst.fd=750 --channel.delay_max=1000 + */ +#define REFSIGNAL_DL_SYNC_CORRELATION_THR (4.0f) /* Correlation threashold normalised in Peak / RMS */ +#define REFSIGNAL_DL_CFO_MIN_MAX_MILD (1000.0f) /* Maximum difference between sub-frame CFO estimation in Hz */ +#define REFSIGNAL_DL_CFO_MIN_MAX_SEVERE (2000.0f) +#define REFSIGNAL_DL_RSRP_MIN_MAX_MILD (6.0f) /* Maximum difference between sub-frame RSRP estimation in dB */ +#define REFSIGNAL_DL_RSRP_MIN_MAX_SEVERE (10.0f) +#define REFSIGNAL_DL_RSRP_FALSE_RATIO_MILD (4.0f) /* Minimum RSRP ratio between real and false in dB */ +#define REFSIGNAL_DL_RSRP_FALSE_RATIO_SEVERE (3.0f) +#define REFSIGNAL_DL_SSS_FALSE_RATIO_MILD (15.0f) /* Minimum SSS strength between real and false (linear) */ +#define REFSIGNAL_DL_SSS_FALSE_RATIO_SEVERE (1.5f) +#define REFSIGNAL_DL_MAX_FAULT_CHECK (1) /* Number of mild faults without declaring false */ +#define REFSIGNAL_DL_CFO_LOW_WEIGHT (0.5f) /* Weight for low CFO estimation (2 kHz) */ +#define REFSIGNAL_DL_CFO_MEDIUM_WEIGHT (0.3f) /* Weight for medium CFO estimation (3.5 kHz) */ +#define REFSIGNAL_DL_CFO_HIGH_WEIGHT (0.2f) /* Weight for high CFO estimation (4.66 kHz) */ + +/* + * Local Helpers + */ +static inline void refsignal_set_results_not_found(srslte_refsignal_dl_sync_t* q) +{ + q->found = false; + q->rsrp_dBfs = NAN; + q->rssi_dBfs = NAN; + q->rsrq_dB = NAN; + q->cfo_Hz = NAN; + q->peak_index = UINT32_MAX; +} + +static inline void refsignal_sf_prepare_correlation(srslte_refsignal_dl_sync_t* q) +{ + uint32_t sf_len = q->ifft.sf_sz; + cf_t* ptr_filt = q->conv_fft_cc.filter_fft; + memcpy(ptr_filt, q->sequences[0], sizeof(cf_t) * sf_len); + bzero(&ptr_filt[sf_len], sizeof(cf_t) * sf_len); + srslte_dft_run_c(&q->conv_fft_cc.filter_plan, ptr_filt, ptr_filt); +} + +static inline void +refsignal_sf_correlate(srslte_refsignal_dl_sync_t* q, cf_t* ptr_in, float* peak_value, uint32_t* peak_idx, float* rms) +{ + // Correlate + srslte_corr_fft_cc_run_opt(&q->conv_fft_cc, ptr_in, q->conv_fft_cc.filter_fft, q->correlation); + + // Find maximum, calculate RMS and peak + uint32_t imax = srslte_vec_max_abs_ci(q->correlation, q->ifft.sf_sz); + + if (peak_idx) { + *peak_idx = imax; + } + + if (peak_value) { + *peak_value = cabsf(q->correlation[imax]); + } + + if (rms) { + *rms = sqrtf(srslte_vec_avg_power_cf(q->correlation, q->ifft.sf_sz)); + } +} + +static inline void refsignal_dl_pss_sss_strength(srslte_refsignal_dl_sync_t* q, + cf_t* buffer, + uint32_t sf_idx, + float* pss_strength, + float* sss_strength, + float* sss_strength_false) +{ + uint32_t symbol_sz = q->ifft.symbol_sz; + uint32_t cp_len0 = SRSLTE_CP_LEN_NORM(0, symbol_sz); + uint32_t cp_len1 = SRSLTE_CP_LEN_NORM(1, symbol_sz); + + uint32_t pss_n = cp_len0 + cp_len1 * 6 + symbol_sz * 6; + uint32_t sss_n = cp_len0 + cp_len1 * 5 + symbol_sz * 5; + + sf_idx %= SRSLTE_NOF_SF_X_FRAME; + + srslte_dl_sf_cfg_t dl_sf_cfg = {}; + dl_sf_cfg.tti = sf_idx; + + float k = (float)(srslte_refsignal_cs_nof_re(&q->refsignal, &dl_sf_cfg, 0)) / (float)SRSLTE_PSS_LEN; + + if (pss_strength) { + cf_t corr = srslte_vec_dot_prod_conj_ccc(&buffer[pss_n], &q->sequences[sf_idx][pss_n], symbol_sz); + *pss_strength = k * __real__(corr * conjf(corr)); + } + + if (sss_strength) { + cf_t corr = srslte_vec_dot_prod_conj_ccc(&buffer[sss_n], &q->sequences[sf_idx][sss_n], symbol_sz); + *sss_strength = k * __real__(corr * conjf(corr)); + } + + if (sss_strength_false) { + uint32_t sf_idx2 = (sf_idx + SRSLTE_NOF_SF_X_FRAME / 2) % SRSLTE_NOF_SF_X_FRAME; + cf_t corr = srslte_vec_dot_prod_conj_ccc(&buffer[sss_n], &q->sequences[sf_idx2][sss_n], symbol_sz); + *sss_strength_false = k * __real__(corr * conjf(corr)); + } +} + int srslte_refsignal_dl_sync_init(srslte_refsignal_dl_sync_t* q) { int ret = SRSLTE_ERROR_INVALID_INPUTS; if (q) { // Initialise internals - bzero(q, sizeof(srslte_refsignal_dl_sync_t)); + memset(q, 0, sizeof(srslte_refsignal_dl_sync_t)); // Initialise Reference signals ret = srslte_refsignal_cs_init(&q->refsignal, SRSLTE_MAX_PRB); // Allocate time buffers for (int i = 0; i < SRSLTE_NOF_SF_X_FRAME; i++) { - q->sequences[i] = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_SF_LEN_MAX); + q->sequences[i] = srslte_vec_cf_malloc(SRSLTE_SF_LEN_MAX); if (!q->sequences[i]) { perror("Allocating sequence\n"); } } // Allocate Temporal OFDM buffer - q->ifft_buffer_in = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_SF_LEN_MAX); + q->ifft_buffer_in = srslte_vec_cf_malloc(SRSLTE_SF_LEN_MAX); if (!q->ifft_buffer_in) { perror("Allocating ifft_buffer_in\n"); } - q->ifft_buffer_out = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_SF_LEN_MAX); + q->ifft_buffer_out = srslte_vec_cf_malloc(SRSLTE_SF_LEN_MAX); if (!q->ifft_buffer_out) { perror("Allocating ifft_buffer_out\n"); } // Allocate correlation - q->correlation = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_SF_LEN_MAX * 2); + q->correlation = srslte_vec_cf_malloc(SRSLTE_SF_LEN_MAX * 2); if (!q->correlation) { perror("Allocating correlation\n"); } @@ -76,6 +192,9 @@ int srslte_refsignal_dl_sync_init(srslte_refsignal_dl_sync_t* q) if (!ret) { ret = srslte_conv_fft_cc_init(&q->conv_fft_cc, q->ifft.sf_sz, q->ifft.sf_sz); } + + // Set default results to not found + refsignal_set_results_not_found(q); } return ret; @@ -125,9 +244,6 @@ int srslte_refsignal_dl_sync_set_cell(srslte_refsignal_dl_sync_t* q, srslte_cell srslte_sss_put_slot(i ? sss_signal5 : sss_signal0, q->ifft_buffer_in, cell.nof_prb, cell.cp); } - // Increase correlation for 2 port eNb - cell.nof_ports = 2; - // Put Reference signals for (int p = 0; p < cell.nof_ports; p++) { ret = srslte_refsignal_cs_put_sf(&q->refsignal, &dl_sf_cfg, p, q->ifft_buffer_in); @@ -185,33 +301,25 @@ void srslte_refsignal_dl_sync_free(srslte_refsignal_dl_sync_t* q) int srslte_refsignal_dl_sync_find_peak(srslte_refsignal_dl_sync_t* q, cf_t* buffer, uint32_t nsamples) { int ret = SRSLTE_ERROR; - float thr = 5.5f; float peak_value = 0.0f; int peak_idx = 0; float rms_avg = 0; uint32_t sf_len = q->ifft.sf_sz; // Load correlation sequence and convert to frequency domain - cf_t* ptr_filt = q->conv_fft_cc.filter_fft; - memcpy(ptr_filt, q->sequences[0], sizeof(cf_t) * sf_len); - bzero(&ptr_filt[sf_len], sizeof(cf_t) * sf_len); - srslte_dft_run_c(&q->conv_fft_cc.filter_plan, ptr_filt, ptr_filt); + refsignal_sf_prepare_correlation(q); // Limit correlate for a frame or less nsamples = SRSLTE_MIN(nsamples - sf_len, SRSLTE_NOF_SF_X_FRAME * sf_len); // Correlation for (int n = 0; n < nsamples; n += sf_len) { - // Set input data, two subframes - cf_t* ptr_in = &buffer[n]; + // Correlate, find maximum, calculate RMS and peak + uint32_t imax = 0; + float peak = 0.0f; + float rms = 0.0f; + refsignal_sf_correlate(q, &buffer[n], &peak, &imax, &rms); - // Correlate - srslte_corr_fft_cc_run_opt(&q->conv_fft_cc, ptr_in, ptr_filt, q->correlation); - - // Find maximum, calculate RMS and peak - uint32_t imax = srslte_vec_max_abs_ci(q->correlation, sf_len); - float peak = cabsf(q->correlation[imax]); - float rms = sqrtf(srslte_vec_avg_power_cf(q->correlation, sf_len)); rms_avg += rms; // Found bigger peak @@ -223,10 +331,27 @@ int srslte_refsignal_dl_sync_find_peak(srslte_refsignal_dl_sync_t* q, cf_t* buff // Condition of peak detection rms_avg /= floorf((float)nsamples / sf_len); - if (peak_value > rms_avg * thr) { + if (peak_value > rms_avg * REFSIGNAL_DL_SYNC_CORRELATION_THR) { ret = peak_idx; } + // Double check sub-frame selection failure due to high PSS + if (ret > 0) { + float sss_strength = 0.0f; + float sss_strength_false = 0.0f; + refsignal_dl_pss_sss_strength(q, &buffer[peak_idx], 0, NULL, &sss_strength, &sss_strength_false); + + float rsrp_lin = 0.0f; + float rsrp_lin_false = 0.0f; + srslte_refsignal_dl_sync_measure_sf(q, &buffer[peak_idx], 0, &rsrp_lin, NULL, NULL); + srslte_refsignal_dl_sync_measure_sf(q, &buffer[peak_idx], 5, &rsrp_lin_false, NULL, NULL); + + // Change base sub-frame + if (sss_strength_false > sss_strength && rsrp_lin_false > rsrp_lin) { + ret += (q->ifft.sf_sz * SRSLTE_NOF_SF_X_FRAME) / 2; + } + } + INFO("pci=%03d; sf_len=%d; imax=%d; peak=%.3f; rms=%.3f; peak/rms=%.3f\n", q->refsignal.cell.id, sf_len, @@ -242,11 +367,19 @@ int srslte_refsignal_dl_sync_find_peak(srslte_refsignal_dl_sync_t* q, cf_t* buff void srslte_refsignal_dl_sync_run(srslte_refsignal_dl_sync_t* q, cf_t* buffer, uint32_t nsamples) { if (q) { - uint32_t sf_len = q->ifft.sf_sz; - uint32_t sf_count = 0; - float rsrp_lin = 0.0f; - float rssi_lin = 0.0f; - float cfo_acc = 0.0f; + uint32_t sf_len = q->ifft.sf_sz; + uint32_t sf_count = 0; + float rsrp_lin = 0.0f; + float rsrp_lin_min = +INFINITY; + float rsrp_lin_max = -INFINITY; + float rssi_lin = 0.0f; + float cfo_acc = 0.0f; + float cfo_min = +INFINITY; + float cfo_max = -INFINITY; + float sss_strength_avg = 0.0f; + float sss_strength_false_avg = 0.0f; + float rsrp_false_avg = 0.0f; + bool false_alarm = false; // Stage 1: find peak int peak_idx = srslte_refsignal_dl_sync_find_peak(q, buffer, nsamples); @@ -264,9 +397,33 @@ void srslte_refsignal_dl_sync_run(srslte_refsignal_dl_sync_t* q, cf_t* buffer, u // Measure subframe rsrp, rssi and accumulate float rsrp = 0.0f, rssi = 0.0f, cfo = 0.0f; srslte_refsignal_dl_sync_measure_sf(q, buf, sf_idx, &rsrp, &rssi, &cfo); + + // Update measurements rsrp_lin += rsrp; + rsrp_lin_min = SRSLTE_MIN(rsrp_lin_min, rsrp); + rsrp_lin_max = SRSLTE_MAX(rsrp_lin_max, rsrp); + rssi_lin += rssi; + cfo_acc += cfo; + cfo_min = SRSLTE_MIN(cfo_min, cfo); + cfo_max = SRSLTE_MAX(cfo_max, cfo); + + // Compute PSS/SSS strength + if (sf_idx % (SRSLTE_NOF_SF_X_FRAME / 2) == 0) { + float sss_strength = 0.0f; + float sss_strength_false = 0.0f; + refsignal_dl_pss_sss_strength(q, buf, sf_idx, NULL, &sss_strength, &sss_strength_false); + + float rsrp_false = 0.0f; + srslte_refsignal_dl_sync_measure_sf(q, buf, sf_idx + 1, &rsrp_false, NULL, NULL); + + sss_strength_avg += sss_strength; + sss_strength_false_avg += sss_strength_false; + rsrp_false_avg += rsrp_false; + } + + // Increment counter sf_count++; } @@ -275,27 +432,82 @@ void srslte_refsignal_dl_sync_run(srslte_refsignal_dl_sync_t* q, cf_t* buffer, u rsrp_lin /= sf_count; rssi_lin /= sf_count; cfo_acc /= sf_count; + sss_strength_avg /= (2.0f * sf_count / SRSLTE_NOF_SF_X_FRAME); + sss_strength_false_avg /= (2.0f * sf_count / SRSLTE_NOF_SF_X_FRAME); + rsrp_false_avg /= (2.0f * sf_count / SRSLTE_NOF_SF_X_FRAME); } - // Calculate in dBm - q->rsrp_dBfs = srslte_convert_power_to_dBm(rsrp_lin); + // RSRP conversion to dB + float rsrp_dB_min = srslte_convert_power_to_dBm(rsrp_lin_min); + float rsrp_dB_max = srslte_convert_power_to_dBm(rsrp_lin_max); + float rsrp_dB = srslte_convert_power_to_dBm(rsrp_lin); + float rsrp_false_dB = srslte_convert_power_to_dBm(rsrp_false_avg); - // Calculate RSSI in dBm - q->rssi_dBfs = srslte_convert_power_to_dBm(rssi_lin); + // Stage 3: Final false alarm decision + uint32_t false_count = 0; + if (sss_strength_avg < sss_strength_false_avg * REFSIGNAL_DL_SSS_FALSE_RATIO_SEVERE) { + false_alarm = true; + } else if (sss_strength_avg < sss_strength_false_avg * REFSIGNAL_DL_SSS_FALSE_RATIO_MILD) { + false_count++; + } - // Calculate RSRQ - q->rsrq_dB = srslte_convert_power_to_dB(q->refsignal.cell.nof_prb) + q->rsrp_dBfs - q->rssi_dBfs; + if (cfo_max - cfo_min > REFSIGNAL_DL_CFO_MIN_MAX_SEVERE) { + false_alarm = true; + } else if (cfo_max - cfo_min > REFSIGNAL_DL_CFO_MIN_MAX_MILD) { + false_count++; + } - q->found = true; - q->cfo_Hz = cfo_acc; - q->peak_index = peak_idx; + if (rsrp_dB_max - rsrp_dB_min > REFSIGNAL_DL_RSRP_MIN_MAX_SEVERE) { + false_alarm = true; + } else if (rsrp_dB_max - rsrp_dB_min > REFSIGNAL_DL_RSRP_MIN_MAX_MILD) { + false_count++; + } + + if (rsrp_dB - rsrp_false_dB < REFSIGNAL_DL_RSRP_FALSE_RATIO_SEVERE) { + false_alarm = true; + } else if (rsrp_dB - rsrp_false_dB < REFSIGNAL_DL_RSRP_FALSE_RATIO_MILD) { + false_count++; + } + + // Allow only one check fail + if (false_count > REFSIGNAL_DL_MAX_FAULT_CHECK) { + false_alarm = true; + } + + INFO("-- pci=%03d; rsrp_dB=(%+.1f|%+.1f|%+.1f); rsrp_max-min=%.1f; rsrp_false_ratio=%.1f; " + "cfo=(%.1f|%.1f|%.1f); cfo_max-min=%.1f; sss_ratio=%f; false_count=%d;\n", + q->refsignal.cell.id, + rsrp_dB_min, + rsrp_dB, + rsrp_dB_max, + rsrp_dB_max - rsrp_dB_min, + rsrp_dB - rsrp_false_dB, + cfo_min, + cfo_acc, + cfo_max, + cfo_max - cfo_min, + sss_strength_avg / sss_strength_false_avg, + false_count); + + if (!false_alarm) { + + // Calculate in dBm + q->rsrp_dBfs = rsrp_dB; + + // Calculate RSSI in dBm + q->rssi_dBfs = srslte_convert_power_to_dBm(rssi_lin); + + // Calculate RSRQ + q->rsrq_dB = srslte_convert_power_to_dB(q->refsignal.cell.nof_prb) + q->rsrp_dBfs - q->rssi_dBfs; + + q->found = true; + q->cfo_Hz = cfo_acc; + q->peak_index = peak_idx; + } else { + refsignal_set_results_not_found(q); + } } else { - q->found = false; - q->rsrp_dBfs = NAN; - q->rssi_dBfs = NAN; - q->rsrq_dB = NAN; - q->cfo_Hz = NAN; - q->peak_index = UINT32_MAX; + refsignal_set_results_not_found(q); } } } @@ -337,7 +549,9 @@ void srslte_refsignal_dl_sync_measure_sf(srslte_refsignal_dl_sync_t* q, rsrp_lin += __real__(corr[l] * conjf(corr[l])); // Calculate RSSI - rssi_lin += srslte_vec_dot_prod_conj_ccc(&buffer[offset], &buffer[offset], symbol_sz); + if (rssi) { + rssi_lin += srslte_vec_dot_prod_conj_ccc(&buffer[offset], &buffer[offset], symbol_sz); + } } // Return measurements @@ -350,10 +564,30 @@ void srslte_refsignal_dl_sync_measure_sf(srslte_refsignal_dl_sync_t* q, } if (cfo) { + + // Distances between symbols + float distance_1 = (cp_len1 + symbol_sz) * 4.0f; // Number of samples between first and second symbol + float distance_2 = + (cp_len1 + symbol_sz) * 3.0f + (cp_len0 - cp_len1); // Number of samples between second and third symbol + + // Averaging weights, all of them must be 1.0f + float low_w = REFSIGNAL_DL_CFO_LOW_WEIGHT / 2.0f; // Two of them + float medium_w = REFSIGNAL_DL_CFO_MEDIUM_WEIGHT / 2.0f; // Two of them + float high_w = REFSIGNAL_DL_CFO_HIGH_WEIGHT; // One of them + + // Initialise average *cfo = 0; - *cfo += cargf(corr[2] * conjf(corr[0])) / (2.0f * M_PI * 7.5f) * 15000.0f; - *cfo += cargf(corr[3] * conjf(corr[1])) / (2.0f * M_PI * 7.5f) * 15000.0f; - *cfo /= 2; + + // Low doppler (2 kHz) + *cfo += cargf(corr[2] * conjf(corr[0])) / (2.0f * M_PI * 7.5f) * 15000.0f * low_w; + *cfo += cargf(corr[3] * conjf(corr[1])) / (2.0f * M_PI * 7.5f) * 15000.0f * low_w; + + // Medium Doppler (3.5 kHz) + *cfo += cargf(corr[1] * conjf(corr[0])) / (2.0f * M_PI * distance_1) * (15000.0f * symbol_sz) * medium_w; + *cfo += cargf(corr[3] * conjf(corr[2])) / (2.0f * M_PI * distance_1) * (15000.0f * symbol_sz) * medium_w; + + // High doppler (4.66 kHz) + *cfo += cargf(corr[2] * conjf(corr[1])) / (2.0f * M_PI * distance_2) * (15000.0f * symbol_sz) * high_w; } } } diff --git a/srsue/test/phy/scell_search_test.cc b/srsue/test/phy/scell_search_test.cc index caa4135f6..65e5c418b 100644 --- a/srsue/test/phy/scell_search_test.cc +++ b/srsue/test/phy/scell_search_test.cc @@ -31,17 +31,19 @@ #include // Common execution parameters -static uint32_t duration_execution_s; -static srslte_cell_t cell_base = {.nof_prb = 6, +static uint32_t duration_execution_s; +static srslte_cell_t cell_base = {.nof_prb = 6, .nof_ports = 1, .id = 0, .cp = SRSLTE_CP_NORM, .phich_length = SRSLTE_PHICH_NORM, .phich_resources = SRSLTE_PHICH_R_1_6, .frame_type = SRSLTE_FDD}; -static std::string intra_meas_log_level; -static std::string cell_list; -static int phy_lib_log_level; +static std::string intra_meas_log_level; +static std::string active_cell_list; +static std::string simulation_cell_list; +static int phy_lib_log_level; +static srsue::phy_args_t phy_args; // On the Fly parameters static int earfcn_dl; @@ -51,9 +53,6 @@ static std::string radio_log_level; static float rx_gain; // Simulation parameters -static uint32_t nof_enb; -static uint16_t cell_id_start; -static uint16_t cell_id_step; static float channel_period_s; static uint32_t cfi; static float ncell_attenuation_dB; @@ -67,6 +66,10 @@ static uint16_t serving_cell_pdsch_rnti; static srslte_tm_t serving_cell_pdsch_tm; static uint16_t serving_cell_pdsch_mcs; +// Parsed PCI lists +static std::set pcis_to_meas = {}; +static std::set pcis_to_simulate = {}; + // PRB allocation helpers static uint32_t prbset_num = 1, last_prbset_num = 1; static uint32_t prbset_orig = 0; @@ -197,7 +200,7 @@ public: float scale = sqrtf(cell_base.nof_prb) / 0.05f / enb_dl.ifft->symbol_sz; // Apply Neighbour cell attenuation - if (enb_dl.cell.id != cell_id_start) { + if (enb_dl.cell.id != *pcis_to_simulate.begin()) { scale *= srslte_convert_dB_to_amplitude(-ncell_attenuation_dB); } @@ -265,16 +268,22 @@ public: } } - void print_stats() + bool print_stats() { printf("\n-- Statistics:\n"); - for (auto& e : cells) { - bool false_alarm = true; + uint32_t true_counts = 0; + uint32_t false_counts = 0; + uint32_t tti_count = (1000 * duration_execution_s) / phy_args.intra_freq_meas_period_ms; + uint32_t ideal_true_counts = (pcis_to_simulate.size() - 1) * tti_count; + uint32_t ideal_false_counts = tti_count * cells.size() - ideal_true_counts; - for (uint32_t i = 0; false_alarm && (i < nof_enb); i++) { - if (e.first == cell_id_start + cell_id_step * i) { - false_alarm = false; - } + for (auto& e : cells) { + bool false_alarm = pcis_to_simulate.find(e.first) == pcis_to_simulate.end(); + + if (false_alarm) { + false_counts += e.second.count; + } else { + true_counts += e.second.count; } printf(" pci=%03d; count=%3d; false=%s; rsrp=%+.1f|%+.1f|%+.1fdBfs; rsrq=%+.1f|%+.1f|%+.1fdB;\n", @@ -288,13 +297,21 @@ public: e.second.rsrq_avg, e.second.rsrq_max); } + + float prob_detection = (ideal_true_counts) ? (float)true_counts / (float)ideal_true_counts : 0.0f; + float prob_false_alarm = (ideal_false_counts) ? (float)false_counts / (float)ideal_false_counts : 0.0f; + printf("\n"); + printf(" Probability of detection: %.6f\n", prob_detection); + printf(" Probability of false alarm: %.6f\n", prob_false_alarm); + + return (prob_detection >= 0.9f && prob_false_alarm <= 0.1f); } }; // shorten boost program options namespace namespace bpo = boost::program_options; -int parse_args(int argc, char** argv, srsue::phy_args_t* phy_args) +int parse_args(int argc, char** argv) { int ret = SRSLTE_SUCCESS; @@ -308,10 +325,10 @@ int parse_args(int argc, char** argv, srsue::phy_args_t* phy_args) ("duration", bpo::value(&duration_execution_s)->default_value(60), "Duration of the execution in seconds") ("cell.nof_prb", bpo::value(&cell_base.nof_prb)->default_value(100), "Cell Number of PRB") ("intra_meas_log_level", bpo::value(&intra_meas_log_level)->default_value("none"), "Intra measurement log level (none, warning, info, debug)") - ("intra_freq_meas_len_ms", bpo::value(&phy_args->intra_freq_meas_len_ms)->default_value(20), "Intra measurement measurement length") - ("intra_freq_meas_period_ms", bpo::value(&phy_args->intra_freq_meas_period_ms)->default_value(200), "Intra measurement measurement period") + ("intra_freq_meas_len_ms", bpo::value(&phy_args.intra_freq_meas_len_ms)->default_value(20), "Intra measurement measurement length") + ("intra_freq_meas_period_ms", bpo::value(&phy_args.intra_freq_meas_period_ms)->default_value(200), "Intra measurement measurement period") ("phy_lib_log_level", bpo::value(&phy_lib_log_level)->default_value(SRSLTE_VERBOSE_NONE), "Phy lib log level (0: none, 1: info, 2: debug)") - ("cell_list", bpo::value(&cell_list)->default_value("10,17,24,31,38,45,52"), "Comma separated neighbour PCI cell list") + ("active_cell_list", bpo::value(&active_cell_list)->default_value("10,17,24,31,38,45,52"), "Comma separated neighbour PCI cell list") ; over_the_air.add_options() @@ -324,9 +341,7 @@ int parse_args(int argc, char** argv, srsue::phy_args_t* phy_args) ; simulation.add_options() - ("nof_enb", bpo::value(&nof_enb)->default_value(4), "Number of eNb") - ("cell_id_start", bpo::value(&cell_id_start)->default_value(10), "Cell id start") - ("cell_id_step", bpo::value(&cell_id_step)->default_value(7), "Cell id step") + ("simulation_cell_list", bpo::value(&simulation_cell_list)->default_value("10,17,24,31,38,45,52"), "Comma separated neighbour PCI cell list") ("cell_cfi", bpo::value(&cfi)->default_value(1), "Cell CFI") ("channel_period_s", bpo::value(&channel_period_s)->default_value(16.8), "Channel period for HST and delay") ("ncell_attenuation", bpo::value(&ncell_attenuation_dB)->default_value(3.0f), "Neighbour cell attenuation relative to serving cell in dB") @@ -363,17 +378,42 @@ int parse_args(int argc, char** argv, srsue::phy_args_t* phy_args) return ret; } +static void pci_list_parse_helper(std::string& list_str, std::set& list) +{ + if (list_str == "all") { + // Add all possible cells + for (int i = 0; i < 504; i++) { + list.insert(i); + } + } else if (list_str == "none") { + // Do nothing + } else if (!list_str.empty()) { + // Remove spaces from neightbour cell list + std::size_t p1 = list_str.find(' '); + while (p1 != std::string::npos) { + list_str.erase(p1); + p1 = list_str.find(' '); + } + + // Add cell to known cells + std::stringstream ss(list_str); + while (ss.good()) { + std::string substr; + getline(ss, substr, ','); + list.insert((uint32_t)strtoul(substr.c_str(), nullptr, 10)); + } + } +} + int main(int argc, char** argv) { - int ret = SRSLTE_SUCCESS; - srsue::phy_args_t phy_args = {}; + int ret; // Parse args - if (parse_args(argc, argv, &phy_args)) { + if (parse_args(argc, argv)) { return SRSLTE_ERROR; } - // Common for simulation and over-the-air auto baseband_buffer = (cf_t*)srslte_vec_malloc(sizeof(cf_t) * SRSLTE_SF_LEN_MAX); srslte_timestamp_t ts = {}; @@ -422,18 +462,15 @@ int main(int argc, char** argv) softbuffer_tx[i] = (srslte_softbuffer_tx_t*)calloc(sizeof(srslte_softbuffer_tx_t), 1); if (!softbuffer_tx[i]) { ERROR("Error allocating softbuffer_tx\n"); - ret = SRSLTE_ERROR; } if (srslte_softbuffer_tx_init(softbuffer_tx[i], cell_base.nof_prb)) { ERROR("Error initiating softbuffer_tx\n"); - ret = SRSLTE_ERROR; } data_tx[i] = (uint8_t*)srslte_vec_malloc(sizeof(uint8_t) * nof_bytes); if (!data_tx[i]) { ERROR("Error allocating data tx\n"); - ret = SRSLTE_ERROR; } else { for (uint32_t j = 0; j < nof_bytes; j++) { data_tx[i][j] = (uint8_t)srslte_random_uniform_int_dist(random_gen, 0, 255); @@ -443,8 +480,11 @@ int main(int argc, char** argv) srslte_random_free(random_gen); } + pci_list_parse_helper(active_cell_list, pcis_to_meas); + pci_list_parse_helper(simulation_cell_list, pcis_to_simulate); + // Set cell_base id with the serving cell - uint32_t serving_cell_id = (nof_enb == 1) ? (cell_id_start + cell_id_step) : cell_id_start; + uint32_t serving_cell_id = *pcis_to_simulate.begin(); cell_base.id = serving_cell_id; logger.set_level(intra_meas_log_level); @@ -452,8 +492,6 @@ int main(int argc, char** argv) intra_measure.init(&common, &rrc, &logger); intra_measure.set_primary_cell(serving_cell_id, cell_base); - std::set pcis_to_meas = {}; - if (earfcn_dl >= 0) { // Create radio log radio_log = std::unique_ptr(new srslte::log_filter("Radio")); @@ -473,58 +511,40 @@ int main(int argc, char** argv) } else { // Create test eNb's if radio is not available - for (uint32_t enb_idx = 0; enb_idx < nof_enb; enb_idx++) { + float channel_init_time_s = 0; + float channel_delay_us = 0; + for (auto& pci : pcis_to_simulate) { // Initialise cell srslte_cell_t cell = cell_base; - cell.id = (cell_id_start + enb_idx * cell_id_step) % 504; + cell.id = pci; // Initialise channel and push back srslte::channel::args_t channel_args; - channel_args.enable = true; + channel_args.enable = (channel_period_s != 0); channel_args.hst_enable = (channel_hst_fd_hz != 0.0f); - channel_args.hst_init_time_s = (float)(enb_idx * channel_period_s) / (float)nof_enb; + channel_args.hst_init_time_s = channel_init_time_s; channel_args.hst_period_s = (float)channel_period_s; channel_args.hst_fd_hz = channel_hst_fd_hz; channel_args.delay_enable = (channel_delay_max_us != 0.0f); - channel_args.delay_min_us = 0; - channel_args.delay_max_us = channel_delay_max_us; + channel_args.delay_min_us = channel_delay_us; + channel_args.delay_max_us = channel_delay_us; channel_args.delay_period_s = (uint32)channel_period_s; - channel_args.delay_init_time_s = (enb_idx * channel_period_s) / nof_enb; + channel_args.delay_init_time_s = channel_init_time_s; test_enb_v.push_back(std::unique_ptr(new test_enb(cell, channel_args))); // Add cell to known cells - if (cell_list.empty()) { + if (active_cell_list.empty()) { pcis_to_meas.insert(cell.id); } - } - } - // Parse cell list - if (cell_list == "all") { - // Add all possible cells - for (int i = 0; i < 504; i++) { - pcis_to_meas.insert(i); - } - } else if (cell_list == "none") { - // Do nothing - } else if (!cell_list.empty()) { - // Remove spaces from neightbour cell list - std::size_t p1 = cell_list.find(' '); - while (p1 != std::string::npos) { - cell_list.erase(p1); - p1 = cell_list.find(' '); - } - - // Add cell to known cells - std::stringstream ss(cell_list); - while (ss.good()) { - std::string substr; - getline(ss, substr, ','); - pcis_to_meas.insert((uint32_t)strtoul(substr.c_str(), nullptr, 10)); + // Increase init time + channel_init_time_s += channel_period_s / (float)pcis_to_simulate.size(); + channel_delay_us += channel_delay_max_us / (float)pcis_to_simulate.size(); } } // pass cells to measure to intra_measure object + intra_measure.set_cells_to_meas(pcis_to_meas); // Run loop @@ -620,6 +640,12 @@ int main(int argc, char** argv) srslte_timestamp_add(&ts, 0, 0.001f); + if (sf_idx > phy_args.intra_freq_meas_period_ms) { + if (sf_idx % phy_args.intra_freq_meas_period_ms == 0) { + intra_measure.wait_meas(); + } + } + intra_measure.write(sf_idx, baseband_buffer, SRSLTE_SF_LEN_PRB(cell_base.nof_prb)); if (sf_idx % 1000 == 0) { printf("Done %.1f%%\n", (double)sf_idx * 100.0 / ((double)duration_execution_s * 1000.0)); @@ -629,7 +655,8 @@ int main(int argc, char** argv) // Stop intra_measure.stop(); - rrc.print_stats(); + ret = rrc.print_stats() ? SRSLTE_SUCCESS : SRSLTE_ERROR; + if (baseband_buffer) { free(baseband_buffer); }