diff --git a/lib/examples/pdsch_ue.c b/lib/examples/pdsch_ue.c index acc598296..b62ae263e 100644 --- a/lib/examples/pdsch_ue.c +++ b/lib/examples/pdsch_ue.c @@ -546,7 +546,7 @@ int main(int argc, char **argv) { exit(-1); } - srslte_chest_dl_cfo_estimate_enable(&ue_dl.chest, prog_args.enable_cfo_ref, 0xff, 0.1); + srslte_chest_dl_cfo_estimate_enable(&ue_dl.chest, prog_args.enable_cfo_ref, 0xff, 0.005); srslte_chest_dl_average_subframe(&ue_dl.chest, prog_args.average_subframe); /* Configure downlink receiver for the SI-RNTI since will be the only one we'll use */ @@ -959,7 +959,7 @@ void *plot_thread_run(void *arg) { plot_real_init(&pce); plot_real_setTitle(&pce, "Channel Response - Magnitude"); plot_real_setLabels(&pce, "Index", "dB"); - plot_real_setYAxisScale(&pce, -40, 40); + plot_real_setYAxisScale(&pce, -M_PI, M_PI); plot_real_init(&p_sync); plot_real_setTitle(&p_sync, "PSS Cross-Corr abs value"); @@ -995,7 +995,11 @@ void *plot_thread_run(void *arg) { tmp_plot2[g+i] = -80; } } - plot_real_setNewData(&pce, tmp_plot2, sz); + uint32_t nrefs = 2*ue_dl.cell.nof_prb; + for (i=0;i= SRSLTE_SYNC_FFT_SZ_MIN && fft_size <= SRSLTE_SYNC_FFT_SZ_MAX && (fft_size%64) == 0) { diff --git a/lib/src/phy/ue/ue_sync.c b/lib/src/phy/ue/ue_sync.c index 58489b92b..b5d89743f 100644 --- a/lib/src/phy/ue/ue_sync.c +++ b/lib/src/phy/ue/ue_sync.c @@ -47,10 +47,14 @@ #define DEFAULT_SAMPLE_OFFSET_CORRECT_PERIOD 0 #define DEFAULT_SFO_EMA_COEFF 0.1 -#define DEFAULT_CFO_BW 0.7 -#define DEFAULT_CFO_PSS_TOL 80 // typical accuracy of PSS estimation. Avoids ping-pong effect -#define DEFAULT_CFO_REF_TOL 5 // typical accuracy of REF estimation -#define DEFAULT_CFO_REF_MAX 300 // Maximum detection offset of REF based estimation +#define DEFAULT_CFO_BW 0.2 +#define DEFAULT_CFO_PSS_MIN 500 // typical bias of PSS estimation. +#define DEFAULT_CFO_REF_MIN 0 // typical bias of REF estimation +#define DEFAULT_CFO_REF_MAX 500 // Maximum detection offset of REF based estimation + +#define DEFAULT_PSS_STABLE_TIMEOUT 100 // Time after which the PSS is considered to be stable and we accept REF-CFO + +#define DEFAULT_CFO_EMA_TRACK 0.1 cf_t dummy_buffer0[15*2048/2]; cf_t dummy_buffer1[15*2048/2]; @@ -116,6 +120,7 @@ clean_exit: void srslte_ue_sync_cfo_reset(srslte_ue_sync_t *q) { + q->cfo_is_copied = false; q->cfo_current_value = 0; srslte_sync_cfo_reset(&q->strack); srslte_sync_cfo_reset(&q->sfind); @@ -129,6 +134,7 @@ void srslte_ue_sync_reset(srslte_ue_sync_t *q) { } else { q->sf_idx = 9; } + q->pss_stable_timeout = false; q->state = SF_FIND; q->frame_ok_cnt = 0; q->frame_no_cnt = 0; @@ -218,11 +224,15 @@ int srslte_ue_sync_init_multi_decim(srslte_ue_sync_t *q, q->max_prb = max_prb; q->cfo_ref_max = DEFAULT_CFO_REF_MAX; - q->cfo_ref_tol = DEFAULT_CFO_REF_TOL; - q->cfo_pss_tol = DEFAULT_CFO_PSS_TOL; - q->cfo_loop_bw = DEFAULT_CFO_BW; + q->cfo_ref_min = DEFAULT_CFO_REF_MIN; + q->cfo_pss_min = DEFAULT_CFO_PSS_MIN; + q->cfo_loop_bw_pss = DEFAULT_CFO_BW; + q->cfo_loop_bw_ref = DEFAULT_CFO_BW; q->cfo_correct_enable = true; + q->pss_stable_cnt = 0; + q->pss_stable_timeout = DEFAULT_PSS_STABLE_TIMEOUT; + if (search_cell) { /* If the cell is unkown, we search PSS/SSS in 5 ms */ @@ -269,11 +279,11 @@ int srslte_ue_sync_init_multi_decim(srslte_ue_sync_t *q, srslte_sync_set_cfo_cp_enable(&q->strack, false); srslte_sync_set_cfo_pss_enable(&q->strack, true); srslte_sync_set_pss_filt_enable(&q->strack, true); - srslte_sync_set_sss_filt_enable(&q->strack, true); + srslte_sync_set_sss_filt_enable(&q->strack, false); // FIXME: CP detection not working very well. Not supporting Extended CP right now srslte_sync_cp_en(&q->strack, false); - srslte_sync_cp_en(&q->sfind, false); + srslte_sync_cp_en(&q->sfind, false); srslte_sync_sss_en(&q->strack, true); @@ -368,6 +378,9 @@ int srslte_ue_sync_set_cell(srslte_ue_sync_t *q, srslte_cell_t cell) srslte_sync_set_threshold(&q->sfind, 2.0); srslte_sync_set_threshold(&q->strack, 1.2); + srslte_sync_set_cfo_ema_alpha(&q->sfind, 0.1); + srslte_sync_set_cfo_ema_alpha(&q->strack, 0.1); + } else { q->sfind.cp = cell.cp; q->strack.cp = cell.cp; @@ -376,7 +389,7 @@ int srslte_ue_sync_set_cell(srslte_ue_sync_t *q, srslte_cell_t cell) srslte_sync_set_N_id_2(&q->strack, cell.id%3); srslte_sync_set_cfo_ema_alpha(&q->sfind, 0.1); - srslte_sync_set_cfo_ema_alpha(&q->strack, 0.1); + srslte_sync_set_cfo_ema_alpha(&q->strack, DEFAULT_CFO_EMA_TRACK); /* In find phase and if the cell is known, do not average pss correlation * because we only capture 1 subframe and do not know where the peak is. @@ -402,22 +415,31 @@ void srslte_ue_sync_get_last_timestamp(srslte_ue_sync_t *q, srslte_timestamp_t * memcpy(timestamp, &q->last_timestamp, sizeof(srslte_timestamp_t)); } -void srslte_ue_sync_set_cfo_loop_bw(srslte_ue_sync_t *q, float bw, float pss_tol, float ref_tol, float ref_max) { - q->cfo_loop_bw = bw; - q->cfo_pss_tol = pss_tol; - q->cfo_ref_tol = ref_tol; +void srslte_ue_sync_set_cfo_loop_bw(srslte_ue_sync_t *q, float bw_pss, float bw_ref, + float pss_tol, float ref_tol, float ref_max, + uint32_t pss_stable_conv_time) { + q->cfo_loop_bw_pss = bw_pss; + q->cfo_loop_bw_ref = bw_ref; + q->cfo_pss_min = pss_tol; + q->cfo_ref_min = ref_tol; q->cfo_ref_max = ref_max; + q->pss_stable_timeout = pss_stable_conv_time; } void srslte_ue_sync_set_cfo_ema(srslte_ue_sync_t *q, float ema) { - srslte_sync_set_cfo_ema_alpha(&q->sfind, ema); srslte_sync_set_cfo_ema_alpha(&q->strack, ema); } void srslte_ue_sync_set_cfo_ref(srslte_ue_sync_t *q, float ref_cfo) { - if (fabsf(ref_cfo)*15000 > q->cfo_ref_tol && fabsf(ref_cfo)*15000 < q->cfo_ref_max) { - q->cfo_current_value += ref_cfo*q->cfo_loop_bw; + // Accept REF-based CFO adjustments only after PSS CFO is stable + if (q->pss_is_stable) { + if (fabsf(ref_cfo)*15000 > q->cfo_ref_min) { + if (fabsf(ref_cfo)*15000 > q->cfo_ref_max) { + ref_cfo = (ref_cfo>0?q->cfo_ref_max:-q->cfo_ref_max)/15000; + } + q->cfo_current_value += ref_cfo*q->cfo_loop_bw_ref; + } } } @@ -437,12 +459,16 @@ float srslte_ue_sync_get_cfo(srslte_ue_sync_t *q) { void srslte_ue_sync_copy_cfo(srslte_ue_sync_t *q, srslte_ue_sync_t *src_obj) { // Copy find object internal CFO averages - srslte_sync_copy_cfo(&q->sfind, &src_obj->sfind); + srslte_sync_copy_cfo(&q->sfind, &src_obj->sfind); // Current CFO is tracking-phase CFO of previous object - q->cfo_current_value = srslte_ue_sync_get_cfo(src_obj); + q->cfo_current_value = src_obj->cfo_current_value; + q->cfo_is_copied = true; } -void srslte_ue_sync_set_cfo_tol(srslte_ue_sync_t *q, float cfo_tol) {} +void srslte_ue_sync_set_cfo_tol(srslte_ue_sync_t *q, float cfo_tol) { + srslte_sync_set_cfo_tol(&q->strack, cfo_tol); + srslte_sync_set_cfo_tol(&q->sfind, cfo_tol); +} float srslte_ue_sync_get_sfo(srslte_ue_sync_t *q) { return q->mean_sfo/5e-3; @@ -507,10 +533,12 @@ static int find_peak_ok(srslte_ue_sync_t *q, cf_t *input_buffer[SRSLTE_MAX_PORTS q->mean_sample_offset = 0; /* Goto Tracking state */ - q->state = SF_TRACK; - + q->state = SF_TRACK; + /* Set CFO values. Since we correct before track, the initial track state is CFO=0 Hz */ - q->cfo_current_value = srslte_sync_get_cfo(&q->sfind); + if (!q->cfo_is_copied) { + q->cfo_current_value = srslte_sync_get_cfo(&q->sfind); + } srslte_sync_cfo_reset(&q->strack); } @@ -550,9 +578,18 @@ static int track_peak_ok(srslte_ue_sync_t *q, uint32_t track_idx) { /* Adjust current CFO estimation with PSS * Since sync track has enabled only PSS-based correlation, get_cfo() returns that value only, already filtered. */ - INFO("TRACK: cfo_current: %f, cfo_strack=%f\n", 15000*q->cfo_current_value, 15000*srslte_sync_get_cfo(&q->strack)); - if (15000*fabsf(srslte_sync_get_cfo(&q->strack)) > q->cfo_pss_tol) { - q->cfo_current_value += srslte_sync_get_cfo(&q->strack)*q->cfo_loop_bw; + INFO("TRACK: cfo_current=%f, cfo_strack=%f\n", 15000*q->cfo_current_value, 15000*srslte_sync_get_cfo(&q->strack)); + if (15000*fabsf(srslte_sync_get_cfo(&q->strack)) > q->cfo_pss_min) { + q->cfo_current_value += srslte_sync_get_cfo(&q->strack)*q->cfo_loop_bw_pss; + q->pss_stable_cnt = 0; + q->pss_is_stable = false; + } else { + if (!q->pss_is_stable) { + q->pss_stable_cnt++; + if (q->pss_stable_cnt >= q->pss_stable_timeout) { + q->pss_is_stable = true; + } + } } // Compute cumulative moving average time offset */ @@ -694,7 +731,7 @@ int srslte_ue_sync_zerocopy_multi(srslte_ue_sync_t *q, cf_t *input_buffer[SRSLTE ret = SRSLTE_ERROR; fprintf(stderr, "Error finding correlation peak (%d)\n", ret); return SRSLTE_ERROR; - case SRSLTE_SYNC_FOUND: + case SRSLTE_SYNC_FOUND: ret = find_peak_ok(q, input_buffer); break; case SRSLTE_SYNC_FOUND_NOSPACE: @@ -769,9 +806,9 @@ int srslte_ue_sync_zerocopy_multi(srslte_ue_sync_t *q, cf_t *input_buffer[SRSLTE fprintf(stderr, "Error processing tracking peak\n"); q->state = SF_FIND; return SRSLTE_SUCCESS; - } - - q->frame_total_cnt++; + } + + q->frame_total_cnt++; } break; } diff --git a/lib/src/phy/ue/ue_ul.c b/lib/src/phy/ue/ue_ul.c index ec2809dfe..e50be05a6 100644 --- a/lib/src/phy/ue/ue_ul.c +++ b/lib/src/phy/ue/ue_ul.c @@ -38,7 +38,7 @@ #define MAX_SFLEN SRSLTE_SF_LEN(srslte_symbol_sz(max_prb)) -#define DEFAULT_CFO_TOL 50.0 // Hz +#define DEFAULT_CFO_TOL 0.0 // Hz int srslte_ue_ul_init(srslte_ue_ul_t *q, cf_t *out_buffer, diff --git a/srsue/hdr/phy/phch_common.h b/srsue/hdr/phy/phch_common.h index 0fe8529da..963f146dc 100644 --- a/srsue/hdr/phy/phch_common.h +++ b/srsue/hdr/phy/phch_common.h @@ -42,131 +42,138 @@ namespace srsue { + +class chest_feedback_itf +{ +public: + virtual void set_cfo(float cfo) = 0; +}; + /* Subclass that manages variables common to all workers */ - class phch_common { - public: - - /* Common variables used by all phy workers */ - phy_interface_rrc::phy_cfg_t *config; - phy_args_t *args; - rrc_interface_phy *rrc; - mac_interface_phy *mac; - srslte_ue_ul_t ue_ul; - - /* Power control variables */ - float pathloss; - float cur_pathloss; - float p0_preamble; - float cur_radio_power; - float cur_pusch_power; - float avg_rsrp_db; - float avg_rsrq_db; - float rx_gain_offset; - float avg_snr_db; - float avg_noise; - float avg_rsrp; +class phch_common { +public: - // Save last TBS for mcs>28 cases - int last_dl_tbs[2*HARQ_DELAY_MS][SRSLTE_MAX_CODEWORDS]; - uint32_t last_dl_tti[2*HARQ_DELAY_MS]; + /* Common variables used by all phy workers */ + phy_interface_rrc::phy_cfg_t *config; + phy_args_t *args; + rrc_interface_phy *rrc; + mac_interface_phy *mac; + srslte_ue_ul_t ue_ul; - int last_ul_tbs[2*HARQ_DELAY_MS]; - uint32_t last_ul_tti[2*HARQ_DELAY_MS]; - srslte_mod_t last_ul_mod[2*HARQ_DELAY_MS]; + /* Power control variables */ + float pathloss; + float cur_pathloss; + float p0_preamble; + float cur_radio_power; + float cur_pusch_power; + float avg_rsrp_db; + float avg_rsrq_db; + float rx_gain_offset; + float avg_snr_db; + float avg_noise; + float avg_rsrp; - phch_common(uint32_t max_mutex = 3); - void init(phy_interface_rrc::phy_cfg_t *config, - phy_args_t *args, - srslte::log *_log, - srslte::radio *_radio, - rrc_interface_phy *rrc, - mac_interface_phy *_mac); - - /* For RNTI searches, -1 means now or forever */ - void set_ul_rnti(srslte_rnti_type_t type, uint16_t rnti_value, int tti_start = -1, int tti_end = -1); - uint16_t get_ul_rnti(uint32_t tti); - srslte_rnti_type_t get_ul_rnti_type(); + // Save last TBS for mcs>28 cases + int last_dl_tbs[2*HARQ_DELAY_MS][SRSLTE_MAX_CODEWORDS]; + uint32_t last_dl_tti[2*HARQ_DELAY_MS]; - void set_dl_rnti(srslte_rnti_type_t type, uint16_t rnti_value, int tti_start = -1, int tti_end = -1); - uint16_t get_dl_rnti(uint32_t tti); - srslte_rnti_type_t get_dl_rnti_type(); - - void set_rar_grant(uint32_t tti, uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN]); - bool get_pending_rar(uint32_t tti, srslte_dci_rar_grant_t *rar_grant = NULL); - - void reset_pending_ack(uint32_t tti); - void set_pending_ack(uint32_t tti, uint32_t I_lowest, uint32_t n_dmrs); - bool get_pending_ack(uint32_t tti); - bool get_pending_ack(uint32_t tti, uint32_t *I_lowest, uint32_t *n_dmrs); - - void worker_end(uint32_t tti, bool tx_enable, cf_t *buffer, uint32_t nof_samples, srslte_timestamp_t tx_time); - - void set_nof_mutex(uint32_t nof_mutex); - - bool sr_enabled; - int sr_last_tx_tti; - - srslte::radio* get_radio(); + int last_ul_tbs[2*HARQ_DELAY_MS]; + uint32_t last_ul_tti[2*HARQ_DELAY_MS]; + srslte_mod_t last_ul_mod[2*HARQ_DELAY_MS]; - void set_cell(const srslte_cell_t &c); - uint32_t get_nof_prb(); - void set_dl_metrics(const dl_metrics_t &m); - void get_dl_metrics(dl_metrics_t &m); - void set_ul_metrics(const ul_metrics_t &m); - void get_ul_metrics(ul_metrics_t &m); - void set_sync_metrics(const sync_metrics_t &m); - void get_sync_metrics(sync_metrics_t &m); + phch_common(uint32_t max_mutex = 3); + void init(phy_interface_rrc::phy_cfg_t *config, + phy_args_t *args, + srslte::log *_log, + srslte::radio *_radio, + rrc_interface_phy *rrc, + mac_interface_phy *_mac); - void reset_ul(); - - private: - - std::vector tx_mutex; - - bool is_first_of_burst; - srslte::radio *radio_h; - float cfo; - srslte::log *log_h; + /* For RNTI searches, -1 means now or forever */ + void set_ul_rnti(srslte_rnti_type_t type, uint16_t rnti_value, int tti_start = -1, int tti_end = -1); + uint16_t get_ul_rnti(uint32_t tti); + srslte_rnti_type_t get_ul_rnti_type(); - - bool ul_rnti_active(uint32_t tti); - bool dl_rnti_active(uint32_t tti); - uint16_t ul_rnti, dl_rnti; - srslte_rnti_type_t ul_rnti_type, dl_rnti_type; - int ul_rnti_start, ul_rnti_end, dl_rnti_start, dl_rnti_end; - - float time_adv_sec; - - srslte_dci_rar_grant_t rar_grant; - bool rar_grant_pending; - uint32_t rar_grant_tti; - - typedef struct { - bool enabled; - uint32_t I_lowest; - uint32_t n_dmrs; - } pending_ack_t; - pending_ack_t pending_ack[TTIMOD_SZ]; + void set_dl_rnti(srslte_rnti_type_t type, uint16_t rnti_value, int tti_start = -1, int tti_end = -1); + uint16_t get_dl_rnti(uint32_t tti); + srslte_rnti_type_t get_dl_rnti_type(); - bool is_first_tx; + void set_rar_grant(uint32_t tti, uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN]); + bool get_pending_rar(uint32_t tti, srslte_dci_rar_grant_t *rar_grant = NULL); - uint32_t nof_workers; - uint32_t nof_mutex; - uint32_t max_mutex; + void reset_pending_ack(uint32_t tti); + void set_pending_ack(uint32_t tti, uint32_t I_lowest, uint32_t n_dmrs); + bool get_pending_ack(uint32_t tti); + bool get_pending_ack(uint32_t tti, uint32_t *I_lowest, uint32_t *n_dmrs); - srslte_cell_t cell; + void worker_end(uint32_t tti, bool tx_enable, cf_t *buffer, uint32_t nof_samples, srslte_timestamp_t tx_time); + + void set_nof_mutex(uint32_t nof_mutex); + + bool sr_enabled; + int sr_last_tx_tti; + + srslte::radio* get_radio(); + + void set_cell(const srslte_cell_t &c); + uint32_t get_nof_prb(); + void set_dl_metrics(const dl_metrics_t &m); + void get_dl_metrics(dl_metrics_t &m); + void set_ul_metrics(const ul_metrics_t &m); + void get_ul_metrics(ul_metrics_t &m); + void set_sync_metrics(const sync_metrics_t &m); + void get_sync_metrics(sync_metrics_t &m); + + void reset_ul(); + +private: + + std::vector tx_mutex; + + bool is_first_of_burst; + srslte::radio *radio_h; + float cfo; + srslte::log *log_h; + + + bool ul_rnti_active(uint32_t tti); + bool dl_rnti_active(uint32_t tti); + uint16_t ul_rnti, dl_rnti; + srslte_rnti_type_t ul_rnti_type, dl_rnti_type; + int ul_rnti_start, ul_rnti_end, dl_rnti_start, dl_rnti_end; + + float time_adv_sec; + + srslte_dci_rar_grant_t rar_grant; + bool rar_grant_pending; + uint32_t rar_grant_tti; + + typedef struct { + bool enabled; + uint32_t I_lowest; + uint32_t n_dmrs; + } pending_ack_t; + pending_ack_t pending_ack[TTIMOD_SZ]; + + bool is_first_tx; + + uint32_t nof_workers; + uint32_t nof_mutex; + uint32_t max_mutex; + + srslte_cell_t cell; + + dl_metrics_t dl_metrics; + uint32_t dl_metrics_count; + bool dl_metrics_read; + ul_metrics_t ul_metrics; + uint32_t ul_metrics_count; + bool ul_metrics_read; + sync_metrics_t sync_metrics; + uint32_t sync_metrics_count; + bool sync_metrics_read; +}; - dl_metrics_t dl_metrics; - uint32_t dl_metrics_count; - bool dl_metrics_read; - ul_metrics_t ul_metrics; - uint32_t ul_metrics_count; - bool ul_metrics_read; - sync_metrics_t sync_metrics; - uint32_t sync_metrics_count; - bool sync_metrics_read; - }; - } // namespace srsue #endif // UEPHYWORKERCOMMON_H diff --git a/srsue/hdr/phy/phch_recv.h b/srsue/hdr/phy/phch_recv.h index 01a296094..a5f290887 100644 --- a/srsue/hdr/phy/phch_recv.h +++ b/srsue/hdr/phy/phch_recv.h @@ -41,7 +41,7 @@ namespace srsue { typedef _Complex float cf_t; -class phch_recv : public thread +class phch_recv : public thread, public chest_feedback_itf { public: phch_recv(); @@ -65,6 +65,9 @@ public: bool status_is_sync(); + // from chest_feedback_itf + void set_cfo(float cfo); + void set_time_adv_sec(float time_adv_sec); void get_current_cell(srslte_cell_t *cell); diff --git a/srsue/hdr/phy/phch_worker.h b/srsue/hdr/phy/phch_worker.h index e7030d777..570d3d0c2 100644 --- a/srsue/hdr/phy/phch_worker.h +++ b/srsue/hdr/phy/phch_worker.h @@ -45,7 +45,7 @@ public: ~phch_worker(); void reset(); void set_common(phch_common *phy); - bool init(uint32_t max_prb, srslte::log *log); + bool init(uint32_t max_prb, srslte::log *log, chest_feedback_itf *chest_loop); bool set_cell(srslte_cell_t cell); @@ -66,6 +66,8 @@ public: int read_ce_abs(float *ce_abs); int read_pdsch_d(cf_t *pdsch_d); void start_plot(); + + float get_ref_cfo(); private: /* Inherited from thread_pool::worker. Function called every subframe to run the DL/UL processing */ @@ -114,6 +116,7 @@ private: /* Common objects */ phch_common *phy; srslte::log *log_h; + chest_feedback_itf *chest_loop; srslte_cell_t cell; bool mem_initiated; bool cell_initiated; diff --git a/srsue/src/main.cc b/srsue/src/main.cc index 1c9b6edc6..201af1b14 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -199,13 +199,46 @@ void parse_args(all_args_t *args, int argc, char *argv[]) { "Enables integer CFO estimation and correction.") ("expert.cfo_correct_tol_hz", - bpo::value(&args->expert.phy.cfo_correct_tol_hz)->default_value(50.0), - "Tolerance (in Hz) for digial CFO compensation.") + bpo::value(&args->expert.phy.cfo_correct_tol_hz)->default_value(0.0), + "Tolerance (in Hz) for digital CFO compensation (needs to be low if average_subframe_enabled=true.") - ("expert.cfo_ema", - bpo::value(&args->expert.phy.cfo_ema)->default_value(0.4), - "CFO Exponential Moving Average coefficient. Lower makes it more robust to noise " - "but vulnerable to periodic interruptions due to VCO corrections.") + ("expert.cfo_pss_ema", + bpo::value(&args->expert.phy.cfo_pss_ema)->default_value(0.1), + "CFO Exponential Moving Average coefficient for PSS estimation during TRACK.") + + ("expert.cfo_ref_ema", + bpo::value(&args->expert.phy.cfo_ref_ema)->default_value(0.01), + "CFO Exponential Moving Average coefficient for RS estimation after PSS acquisition") + + ("expert.cfo_ref_mask", + bpo::value(&args->expert.phy.cfo_ref_mask)->default_value(1023), + "Bitmask for subframes on which to run RS estimation (set to 0 to disable, default all sf)") + + ("expert.cfo_loop_bw_pss", + bpo::value(&args->expert.phy.cfo_loop_bw_pss)->default_value(0.05), + "CFO feedback loop bandwidth for samples from PSS") + + ("expert.cfo_loop_bw_ref", + bpo::value(&args->expert.phy.cfo_loop_bw_ref)->default_value(0.01), + "CFO feedback loop bandwidth for samples from RS") + + ("expert.cfo_loop_pss_tol", + bpo::value(&args->expert.phy.cfo_loop_pss_tol)->default_value(300), + "Tolerance (in Hz) of the PSS estimation method. Below this value, PSS estimation does not feeds back the loop" + "and RS estimations are used instead (when available)") + + ("expert.cfo_loop_ref_min", + bpo::value(&args->expert.phy.cfo_loop_ref_min)->default_value(0), + "Tolerance (in Hz) of the RS estimation method. Below this value, RS estimation does not feeds back the loop") + + + ("expert.cfo_loop_pss_conv", + bpo::value(&args->expert.phy.cfo_loop_pss_conv)->default_value(50), + "After the PSS estimation is below cfo_loop_pss_tol for cfo_loop_pss_timeout times consecutively, RS adjustments are allowed.") + + ("expert.average_subframe_enabled", + bpo::value(&args->expert.phy.average_subframe_enabled)->default_value(true), + "Averages in the time domain the channel estimates within 1 subframe. Needs accurate CFO correction.") ("expert.time_correct_period", bpo::value(&args->expert.phy.time_correct_period)->default_value(5), diff --git a/srsue/src/phy/phch_recv.cc b/srsue/src/phy/phch_recv.cc index 37f7970ee..652a848bd 100644 --- a/srsue/src/phy/phch_recv.cc +++ b/srsue/src/phy/phch_recv.cc @@ -98,9 +98,6 @@ void phch_recv:: init(srslte::radio_multi *_radio_handler, mac_interface_phy *_ srslte_ue_cellsearch_set_nof_valid_frames(&cs, 2); - // Set options defined in expert section - set_ue_sync_opts(&cs.ue_sync); - last_gain = 40; if (do_agc) { srslte_ue_sync_start_agc(&cs.ue_sync, callback_set_rx_gain, last_gain); @@ -184,6 +181,11 @@ void phch_recv::radio_error() { radio_is_resetting=false; } +void phch_recv::set_cfo(float cfo) { + Debug("set_ref_cfo=%f\n",cfo*15000); + srslte_ue_sync_set_cfo_ref(&ue_sync, cfo); +} + bool phch_recv::wait_radio_reset() { int cnt=0; while(cnt < 20 && radio_is_resetting) { @@ -211,8 +213,13 @@ void phch_recv::set_ue_sync_opts(srslte_ue_sync_t *q) { srslte_ue_sync_set_cfo_i_enable(q, true); } - srslte_ue_sync_set_cfo_ema(q, worker_com->args->cfo_ema); + srslte_ue_sync_set_cfo_ema(q, worker_com->args->cfo_pss_ema); srslte_ue_sync_set_cfo_tol(q, worker_com->args->cfo_correct_tol_hz); + srslte_ue_sync_set_cfo_loop_bw(q, worker_com->args->cfo_loop_bw_pss, worker_com->args->cfo_loop_bw_ref, + worker_com->args->cfo_loop_pss_tol, + worker_com->args->cfo_loop_ref_min, + worker_com->args->cfo_loop_pss_tol, + worker_com->args->cfo_loop_pss_conv); int time_correct_period = worker_com->args->time_correct_period; if (time_correct_period > 0) { @@ -320,15 +327,13 @@ int phch_recv::cell_search(int force_N_id_2) { return false; } - // Set options defined in expert section - set_ue_sync_opts(&ue_mib_sync.ue_sync); - if (do_agc) { srslte_ue_sync_start_agc(&ue_mib_sync.ue_sync, callback_set_rx_gain, last_gain); } srslte_ue_sync_reset(&ue_mib_sync.ue_sync); srslte_ue_sync_copy_cfo(&ue_mib_sync.ue_sync, &cs.ue_sync); + Info("Copied cfo=%f Hz from cs_sync to ue_mib\n", srslte_ue_sync_get_cfo(&ue_mib_sync.ue_sync)); /* Find and decode MIB */ int sfn_offset; @@ -341,6 +346,7 @@ int phch_recv::cell_search(int force_N_id_2) { srslte_ue_sync_reset(&ue_sync); srslte_ue_sync_copy_cfo(&ue_sync, &ue_mib_sync.ue_sync); + Info("Copied cfo=%f Hz from ue_mib_sync to ue_sync\n", srslte_ue_sync_get_cfo(&ue_sync)); if (ret == 1) { srslte_pbch_mib_unpack(bch_payload, &cell, NULL); @@ -731,6 +737,9 @@ void phch_recv::run_thread() { worker->set_cfo(ul_dl_factor * metrics.cfo / 15000); worker_com->set_sync_metrics(metrics); + Debug("current_cfo=%f, pss_stable_cnt=%d, cfo_pss=%f Hz\n", + metrics.cfo, ue_sync.pss_stable_cnt, srslte_sync_get_cfo(&ue_sync.strack)*15000); + worker->set_sample_offset(srslte_ue_sync_get_sfo(&ue_sync)/1000); /* Compute TX time: Any transmission happens in TTI+4 thus advance 4 ms the reception time */ diff --git a/srsue/src/phy/phch_worker.cc b/srsue/src/phy/phch_worker.cc index 7b4ec48bd..fefa7d250 100644 --- a/srsue/src/phy/phch_worker.cc +++ b/srsue/src/phy/phch_worker.cc @@ -57,7 +57,9 @@ namespace srsue { phch_worker::phch_worker() : tr_exec(10240) { - phy = NULL; + phy = NULL; + chest_loop = NULL; + bzero(signal_buffer, sizeof(cf_t*)*SRSLTE_MAX_PORTS); mem_initiated = false; @@ -104,9 +106,11 @@ void phch_worker::set_common(phch_common* phy_) phy = phy_; } -bool phch_worker::init(uint32_t max_prb, srslte::log *log_h) +bool phch_worker::init(uint32_t max_prb, srslte::log *log_h, chest_feedback_itf *chest_loop) { this->log_h = log_h; + this->chest_loop = chest_loop; + // ue_sync in phy.cc requires a buffer for 3 subframes for (uint32_t i=0;iargs->nof_rx_ant;i++) { signal_buffer[i] = (cf_t*) srslte_vec_malloc(3 * sizeof(cf_t) * SRSLTE_SF_LEN_PRB(max_prb)); @@ -126,6 +130,8 @@ bool phch_worker::init(uint32_t max_prb, srslte::log *log_h) return false; } + srslte_chest_dl_average_subframe(&ue_dl.chest, phy->args->average_subframe_enabled); + srslte_chest_dl_cfo_estimate_enable(&ue_dl.chest, phy->args->cfo_ref_mask!=0, phy->args->cfo_ref_mask, phy->args->cfo_ref_ema); srslte_ue_ul_set_normalization(&ue_ul, true); srslte_ue_ul_set_cfo_enable(&ue_ul, true); @@ -134,6 +140,10 @@ bool phch_worker::init(uint32_t max_prb, srslte::log *log_h) return true; } +float phch_worker::get_ref_cfo() { + return srslte_chest_dl_get_cfo(&ue_dl.chest); +} + bool phch_worker::set_cell(srslte_cell_t cell_) { if (cell.id != cell_.id || !cell_initiated) { @@ -221,6 +231,11 @@ void phch_worker::work_imp() bool snr_th_ok = 10*log10(srslte_chest_dl_get_snr(&ue_dl.chest))>1.0; + // Call feedback loop for chest + if (chest_loop && ((1<<(tti%10)) & phy->args->cfo_ref_mask)) { + chest_loop->set_cfo(srslte_chest_dl_get_cfo(&ue_dl.chest)); + } + if (chest_ok && snr_th_ok) { /***** Downlink Processing *******/ @@ -394,11 +409,8 @@ void phch_worker::work_imp() bool phch_worker::extract_fft_and_pdcch_llr() { - bool decode_pdcch = false; - if (phy->get_ul_rnti(tti) || phy->get_dl_rnti(tti) || phy->get_pending_rar(tti)) { - decode_pdcch = true; - } - + bool decode_pdcch = true; + /* Without a grant, we might need to do fft processing if need to decode PHICH */ if (phy->get_pending_ack(tti) || decode_pdcch) { @@ -1213,13 +1225,18 @@ int phch_worker::read_ce_abs(float *ce_abs) { int sz = srslte_symbol_sz(cell.nof_prb); bzero(ce_abs, sizeof(float)*sz); int g = (sz - 12*cell.nof_prb)/2; - for (i = 0; i < 12*cell.nof_prb; i++) { +/* for (i = 0; i < 12*cell.nof_prb; i++) { ce_abs[g+i] = 20 * log10f(cabsf(ue_dl.ce_m[0][0][i])); if (isinf(ce_abs[g+i])) { ce_abs[g+i] = -80; } } - return sz; +*/ + uint32_t nrefs = 2*ue_dl.cell.nof_prb; + for (i=0;iavg_snr_db = 10*log10(phy->avg_rsrp/phy->avg_noise); + phy->avg_snr_db = 10*log10(phy->avg_rsrp/phy->avg_noise); // Store metrics dl_metrics.n = phy->avg_noise; @@ -1371,7 +1388,7 @@ void *plot_thread_run(void *arg) { plot_real_init(&pce); plot_real_setTitle(&pce, (char*) "Channel Response - Magnitude"); plot_real_setLabels(&pce, (char*) "Index", (char*) "dB"); - plot_real_setYAxisScale(&pce, -40, 40); + plot_real_setYAxisScale(&pce, -1000, 1000); plot_scatter_init(&pconst); plot_scatter_setTitle(&pconst, (char*) "PDSCH - Equalized Symbols"); diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc index 6dfb2c6d7..0daecf0de 100644 --- a/srsue/src/phy/phy.cc +++ b/srsue/src/phy/phy.cc @@ -37,7 +37,6 @@ #include "srslte/common/threads.h" #include "srslte/common/log.h" #include "phy/phy.h" -#include "phy/phch_worker.h" #define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) #define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) @@ -134,7 +133,7 @@ void phy::run_thread() { // Add workers to workers pool and start threads for (uint32_t i=0;iworker_cpu_mask); } diff --git a/srsue/ue.conf.example b/srsue/ue.conf.example index 867221e87..7bd6fae18 100644 --- a/srsue/ue.conf.example +++ b/srsue/ue.conf.example @@ -137,13 +137,7 @@ enable = false # equalizer_mode: Selects equalizer mode. Valid modes are: "mmse", "zf" or any # non-negative real number to indicate a regularized zf coefficient. # Default is MMSE. -# cfo_ema: CFO Exponential Moving Average coefficient. Lower makes it more robust to noise -# but vulnerable to periodic interruptions due to VCO corrections. -# cfo_integer_enabled: Enables integer CFO estimation and correction. This needs improvement -# and may lead to incorrect synchronization. Use with caution. -# cfo_correct_tol_hz: Tolerance (in Hz) for digial CFO compensation. Lower tolerance means that -# a new table will be generated more often. -# time_correct_period: Period for sampling time offset correction. Default is 10 (ue_sync.c), +# time_correct_period: Period for sampling time offset correction. Default is 10 (ue_sync.c), # good for long channels. For best performance at highest SNR reduce it to 1. # sfo_correct_disable: Disables phase correction before channel estimation to compensate for # sampling frequency offset. Default is enabled. @@ -155,10 +149,28 @@ enable = false # # pregenerate_signals: Pregenerate uplink signals after attach. Improves CPU performance. # +# average_subframe_enabled: Averages in the time domain the channel estimates within 1 subframe. +# Needs accurate CFO correction. +# # metrics_csv_enable: Write UE metrics to CSV file. # # metrics_csv_filename: File path to use for CSV metrics. # +# cfo_integer_enabled: Enables integer CFO estimation and correction. This needs improvement +# and may lead to incorrect synchronization. Use with caution. +# cfo_correct_tol_hz: Tolerance (in Hz) for digial CFO compensation. Lower tolerance means that +# a new table will be generated more often. +# +# cfo_pss_ema: CFO Exponential Moving Average coefficient for PSS estimation during TRACK. +# cfo_ref_ema: CFO Exponential Moving Average coefficient for RS estimation after PSS acquisition +# cfo_ref_mask: Bitmask for subframes on which to run RS estimation (set to 0 to disable, default sf=[1, 5]) +# cfo_loop_bw: CFO feedback loop bandwidth for samples from PSS or RS +# cfo_loop_pss_tol: Tolerance (in Hz) of the PSS estimation method. Below this value, PSS estimation does not feeds back the loop +# and RS estimations are used instead (when available) +# cfo_loop_ref_min: Tolerance (in Hz) of the RS estimation method. Below this value, RS estimation does not feeds back the loop +# cfo_loop_pss_timeout: After the PSS estimation is below cfo_loop_pss_tol for cfo_loop_pss_timeout times consecutively, +# RS adjustments are allowed. +# ##################################################################### [expert] #ip_netmask = 255.255.255.0 @@ -172,17 +184,27 @@ enable = false #attach_enable_64qam = false #nof_phy_threads = 2 #equalizer_mode = mmse -#cfo_ema = 0.4 -#cfo_integer_enabled = false -#cfo_correct_tol_hz = 50 #time_correct_period = 5 #sfo_correct_disable = false #sss_algorithm = full #estimator_fil_w = 0.1 +#average_subframe_enabled = true #pregenerate_signals = false #metrics_csv_enable = false #metrics_csv_filename = /tmp/ue_metrics.csv +# CFO related values +#cfo_integer_enabled = false +#cfo_correct_tol_hz = 0 +#cfo_pss_ema = 0.1 +#cfo_ref_ema = 0.01 +#cfo_ref_mask = 1023 +#cfo_loop_bw_pss = 0.05 +#cfo_loop_bw_ref = 0.01 +#cfo_loop_pss_tol = 300 +#cfo_loop_ref_min = 0 +#cfo_loop_pss_conv = 50 + ##################################################################### # Manual RF calibration #