|
|
|
/**
|
|
|
|
*
|
|
|
|
* \section COPYRIGHT
|
|
|
|
*
|
|
|
|
* Copyright 2013-2020 Software Radio Systems Limited
|
|
|
|
*
|
|
|
|
* By using this file, you agree to the terms and conditions set
|
|
|
|
* forth in the LICENSE file which can be found at the top level of
|
|
|
|
* the distribution.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "srsenb/hdr/phy/prach_worker.h"
|
|
|
|
#include "srslte/srslte.h"
|
|
|
|
|
|
|
|
namespace srsenb {
|
|
|
|
|
|
|
|
int prach_worker::init(const srslte_cell_t& cell_,
|
|
|
|
const srslte_prach_cfg_t& prach_cfg_,
|
|
|
|
stack_interface_phy_lte* stack_,
|
|
|
|
srslte::log* log_h_,
|
|
|
|
int priority)
|
|
|
|
{
|
|
|
|
log_h = log_h_;
|
|
|
|
stack = stack_;
|
|
|
|
prach_cfg = prach_cfg_;
|
|
|
|
cell = cell_;
|
|
|
|
|
|
|
|
max_prach_offset_us = 50;
|
|
|
|
|
|
|
|
if (srslte_prach_init(&prach, srslte_symbol_sz(cell.nof_prb))) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (srslte_prach_set_cfg(&prach, &prach_cfg, cell.nof_prb)) {
|
|
|
|
ERROR("Error initiating PRACH\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
srslte_prach_set_detect_factor(&prach, 60);
|
|
|
|
|
|
|
|
nof_sf = (uint32_t)ceilf(prach.T_tot * 1000);
|
|
|
|
|
|
|
|
start(priority);
|
|
|
|
initiated = true;
|
|
|
|
|
|
|
|
sf_cnt = 0;
|
|
|
|
|
|
|
|
#if defined(ENABLE_GUI) and ENABLE_PRACH_GUI
|
|
|
|
char title[32] = {};
|
|
|
|
snprintf(title, sizeof(title), "PRACH buffer %d", cc_idx);
|
|
|
|
|
|
|
|
sdrgui_init();
|
|
|
|
plot_real_init(&plot_real);
|
|
|
|
plot_real_setTitle(&plot_real, title);
|
|
|
|
plot_real_setXAxisAutoScale(&plot_real, true);
|
|
|
|
plot_real_setYAxisAutoScale(&plot_real, true);
|
|
|
|
plot_real_addToWindowGrid(&plot_real, (char*)"PRACH", 0, cc_idx);
|
|
|
|
#endif // defined(ENABLE_GUI) and ENABLE_PRACH_GUI
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void prach_worker::stop()
|
|
|
|
{
|
|
|
|
running = false;
|
|
|
|
sf_buffer* s = nullptr;
|
|
|
|
pending_buffers.push(s);
|
|
|
|
wait_thread_finish();
|
|
|
|
|
|
|
|
srslte_prach_free(&prach);
|
|
|
|
}
|
|
|
|
|
|
|
|
void prach_worker::set_max_prach_offset_us(float delay_us)
|
|
|
|
{
|
|
|
|
max_prach_offset_us = delay_us;
|
|
|
|
}
|
|
|
|
|
|
|
|
int prach_worker::new_tti(uint32_t tti_rx, cf_t* buffer_rx)
|
|
|
|
{
|
|
|
|
// Save buffer only if it's a PRACH TTI
|
|
|
|
if (srslte_prach_tti_opportunity(&prach, tti_rx, -1) || sf_cnt) {
|
|
|
|
if (sf_cnt == 0) {
|
|
|
|
current_buffer = buffer_pool.allocate();
|
|
|
|
if (!current_buffer) {
|
|
|
|
log_h->warning("PRACH skipping tti=%d due to lack of available buffers\n", tti_rx);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!current_buffer) {
|
|
|
|
log_h->error("PRACH: Expected available current_buffer\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (current_buffer->nof_samples + SRSLTE_SF_LEN_PRB(cell.nof_prb) < sf_buffer_sz) {
|
|
|
|
memcpy(¤t_buffer->samples[sf_cnt * SRSLTE_SF_LEN_PRB(cell.nof_prb)],
|
|
|
|
buffer_rx,
|
|
|
|
sizeof(cf_t) * SRSLTE_SF_LEN_PRB(cell.nof_prb));
|
|
|
|
current_buffer->nof_samples += SRSLTE_SF_LEN_PRB(cell.nof_prb);
|
|
|
|
if (sf_cnt == 0) {
|
|
|
|
current_buffer->tti = tti_rx;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log_h->error("PRACH: Not enough space in current_buffer\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
sf_cnt++;
|
|
|
|
if (sf_cnt == nof_sf) {
|
|
|
|
sf_cnt = 0;
|
|
|
|
pending_buffers.push(current_buffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int prach_worker::run_tti(sf_buffer* b)
|
|
|
|
{
|
|
|
|
if (srslte_prach_tti_opportunity(&prach, b->tti, -1)) {
|
|
|
|
// Detect possible PRACHs
|
|
|
|
if (srslte_prach_detect_offset(&prach,
|
|
|
|
prach_cfg.freq_offset,
|
|
|
|
&b->samples[prach.N_cp],
|
|
|
|
nof_sf * SRSLTE_SF_LEN_PRB(cell.nof_prb) - prach.N_cp,
|
|
|
|
prach_indices,
|
|
|
|
prach_offsets,
|
|
|
|
prach_p2avg,
|
|
|
|
&prach_nof_det)) {
|
|
|
|
log_h->error("Error detecting PRACH\n");
|
|
|
|
return SRSLTE_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prach_nof_det) {
|
|
|
|
for (uint32_t i = 0; i < prach_nof_det; i++) {
|
|
|
|
log_h->info("PRACH: cc=%d, %d/%d, preamble=%d, offset=%.1f us, peak2avg=%.1f, max_offset=%.1f us\n",
|
|
|
|
cc_idx,
|
|
|
|
i,
|
|
|
|
prach_nof_det,
|
|
|
|
prach_indices[i],
|
|
|
|
prach_offsets[i] * 1e6,
|
|
|
|
prach_p2avg[i],
|
|
|
|
max_prach_offset_us);
|
|
|
|
|
|
|
|
if (prach_offsets[i] * 1e6 < max_prach_offset_us) {
|
|
|
|
// Convert time offset to Time Alignment command
|
|
|
|
uint32_t n_ta = (uint32_t)(prach_offsets[i] / (16 * SRSLTE_LTE_TS));
|
|
|
|
|
|
|
|
stack->rach_detected(b->tti, cc_idx, prach_indices[i], n_ta);
|
|
|
|
|
|
|
|
#if defined(ENABLE_GUI) and ENABLE_PRACH_GUI
|
|
|
|
uint32_t nof_samples = SRSLTE_MIN(nof_sf * SRSLTE_SF_LEN_PRB(cell.nof_prb), 3 * SRSLTE_SF_LEN_MAX);
|
|
|
|
srslte_vec_abs_cf(b->samples, plot_buffer.data(), nof_samples);
|
|
|
|
plot_real_setNewData(&plot_real, plot_buffer.data(), nof_samples);
|
|
|
|
#endif // defined(ENABLE_GUI) and ENABLE_PRACH_GUI
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void prach_worker::run_thread()
|
|
|
|
{
|
|
|
|
running = true;
|
|
|
|
while (running) {
|
|
|
|
sf_buffer* b = pending_buffers.wait_pop();
|
|
|
|
if (running && b) {
|
|
|
|
int ret = run_tti(b);
|
|
|
|
b->reset();
|
|
|
|
buffer_pool.deallocate(b);
|
|
|
|
if (ret) {
|
|
|
|
running = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace srsenb
|