You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

428 lines
12 KiB
C++

/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The srsLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the srsLTE library.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <string.h>
#include <strings.h>
#include <pthread.h>
#include <unistd.h>
#include "srslte/srslte.h"
#include "srslte/ue_itf/phy.h"
#include "srslte/ue_itf/prach.h"
#include "srslte/ue_itf/ul_buffer.h"
#include "srslte/ue_itf/dl_buffer.h"
namespace srslte {
namespace ue {
bool phy::init(srslte::radio* radio_handler_, srslte::ue::tti_sync* ttisync_)
{
started = false;
ttisync = ttisync_;
radio_handler = radio_handler_;
ul_buffer_queue = new queue(6, sizeof(ul_buffer));
dl_buffer_queue = new queue(6, sizeof(dl_buffer));
// Set default params
params_db.set_param(params::CELLSEARCH_TIMEOUT_PSS_NFRAMES, 100);
params_db.set_param(params::CELLSEARCH_TIMEOUT_PSS_CORRELATION_THRESHOLD, 160);
params_db.set_param(params::CELLSEARCH_TIMEOUT_MIB_NFRAMES, 100);
pthread_attr_t attr;
struct sched_param param;
param.sched_priority = 99;
pthread_attr_init(&attr);
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
pthread_attr_setschedparam(&attr, &param);
if (!pthread_create(&phy_thread, &attr, phy_thread_fnc, this)) {
started = true;
} else {
perror("pthread_create");
}
pthread_attr_destroy(&attr);
return started;
}
void phy::stop()
{
started = false;
pthread_join(phy_thread, NULL);
for (int i=0;i<6;i++) {
((ul_buffer*) ul_buffer_queue->get(i))->free_cell();
((dl_buffer*) dl_buffer_queue->get(i))->free_cell();
}
delete ul_buffer_queue;
delete dl_buffer_queue;
prach_buffer.free_cell();
}
radio* phy::get_radio() {
return radio_handler;
}
void phy::set_timeadv_rar(uint32_t ta_cmd) {
n_ta = srslte_N_ta_new_rar(ta_cmd);
time_adv_sec = SRSLTE_TA_OFFSET+((float) n_ta)*SRSLTE_LTE_TS;
INFO("Set TA RAR: ta_cmd: %d, n_ta: %d, ta_usec: %.1f\n", ta_cmd, n_ta, time_adv_sec*1e6);
}
void phy::set_timeadv(uint32_t ta_cmd) {
n_ta = srslte_N_ta_new(n_ta, ta_cmd);
time_adv_sec = SRSLTE_TA_OFFSET+((float) n_ta)*SRSLTE_LTE_TS;
INFO("Set TA: ta_cmd: %d, n_ta: %d, ta_usec: %.1f\n", ta_cmd, n_ta, time_adv_sec*1e6);
}
void phy::rar_ul_grant(uint32_t rba, uint32_t trunc_mcs, bool hopping_flag, sched_grant *grant)
{
uint32_t n_ho = params_db.get_param(params::PUSCH_HOPPING_OFFSET);
srslte_ra_pusch_t *ra_pusch = (srslte_ra_pusch_t*) grant->get_grant_ptr();
srslte_dci_rar_to_ra_ul(rba, trunc_mcs, hopping_flag, cell.nof_prb, ra_pusch);
srslte_ra_ul_alloc(&ra_pusch->prb_alloc, ra_pusch, n_ho, cell.nof_prb);
if (SRSLTE_VERBOSE_ISINFO()) {
srslte_ra_pusch_fprint(stdout, ra_pusch, cell.nof_prb);
}
}
void phy::set_param(params::param_t param, int64_t value) {
params_db.set_param(param, value);
}
// FIXME: Add PRACH power control
bool phy::send_prach(uint32_t preamble_idx)
{
if (phy_state == RXTX) {
return prach_buffer.prepare_to_send(preamble_idx);
}
return false;
}
int phy::get_prach_transmitted_tti()
{
return prach_buffer.get_transmitted_tti();
}
// Do fast measurement on RSSI and/or PSS autocorrelation energy or PSR
bool phy::measure()
{
if (phy_state == IDLE) {
// capture and do measurement
}
return false;
}
bool phy::start_rxtx()
{
if (phy_state == IDLE) {
if (cell_is_set) {
// Set RX/TX sampling rate
radio_handler->set_rx_srate((float) srslte_sampling_freq_hz(cell.nof_prb));
radio_handler->set_tx_srate((float) srslte_sampling_freq_hz(cell.nof_prb));
phy_state = RXTX;
return true;
} else {
fprintf(stderr, "Can not change state to RXTX: cell is not set\n");
}
} else {
fprintf(stderr, "Can not change state to RXTX: invalid state %d\n", phy_state);
}
return false;
}
bool phy::stop_rxtx()
{
if (phy_state == RXTX) {
// Stop streaming
radio_handler->stop_rx();
phy_state = IDLE;
return true;
} else {
fprintf(stderr, "Can not change state to RXTX: invalid state %d\n", phy_state);
}
return false;
}
bool phy::status_is_idle() {
return phy_state == IDLE;
}
bool phy::status_is_rxtx() {
return phy_state == RXTX;
}
uint32_t phy::get_current_tti() {
return ttisync->get_producer_cntr();
}
uint32_t phy::tti_to_SFN(uint32_t tti) {
return tti/10;
}
uint32_t phy::tti_to_subf(uint32_t tti) {
return tti%10;
}
void* phy::phy_thread_fnc(void *arg) {
phy* phy = static_cast<srslte::ue::phy*>(arg);
phy->main_radio_loop();
return NULL;
}
int radio_recv_wrapper_cs(void *h,void *data, uint32_t nsamples, srslte_timestamp_t *rx_time)
{
radio *radio_handler = (radio*) h;
return radio_handler->rx_now(data, nsamples, rx_time);
}
bool phy::set_cell(srslte_cell_t cell_) {
if (phy_state == IDLE) {
cell_is_set = false;
cell = cell_;
if (!srslte_ue_mib_init(&ue_mib, cell))
{
if (!srslte_ue_sync_init(&ue_sync, cell, radio_recv_wrapper_cs, radio_handler))
{
srslte_ue_sync_set_cfo(&ue_sync, cellsearch_cfo);
if (prach_buffer.init_cell(cell, &params_db)) {
for(uint32_t i=0;i<6;i++) {
((ul_buffer*) ul_buffer_queue->get(i))->init_cell(cell, &params_db);
((dl_buffer*) dl_buffer_queue->get(i))->init_cell(cell, &params_db);
((dl_buffer*) dl_buffer_queue->get(i))->buffer_id = i;
((ul_buffer*) ul_buffer_queue->get(i))->ready();
((dl_buffer*) dl_buffer_queue->get(i))->release();
}
cell_is_set = true;
}
} else {
fprintf(stderr, "Error setting cell: initiating ue_sync");
}
} else {
fprintf(stderr, "Error setting cell: initiating ue_mib\n");
}
} else {
fprintf(stderr, "Error setting cell: Invalid state %d\n", phy_state);
}
return cell_is_set;
}
ul_buffer* phy::get_ul_buffer(uint32_t tti)
{
return (ul_buffer*) ul_buffer_queue->get(tti);
}
ul_buffer* phy::get_ul_buffer_adv(uint32_t tti)
{
return (ul_buffer*) ul_buffer_queue->get(tti + ul_buffer::tx_advance_sf);
}
dl_buffer* phy::get_dl_buffer(uint32_t tti)
{
return (dl_buffer*) dl_buffer_queue->get(tti);
}
bool phy::decode_mib(uint32_t N_id_2, srslte_cell_t *cell, uint8_t payload[SRSLTE_BCH_PAYLOAD_LEN]) {
return decode_mib_N_id_2((int) N_id_2, cell, payload);
}
bool phy::decode_mib_best(srslte_cell_t *cell, uint8_t payload[SRSLTE_BCH_PAYLOAD_LEN]) {
return decode_mib_N_id_2(-1, cell, payload);
}
bool phy::decode_mib_N_id_2(int force_N_id_2, srslte_cell_t *cell_ptr, uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN])
{
srslte_ue_cellsearch_result_t found_cells[3];
srslte_ue_cellsearch_t cs;
bzero(found_cells, 3*sizeof(srslte_ue_cellsearch_result_t));
if (srslte_ue_cellsearch_init(&cs, radio_recv_wrapper_cs, radio_handler)) {
return false;
}
srslte_ue_cellsearch_set_nof_frames_to_scan(&cs, params_db.get_param(params::CELLSEARCH_TIMEOUT_PSS_NFRAMES));
srslte_ue_cellsearch_set_threshold(&cs, (float)
params_db.get_param(params::CELLSEARCH_TIMEOUT_PSS_CORRELATION_THRESHOLD)/10);
radio_handler->set_rx_srate(1920000.0);
radio_handler->start_rx();
/* Find a cell in the given N_id_2 or go through the 3 of them to find the strongest */
uint32_t max_peak_cell = 0;
int ret = SRSLTE_ERROR;
if (force_N_id_2 >= 0 && force_N_id_2 < 3) {
ret = srslte_ue_cellsearch_scan_N_id_2(&cs, force_N_id_2, &found_cells[force_N_id_2]);
max_peak_cell = force_N_id_2;
} else {
ret = srslte_ue_cellsearch_scan(&cs, found_cells, &max_peak_cell);
}
radio_handler->stop_rx();
srslte_ue_cellsearch_free(&cs);
if (ret < 0) {
fprintf(stderr, "Error decoding MIB: Error searching PSS\n");
return false;
} else if (ret == 0) {
fprintf(stderr, "Error decoding MIB: Could not find any PSS in this frequency\n");
return false;
}
// Save result
cell_ptr->id = found_cells[max_peak_cell].cell_id;
cell_ptr->cp = found_cells[max_peak_cell].cp;
cellsearch_cfo = found_cells[max_peak_cell].cfo;
INFO("\nFound CELL ID: %d CP: %s, CFO: %f\n", cell_ptr->id, srslte_cp_string(cell_ptr->cp), cellsearch_cfo);
srslte_ue_mib_sync_t ue_mib_sync;
if (srslte_ue_mib_sync_init(&ue_mib_sync, cell_ptr->id, cell_ptr->cp, radio_recv_wrapper_cs, radio_handler)) {
return false;
}
/* Find and decode MIB */
uint32_t sfn, sfn_offset;
radio_handler->start_rx();
ret = srslte_ue_mib_sync_decode(&ue_mib_sync, params_db.get_param(params::CELLSEARCH_TIMEOUT_MIB_NFRAMES),
bch_payload, &cell_ptr->nof_ports, &sfn_offset);
radio_handler->stop_rx();
srslte_ue_mib_sync_free(&ue_mib_sync);
if (ret == 1) {
srslte_pbch_mib_unpack(bch_payload, cell_ptr, NULL);
return true;
} else {
printf("Error decoding MIB: Error decoding PBCH\n");
return false;
}
}
int phy::sync_sfn(void) {
cf_t *sf_buffer = NULL;
int ret = SRSLTE_ERROR;
uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN];
srslte_ue_sync_decode_sss_on_track(&ue_sync, true);
ret = srslte_ue_sync_get_buffer(&ue_sync, &sf_buffer);
if (ret < 0) {
fprintf(stderr, "Error calling ue_sync_get_buffer");
return -1;
}
if (ret == 1) {
if (srslte_ue_sync_get_sfidx(&ue_sync) == 0) {
uint32_t sfn_offset=0;
srslte_pbch_decode_reset(&ue_mib.pbch);
int n = srslte_ue_mib_decode(&ue_mib, sf_buffer, bch_payload, NULL, &sfn_offset);
if (n < 0) {
fprintf(stderr, "Error decoding MIB while synchronising SFN");
return -1;
} else if (n == SRSLTE_UE_MIB_FOUND) {
uint32_t sfn;
srslte_pbch_mib_unpack(bch_payload, &cell, &sfn);
sfn = (sfn + sfn_offset)%1024;
ttisync->set_producer_cntr(10*sfn+1);
srslte_ue_sync_decode_sss_on_track(&ue_sync, false);
return 1;
}
}
}
return 0;
}
void phy::run_rx_tx_state()
{
int ret;
if (!is_sfn_synched) {
if (!radio_is_streaming) {
// Start streaming
radio_handler->start_rx();
radio_is_streaming = true;
}
ret = sync_sfn();
switch(ret) {
default:
phy_state = IDLE;
break;
case 1:
is_sfn_synched = true;
break;
case 0:
break;
}
} else {
uint32_t current_tti = ttisync->get_producer_cntr();
float cfo = srslte_ue_sync_get_cfo(&ue_sync)/15000;
// Prepare transmission for the next tti
srslte_timestamp_add(&last_rx_time, 0, 1e-3);
// send prach if we have to
if (prach_buffer.is_ready_to_send(current_tti)) {
prach_buffer.send(radio_handler, cfo, last_rx_time);
}
// send ul buffer if we have to
if (get_ul_buffer_adv(current_tti)->is_released()) {
get_ul_buffer_adv(current_tti)->send(radio_handler, time_adv_sec, cfo, last_rx_time);
}
// Receive alligned buffer for the current tti
get_dl_buffer(current_tti)->recv_ue_sync(&ue_sync, &last_rx_time);
ttisync->increase();
}
}
void phy::main_radio_loop() {
while(started) {
switch(phy_state) {
case IDLE:
usleep(50000);
break;
case RXTX:
run_rx_tx_state();
break;
}
}
}
}
}