diff --git a/lib/examples/synch_file.c b/lib/examples/synch_file.c index eac52d2e9..0990ebae2 100644 --- a/lib/examples/synch_file.c +++ b/lib/examples/synch_file.c @@ -220,10 +220,15 @@ int main(int argc, char **argv) { cfo[frame_cnt] = srslte_pss_cfo_compute(&pss[N_id_2], &input[peak_pos[N_id_2]-128]); printf("\t%d\t%d\t%d\t%d\t%.3f\t\t%3d\t%d\t%d\t%.3f\n", - frame_cnt,N_id_2, srslte_sss_N_id_1(&sss[N_id_2], m0, m1), - srslte_sss_subframe(m0, m1), peak_value[N_id_2], - peak_pos[N_id_2], m0, m1, - cfo[frame_cnt]); + frame_cnt, + N_id_2, + srslte_sss_N_id_1(&sss[N_id_2], m0, m1, m1_value + m0_value), + srslte_sss_subframe(m0, m1), + peak_value[N_id_2], + peak_pos[N_id_2], + m0, + m1, + cfo[frame_cnt]); } } gettimeofday(&tdata[2], NULL); diff --git a/lib/include/srslte/phy/sync/sss.h b/lib/include/srslte/phy/sync/sss.h index 40249f687..2ed8066ee 100644 --- a/lib/include/srslte/phy/sync/sss.h +++ b/lib/include/srslte/phy/sync/sss.h @@ -131,9 +131,7 @@ SRSLTE_API int srslte_sss_m0m1_diff(srslte_sss_t *q, SRSLTE_API uint32_t srslte_sss_subframe(uint32_t m0, uint32_t m1); -SRSLTE_API int srslte_sss_N_id_1(srslte_sss_t *q, - uint32_t m0, - uint32_t m1); +SRSLTE_API int srslte_sss_N_id_1(srslte_sss_t* q, uint32_t m0, uint32_t m1, float corr); SRSLTE_API int srslte_sss_frame(srslte_sss_t *q, cf_t *input, diff --git a/lib/include/srslte/phy/sync/sync.h b/lib/include/srslte/phy/sync/sync.h index ba1b1539e..27bcf3fbf 100644 --- a/lib/include/srslte/phy/sync/sync.h +++ b/lib/include/srslte/phy/sync/sync.h @@ -118,6 +118,7 @@ typedef struct SRSLTE_API { bool sss_generated; bool sss_detected; bool sss_available; + float sss_corr; srslte_dft_plan_t idftp_sss; cf_t sss_recv[SRSLTE_SYMBOL_SZ_MAX]; cf_t sss_signal[2][SRSLTE_SYMBOL_SZ_MAX]; @@ -242,6 +243,8 @@ SRSLTE_API srslte_pss_t* srslte_sync_get_cur_pss_obj(srslte_sync_t *q); SRSLTE_API bool srslte_sync_sss_detected(srslte_sync_t *q); +SRSLTE_API float srslte_sync_sss_correlation_peak(srslte_sync_t* q); + SRSLTE_API bool srslte_sync_sss_available(srslte_sync_t* q); /* Enables/Disables CP detection */ diff --git a/lib/src/phy/sync/find_sss.c b/lib/src/phy/sync/find_sss.c index 7eec97b24..c7468b269 100644 --- a/lib/src/phy/sync/find_sss.c +++ b/lib/src/phy/sync/find_sss.c @@ -65,18 +65,30 @@ static void corr_all_sz_partial(cf_t z[SRSLTE_SSS_N], float s[SRSLTE_SSS_N][SRSL static void extract_pair_sss(srslte_sss_t *q, const cf_t *input, cf_t *ce, cf_t y[2][SRSLTE_SSS_N]) { cf_t input_fft[SRSLTE_SYMBOL_SZ_MAX]; + // Run FFT srslte_dft_run_c(&q->dftp_input, input, input_fft); - + + // Equalize if available channel estimate if (ce) { srslte_vec_div_ccc(&input_fft[q->fft_size/2-SRSLTE_SSS_N], ce, &input_fft[q->fft_size/2-SRSLTE_SSS_N], 2*SRSLTE_SSS_N); } - + + // Extract FFT Data for (int i = 0; i < SRSLTE_SSS_N; i++) { y[0][i] = input_fft[q->fft_size/2-SRSLTE_SSS_N + 2 * i]; y[1][i] = input_fft[q->fft_size/2-SRSLTE_SSS_N + 2 * i + 1]; } + // Normalize + for (int i = 0; i < 2; i++) { + float avg_pow = srslte_vec_avg_power_cf(y[i], SRSLTE_SSS_N); + float rms = (avg_pow != 0.0f) ? sqrtf(avg_pow) : 1.0f; + + srslte_vec_sc_prod_cfc(y[i], 1.0 / rms, y[i], SRSLTE_SSS_N); + } + + // Unmask signal with sequence generated from NID2 srslte_vec_prod_cfc(y[0], q->fc_tables[q->N_id_2].c[0], y[0], SRSLTE_SSS_N); srslte_vec_prod_cfc(y[1], q->fc_tables[q->N_id_2].c[1], y[1], SRSLTE_SSS_N); diff --git a/lib/src/phy/sync/sss.c b/lib/src/phy/sync/sss.c index 188cb04d3..209bbb84f 100644 --- a/lib/src/phy/sync/sss.c +++ b/lib/src/phy/sync/sss.c @@ -133,16 +133,22 @@ uint32_t srslte_sss_subframe(uint32_t m0, uint32_t m1) { } /** Returns the N_id_1 value based on the m0 and m1 values */ -int srslte_sss_N_id_1(srslte_sss_t *q, uint32_t m0, uint32_t m1) { - int N_id_1 = -1; - if (m1 > m0) { - if (m0 < 30 && m1 - 1 < 30) { - N_id_1 = q->N_id_1_table[m0][m1 - 1]; - } - } else { - if (m1 < 30 && m0 - 1 < 30) { - N_id_1 = q->N_id_1_table[m1][m0 - 1]; +int srslte_sss_N_id_1(srslte_sss_t* q, uint32_t m0, uint32_t m1, float corr) +{ + int N_id_1 = SRSLTE_ERROR; + + // Check threshold, consider not found (error) if the correlation is not above the threshold + if (corr > q->corr_peak_threshold) { + if (m1 > m0) { + if (m0 < 30 && m1 - 1 < 30) { + N_id_1 = q->N_id_1_table[m0][m1 - 1]; + } + } else { + if (m1 < 30 && m0 - 1 < 30) { + N_id_1 = q->N_id_1_table[m1][m0 - 1]; + } } - } + } + return N_id_1; } diff --git a/lib/src/phy/sync/sync.c b/lib/src/phy/sync/sync.c index 8bf29c586..42fd2296c 100644 --- a/lib/src/phy/sync/sync.c +++ b/lib/src/phy/sync/sync.c @@ -271,6 +271,11 @@ bool srslte_sync_sss_detected(srslte_sync_t* q) return q->sss_detected; } +float srslte_sync_sss_correlation_peak(srslte_sync_t* q) +{ + return q->sss_corr; +} + bool srslte_sync_sss_available(srslte_sync_t* q) { return q->sss_available; @@ -544,11 +549,12 @@ static bool sync_sss_symbol(srslte_sync_t* q, const cf_t* input, uint32_t* sf_id *corr = q->m0_value + q->m1_value; *sf_idx = srslte_sss_subframe(q->m0, q->m1); - ret = srslte_sss_N_id_1(&q->sss, q->m0, q->m1); + ret = srslte_sss_N_id_1(&q->sss, q->m0, q->m1, *corr); if (ret >= 0) { *N_id_1 = (uint32_t)ret; - INFO("SSS detected N_id_1=%d, sf_idx=%d, %s CP\n", + INFO("SSS detected N_id_1=%d (corr=%7.1f), sf_idx=%d, %s CP\n", *N_id_1, + *corr, *sf_idx, SRSLTE_CP_ISNORM(q->cp) ? "Normal" : "Extended"); return true; @@ -741,6 +747,7 @@ srslte_sync_find_ret_t srslte_sync_find(srslte_sync_t *q, const cf_t *input, uin } q->sss_available = true; + q->sss_detected = false; for (uint32_t f = 0; f < nof_frame_type_trials; f++) { if (frame_type_trials[f] == SRSLTE_FDD) { sss_idx = (int)find_offset + peak_pos - 2 * SRSLTE_SYMBOL_SZ(q->fft_size, q->cp) + @@ -766,7 +773,9 @@ srslte_sync_find_ret_t srslte_sync_find(srslte_sync_t *q, const cf_t *input, uin } sss_ptr = q->sss_filt; } - q->sss_detected = sync_sss_symbol(q, sss_ptr, &sf_idx[f], &N_id_1[f], &sss_corr[f]); + + // Consider SSS detected if at least one trial found the SSS + q->sss_detected |= sync_sss_symbol(q, sss_ptr, &sf_idx[f], &N_id_1[f], &sss_corr[f]); } else { q->sss_available = false; } @@ -777,10 +786,12 @@ srslte_sync_find_ret_t srslte_sync_find(srslte_sync_t *q, const cf_t *input, uin q->frame_type = SRSLTE_FDD; q->sf_idx = sf_idx[0]; q->N_id_1 = N_id_1[0]; + q->sss_corr = sss_corr[0]; } else { q->frame_type = SRSLTE_TDD; q->sf_idx = sf_idx[1] + 1; q->N_id_1 = N_id_1[1]; + q->sss_corr = sss_corr[1]; } DEBUG("SYNC: Detected SSS %s, corr=%.2f/%.2f\n", q->frame_type == SRSLTE_FDD ? "FDD" : "TDD", @@ -793,6 +804,7 @@ srslte_sync_find_ret_t srslte_sync_find(srslte_sync_t *q, const cf_t *input, uin q->sf_idx = sf_idx[0] + 1; } q->N_id_1 = N_id_1[0]; + q->sss_corr = sss_corr[0]; } } diff --git a/lib/src/phy/sync/test/pss_file.c b/lib/src/phy/sync/test/pss_file.c index a596d88d8..3b89e32c0 100644 --- a/lib/src/phy/sync/test/pss_file.c +++ b/lib/src/phy/sync/test/pss_file.c @@ -233,21 +233,21 @@ int main(int argc, char **argv) { int sss_idx = peak_idx-2*fft_size-(SRSLTE_CP_ISNORM(cp)?SRSLTE_CP_LEN(fft_size, SRSLTE_CP_NORM_LEN):SRSLTE_CP_LEN(fft_size, SRSLTE_CP_EXT_LEN)); if (sss_idx >= 0 && sss_idx < flen-fft_size) { srslte_sss_m0m1_partial(&sss, &buffer[sss_idx], 3, NULL, &m0, &m0_value, &m1, &m1_value); - if (srslte_sss_N_id_1(&sss, m0, m1) != N_id_1) { + if (srslte_sss_N_id_1(&sss, m0, m1, m1_value + m0_value) != N_id_1) { sss_error2++; } INFO("sf_idx = %d\n", srslte_sss_subframe(m0, m1)); - INFO("Partial N_id_1: %d\n", srslte_sss_N_id_1(&sss, m0, m1)); + INFO("Partial N_id_1: %d\n", srslte_sss_N_id_1(&sss, m0, m1, m1_value + m0_value)); srslte_sss_m0m1_diff(&sss, &buffer[sss_idx], &m0, &m0_value, &m1, &m1_value); - if (srslte_sss_N_id_1(&sss, m0, m1) != N_id_1) { + if (srslte_sss_N_id_1(&sss, m0, m1, m1_value + m0_value) != N_id_1) { sss_error3++; } - INFO("Diff N_id_1: %d\n", srslte_sss_N_id_1(&sss, m0, m1)); + INFO("Diff N_id_1: %d\n", srslte_sss_N_id_1(&sss, m0, m1, m1_value + m0_value)); srslte_sss_m0m1_partial(&sss, &buffer[sss_idx], 1, NULL, &m0, &m0_value, &m1, &m1_value); - if (srslte_sss_N_id_1(&sss, m0, m1) != N_id_1) { + if (srslte_sss_N_id_1(&sss, m0, m1, m1_value + m0_value) != N_id_1) { sss_error1++; } - INFO("Full N_id_1: %d\n", srslte_sss_N_id_1(&sss, m0, m1)); + INFO("Full N_id_1: %d\n", srslte_sss_N_id_1(&sss, m0, m1, m1_value + m0_value)); } // Estimate CP diff --git a/lib/src/phy/sync/test/pss_usrp.c b/lib/src/phy/sync/test/pss_usrp.c index e869bbd94..dd7168ff4 100644 --- a/lib/src/phy/sync/test/pss_usrp.c +++ b/lib/src/phy/sync/test/pss_usrp.c @@ -265,20 +265,20 @@ int main(int argc, char **argv) { // Filter SSS srslte_pss_filter(&pss, &buffer[sss_idx], &buffer[sss_idx]); - INFO("Full N_id_1: %d\n", srslte_sss_N_id_1(&sss, m0, m1)); + INFO("Full N_id_1: %d\n", srslte_sss_N_id_1(&sss, m0, m1, m1_value + m0_value)); srslte_sss_m0m1_partial(&sss, &buffer[sss_idx], 1, ce, &m0, &m0_value, &m1, &m1_value); - if (srslte_sss_N_id_1(&sss, m0, m1) != N_id_1) { + if (srslte_sss_N_id_1(&sss, m0, m1, m1_value + m0_value) != N_id_1) { sss_error2++; } - INFO("Partial N_id_1: %d\n", srslte_sss_N_id_1(&sss, m0, m1)); + INFO("Partial N_id_1: %d\n", srslte_sss_N_id_1(&sss, m0, m1, m1_value + m0_value)); srslte_sss_m0m1_diff_coh(&sss, &buffer[sss_idx], ce, &m0, &m0_value, &m1, &m1_value); - if (srslte_sss_N_id_1(&sss, m0, m1) != N_id_1) { + if (srslte_sss_N_id_1(&sss, m0, m1, m1_value + m0_value) != N_id_1) { sss_error3++; } - INFO("Diff N_id_1: %d\n", srslte_sss_N_id_1(&sss, m0, m1)); + INFO("Diff N_id_1: %d\n", srslte_sss_N_id_1(&sss, m0, m1, m1_value + m0_value)); } srslte_sss_m0m1_partial(&sss, &buffer[sss_idx], 1, NULL, &m0, &m0_value, &m1, &m1_value); - if (srslte_sss_N_id_1(&sss, m0, m1) != N_id_1) { + if (srslte_sss_N_id_1(&sss, m0, m1, m1_value + m0_value) != N_id_1) { sss_error1++; } diff --git a/srsue/hdr/phy/scell/measure.h b/srsue/hdr/phy/scell/measure.h index 310b21dcd..287306d08 100644 --- a/srsue/hdr/phy/scell/measure.h +++ b/srsue/hdr/phy/scell/measure.h @@ -51,6 +51,7 @@ public: float rsrp(); float rsrq(); float snr(); + float cfo(); uint32_t frame_st_idx(); void set_rx_gain_offset(float rx_gain_offset); @@ -63,7 +64,7 @@ private: uint32_t nof_subframes; uint32_t current_prb; float rx_gain_offset; - float mean_rsrp, mean_rsrq, mean_snr, mean_rssi; + float mean_rsrp, mean_rsrq, mean_snr, mean_rssi, mean_cfo; uint32_t final_offset; const static int RSRP_MEASURE_NOF_FRAMES = 5; }; diff --git a/srsue/src/phy/scell/measure.cc b/srsue/src/phy/scell/measure.cc index f5c8f4e17..f1dd0a149 100644 --- a/srsue/src/phy/scell/measure.cc +++ b/srsue/src/phy/scell/measure.cc @@ -70,6 +70,7 @@ void measure::reset() mean_rsrq = 0; mean_snr = 0; mean_rssi = 0; + mean_cfo = 0; } void measure::set_cell(srslte_cell_t cell) @@ -101,6 +102,11 @@ float measure::snr() return mean_snr; } +float measure::cfo() +{ + return mean_cfo; +} + uint32_t measure::frame_st_idx() { return final_offset; @@ -196,6 +202,7 @@ measure::ret_code measure::run_subframe(uint32_t sf_idx) float rsrp = ue_dl.chest_res.rsrp_neigh; float rsrq = ue_dl.chest_res.rsrq; float snr = ue_dl.chest_res.snr_db; + float cfo = ue_dl.chest_res.cfo; float rssi = srslte_vec_avg_power_cf(buffer[0], (uint32_t)SRSLTE_SF_LEN_PRB(current_prb)); if (cnt == 0) { @@ -203,11 +210,13 @@ measure::ret_code measure::run_subframe(uint32_t sf_idx) mean_rsrq = rsrq; mean_snr = snr; mean_rssi = rssi; + mean_cfo = cfo; } else { mean_rsrp = SRSLTE_VEC_CMA(rsrp, mean_rsrp, cnt); mean_rsrq = SRSLTE_VEC_CMA(rsrq, mean_rsrq, cnt); mean_snr = SRSLTE_VEC_CMA(snr, mean_snr, cnt); mean_rssi = SRSLTE_VEC_CMA(rssi, mean_rssi, cnt); + mean_cfo = SRSLTE_VEC_CMA(cfo, mean_cfo, cnt); } cnt++; diff --git a/srsue/src/phy/scell/scell_recv.cc b/srsue/src/phy/scell/scell_recv.cc index ac2c00da2..c5c8a5169 100644 --- a/srsue/src/phy/scell/scell_recv.cc +++ b/srsue/src/phy/scell/scell_recv.cc @@ -64,15 +64,16 @@ void scell_recv::init(srslte::log* _log_h, bool _sic_pss_enabled, uint32_t max_s return; } srslte_sync_set_sss_algorithm(&sync_find, SSS_FULL); + srslte_sync_set_cfo_cp_enable(&sync_find, false, 0); srslte_sync_cp_en(&sync_find, false); - srslte_sync_set_cfo_pss_enable(&sync_find, true); - srslte_sync_set_threshold(&sync_find, 1.7); - srslte_sync_set_em_alpha(&sync_find, 0.3); + srslte_sync_set_cfo_pss_enable(&sync_find, false); + srslte_sync_set_threshold(&sync_find, 2.0f); // The highest the best for avoiding ghost cells + srslte_sync_set_em_alpha(&sync_find, 0.3f); + srslte_sss_set_threshold(&sync_find.sss, 300.0); // A higher value will avoid false alarms but reduce detection // Configure FIND object behaviour (this configuration is always the same) srslte_sync_set_cfo_ema_alpha(&sync_find, 1.0); srslte_sync_set_cfo_i_enable(&sync_find, false); - srslte_sync_set_cfo_pss_enable(&sync_find, true); srslte_sync_set_pss_filt_enable(&sync_find, true); srslte_sync_set_sss_eq_enable(&sync_find, true); @@ -132,29 +133,48 @@ int scell_recv::find_cells( srslte_sync_cfo_reset(&sync_find); sync_res = SRSLTE_SYNC_NOFOUND; + bool sss_detected = false; cell_id = 0; float max_peak = -1; uint32_t max_sf5 = 0; uint32_t max_sf_idx = 0; + float sss_correlation_peak_max = 0.0f; + for (uint32_t sf5_cnt = 0; sf5_cnt < nof_sf / 5; sf5_cnt++) { sync_res = srslte_sync_find(&sync_find, input_buffer, sf5_cnt * 5 * sf_len, &peak_idx); - Debug("INTRA: n_id_2=%d, cnt=%d/%d, sync_res=%d, sf_idx=%d, peak_idx=%d, peak_value=%f\n", + Debug("INTRA: n_id_2=%d, cnt=%d/%d, sync_res=%d, sf_idx=%d, peak_idx=%d, peak_value=%f, sss_detected=%d\n", n_id_2, sf5_cnt, nof_sf / 5, sync_res, srslte_sync_get_sf_idx(&sync_find), peak_idx, - sync_find.peak_value); + sync_find.peak_value, + srslte_sync_sss_detected(&sync_find)); - if (sync_find.peak_value > max_peak && sync_res == SRSLTE_SYNC_FOUND) { + if (sync_find.peak_value > max_peak && sync_res == SRSLTE_SYNC_FOUND && + srslte_sync_sss_detected(&sync_find)) { max_sf5 = sf5_cnt; max_sf_idx = srslte_sync_get_sf_idx(&sync_find); - cell_id = srslte_sync_get_cell_id(&sync_find); + + // Uses the cell ID from the highest SSS correlation peak + if (sss_correlation_peak_max < srslte_sync_sss_correlation_peak(&sync_find)) { + // Set the cell ID + cell_id = srslte_sync_get_cell_id(&sync_find); + + // Update the maximum value + sss_correlation_peak_max = srslte_sync_sss_correlation_peak(&sync_find); + } + sss_detected = true; } } + // If the SSS was not detected, the cell id is not reliable. So, consider no sync found + if (!sss_detected) { + sync_res = SRSLTE_SYNC_NOFOUND; + } + switch (sync_res) { case SRSLTE_SYNC_ERROR: return SRSLTE_ERROR; @@ -196,17 +216,20 @@ int scell_recv::find_cells( cells[nof_cells].rsrq = measure_p.rsrq(); cells[nof_cells].offset = measure_p.frame_st_idx(); - Info("INTRA: Found neighbour cell %d: PCI=%03d, RSRP=%5.1f dBm, peak_idx=%5d, peak_value=%3.2f, " - "sf=%d, max_sf=%d, n_id_2=%d, CFO=%6.1f Hz\n", + Info("INTRA: Found neighbour cell %d: PCI=%03d, RSRP=%5.1f dBm, SNR=%5.1f dB, peak_idx=%5d, " + "peak_value=%3.2f, " + "sf=%d, max_sf=%d, n_id_2=%d, CFO=%6.1f/%6.1fHz\n", nof_cells, cell_id, measure_p.rsrp(), + measure_p.snr(), measure_p.frame_st_idx(), sync_find.peak_value, sf_idx, max_sf5, n_id_2, - 15000 * srslte_sync_get_cfo(&sync_find)); + 15000 * srslte_sync_get_cfo(&sync_find), + 15000 * measure_p.cfo()); nof_cells++; diff --git a/srsue/test/phy/CMakeLists.txt b/srsue/test/phy/CMakeLists.txt index 1ea5cbb21..293e7887a 100644 --- a/srsue/test/phy/CMakeLists.txt +++ b/srsue/test/phy/CMakeLists.txt @@ -18,17 +18,22 @@ # and at http://www.gnu.org/licenses/. # +include_directories( + ${Boost_INCLUDE_DIRS} + ${SEC_INCLUDE_DIRS} + ${PROJECT_SOURCE_DIR} +) + +link_directories( + ${Boost_LIBRARY_DIRS} + ${SEC_LIBRARY_DIRS} +) + add_executable(scell_search_test scell_search_test.cc) -target_link_libraries(scell_search_test srsue_phy - srsue_stack - srsue_upper - srsue_mac - srsue_rrc +target_link_libraries(scell_search_test + srsue_phy srslte_common - srslte_phy srslte_radio - srslte_upper rrc_asn1 - ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) add_test(scell_search_test scell_search_test) diff --git a/srsue/test/phy/scell_search_test.cc b/srsue/test/phy/scell_search_test.cc index c1fdc72a4..e7e1a89f1 100644 --- a/srsue/test/phy/scell_search_test.cc +++ b/srsue/test/phy/scell_search_test.cc @@ -19,100 +19,97 @@ * */ +#include +#include +#include +#include #include #include +#include #include #include #include -// Simulation parameters -static uint16_t pdsch_rnti = 0x1234; -static srslte_cell_t cell_base = {.nof_prb = 6, +// Common execution parameters +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 uint32_t nof_enb = 3; -static uint16_t cell_id_start = 0; -static uint16_t cell_id_step = 1; -static float channel_period_s = 7.2f; -static uint32_t sim_time_periods = 10; -static uint32_t cfi = 1; - -static srslte::channel::args_t channel_args_base; - -// Constant -static const srslte_tm_t transmission_mode = SRSLTE_TM1; - -int work_enb(srslte_enb_dl_t* enb_dl, - srslte_dl_sf_cfg_t* dl_sf, - srslte_dci_cfg_t* dci_cfg, - srslte_dci_dl_t* dci, - srslte_softbuffer_tx_t** softbuffer_tx, - uint8_t** data_tx) -{ - int ret = SRSLTE_ERROR; - - srslte_enb_dl_put_base(enb_dl, dl_sf); - - // Put PDSCH only if required - if (dci && dci_cfg && data_tx && softbuffer_tx) { - if (srslte_enb_dl_put_pdcch_dl(enb_dl, dci_cfg, dci)) { - ERROR("Error putting PDCCH sf_idx=%d\n", dl_sf->tti); - goto quit; - } - - // Create pdsch config - srslte_pdsch_cfg_t pdsch_cfg; - if (srslte_ra_dl_dci_to_grant(&enb_dl->cell, dl_sf, transmission_mode, false, dci, &pdsch_cfg.grant)) { - ERROR("Computing DL grant sf_idx=%d\n", dl_sf->tti); - goto quit; - } - char str[512]; - srslte_dci_dl_info(dci, str, 512); - INFO("eNb PDCCH: rnti=0x%x, %s\n", pdsch_rnti, str); +static std::string intra_meas_log_level; +static int phy_lib_log_level; - for (uint32_t i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { - pdsch_cfg.softbuffers.tx[i] = softbuffer_tx[i]; - } +// On the Fly parameters +static int earfcn_dl; +static std::string radio_device_args; +static std::string radio_device_name; +static std::string radio_log_level; +static float rx_gain; - // Enable power allocation - pdsch_cfg.power_scale = true; - pdsch_cfg.p_a = 0.0f; // 0 dB - pdsch_cfg.p_b = (transmission_mode > SRSLTE_TM1) ? 1 : 0; // 0 dB - pdsch_cfg.rnti = pdsch_rnti; - pdsch_cfg.meas_time_en = false; +// 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; +static float channel_hst_fd_hz; +static float channel_delay_max_us; +static std::string channel_log_level; + +// Simulation Serving cell PDSCH parameters +static bool serving_cell_pdsch_enable; +static uint16_t serving_cell_pdsch_rnti; +static srslte_tm_t serving_cell_pdsch_tm; +static uint16_t serving_cell_pdsch_mcs; + +// PRB allocation helpers +static uint32_t prbset_num = 1, last_prbset_num = 1; +static uint32_t prbset_orig = 0; + +unsigned int reverse(unsigned int x) +{ + x = (((x & (uint32_t)0xaaaaaaaa) >> (uint32_t)1) | ((x & (uint32_t)0x55555555) << (uint32_t)1)); + x = (((x & (uint32_t)0xcccccccc) >> (uint32_t)2) | ((x & (uint32_t)0x33333333) << (uint32_t)2)); + x = (((x & (uint32_t)0xf0f0f0f0) >> (uint32_t)4) | ((x & (uint32_t)0x0f0f0f0f) << (uint32_t)4)); + x = (((x & (uint32_t)0xff00ff00) >> (uint32_t)8) | ((x & (uint32_t)0x00ff00ff) << (uint32_t)8)); + return ((x >> (uint32_t)16) | (x << (uint32_t)16)); +} - if (srslte_enb_dl_put_pdsch(enb_dl, &pdsch_cfg, data_tx) < 0) { - ERROR("Error putting PDSCH sf_idx=%d\n", dl_sf->tti); - goto quit; +uint32_t prbset_to_bitmask() +{ + uint32_t mask = 0; + auto nb = (uint32_t)ceilf((float)cell_base.nof_prb / srslte_ra_type0_P(cell_base.nof_prb)); + for (uint32_t i = 0; i < nb; i++) { + if (i >= prbset_orig && i < prbset_orig + prbset_num) { + mask = mask | ((uint32_t)0x1 << i); } - srslte_pdsch_tx_info(&pdsch_cfg, str, 512); - INFO("eNb PDSCH: rnti=0x%x, %s\n", pdsch_rnti, str); } - - srslte_enb_dl_gen_signal(enb_dl); - - ret = SRSLTE_SUCCESS; - -quit: - return ret; + return reverse(mask) >> (uint32_t)(32 - nb); } +// Test eNb class class test_enb { private: srslte_enb_dl_t enb_dl; srslte::channel_ptr channel; cf_t* signal_buffer[SRSLTE_MAX_PORTS] = {}; + srslte::log_filter channel_log; public: - test_enb(const srslte_cell_t& cell, const srslte::channel::args_t& channel_args) + test_enb(const srslte_cell_t& cell, const srslte::channel::args_t& channel_args) : + enb_dl(), + channel_log("Channel pci=" + std::to_string(cell.id)) { + channel_log.set_level(channel_log_level); + channel = srslte::channel_ptr(new srslte::channel(channel_args, cell_base.nof_ports)); channel->set_srate(srslte_sampling_freq_hz(cell.nof_prb)); + channel->set_logger(&channel_log); // Allocate buffer for eNb for (uint32_t i = 0; i < cell_base.nof_ports; i++) { @@ -130,7 +127,7 @@ public: ERROR("Error setting eNb DL cell\n"); } - if (srslte_enb_dl_add_rnti(&enb_dl, pdsch_rnti)) { + if (srslte_enb_dl_add_rnti(&enb_dl, serving_cell_pdsch_rnti)) { ERROR("Error adding RNTI\n"); } } @@ -158,13 +155,13 @@ public: // Create pdsch config srslte_pdsch_cfg_t pdsch_cfg; - if (srslte_ra_dl_dci_to_grant(&enb_dl.cell, dl_sf, transmission_mode, false, dci, &pdsch_cfg.grant)) { + if (srslte_ra_dl_dci_to_grant(&enb_dl.cell, dl_sf, serving_cell_pdsch_tm, false, dci, &pdsch_cfg.grant)) { ERROR("Computing DL grant sf_idx=%d\n", dl_sf->tti); goto quit; } char str[512]; srslte_dci_dl_info(dci, str, 512); - INFO("eNb PDCCH: rnti=0x%x, %s\n", pdsch_rnti, str); + INFO("eNb PDCCH: rnti=0x%x, %s\n", serving_cell_pdsch_rnti, str); for (uint32_t i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { pdsch_cfg.softbuffers.tx[i] = softbuffer_tx[i]; @@ -173,8 +170,8 @@ public: // Enable power allocation pdsch_cfg.power_scale = true; pdsch_cfg.p_a = 0.0f; // 0 dB - pdsch_cfg.p_b = (transmission_mode > SRSLTE_TM1) ? 1 : 0; // 0 dB - pdsch_cfg.rnti = pdsch_rnti; + pdsch_cfg.p_b = (serving_cell_pdsch_tm > SRSLTE_TM1) ? 1 : 0; // 0 dB + pdsch_cfg.rnti = serving_cell_pdsch_rnti; pdsch_cfg.meas_time_en = false; if (srslte_enb_dl_put_pdsch(&enb_dl, &pdsch_cfg, data_tx) < 0) { @@ -182,7 +179,7 @@ public: goto quit; } srslte_pdsch_tx_info(&pdsch_cfg, str, 512); - INFO("eNb PDSCH: rnti=0x%x, %s\n", pdsch_rnti, str); + INFO("eNb PDSCH: rnti=0x%x, %s\n", serving_cell_pdsch_rnti, str); } srslte_enb_dl_gen_signal(&enb_dl); @@ -194,6 +191,13 @@ public: for (uint32_t i = 1; i < enb_dl.cell.nof_ports; i++) { srslte_vec_sum_ccc(signal_buffer[0], signal_buffer[i], signal_buffer[0], sf_len); } + if (enb_dl.cell.id != cell_base.id) { + srslte_vec_sc_prod_cfc( + signal_buffer[0], + powf(10.0f, (-ncell_attenuation_dB + (enb_dl.cell.id == cell_id_start + cell_id_step ? 3 : 0)) / 20.0f), + signal_buffer[0], + sf_len); + } srslte_vec_sum_ccc(signal_buffer[0], baseband_buffer, baseband_buffer, sf_len); ret = SRSLTE_SUCCESS; @@ -218,78 +222,348 @@ public: class dummy_rrc : public srsue::rrc_interface_phy_lte { public: + typedef struct { + float rsrp; + float rsrq; + uint32_t count; + } cell_meas_t; + + std::map cells; + void in_sync() override {} void out_of_sync() override {} void new_phy_meas(float rsrp, float rsrq, uint32_t tti, int earfcn, int pci) override { - printf("[new_phy_meas] tti=%d earfcn=%d; pci=%d; rsrp=%+.1f; rsrq=%+.1f;\n", tti, earfcn, pci, rsrp, rsrq); + if (!cells.count(pci)) { + cells[pci].rsrp = rsrp; + cells[pci].rsrq = rsrq; + cells[pci].count = 1; + } else { + cells[pci].rsrp = (rsrp + cells[pci].rsrp * cells[pci].count) / (cells[pci].count + 1); + cells[pci].rsrq = (rsrq + cells[pci].rsrq * cells[pci].count) / (cells[pci].count + 1); + cells[pci].count++; + } + } + + void print_stats() + { + printf("\n-- Statistics:\n"); + for (auto& e : cells) { + bool false_alarm = true; + + for (uint32_t i = 0; false_alarm && (i < nof_enb); i++) { + if (e.first == cell_id_start + cell_id_step * i) { + false_alarm = false; + } + } + + printf(" pci=%03d; count=%3d; false=%s; rsrp=%+.1f\n", + e.first, + e.second.count, + false_alarm ? "y" : "n", + e.second.rsrp); + } } }; +// shorten boost program options namespace +namespace bpo = boost::program_options; + +int parse_args(int argc, char** argv) +{ + int ret = SRSLTE_SUCCESS; + + bpo::options_description options; + bpo::options_description common("Common execution options"); + bpo::options_description over_the_air("Over the air execution options"); + bpo::options_description simulation("Over the air execution options"); + + // clang-format off + common.add_options() + ("duration", bpo::value(&duration_execution_s)->default_value(300), "Duration of the execution in seconds") + ("cell.nof_prb", bpo::value(&cell_base.nof_prb)->default_value(6), "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)") + ("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)") + ; + + over_the_air.add_options() + ("rf.dl_earfcn", bpo::value(&earfcn_dl)->default_value(-1), "DL EARFCN (setting this param enables over-the-air execution)") + ("rf.device_name", bpo::value(&radio_device_name)->default_value("auto"), "RF Device Name") + ("rf.device_args", bpo::value(&radio_device_args)->default_value("auto"), "RF Device arguments") + ("rf.log_level", bpo::value(&radio_log_level)->default_value("info"), "RF Log level (none, warning, info, debug)") + ("rf.rx_gain", bpo::value(&rx_gain)->default_value(30.0f), "RF Receiver gain in dB") + ("radio_log_level", bpo::value(&radio_log_level)->default_value("info"), "RF Log level") + ; + + simulation.add_options() + ("nof_enb", bpo::value(&nof_enb)->default_value(3), "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") + ("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") + ("channel.hst.fd", bpo::value(&channel_hst_fd_hz)->default_value(750.0f), "Channel High Speed Train doppler in Hz. Set to 0 for disabling") + ("channel.delay_max", bpo::value(&channel_delay_max_us)->default_value(4.7f), "Maximum simulated delay in microseconds. Set to 0 for disabling") + ("channel.log_level", bpo::value(&channel_log_level)->default_value("info"), "Channel simulator logging level") + ("serving_cell_pdsch_enable", bpo::value(&serving_cell_pdsch_enable)->default_value(true), "Enable simulated PDSCH in serving cell") + ("serving_cell_pdsch_rnti", bpo::value(&serving_cell_pdsch_rnti)->default_value(0x1234), "Simulated PDSCH RNTI") + ("serving_cell_pdsch_tm", bpo::value((int*) &serving_cell_pdsch_tm)->default_value(SRSLTE_TM1), "Simulated Transmission mode 0: TM1, 1: TM2, 2: TM3, 3: TM4") + ("serving_cell_pdsch_mcs", bpo::value(&serving_cell_pdsch_mcs)->default_value(20), "Simulated PDSCH MCS") + ; + // clang-format on + + options.add(common).add(over_the_air).add(simulation).add_options()("help", "Show this message"); + + bpo::variables_map vm; + try { + bpo::store(bpo::command_line_parser(argc, argv).options(options).run(), vm); + bpo::notify(vm); + } catch (bpo::error& e) { + std::cerr << e.what() << std::endl; + ret = SRSLTE_ERROR; + } + + // help option was given or error - print usage and exit + if (vm.count("help") || ret) { + std::cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << std::endl << std::endl; + std::cout << options << std::endl << std::endl; + ret = SRSLTE_ERROR; + } + + return ret; +} + int main(int argc, char** argv) { - auto baseband_buffer = (cf_t*)srslte_vec_malloc(sizeof(cf_t) * SRSLTE_SF_LEN_MAX); + // Parse 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 = {}; + srsue::scell::intra_measure intra_measure; + srslte::log_filter logger("intra_measure"); + dummy_rrc rrc; + srsue::phy_common common(1); + srsue::phy_args_t phy_args; + + // Simulation only std::vector > test_enb_v; - srslte_timestamp_t ts = {}; - srsue::scell::intra_measure intra_measure; - srslte::log_filter logger("intra_measure"); - dummy_rrc rrc; - - srsue::phy_common common(1); - srsue::phy_args_t phy_args; - common.args = &phy_args; - phy_args.estimator_fil_auto = false; - phy_args.estimator_fil_order = 4; - phy_args.estimator_fil_stddev = 1.0f; - phy_args.sic_pss_enabled = false; - phy_args.intra_freq_meas_len_ms = 20; - phy_args.intra_freq_meas_period_ms = 200; + uint8_t* data_tx[SRSLTE_MAX_TB] = {}; + srslte_softbuffer_tx_t* softbuffer_tx[SRSLTE_MAX_TB] = {}; + + // Over-the-air only + std::unique_ptr radio = nullptr; + std::unique_ptr radio_log = nullptr; + + // Set Receiver args + common.args = &phy_args; + phy_args.estimator_fil_auto = false; + phy_args.estimator_fil_order = 4; + phy_args.estimator_fil_stddev = 1.0f; + phy_args.sic_pss_enabled = false; + phy_args.intra_freq_meas_len_ms = 20; + phy_args.intra_freq_meas_period_ms = 200; + phy_args.interpolate_subframe_enabled = false; + phy_args.nof_rx_ant = 1; + phy_args.cfo_is_doppler = true; + phy_args.cfo_integer_enabled = false; + phy_args.cfo_correct_tol_hz = 1.0f; + phy_args.cfo_pss_ema = DEFAULT_CFO_EMA_TRACK; + phy_args.cfo_ref_mask = 1023; + phy_args.cfo_loop_bw_pss = DEFAULT_CFO_BW_PSS; + phy_args.cfo_loop_bw_ref = DEFAULT_CFO_BW_REF; + phy_args.cfo_loop_pss_tol = DEFAULT_CFO_PSS_MIN; + phy_args.cfo_loop_ref_min = DEFAULT_CFO_REF_MIN; + phy_args.cfo_loop_pss_conv = DEFAULT_PSS_STABLE_TIMEOUT; + phy_args.snr_estim_alg = "refs"; + phy_args.snr_ema_coeff = 0.1f; + + // Set phy-lib logging level + srslte_verbose = phy_lib_log_level; + + // Allocate PDSCH data and tx-soft-buffers only if pdsch is enabled and radio is not available + for (int i = 0; i < SRSLTE_MAX_TB && serving_cell_pdsch_enable && radio == nullptr; i++) { + srslte_random_t random_gen = srslte_random_init(serving_cell_pdsch_rnti); + + const size_t nof_bytes = (6144 * 16 * 3 / 8); + softbuffer_tx[i] = (srslte_softbuffer_tx_t*)calloc(sizeof(srslte_softbuffer_tx_t), 1); + if (!softbuffer_tx[i]) { + ERROR("Error allocating softbuffer_tx\n"); + } + + if (srslte_softbuffer_tx_init(softbuffer_tx[i], cell_base.nof_prb)) { + ERROR("Error initiating softbuffer_tx\n"); + } + + data_tx[i] = (uint8_t*)srslte_vec_malloc(sizeof(uint8_t) * nof_bytes); + if (!data_tx[i]) { + ERROR("Error allocating data tx\n"); + } + + for (uint32_t j = 0; j < nof_bytes; j++) { + data_tx[i][j] = (uint8_t)srslte_random_uniform_int_dist(random_gen, 0, 255); + } + + srslte_random_free(random_gen); + } + + // 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; + cell_base.id = serving_cell_id; + + logger.set_level(intra_meas_log_level); intra_measure.init(&common, &rrc, &logger); - intra_measure.set_primay_cell(0, cell_base); - intra_measure.add_cell(cell_id_start); - - for (uint32_t enb_idx = 0; enb_idx < nof_enb; enb_idx++) { - // Initialise cell - srslte_cell_t cell = cell_base; - cell.id = cell_id_start + enb_idx * cell_id_step; - - // Initialise channel and push back - srslte::channel::args_t channel_args; - channel_args.enable = true; - channel_args.hst_enable = true; - channel_args.hst_init_time_s = 0.0f; //(float) channel_period_s / (float) nof_enb * enb_idx; - channel_args.hst_period_s = 7.2f; //(float) channel_period_s; - channel_args.hst_fd_hz = 750.0f; - channel_args.delay_enable = enb_idx != 0; - channel_args.delay_min_us = 10.0; - channel_args.delay_max_us = 300.0; - channel_args.delay_period_s = channel_period_s; - test_enb_v.push_back(std::unique_ptr(new test_enb(cell, channel_args))); + intra_measure.set_primay_cell(serving_cell_id, cell_base); + intra_measure.add_cell(serving_cell_id); + + if (earfcn_dl >= 0) { + // Create radio log + radio_log = std::unique_ptr(new srslte::log_filter("Radio")); + radio_log->set_level(radio_log_level); + + // Create radio + radio = std::unique_ptr(new srslte::radio()); + + // Init radio + radio->init(radio_log.get(), (char*)radio_device_args.c_str(), (char*)radio_device_name.c_str(), 1); + + // Set sampling rate + radio->set_rx_srate(srslte_sampling_freq_hz(cell_base.nof_prb)); + + // Set frequency + radio->set_rx_freq(0, srslte_band_fd(earfcn_dl) * 1e6); + + } else { + // Create test eNb's if radio is not available + for (uint32_t enb_idx = 0; enb_idx < nof_enb; enb_idx++) { + // Initialise cell + srslte_cell_t cell = cell_base; + cell.id = (cell_id_start + enb_idx * cell_id_step) % 504; + + // Initialise channel and push back + srslte::channel::args_t channel_args; + channel_args.enable = true; + 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_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_period_s = (uint32)channel_period_s; + channel_args.delay_init_time_s = (enb_idx * channel_period_s) / nof_enb; + test_enb_v.push_back(std::unique_ptr(new test_enb(cell, channel_args))); + } } - for (uint32_t period = 0; period < sim_time_periods; period++) { - for (uint32_t k = 0, sf_idx = 0; k < channel_period_s * 1000; k++, sf_idx++) { - srslte_dl_sf_cfg_t sf_cfg_dl = {}; - sf_cfg_dl.tti = sf_idx % 10; - sf_cfg_dl.cfi = cfi; - sf_cfg_dl.sf_type = SRSLTE_SF_NORM; + // Run Programm + for (uint32_t sf_idx = 0; sf_idx < duration_execution_s * 1000; sf_idx++) { + srslte_dl_sf_cfg_t sf_cfg_dl = {}; + sf_cfg_dl.tti = sf_idx % 10240; + sf_cfg_dl.cfi = cfi; + sf_cfg_dl.sf_type = SRSLTE_SF_NORM; + + // Clean buffer + bzero(baseband_buffer, sizeof(cf_t) * SRSLTE_SF_LEN_MAX); - // Clean buffer - bzero(baseband_buffer, sizeof(cf_t) * SRSLTE_SF_LEN_MAX); + if (radio) { + // Receive radio + radio->rx_now(&baseband_buffer, SRSLTE_SF_LEN_PRB(cell_base.nof_prb), &ts); + } else { + // Run eNb simulator + bool put_pdsch = serving_cell_pdsch_enable; for (auto& enb : test_enb_v) { - enb->work(&sf_cfg_dl, nullptr, nullptr, nullptr, nullptr, baseband_buffer, ts); + if (put_pdsch) { + // Reset pdsch put flag + put_pdsch = false; + + // DCI Configuration + srslte_dci_dl_t dci; + srslte_dci_cfg_t dci_cfg; + dci_cfg.srs_request_enabled = false; + dci_cfg.ra_format_enabled = false; + dci_cfg.multiple_csi_request_enabled = false; + + // DCI Fixed values + dci.pid = 0; + dci.pinfo = 0; + dci.rnti = serving_cell_pdsch_rnti; + dci.is_tdd = false; + dci.is_dwpts = false; + dci.is_ra_order = false; + dci.tb_cw_swap = false; + dci.pconf = false; + dci.power_offset = false; + dci.tpc_pucch = false; + dci.ra_preamble = false; + dci.ra_mask_idx = false; + dci.srs_request = false; + dci.srs_request_present = false; + dci.cif_present = false; + dci_cfg.cif_enabled = false; + + // Set PRB Allocation type + dci.alloc_type = SRSLTE_RA_ALLOC_TYPE0; + prbset_num = (int)ceilf((float)cell_base.nof_prb / srslte_ra_type0_P(cell_base.nof_prb)); + last_prbset_num = prbset_num; + dci.type0_alloc.rbg_bitmask = prbset_to_bitmask(); + dci.location.L = 0; + dci.location.ncce = 0; + + // Set TB + if (serving_cell_pdsch_tm < SRSLTE_TM3) { + dci.format = SRSLTE_DCI_FORMAT1; + dci.tb[0].mcs_idx = serving_cell_pdsch_mcs; + dci.tb[0].rv = 0; + dci.tb[0].ndi = false; + dci.tb[0].cw_idx = 0; + dci.tb[1].mcs_idx = 0; + dci.tb[1].rv = 1; + } else if (serving_cell_pdsch_tm == SRSLTE_TM3) { + dci.format = SRSLTE_DCI_FORMAT2A; + for (uint32_t i = 0; i < SRSLTE_MAX_TB; i++) { + dci.tb[i].mcs_idx = serving_cell_pdsch_mcs; + dci.tb[i].rv = 0; + dci.tb[i].ndi = false; + dci.tb[i].cw_idx = i; + } + } else if (serving_cell_pdsch_tm == SRSLTE_TM4) { + dci.format = SRSLTE_DCI_FORMAT2; + dci.pinfo = 0; + for (uint32_t i = 0; i < SRSLTE_MAX_TB; i++) { + dci.tb[i].mcs_idx = serving_cell_pdsch_mcs; + dci.tb[i].rv = 0; + dci.tb[i].ndi = false; + dci.tb[i].cw_idx = i; + } + } else { + ERROR("Wrong transmission mode (%d)\n", serving_cell_pdsch_tm); + } + enb->work(&sf_cfg_dl, &dci_cfg, &dci, softbuffer_tx, data_tx, baseband_buffer, ts); + } else { + enb->work(&sf_cfg_dl, nullptr, nullptr, nullptr, nullptr, baseband_buffer, ts); + } } + } - srslte_timestamp_add(&ts, 0, 0.001f); + srslte_timestamp_add(&ts, 0, 0.001f); - intra_measure.write(k, baseband_buffer, SRSLTE_SF_LEN_PRB(cell_base.nof_prb)); - if (k % 1000 == 0) { - printf("Done %.1f%%\n", (double)k * 100.0 / ((double)sim_time_periods * channel_period_s * 1000.0)); - } + intra_measure.write(sf_idx % 10240, 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)); } } + + // Stop intra_measure.stop(); - srslte_dft_exit(); + + rrc.print_stats(); + if (baseband_buffer) { + free(baseband_buffer); + } } \ No newline at end of file