diff --git a/srsue/hdr/phy/async_scell_recv.h b/srsue/hdr/phy/async_scell_recv.h index d2703621a..0733c3158 100644 --- a/srsue/hdr/phy/async_scell_recv.h +++ b/srsue/hdr/phy/async_scell_recv.h @@ -58,6 +58,8 @@ public: void out_of_sync(); void set_cfo(float cfo); + float get_tx_cfo(); + // From UE configuration void set_agc_enable(bool enable); bool set_scell_cell(uint32_t carrier_idx, srslte_cell_t* _cell, uint32_t dl_earfcn); @@ -68,7 +70,7 @@ public: double set_rx_gain(double gain); int radio_recv_fnc(cf_t* data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t* rx_time); bool tti_align(uint32_t tti); - void read_sf(cf_t** dst, srslte_timestamp_t* timestamp); + void read_sf(cf_t** dst, srslte_timestamp_t* timestamp, int* next_offset); private: class phch_scell_recv_buffer @@ -76,12 +78,14 @@ private: private: uint32_t tti; srslte_timestamp_t timestamp; + int next_offset; cf_t* buffer[SRSLTE_MAX_PORTS]; public: phch_scell_recv_buffer() { tti = 0; + next_offset = 0; bzero(×tamp, sizeof(timestamp)); for (cf_t*& b : buffer) { b = nullptr; @@ -108,9 +112,10 @@ private: } } - void set_sf(uint32_t _tti, srslte_timestamp_t* _timestamp) + void set_sf(uint32_t _tti, srslte_timestamp_t* _timestamp, const int& _next_offset) { tti = _tti; + next_offset = _next_offset; srslte_timestamp_copy(×tamp, _timestamp); } @@ -119,6 +124,11 @@ private: cf_t** get_buffer_ptr() { return buffer; } void get_timestamp(srslte_timestamp_t* _timestamp) { srslte_timestamp_copy(_timestamp, ×tamp); } + void get_next_offset(int* _next_offset) + { + if (_next_offset) + *_next_offset = next_offset; + } }; static const uint32_t ASYNC_NOF_BUFFERS = SRSLTE_NOF_SF_X_FRAME; @@ -165,6 +175,8 @@ private: uint32_t in_sync_cnt; cf_t* sf_buffer[SRSLTE_MAX_PORTS]; + uint32_t current_sflen; + int next_radio_offset; const static uint32_t NOF_OUT_OF_SYNC_SF = 200; const static uint32_t NOF_IN_SYNC_SF = 100; diff --git a/srsue/hdr/phy/phy_common.h b/srsue/hdr/phy/phy_common.h index bc866fb96..bf518ee10 100644 --- a/srsue/hdr/phy/phy_common.h +++ b/srsue/hdr/phy/phy_common.h @@ -150,8 +150,8 @@ public: void get_dl_metrics(dl_metrics_t m[SRSLTE_MAX_CARRIERS]); void set_ul_metrics(const ul_metrics_t m, uint32_t cc_idx); void get_ul_metrics(ul_metrics_t m[SRSLTE_MAX_CARRIERS]); - void set_sync_metrics(const sync_metrics_t& m); - void get_sync_metrics(sync_metrics_t& m); + void set_sync_metrics(const uint32_t& cc_idx, const sync_metrics_t& m); + void get_sync_metrics(sync_metrics_t m[SRSLTE_MAX_CARRIERS]); void reset(); @@ -223,7 +223,7 @@ private: ul_metrics_t ul_metrics[SRSLTE_MAX_CARRIERS]; uint32_t ul_metrics_count; bool ul_metrics_read; - sync_metrics_t sync_metrics; + sync_metrics_t sync_metrics[SRSLTE_MAX_CARRIERS]; uint32_t sync_metrics_count; bool sync_metrics_read; diff --git a/srsue/hdr/phy/phy_metrics.h b/srsue/hdr/phy/phy_metrics.h index fdde3fe09..0ca6341fe 100644 --- a/srsue/hdr/phy/phy_metrics.h +++ b/srsue/hdr/phy/phy_metrics.h @@ -55,7 +55,7 @@ struct ul_metrics_t struct phy_metrics_t { - sync_metrics_t sync; + sync_metrics_t sync[SRSLTE_MAX_CARRIERS]; dl_metrics_t dl[SRSLTE_MAX_CARRIERS]; ul_metrics_t ul[SRSLTE_MAX_CARRIERS]; uint32_t nof_active_cc; diff --git a/srsue/hdr/phy/sf_worker.h b/srsue/hdr/phy/sf_worker.h index d85295527..77bd39082 100644 --- a/srsue/hdr/phy/sf_worker.h +++ b/srsue/hdr/phy/sf_worker.h @@ -53,7 +53,7 @@ public: void set_tti(uint32_t tti, uint32_t tx_worker_cnt); void set_tx_time(uint32_t radio_idx, srslte_timestamp_t tx_time, int next_offset); void set_prach(cf_t* prach_ptr, float prach_power); - void set_cfo(float cfo); + void set_cfo(const uint32_t& cc_idx, float cfo); void set_tdd_config(srslte_tdd_config_t config); void set_pcell_config(phy_interface_rrc_lte::phy_cfg_t* phy_cfg); diff --git a/srsue/hdr/phy/sync.h b/srsue/hdr/phy/sync.h index ffc9f4fed..66b1020b1 100644 --- a/srsue/hdr/phy/sync.h +++ b/srsue/hdr/phy/sync.h @@ -269,7 +269,8 @@ private: intra_measure intra_freq_meas; uint32_t current_sflen; - int next_offset; + int next_offset; // Sample offset triggered by Time aligment commands + int next_radio_offset[SRSLTE_MAX_RADIOS]; // Sample offset triggered by SFO compensation // Pointers to other classes stack_interface_phy_lte* stack; diff --git a/srsue/src/metrics_csv.cc b/srsue/src/metrics_csv.cc index d969bdb69..e8de1280f 100644 --- a/srsue/src/metrics_csv.cc +++ b/srsue/src/metrics_csv.cc @@ -79,7 +79,7 @@ void metrics_csv::set_metrics(ue_metrics_t &metrics, const uint32_t period_usec) // Print PHY metrics for first CC file << float_to_string(metrics.phy.dl[0].rsrp, 2); file << float_to_string(metrics.phy.dl[0].pathloss, 2); - file << float_to_string(metrics.phy.sync.cfo, 2); + file << float_to_string(metrics.phy.sync[0].cfo, 2); file << float_to_string(metrics.phy.dl[0].mcs, 2); file << float_to_string(metrics.phy.dl[0].sinr, 2); file << float_to_string(metrics.phy.dl[0].turbo_iters, 2); @@ -104,7 +104,7 @@ void metrics_csv::set_metrics(ue_metrics_t &metrics, const uint32_t period_usec) file << float_to_string(0, 2); } - file << float_to_string(metrics.phy.sync.ta_us, 2); + file << float_to_string(metrics.phy.sync[0].ta_us, 2); file << float_to_string(metrics.phy.ul[0].mcs, 2); file << float_to_string((float)metrics.stack.mac[0].ul_buffer, 2); diff --git a/srsue/src/metrics_stdout.cc b/srsue/src/metrics_stdout.cc index 094346710..f59f859d6 100644 --- a/srsue/src/metrics_stdout.cc +++ b/srsue/src/metrics_stdout.cc @@ -79,7 +79,7 @@ void metrics_stdout::set_metrics(ue_metrics_t &metrics, const uint32_t period_us cout << " " << r; cout << float_to_string(metrics.phy.dl[r].rsrp, 2); cout << float_to_string(metrics.phy.dl[r].pathloss, 2); - cout << float_to_eng_string(metrics.phy.sync.cfo, 2); + cout << float_to_eng_string(metrics.phy.sync[r].cfo, 2); cout << float_to_string(metrics.phy.dl[r].mcs, 2); cout << float_to_string(metrics.phy.dl[r].sinr, 2); cout << float_to_string(metrics.phy.dl[r].turbo_iters, 2); @@ -91,7 +91,7 @@ void metrics_stdout::set_metrics(ue_metrics_t &metrics, const uint32_t period_us cout << float_to_string(0, 1) << "%"; } - cout << float_to_string(metrics.phy.sync.ta_us, 2); + cout << float_to_string(metrics.phy.sync[r].ta_us, 2); cout << float_to_string(metrics.phy.ul[r].mcs, 2); cout << float_to_eng_string((float)metrics.stack.mac[r].ul_buffer, 2); diff --git a/srsue/src/phy/async_scell_recv.cc b/srsue/src/phy/async_scell_recv.cc index 3dc17081f..0a5ba389c 100644 --- a/srsue/src/phy/async_scell_recv.cc +++ b/srsue/src/phy/async_scell_recv.cc @@ -56,6 +56,8 @@ async_scell_recv::async_scell_recv() : thread() bzero(sf_buffer, sizeof(sf_buffer)); running = false; radio_idx = 1; + current_sflen = 0; + next_radio_offset = 0; } async_scell_recv::~async_scell_recv() @@ -160,6 +162,25 @@ void async_scell_recv::set_cfo(float cfo) srslte_ue_sync_set_cfo_ref(&ue_sync, cfo); } +float async_scell_recv::get_tx_cfo() +{ + float cfo = srslte_ue_sync_get_cfo(&ue_sync); + + float ret = cfo * ul_dl_factor; + + if (worker_com->args->cfo_is_doppler) { + ret *= -1; + } else { + /* Compensates the radio frequency offset applied equally to DL and UL. Does not work in doppler mode */ + if (radio_h->get_freq_offset() != 0.0f) { + const float offset_hz = (float)radio_h->get_freq_offset() * (1.0f - ul_dl_factor); + ret = cfo - offset_hz; + } + } + + return ret / 15000; +} + void async_scell_recv::set_agc_enable(bool enable) { do_agc = enable; @@ -187,6 +208,13 @@ int async_scell_recv::radio_recv_fnc(cf_t* data[SRSLTE_MAX_PORTS], uint32_t nsam if (running) { if (radio_h->rx_now(radio_idx, data, nsamples, rx_time)) { + int offset = nsamples - current_sflen; + if (abs(offset) < 10 && offset != 0) { + next_radio_offset += offset; + } else if (nsamples < 10) { + next_radio_offset += nsamples; + } + log_h->debug("SYNC: received %d samples from radio\n", nsamples); ret = nsamples; } else { @@ -288,6 +316,8 @@ bool async_scell_recv::set_scell_cell(uint32_t carrier_idx, srslte_cell_t* _cell double srate = srslte_sampling_freq_hz(_cell->nof_prb); radio_h->set_rx_srate(radio_idx, srate); radio_h->set_tx_srate(radio_idx, srate); + current_sflen = (uint32_t)SRSLTE_SF_LEN_PRB(_cell->nof_prb); + Info("Setting SRate to %.2f MHz\n", srate / 1e6); } @@ -385,7 +415,8 @@ void async_scell_recv::state_write_buffer() srslte_ue_sync_get_last_timestamp(&ue_sync, &rx_time); // Extract essential information - buffer->set_sf(tti, &rx_time); + buffer->set_sf(tti, &rx_time, next_radio_offset); + next_radio_offset = 0; // Increment write index buffer_write_idx = (buffer_write_idx + 1) % ASYNC_NOF_BUFFERS; @@ -448,6 +479,17 @@ void async_scell_recv::run_thread() break; } + // Load metrics + sync_metrics_t metrics = {}; + metrics.sfo = srslte_ue_sync_get_sfo(&ue_sync); + metrics.cfo = srslte_ue_sync_get_cfo(&ue_sync); + metrics.ta_us = NAN; + for (uint32_t i = 0; i < worker_com->args->nof_carriers; i++) { + if (worker_com->args->carrier_map[i].radio_idx == radio_idx) { + worker_com->set_sync_metrics(i, metrics); + } + } + // Increment tti tti = (tti + 1) % 10240; } else if (ret == 0) { @@ -533,7 +575,7 @@ bool async_scell_recv::tti_align(uint32_t tti) return ret; } -void async_scell_recv::read_sf(cf_t** dst, srslte_timestamp_t* timestamp) +void async_scell_recv::read_sf(cf_t** dst, srslte_timestamp_t* timestamp, int* next_offset) { pthread_mutex_lock(&mutex_buffer); @@ -567,6 +609,7 @@ void async_scell_recv::read_sf(cf_t** dst, srslte_timestamp_t* timestamp) } buffer->get_timestamp(timestamp); + buffer->get_next_offset(next_offset); // Increment read index buffer_read_idx = (buffer_read_idx + 1) % ASYNC_NOF_BUFFERS; diff --git a/srsue/src/phy/phy_common.cc b/srsue/src/phy/phy_common.cc index 262b39f8f..0978d1ed3 100644 --- a/srsue/src/phy/phy_common.cc +++ b/srsue/src/phy/phy_common.cc @@ -68,7 +68,7 @@ phy_common::phy_common(uint32_t max_workers) : tx_sem(max_workers) bzero(&ul_metrics, sizeof(ul_metrics_t) * SRSLTE_MAX_CARRIERS); ul_metrics_read = true; ul_metrics_count = 0; - bzero(&sync_metrics, sizeof(sync_metrics_t)); + ZERO_OBJECT(sync_metrics); sync_metrics_read = true; sync_metrics_count = 0; @@ -643,23 +643,26 @@ void phy_common::get_ul_metrics(ul_metrics_t m[SRSLTE_MAX_RADIOS]) ul_metrics_read = true; } -void phy_common::set_sync_metrics(const sync_metrics_t& m) +void phy_common::set_sync_metrics(const uint32_t& cc_idx, const sync_metrics_t& m) { - if (sync_metrics_read) { - sync_metrics = m; + sync_metrics[cc_idx] = m; sync_metrics_count = 1; - sync_metrics_read = false; + if (cc_idx == 0) + sync_metrics_read = false; } else { - sync_metrics_count++; - sync_metrics.cfo = sync_metrics.cfo + (m.cfo - sync_metrics.cfo) / sync_metrics_count; - sync_metrics.sfo = sync_metrics.sfo + (m.sfo - sync_metrics.sfo) / sync_metrics_count; + if (cc_idx == 0) + sync_metrics_count++; + sync_metrics[cc_idx].cfo = sync_metrics[cc_idx].cfo + (m.cfo - sync_metrics[cc_idx].cfo) / sync_metrics_count; + sync_metrics[cc_idx].sfo = sync_metrics[cc_idx].sfo + (m.sfo - sync_metrics[cc_idx].sfo) / sync_metrics_count; } } -void phy_common::get_sync_metrics(sync_metrics_t& m) +void phy_common::get_sync_metrics(sync_metrics_t m[SRSLTE_MAX_CARRIERS]) { - m = sync_metrics; + for (uint32_t i = 0; i < args->nof_carriers; i++) { + m[i] = sync_metrics[i]; + } sync_metrics_read = true; } diff --git a/srsue/src/phy/sf_worker.cc b/srsue/src/phy/sf_worker.cc index 2c481cbec..caedf3160 100644 --- a/srsue/src/phy/sf_worker.cc +++ b/srsue/src/phy/sf_worker.cc @@ -158,11 +158,9 @@ void sf_worker::set_prach(cf_t* prach_ptr, float prach_power) this->prach_power = prach_power; } -void sf_worker::set_cfo(float cfo) +void sf_worker::set_cfo(const uint32_t& cc_idx, float cfo) { - for (uint32_t cc_idx = 0; cc_idx < cc_workers.size(); cc_idx++) { - cc_workers[cc_idx]->set_cfo(cfo); - } + cc_workers[cc_idx]->set_cfo(cfo); } void sf_worker::set_crnti(uint16_t rnti) @@ -402,9 +400,9 @@ float sf_worker::get_sync_error() float sf_worker::get_cfo() { - sync_metrics_t sync_metrics = {}; + sync_metrics_t sync_metrics[SRSLTE_MAX_CARRIERS] = {}; phy->get_sync_metrics(sync_metrics); - return sync_metrics.cfo; + return sync_metrics[0].cfo; } } // namespace srsue diff --git a/srsue/src/phy/sync.cc b/srsue/src/phy/sync.cc index d81f59b7e..c13f8ece1 100644 --- a/srsue/src/phy/sync.cc +++ b/srsue/src/phy/sync.cc @@ -147,6 +147,7 @@ void sync::reset() tx_worker_cnt = 0; time_adv_sec = 0; next_offset = 0; + ZERO_OBJECT(next_radio_offset); srate_mode = SRATE_NONE; current_earfcn = -1; sfn_p.reset(); @@ -477,20 +478,24 @@ void sync::run_thread() // Request TTI aligment if (scell_sync->at(i)->tti_align(tti)) { - scell_sync->at(i)->read_sf(buffer[i + 1], &tx_time); + scell_sync->at(i)->read_sf(buffer[i + 1], &tx_time, &next_radio_offset[i + 1]); srslte_timestamp_add(&tx_time, 0, TX_DELAY * 1e-3 - time_adv_sec); } else { // Failed, keep default Timestamp // Error("SCell asynchronous failed to synchronise (%d)\n", i); } - worker->set_tx_time(i + 1, tx_time, next_offset); + worker->set_tx_time(i + 1, tx_time, next_radio_offset[i + 1] + next_offset); } metrics.sfo = srslte_ue_sync_get_sfo(&ue_sync); metrics.cfo = srslte_ue_sync_get_cfo(&ue_sync); - metrics.ta_us = time_adv_sec*1e6; - worker_com->set_sync_metrics(metrics); + metrics.ta_us = time_adv_sec * 1e6f; + for (uint32_t i = 0; i < worker_com->args->nof_carriers; i++) { + if (worker_com->args->carrier_map[i].radio_idx == 0) { + worker_com->set_sync_metrics(i, metrics); + } + } // Check if we need to TX a PRACH if (prach_buffer->is_ready_to_send(tti)) { @@ -511,10 +516,31 @@ void sync::run_thread() } worker->set_prach(prach_ptr?&prach_ptr[prach_sf_cnt*SRSLTE_SF_LEN_PRB(cell.nof_prb)]:NULL, prach_power); - worker->set_cfo(get_tx_cfo()); + + // Set CFO for all Carriers + for (uint32_t cc = 0; cc < worker_com->args->nof_carriers; cc++) { + float cfo; + + // Get radio index for the given carrier + uint32_t radio_idx = worker_com->args->carrier_map[cc].radio_idx; + + if (radio_idx == 0) { + // Use local CFO + cfo = get_tx_cfo(); + } else { + // Request CFO in the asynchronous receiver + cfo = scell_sync->at(radio_idx - 1)->get_tx_cfo(); + } + + worker->set_cfo(cc, cfo); + } + worker->set_tti(tti, tx_worker_cnt); - worker->set_tx_time(0, tx_time, next_offset); + worker->set_tx_time(0, tx_time, next_radio_offset[0] + next_offset); next_offset = 0; + ZERO_OBJECT(next_radio_offset); + + // Process time aligment command if (next_time_adv_sec != time_adv_sec) { time_adv_sec = next_time_adv_sec; } @@ -867,7 +893,7 @@ bool sync::set_frequency() void sync::set_sampling_rate() { float new_srate = (float)srslte_sampling_freq_hz(cell.nof_prb); - current_sflen = SRSLTE_SF_LEN_PRB(cell.nof_prb); + current_sflen = (uint32_t)SRSLTE_SF_LEN_PRB(cell.nof_prb); if (current_srate != new_srate || srate_mode != SRATE_CAMP) { current_srate = new_srate; Info("SYNC: Setting sampling rate %.2f MHz\n", current_srate/1000000); @@ -900,9 +926,9 @@ int sync::radio_recv_fnc(cf_t* data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte if (radio_h->rx_now(0, data, nsamples, rx_time)) { int offset = nsamples - current_sflen; if (abs(offset) < 10 && offset != 0) { - next_offset += offset; + next_radio_offset[0] = offset; } else if (nsamples < 10) { - next_offset += nsamples; + next_radio_offset[0] = nsamples; } log_h->debug("SYNC: received %d samples from radio\n", nsamples);