diff --git a/lib/examples/CMakeLists.txt b/lib/examples/CMakeLists.txt index 05e38dece..d9ba8cec3 100644 --- a/lib/examples/CMakeLists.txt +++ b/lib/examples/CMakeLists.txt @@ -18,7 +18,6 @@ # and at http://www.gnu.org/licenses/. # - ################################################################# # Applications ################################################################# @@ -72,6 +71,12 @@ if(RF_FOUND) add_executable(usrp_capture_sync usrp_capture_sync.c) target_link_libraries(usrp_capture_sync srslte_phy srslte_rf) + add_executable(usrp_capture_sync_nbiot usrp_capture_sync_nbiot.c) + target_link_libraries(usrp_capture_sync_nbiot srslte_rf srslte_phy) + + add_executable(cell_search_nbiot cell_search_nbiot.c) + target_link_libraries(cell_search_nbiot srslte_rf srslte_phy) + add_executable(usrp_txrx usrp_txrx.c) target_link_libraries(usrp_txrx srslte_phy srslte_rf) @@ -79,6 +84,4 @@ if(RF_FOUND) else(RF_FOUND) message(STATUS " examples will NOT BE INSTALLED.") -endif(RF_FOUND) - - +endif(RF_FOUND) \ No newline at end of file diff --git a/lib/examples/cell_search_nbiot.c b/lib/examples/cell_search_nbiot.c new file mode 100644 index 000000000..08def3abe --- /dev/null +++ b/lib/examples/cell_search_nbiot.c @@ -0,0 +1,261 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * A copy of the GNU Affero 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 +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/rf/rf_utils.h" +#include "srslte/phy/ue/ue_cell_search_nbiot.h" + +#ifndef DISABLE_RF +#include "srslte/phy/rf/rf.h" +#endif + +#define MHZ 1000000 +#define SAMP_FREQ 1920000 +#define FLEN 9600 +#define FLEN_PERIOD 0.005 + +#define MAX_EARFCN 1000 +#define RASTER_OFFSET 2500 + +#define NUM_RASTER_OFFSET 5 +double raster_offset[NUM_RASTER_OFFSET] = {0.0, 2500.0, -2500.0, 7500.0, -7500.0}; + +int band = -1; +int earfcn_start = -1, earfcn_end = -1; + +cell_search_cfg_t cell_detect_config = {.max_frames_pbch = SRSLTE_DEFAULT_MAX_FRAMES_NPBCH, + .max_frames_pss = SRSLTE_DEFAULT_MAX_FRAMES_NPSS, + .nof_valid_pss_frames = SRSLTE_DEFAULT_NOF_VALID_NPSS_FRAMES, + .init_agc = 0.0, + .force_tdd = false}; + +struct cells { + srslte_nbiot_cell_t cell; + float freq; + int dl_earfcn; + float power; +}; +struct cells results[1024]; + +float rf_gain = 70.0; +char* rf_args = ""; +bool scan_raster_offset = false; + +void usage(char* prog) +{ + printf("Usage: %s [agsendtvb] -b band\n", prog); + printf("\t-a RF args [Default %s]\n", rf_args); + printf("\t-g RF gain [Default %.2f dB]\n", rf_gain); + printf("\t-s earfcn_start [Default All]\n"); + printf("\t-e earfcn_end [Default All]\n"); + printf("\t-r Also scan frequencies with raster offset [Default %s]\n", scan_raster_offset ? "Yes" : "No"); + printf("\t-n nof_frames_total [Default 100]\n"); + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +void parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "agserndvb")) != -1) { + switch (opt) { + case 'a': + rf_args = argv[optind]; + break; + case 'b': + band = (int)strtol(argv[optind], NULL, 10); + break; + case 's': + earfcn_start = (int)strtol(argv[optind], NULL, 10); + break; + case 'e': + earfcn_end = (int)strtol(argv[optind], NULL, 10); + break; + case 'n': + cell_detect_config.max_frames_pss = (int)strtol(argv[optind], NULL, 10); + break; + case 'g': + rf_gain = strtof(argv[optind], NULL); + break; + case 'r': + scan_raster_offset = true; + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (band == -1) { + usage(argv[0]); + exit(-1); + } +} + +int srslte_rf_recv_wrapper(void* h, cf_t* data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t* t) +{ + DEBUG(" ---- Receive %d samples ---- \n", nsamples); + void* ptr[SRSLTE_MAX_PORTS]; + for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { + ptr[i] = data[i]; + } + return srslte_rf_recv_with_time_multi(h, ptr, nsamples, true, NULL, NULL); +} + +bool go_exit = false; + +void sig_int_handler(int signo) +{ + printf("SIGINT received. Exiting...\n"); + if (signo == SIGINT) { + go_exit = true; + } +} + +float srslte_rf_set_rx_gain_wrapper(void* h, float f) +{ + return srslte_rf_set_rx_gain((srslte_rf_t*)h, f); +} + +int main(int argc, char** argv) +{ + int n; + srslte_rf_t rf; + srslte_ue_cellsearch_nbiot_t cs; + srslte_nbiot_ue_cellsearch_result_t found_cells[3]; + int nof_freqs; + srslte_earfcn_t channels[MAX_EARFCN]; + uint32_t freq; + uint32_t n_found_cells = 0; + + parse_args(argc, argv); + + printf("Opening RF device...\n"); + if (srslte_rf_open(&rf, rf_args)) { + fprintf(stderr, "Error opening rf\n"); + exit(-1); + } + if (!cell_detect_config.init_agc) { + srslte_rf_set_rx_gain(&rf, rf_gain); + } else { + printf("Starting AGC thread...\n"); + if (srslte_rf_start_gain_thread(&rf, false)) { + fprintf(stderr, "Error opening rf\n"); + exit(-1); + } + srslte_rf_set_rx_gain(&rf, 50); + } + + // Supress RF messages + srslte_rf_suppress_stdout(&rf); + + nof_freqs = srslte_band_get_fd_band(band, channels, earfcn_start, earfcn_end, MAX_EARFCN); + if (nof_freqs < 0) { + fprintf(stderr, "Error getting EARFCN list\n"); + exit(-1); + } + + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); + signal(SIGINT, sig_int_handler); + + for (freq = 0; freq < nof_freqs && !go_exit; freq++) { + for (int i = 0; i < (scan_raster_offset ? NUM_RASTER_OFFSET : 1); i++) { + // set rf_freq + double rf_freq = channels[freq].fd * MHZ + raster_offset[i]; + srslte_rf_set_rx_freq(&rf, 0, rf_freq); + INFO("Set rf_freq to %.3f Hz\n", rf_freq); + + printf("[%3d/%d]: EARFCN %d Freq. %.2f MHz looking for NPSS.\n", freq, nof_freqs, channels[freq].id, rf_freq); + fflush(stdout); + + if (SRSLTE_VERBOSE_ISINFO()) { + printf("\n"); + } + + bzero(found_cells, 3 * sizeof(srslte_nbiot_ue_cellsearch_result_t)); + + if (srslte_ue_cellsearch_nbiot_init(&cs, cell_detect_config.max_frames_pss, srslte_rf_recv_wrapper, (void*)&rf)) { + fprintf(stderr, "Error initiating UE cell detect\n"); + exit(-1); + } + + if (cell_detect_config.max_frames_pss) { + srslte_ue_cellsearch_nbiot_set_nof_valid_frames(&cs, cell_detect_config.nof_valid_pss_frames); + } + if (cell_detect_config.init_agc) { + srslte_nbiot_ue_sync_start_agc(&cs.ue_sync, srslte_rf_set_rx_gain_wrapper, cell_detect_config.init_agc); + } + + INFO("Setting sampling frequency %.2f MHz for NPSS search\n", SRSLTE_CS_SAMP_FREQ / 1000000); + srslte_rf_set_rx_srate(&rf, SRSLTE_CS_SAMP_FREQ); + INFO("Starting receiver...\n"); + srslte_rf_start_rx_stream(&rf, false); + + n = srslte_ue_cellsearch_nbiot_scan(&cs); + if (n == SRSLTE_SUCCESS) { + srslte_rf_stop_rx_stream(&rf); + n = srslte_ue_cellsearch_nbiot_detect(&cs, found_cells); + if (n == SRSLTE_SUCCESS) { + srslte_nbiot_cell_t cell; + cell.n_id_ncell = found_cells[0].n_id_ncell; + cell.base.cp = SRSLTE_CP_NORM; + + // TODO: add MIB decoding + printf("Found CELL ID %d.\n", cell.n_id_ncell); + memcpy(&results[n_found_cells].cell, &cell, sizeof(srslte_nbiot_cell_t)); + results[n_found_cells].freq = channels[freq].fd; + results[n_found_cells].dl_earfcn = channels[freq].id; + results[n_found_cells].power = found_cells[0].peak; + n_found_cells++; + } else { + printf("Cell found but couldn't detect ID.\n"); + } + } + srslte_ue_cellsearch_nbiot_free(&cs); + } + } + + printf("\n\nFound %d cells\n", n_found_cells); + for (int i = 0; i < n_found_cells; i++) { + printf("Found CELL %.1f MHz, EARFCN=%d, PHYID=%d, NPSS power=%.1f dBm\n", + results[i].freq, + results[i].dl_earfcn, + results[i].cell.n_id_ncell, + 10 * log10(results[i].power)); + } + + printf("\nBye\n"); + + srslte_rf_close(&rf); + exit(0); +} diff --git a/lib/examples/usrp_capture_sync_nbiot.c b/lib/examples/usrp_capture_sync_nbiot.c new file mode 100644 index 000000000..038a83ad8 --- /dev/null +++ b/lib/examples/usrp_capture_sync_nbiot.c @@ -0,0 +1,193 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * A copy of the GNU Affero 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/io/filesink.h" +#include "srslte/phy/rf/rf.h" +#include "srslte/phy/ue/ue_mib_nbiot.h" +#include "srslte/phy/ue/ue_sync_nbiot.h" +#include "srslte/phy/utils/debug.h" + +static bool keep_running = true; +char* output_file_name = NULL; +char* rf_args = ""; +float rf_gain = 60.0, rf_freq = -1.0; +int nof_prb = 6; +int nof_subframes = -1; + +void int_handler(int dummy) +{ + keep_running = false; +} + +void usage(char* prog) +{ + printf("Usage: %s [agrtnv] -f rx_frequency_hz -o output_file\n", prog); + printf("\t-a RF args [Default %s]\n", rf_args); + printf("\t-g RF Gain [Default %.2f dB]\n", rf_gain); + printf("\t-n nof_subframes [Default %d]\n", nof_subframes); + printf("\t-v verbose\n"); +} + +void parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "agnvfto")) != -1) { + switch (opt) { + case 'o': + output_file_name = argv[optind]; + break; + case 'a': + rf_args = argv[optind]; + break; + case 'g': + rf_gain = strtof(argv[optind], NULL); + break; + case 'f': + rf_freq = strtof(argv[optind], NULL); + break; + case 'n': + nof_subframes = (int)strtol(argv[optind], NULL, 10); + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (&rf_freq < 0 || output_file_name == NULL) { + usage(argv[0]); + exit(-1); + } +} + +int srslte_rf_recv_wrapper(void* h, void* data, uint32_t nsamples, srslte_timestamp_t* t) +{ + DEBUG(" ---- Receive %d samples ---- \n", nsamples); + return srslte_rf_recv(h, data, nsamples, 1); +} + +int main(int argc, char** argv) +{ + signal(SIGINT, int_handler); + + parse_args(argc, argv); + + srslte_filesink_t sink; + srslte_filesink_init(&sink, output_file_name, SRSLTE_COMPLEX_FLOAT_BIN); + + printf("Opening RF device...\n"); + srslte_rf_t rf; + if (srslte_rf_open(&rf, rf_args)) { + fprintf(stderr, "Error opening rf\n"); + exit(-1); + } + + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); + + printf("Set RX freq: %.6f MHz\n", srslte_rf_set_rx_freq(&rf, 0, rf_freq) / 1000000); + printf("Set RX gain: %.1f dB\n", srslte_rf_set_rx_gain(&rf, rf_gain)); + int srate = srslte_sampling_freq_hz(nof_prb); + if (srate != -1) { + printf("Setting sampling rate %.2f MHz\n", (float)srate / 1000000); + float srate_rf = srslte_rf_set_rx_srate(&rf, (double)srate); + if (srate_rf != srate) { + fprintf(stderr, "Could not set sampling rate\n"); + exit(-1); + } + } else { + fprintf(stderr, "Invalid number of PRB %d\n", nof_prb); + exit(-1); + } + srslte_rf_start_rx_stream(&rf, false); + + srslte_nbiot_cell_t cell = {}; + cell.base.nof_prb = nof_prb; + cell.base.nof_ports = 1; + + cf_t* buff_ptrs[SRSLTE_MAX_PORTS] = {NULL, NULL, NULL, NULL}; + buff_ptrs[0] = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_SF_LEN_PRB_NBIOT * 10); + + srslte_nbiot_ue_sync_t ue_sync; + if (srslte_nbiot_ue_sync_init(&ue_sync, cell, srslte_rf_recv_wrapper, (void*)&rf)) { + fprintf(stderr, "Error initiating ue_sync\n"); + exit(-1); + } + + int32_t nof_warmup_subframes = 1024; + uint32_t subframe_count = 0; + bool start_capture = false; + bool stop_capture = false; + while ((subframe_count < nof_subframes || nof_subframes == -1) && !stop_capture) { + int n = srslte_nbiot_ue_sync_zerocopy_multi(&ue_sync, buff_ptrs); + if (n < 0) { + fprintf(stderr, "Error receiving samples\n"); + exit(-1); + } + + if (n == 1) { + if (!start_capture) { + if (nof_warmup_subframes <= 0) { + if (srslte_nbiot_ue_sync_get_sfidx(&ue_sync) == 9) { + printf("Starting capture ..\n"); + start_capture = true; + } + } + nof_warmup_subframes--; + } else { + printf("Writing subframe %d (%d/%d) to file (cfo=%6.2f kHz)\n", + srslte_nbiot_ue_sync_get_sfidx(&ue_sync), + subframe_count, + nof_subframes, + srslte_nbiot_ue_sync_get_cfo(&ue_sync) / 1000); + srslte_filesink_write(&sink, buff_ptrs[0], SRSLTE_SF_LEN_PRB(nof_prb)); + subframe_count++; + } + } + if (!keep_running) { + if (!start_capture || (start_capture && srslte_nbiot_ue_sync_get_sfidx(&ue_sync) == 9)) { + printf("Stopping capture ..\n"); + stop_capture = true; + } + } + } + + srslte_filesink_free(&sink); + srslte_rf_close(&rf); + srslte_nbiot_ue_sync_free(&ue_sync); + + printf("Ok - wrote %d subframes\n", subframe_count); + exit(0); +} diff --git a/lib/include/srslte/phy/ue/ue_cell_search_nbiot.h b/lib/include/srslte/phy/ue/ue_cell_search_nbiot.h new file mode 100644 index 000000000..539595477 --- /dev/null +++ b/lib/include/srslte/phy/ue/ue_cell_search_nbiot.h @@ -0,0 +1,80 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * A copy of the GNU Affero 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/. + * + */ + +#ifndef SRSLTE_UE_CELL_SEARCH_NBIOT_H +#define SRSLTE_UE_CELL_SEARCH_NBIOT_H + +#include "srslte/config.h" +#include "srslte/phy/ch_estimation/chest_dl_nbiot.h" +#include "srslte/phy/dft/ofdm.h" +#include "srslte/phy/phch/npbch.h" +#include "srslte/phy/sync/cfo.h" +#include "srslte/phy/ue/ue_mib_nbiot.h" +#include "srslte/phy/ue/ue_sync_nbiot.h" + +#define SRSLTE_CS_NOF_PRB 6 +#define SRSLTE_CS_SAMP_FREQ 1920000.0 + +typedef struct SRSLTE_API { + uint32_t n_id_ncell; + float peak; + float mode; + float psr; + float cfo; +} srslte_nbiot_ue_cellsearch_result_t; + +/** + * \brief Wrapper for the nbiot_ue_sync object. + * + * This object is a wrapper to the nbiot_ue_sync object. It receives + * several synchronized frames and obtains the most common n_id_ncell. + * + * The I/O stream device sampling frequency must be set to 1.92 MHz + * (SRSLTE_CS_SAMP_FREQ constant) before calling to + * srslte_ue_cellsearch_nbiot_scan() functions. + */ +typedef struct SRSLTE_API { + srslte_nbiot_ue_sync_t ue_sync; + + cf_t* rx_buffer[SRSLTE_MAX_PORTS]; + cf_t* nsss_buffer; + int nsss_sf_counter; + + uint32_t max_frames; + uint32_t nof_valid_frames; // number of frames to scan +} srslte_ue_cellsearch_nbiot_t; + +SRSLTE_API int +srslte_ue_cellsearch_nbiot_init(srslte_ue_cellsearch_nbiot_t* q, + uint32_t max_frames_total, + int(recv_callback)(void*, cf_t* [SRSLTE_MAX_PORTS], uint32_t, srslte_timestamp_t*), + void* stream_handler); + +SRSLTE_API void srslte_ue_cellsearch_nbiot_free(srslte_ue_cellsearch_nbiot_t* q); + +SRSLTE_API int srslte_ue_cellsearch_nbiot_scan(srslte_ue_cellsearch_nbiot_t* q); + +SRSLTE_API int srslte_ue_cellsearch_nbiot_detect(srslte_ue_cellsearch_nbiot_t* q, + srslte_nbiot_ue_cellsearch_result_t* found_cells); + +SRSLTE_API int srslte_ue_cellsearch_nbiot_set_nof_valid_frames(srslte_ue_cellsearch_nbiot_t* q, uint32_t nof_frames); + +#endif // SRSLTE_UE_CELL_SEARCH_NBIOT_H diff --git a/lib/include/srslte/phy/ue/ue_mib_nbiot.h b/lib/include/srslte/phy/ue/ue_mib_nbiot.h new file mode 100644 index 000000000..7d4a0f53e --- /dev/null +++ b/lib/include/srslte/phy/ue/ue_mib_nbiot.h @@ -0,0 +1,111 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * A copy of the GNU Affero 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/. + * + */ + +#ifndef SRSLTE_UE_MIB_NBIOT_H +#define SRSLTE_UE_MIB_NBIOT_H + +#include "srslte/config.h" +#include "srslte/phy/ch_estimation/chest_dl_nbiot.h" +#include "srslte/phy/dft/ofdm.h" +#include "srslte/phy/phch/npbch.h" +#include "srslte/phy/sync/cfo.h" +#include "srslte/phy/ue/ue_sync_nbiot.h" + +#define SRSLTE_UE_MIB_NBIOT_NOF_PRB 6 +#define SRSLTE_UE_MIB_NBIOT_FOUND 1 +#define SRSLTE_UE_MIB_NBIOT_NOTFOUND 0 + +/** + * \brief This object decodes the MIB-NB from the NPBCH of an NB-IoT LTE signal. + * + * The function srslte_ue_mib_nbiot_decode() shall be called multiple + * times, each passing a number of samples multiple of 19200, + * sampled at 1.92 MHz (that is, 10 ms of samples). + * + * The function uses the sync_t object to find the NPSS sequence and + * decode the NPBCH to obtain the MIB. + * + * The function returns 0 until the MIB is decoded. + */ + +typedef struct SRSLTE_API { + srslte_sync_nbiot_t sfind; + + cf_t* sf_symbols; + cf_t* ce[SRSLTE_MAX_PORTS]; + + srslte_ofdm_t fft; + srslte_chest_dl_nbiot_t chest; + srslte_npbch_t npbch; + + uint8_t last_bch_payload[SRSLTE_MIB_NB_LEN]; + uint32_t nof_tx_ports; + uint32_t nof_rx_antennas; + uint32_t sfn_offset; + + uint32_t frame_cnt; +} srslte_ue_mib_nbiot_t; + +SRSLTE_API int srslte_ue_mib_nbiot_init(srslte_ue_mib_nbiot_t* q, cf_t** in_buffer, uint32_t max_prb); + +SRSLTE_API int srslte_ue_mib_nbiot_set_cell(srslte_ue_mib_nbiot_t* q, srslte_nbiot_cell_t cell); + +SRSLTE_API void srslte_ue_mib_nbiot_free(srslte_ue_mib_nbiot_t* q); + +SRSLTE_API void srslte_ue_mib_nbiot_reset(srslte_ue_mib_nbiot_t* q); + +SRSLTE_API int srslte_ue_mib_nbiot_decode(srslte_ue_mib_nbiot_t* q, + cf_t* input, + uint8_t* bch_payload, + uint32_t* nof_tx_ports, + int* sfn_offset); + +/* This interface uses ue_mib and ue_sync to first get synchronized subframes + * and then decode MIB + * + * This object calls the pbch object with nof_ports=0 for blind nof_ports determination + */ +typedef struct { + srslte_ue_mib_nbiot_t ue_mib; + srslte_nbiot_ue_sync_t ue_sync; + cf_t* sf_buffer[SRSLTE_MAX_PORTS]; + uint32_t nof_rx_antennas; +} srslte_ue_mib_sync_nbiot_t; + +SRSLTE_API int +srslte_ue_mib_sync_nbiot_init_multi(srslte_ue_mib_sync_nbiot_t* q, + int(recv_callback)(void*, cf_t* [SRSLTE_MAX_PORTS], uint32_t, srslte_timestamp_t*), + uint32_t nof_rx_antennas, + void* stream_handler); + +SRSLTE_API void srslte_ue_mib_sync_nbiot_free(srslte_ue_mib_sync_nbiot_t* q); + +SRSLTE_API int srslte_ue_mib_sync_nbiot_set_cell(srslte_ue_mib_sync_nbiot_t* q, srslte_nbiot_cell_t cell); + +SRSLTE_API void srslte_ue_mib_sync_nbiot_reset(srslte_ue_mib_sync_nbiot_t* q); + +SRSLTE_API int srslte_ue_mib_sync_nbiot_decode(srslte_ue_mib_sync_nbiot_t* q, + uint32_t max_frames_timeout, + uint8_t* bch_payload, + uint32_t* nof_tx_ports, + int* sfn_offset); + +#endif // SRSLTE_UE_MIB_NBIOT_H diff --git a/lib/include/srslte/phy/ue/ue_sync_nbiot.h b/lib/include/srslte/phy/ue/ue_sync_nbiot.h new file mode 100644 index 000000000..0126cba98 --- /dev/null +++ b/lib/include/srslte/phy/ue/ue_sync_nbiot.h @@ -0,0 +1,165 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * A copy of the GNU Affero 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/. + * + */ + +#ifndef SRSLTE_UE_SYNC_NBIOT_H +#define SRSLTE_UE_SYNC_NBIOT_H + +#include + +#include "srslte/config.h" +#include "srslte/phy/agc/agc.h" +#include "srslte/phy/ch_estimation/chest_dl_nbiot.h" +#include "srslte/phy/common/timestamp.h" +#include "srslte/phy/dft/ofdm.h" +#include "srslte/phy/io/filesource.h" +#include "srslte/phy/phch/npbch.h" +#include "srslte/phy/sync/cfo.h" +#include "srslte/phy/sync/sync_nbiot.h" + +typedef srslte_ue_sync_state_t srslte_nbiot_ue_sync_state_t; + +//#define MEASURE_EXEC_TIME + +typedef struct SRSLTE_API { + srslte_sync_nbiot_t sfind; + srslte_sync_nbiot_t strack; + + srslte_agc_t agc; + bool do_agc; + uint32_t agc_period; + + void* stream; + void* stream_single; + int (*recv_callback)(void*, cf_t* [SRSLTE_MAX_PORTS], uint32_t, srslte_timestamp_t*); + int (*recv_callback_single)(void*, void*, uint32_t, srslte_timestamp_t*); + srslte_timestamp_t last_timestamp; + + srslte_filesource_t file_source; + bool file_mode; + float file_cfo; + srslte_cfo_t file_cfo_correct; + + srslte_nbiot_ue_sync_state_t state; + + uint32_t nof_rx_antennas; + uint32_t frame_len; + uint32_t fft_size; + uint32_t nof_recv_sf; // Number of subframes received each call to srslte_ue_sync_get_buffer + uint32_t nof_avg_find_frames; + uint32_t frame_find_cnt; + uint32_t sf_len; + + /* These count half frames (5ms) */ + uint64_t frame_ok_cnt; + uint32_t frame_no_cnt; + uint32_t frame_total_cnt; + + /* this is the system frame number (SFN) */ + uint32_t frame_number; + + srslte_nbiot_cell_t cell; + uint32_t sf_idx; + bool correct_cfo; + + uint32_t peak_idx; + int next_rf_sample_offset; + int last_sample_offset; + float mean_sample_offset; + float mean_sfo; + uint32_t sample_offset_correct_period; + float sfo_ema; + uint32_t max_prb; + +#ifdef MEASURE_EXEC_TIME + float mean_exec_time; +#endif +} srslte_nbiot_ue_sync_t; + +SRSLTE_API int srslte_nbiot_ue_sync_init(srslte_nbiot_ue_sync_t* q, + srslte_nbiot_cell_t cell, + int(recv_callback)(void*, void*, uint32_t, srslte_timestamp_t*), + void* stream_handler); + +SRSLTE_API int +srslte_nbiot_ue_sync_init_multi(srslte_nbiot_ue_sync_t* q, + uint32_t max_prb, + int(recv_callback)(void*, cf_t* [SRSLTE_MAX_PORTS], uint32_t, srslte_timestamp_t*), + uint32_t nof_rx_antennas, + void* stream_handler); + +SRSLTE_API int srslte_nbiot_ue_sync_init_file(srslte_nbiot_ue_sync_t* q, + srslte_nbiot_cell_t cell, + char* file_name, + int offset_time, + float offset_freq); + +SRSLTE_API int srslte_nbiot_ue_sync_init_file_multi(srslte_nbiot_ue_sync_t* q, + srslte_nbiot_cell_t cell, + char* file_name, + int offset_time, + float offset_freq, + uint32_t nof_rx_ant); + +SRSLTE_API void srslte_nbiot_ue_sync_free(srslte_nbiot_ue_sync_t* q); + +SRSLTE_API int srslte_nbiot_ue_sync_set_cell(srslte_nbiot_ue_sync_t* q, srslte_nbiot_cell_t cell); + +SRSLTE_API int srslte_nbiot_ue_sync_start_agc(srslte_nbiot_ue_sync_t* q, + float(set_gain_callback)(void*, float), + float init_gain_value); + +SRSLTE_API uint32_t srslte_nbiot_ue_sync_sf_len(srslte_nbiot_ue_sync_t* q); + +SRSLTE_API int srslte_nbiot_ue_sync_get_buffer(srslte_nbiot_ue_sync_t* q, cf_t** sf_symbols); + +SRSLTE_API void srslte_nbiot_ue_sync_set_agc_period(srslte_nbiot_ue_sync_t* q, uint32_t period); + +/* CAUTION: input_buffer MUST have space for 2 subframes */ +SRSLTE_API int srslte_nbiot_ue_sync_zerocopy(srslte_nbiot_ue_sync_t* q, cf_t* input_buffer); + +SRSLTE_API int srslte_nbiot_ue_sync_zerocopy_multi(srslte_nbiot_ue_sync_t* q, cf_t* input_buffer[SRSLTE_MAX_PORTS]); + +SRSLTE_API void srslte_nbiot_ue_sync_set_cfo(srslte_nbiot_ue_sync_t* q, float cfo); + +SRSLTE_API void srslte_nbiot_ue_sync_reset(srslte_nbiot_ue_sync_t* q); + +SRSLTE_API srslte_nbiot_ue_sync_state_t srslte_nbiot_ue_sync_get_state(srslte_nbiot_ue_sync_t* q); + +SRSLTE_API uint32_t srslte_nbiot_ue_sync_get_sfidx(srslte_nbiot_ue_sync_t* q); + +SRSLTE_API void srslte_nbiot_ue_sync_set_cfo_enable(srslte_nbiot_ue_sync_t* q, bool enable); + +SRSLTE_API float srslte_nbiot_ue_sync_get_cfo(srslte_nbiot_ue_sync_t* q); + +SRSLTE_API float srslte_nbiot_ue_sync_get_sfo(srslte_nbiot_ue_sync_t* q); + +SRSLTE_API void srslte_nbiot_ue_sync_set_cfo_ema(srslte_nbiot_ue_sync_t* q, float ema); + +SRSLTE_API void srslte_nbiot_ue_sync_set_cfo_tol(srslte_nbiot_ue_sync_t* q, float cfo_tol); + +SRSLTE_API int srslte_nbiot_ue_sync_get_last_sample_offset(srslte_nbiot_ue_sync_t* q); + +SRSLTE_API void srslte_nbiot_ue_sync_set_sample_offset_correct_period(srslte_nbiot_ue_sync_t* q, + uint32_t nof_subframes); + +SRSLTE_API void srslte_nbiot_ue_sync_get_last_timestamp(srslte_nbiot_ue_sync_t* q, srslte_timestamp_t* timestamp); + +#endif // SRSLTE_UE_SYNC_NBIOT_H \ No newline at end of file diff --git a/lib/src/phy/ue/test/CMakeLists.txt b/lib/src/phy/ue/test/CMakeLists.txt index 481f16b72..9578d8356 100644 --- a/lib/src/phy/ue/test/CMakeLists.txt +++ b/lib/src/phy/ue/test/CMakeLists.txt @@ -29,3 +29,17 @@ add_test(gen_ack_test gen_ack_test) add_executable(pucch_resource_test pucch_resource_test.c) target_link_libraries(pucch_resource_test srslte_phy) add_test(pucch_resource_test pucch_resource_test) + +if(RF_FOUND) + add_executable(ue_mib_sync_test_nbiot_usrp ue_mib_sync_test_nbiot_usrp.c) + target_link_libraries(ue_mib_sync_test_nbiot_usrp srslte_phy srslte_rf pthread) + + add_executable(ue_sync_test_nbiot_usrp ue_sync_test_nbiot_usrp.c) + target_link_libraries(ue_sync_test_nbiot_usrp srslte_rf srslte_phy pthread) + if(SRSGUI_FOUND) + include_directories(${SRSGUI_INCLUDE_DIRS}) + target_link_libraries(ue_sync_test_nbiot_usrp ${SRSGUI_LIBRARIES}) + else(SRSGUI_FOUND) + add_definitions(-DDISABLE_GRAPHICS) + endif(SRSGUI_FOUND) +endif(RF_FOUND) \ No newline at end of file diff --git a/lib/src/phy/ue/test/ue_mib_sync_test_nbiot_usrp.c b/lib/src/phy/ue/test/ue_mib_sync_test_nbiot_usrp.c new file mode 100644 index 000000000..259b5eb64 --- /dev/null +++ b/lib/src/phy/ue/test/ue_mib_sync_test_nbiot_usrp.c @@ -0,0 +1,181 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * A copy of the GNU Affero 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/phch/npbch.h" +#include "srslte/phy/rf/rf.h" +#include "srslte/phy/sync/sync_nbiot.h" +#include "srslte/phy/ue/ue_mib_nbiot.h" +#include "srslte/phy/utils/debug.h" + +char* rf_args = ""; +float rf_gain = 70.0, rf_freq = -1.0; +int nof_frames = -1; +uint32_t fft_size = 128; +float threshold = 20.0; +srslte_cp_t cp = SRSLTE_CP_NORM; +srslte_nbiot_cell_t cell = {}; + +void usage(char* prog) +{ + printf("Usage: %s [aedgtvnpR] -f rx_frequency_hz\n", prog); + printf("\t-a RF args [Default %s]\n", rf_args); + printf("\t-g RF Gain [Default %.2f dB]\n", rf_gain); + printf("\t-n nof_frames [Default %d]\n", nof_frames); + printf("\t-l n_id_ncell to sync [Default %d]\n", cell.n_id_ncell); + printf("\t-R Is R14 cell [Default %s]\n", cell.is_r14 ? "Yes" : "No"); + printf("\t-s symbol_sz [Default %d]\n", fft_size); + printf("\t-t threshold [Default %.2f]\n", threshold); + printf("\t-v srslte_verbose\n"); +} + +void parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "adgtvsfilR")) != -1) { + switch (opt) { + case 'a': + rf_args = argv[optind]; + break; + case 'g': + rf_gain = strtof(argv[optind], NULL); + break; + case 'f': + rf_freq = strtof(argv[optind], NULL); + break; + case 't': + threshold = strtof(argv[optind], NULL); + break; + case 'l': + cell.n_id_ncell = (int)strtol(argv[optind], NULL, 10); + break; + case 'R': + cell.is_r14 = true; + break; + case 's': + fft_size = (int)strtol(argv[optind], NULL, 10); + break; + case 'n': + nof_frames = (int)strtol(argv[optind], NULL, 10); + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (rf_freq < 0) { + usage(argv[0]); + exit(-1); + } +} + +bool go_exit = false; +void sig_int_handler(int signo) +{ + printf("SIGINT received. Exiting...\n"); + if (signo == SIGINT) { + go_exit = true; + } +} + +int srslte_rf_recv_wrapper(void* h, cf_t* data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t* t) +{ + DEBUG(" ---- Receive %d samples ---- \n", nsamples); + void* ptr[SRSLTE_MAX_PORTS]; + for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { + ptr[i] = data[i]; + } + return srslte_rf_recv_with_time_multi(h, ptr, nsamples, true, NULL, NULL); +} + +int main(int argc, char** argv) +{ + // init cell struct + cell.base.nof_prb = 1; + + parse_args(argc, argv); + + signal(SIGINT, sig_int_handler); + + float srate = 15000.0 * fft_size; + int32_t flen = srate * 10 / 1000; + + printf("Frame length %d samples\n", flen); + + printf("Opening RF device...\n"); + srslte_rf_t rf; + if (srslte_rf_open(&rf, rf_args)) { + fprintf(stderr, "Error opening rf\n"); + exit(-1); + } + + printf("Set RX rate: %.2f MHz\n", srslte_rf_set_rx_srate(&rf, srate) / 1000000); + printf("Set RX gain: %.1f dB\n", srslte_rf_set_rx_gain(&rf, rf_gain)); + printf("Set RX freq: %.2f MHz\n", srslte_rf_set_rx_freq(&rf, 0, rf_freq) / 1000000); + + srslte_ue_mib_sync_nbiot_t mib_sync; + if (srslte_ue_mib_sync_nbiot_init_multi( + &mib_sync, srslte_rf_recv_wrapper, SRSLTE_NBIOT_NUM_RX_ANTENNAS, (void*)&rf)) { + fprintf(stderr, "Error initializing MIB sync object\n"); + exit(-1); + } + + if (srslte_ue_mib_sync_nbiot_set_cell(&mib_sync, cell) != SRSLTE_SUCCESS) { + fprintf(stderr, "Error setting cell for MIB sync object\n"); + exit(-1); + } + + srslte_rf_start_rx_stream(&rf, false); + + int max_frames = 2 * SRSLTE_NPBCH_NUM_FRAMES; + + printf("Trying to receive MIB-NB for n_id_ncell=%d for at most %d frames\n", cell.n_id_ncell, nof_frames); + int sfn_offset = 0; + uint8_t bch_payload_rx[SRSLTE_MIB_NB_LEN] = {}; + int ret = srslte_ue_mib_sync_nbiot_decode(&mib_sync, max_frames, bch_payload_rx, &cell.nof_ports, &sfn_offset); + if (ret == SRSLTE_UE_MIB_NBIOT_FOUND) { + srslte_mib_nb_t mib_nb; + srslte_npbch_mib_unpack(bch_payload_rx, &mib_nb); + srslte_mib_nb_printf(stdout, cell, &mib_nb); + printf("CFO: %+6.2f kHz\n", srslte_nbiot_ue_sync_get_cfo(&mib_sync.ue_sync) / 1000); + } else { + printf("Failed!\n"); + } + + srslte_ue_mib_sync_nbiot_free(&mib_sync); + srslte_rf_close(&rf); + + srslte_dft_exit(); + + return SRSLTE_SUCCESS; +} diff --git a/lib/src/phy/ue/test/ue_sync_test_nbiot_usrp.c b/lib/src/phy/ue/test/ue_sync_test_nbiot_usrp.c new file mode 100644 index 000000000..a5a35ecd3 --- /dev/null +++ b/lib/src/phy/ue/test/ue_sync_test_nbiot_usrp.c @@ -0,0 +1,275 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * A copy of the GNU Affero 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 "srslte/phy/rf/rf.h" +#include "srslte/phy/sync/sync_nbiot.h" +#include "srslte/phy/ue/ue_sync_nbiot.h" +#include "srslte/srslte.h" +#include +#include +#include +#include +#include +#include + +#define CFO_TABLE_MAX_IDX 1024 + +#ifndef DISABLE_GRAPHICS +void init_plots(); +void do_plots_npss(float* corr, float energy, uint32_t size); +void do_plots_cfo(float* cfo_table, uint32_t size); +#endif + +bool disable_plots = false; +char* rf_args = ""; +float rf_gain = 70.0, rf_freq = -1.0; +int nof_frames = -1; +uint32_t fft_size = 128; +float threshold = 20.0; +int N_id_2_sync = -1; +float cfo_ema = 0.2; +float do_cfo_corr = true; +srslte_cp_t cp = SRSLTE_CP_NORM; + +srslte_nbiot_cell_t cell = { + .base = {.nof_prb = 1, .nof_ports = 1, .cp = SRSLTE_CP_NORM, .id = 0}, + .nbiot_prb = 0, +}; + +void usage(char* prog) +{ + printf("Usage: %s [aedgtvndp] -f rx_frequency_hz\n", prog); + printf("\t-a RF args [Default %s]\n", rf_args); + printf("\t-g RF Gain [Default %.2f dB]\n", rf_gain); + printf("\t-n nof_frames [Default %d]\n", nof_frames); + printf("\t-c Set CFO EMA value [Default %f]\n", cfo_ema); + printf("\t-C Disable CFO correction [Default Enabled]\n"); + printf("\t-s symbol_sz [Default %d]\n", fft_size); + printf("\t-t threshold [Default %.2f]\n", threshold); +#ifndef DISABLE_GRAPHICS + printf("\t-d disable plots [Default enabled]\n"); +#else + printf("\t plots are disabled. Graphics library not available\n"); +#endif + printf("\t-v srslte_verbose\n"); +} + +void parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "acCdgntvsfil")) != -1) { + switch (opt) { + case 'a': + rf_args = argv[optind]; + break; + case 'g': + rf_gain = strtof(argv[optind], NULL); + break; + case 'f': + rf_freq = strtof(argv[optind], NULL); + break; + case 't': + threshold = strtof(argv[optind], NULL); + break; + case 'c': + cfo_ema = strtof(argv[optind], NULL); + break; + case 'C': + do_cfo_corr = false; + break; + case 's': + fft_size = (int)strtol(argv[optind], NULL, 10); + break; + case 'n': + nof_frames = (int)strtol(argv[optind], NULL, 10); + break; + case 'd': + disable_plots = true; + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (rf_freq < 0) { + usage(argv[0]); + exit(-1); + } +} + +bool go_exit = false; +void sig_int_handler(int signo) +{ + printf("SIGINT received. Exiting...\n"); + if (signo == SIGINT) { + go_exit = true; + } +} + +int srslte_rf_recv_wrapper_cs(void* h, void* data, uint32_t nsamples, srslte_timestamp_t* t) +{ + DEBUG(" ---- Receive %d samples ---- \n", nsamples); + return srslte_rf_recv(h, data, nsamples, 1); +} + +int main(int argc, char** argv) +{ + parse_args(argc, argv); + +#ifndef DISABLE_GRAPHICS + float cfo_table[CFO_TABLE_MAX_IDX]; + uint32_t cfo_table_index = 0; + uint32_t cfo_num_plot = CFO_TABLE_MAX_IDX; + + if (!disable_plots) { + init_plots(); + } +#endif + + signal(SIGINT, sig_int_handler); + + float srate = 15000.0 * fft_size; + int32_t flen = srate * 10 / 1000; + + printf("Frame length %d samples\n", flen); + + printf("Opening RF device...\n"); + srslte_rf_t rf; + if (srslte_rf_open(&rf, rf_args)) { + fprintf(stderr, "Error opening rf\n"); + exit(-1); + } + + printf("Set RX rate: %.2f MHz\n", srslte_rf_set_rx_srate(&rf, srate) / 1000000); + printf("Set RX gain: %.1f dB\n", srslte_rf_set_rx_gain(&rf, rf_gain)); + printf("Set RX freq: %.2f MHz\n", srslte_rf_set_rx_freq(&rf, 0, rf_freq) / 1000000); + + // Allocate memory for rx'ing samples (1 full frame) + cf_t* rx_buffer[SRSLTE_MAX_PORTS] = {NULL, NULL, NULL, NULL}; + for (uint32_t i = 0; i < SRSLTE_NBIOT_NUM_RX_ANTENNAS; i++) { + rx_buffer[i] = srslte_vec_malloc(10 * SRSLTE_SF_LEN_PRB_NBIOT * sizeof(cf_t)); + if (!rx_buffer[i]) { + perror("malloc"); + goto clean_exit; + } + } + + srslte_nbiot_ue_sync_t ue_sync; + if (srslte_nbiot_ue_sync_init(&ue_sync, cell, srslte_rf_recv_wrapper_cs, (void*)&rf)) { + fprintf(stderr, "Error initiating ue_sync\n"); + exit(-1); + } + + srslte_nbiot_ue_sync_set_cfo_enable(&ue_sync, do_cfo_corr); + srslte_nbiot_ue_sync_set_cfo_ema(&ue_sync, cfo_ema); + + srslte_rf_start_rx_stream(&rf, false); + + int frame_cnt = 0; + printf("Trying to keep syncronized to cell for %d frames\n", nof_frames); + + while ((frame_cnt < nof_frames || nof_frames == -1) && !go_exit) { + if (srslte_nbiot_ue_sync_zerocopy_multi(&ue_sync, rx_buffer) < 0) { + fprintf(stderr, "Error calling srslte_nbiot_ue_sync_work()\n"); + break; + } + + if (srslte_nbiot_ue_sync_get_sfidx(&ue_sync) == 0) { + printf("CFO: %+6.2f kHz\r", srslte_nbiot_ue_sync_get_cfo(&ue_sync) / 1000); + frame_cnt++; + } + +#ifndef DISABLE_GRAPHICS + if (!disable_plots) { + // get current CFO estimate + cfo_table[cfo_table_index++] = srslte_nbiot_ue_sync_get_cfo(&ue_sync) / 1000; + if (cfo_table_index == cfo_num_plot) { + do_plots_cfo(cfo_table, cfo_num_plot); + cfo_table_index = 0; + } + } + + if (!disable_plots) { + srslte_npss_synch_t* pss_obj = ue_sync.state == SF_FIND ? &ue_sync.sfind.npss : &ue_sync.strack.npss; + int len = SRSLTE_NPSS_CORR_FILTER_LEN + pss_obj->frame_size - 1; + int max = srslte_vec_max_fi(pss_obj->conv_output_avg, pss_obj->frame_size + pss_obj->fft_size - 1); + do_plots_npss(pss_obj->conv_output_avg, pss_obj->conv_output_avg[max], len); + } +#endif + } + +clean_exit: + srslte_nbiot_ue_sync_free(&ue_sync); + srslte_rf_close(&rf); + + for (uint32_t i = 0; i < SRSLTE_MAX_PORTS; i++) { + if (rx_buffer[i] != NULL) { + free(rx_buffer[i]); + } + } + + exit(0); +} + +/********************************************************************** + * Plotting Functions + ***********************************************************************/ +#ifndef DISABLE_GRAPHICS + +#include "srsgui/srsgui.h" +plot_real_t npss_plot; +plot_real_t cfo_plot; + +float tmp[1000000]; + +void init_plots() +{ + sdrgui_init(); + plot_real_init(&npss_plot); + plot_real_setTitle(&npss_plot, "NPSS xCorr"); + plot_real_setLabels(&npss_plot, "Index", "Absolute value"); + plot_real_setYAxisScale(&npss_plot, 0, 1); + + plot_real_init(&cfo_plot); + plot_real_setTitle(&cfo_plot, "Carrier Frequency Offset"); + plot_real_setLabels(&cfo_plot, "subframe index", "kHz"); + + plot_real_setYAxisScale(&cfo_plot, -3.5, 3.5); + + plot_real_addToWindowGrid(&npss_plot, (char*)"nbiot_ue_sync", 0, 0); + plot_real_addToWindowGrid(&cfo_plot, (char*)"nbiot_ue_sync", 0, 1); +} + +void do_plots_npss(float* corr, float peak, uint32_t size) +{ + srslte_vec_sc_prod_fff(corr, 1. / peak, tmp, size); + plot_real_setNewData(&npss_plot, tmp, size); +} + +void do_plots_cfo(float* cfo_table, uint32_t size) +{ + plot_real_setNewData(&cfo_plot, cfo_table, size); +} + +#endif diff --git a/lib/src/phy/ue/ue_cell_search_nbiot.c b/lib/src/phy/ue/ue_cell_search_nbiot.c new file mode 100644 index 000000000..af3df1c53 --- /dev/null +++ b/lib/src/phy/ue/ue_cell_search_nbiot.c @@ -0,0 +1,158 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * A copy of the GNU Affero 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 +#include +#include +#include +#include + +#include "srslte/phy/ue/ue_cell_search_nbiot.h" + +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + +int srslte_ue_cellsearch_nbiot_init(srslte_ue_cellsearch_nbiot_t* q, + uint32_t max_frames_total, + int(recv_callback)(void*, cf_t* [SRSLTE_MAX_PORTS], uint32_t, srslte_timestamp_t*), + void* stream_handler) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) { + ret = SRSLTE_ERROR; + bzero(q, sizeof(srslte_ue_cellsearch_nbiot_t)); + + if (srslte_nbiot_ue_sync_init_multi( + &q->ue_sync, SRSLTE_NBIOT_MAX_PRB, recv_callback, SRSLTE_NBIOT_NUM_RX_ANTENNAS, stream_handler)) { + fprintf(stderr, "Error initiating ue_sync\n"); + goto clean_exit; + } + + for (uint32_t i = 0; i < SRSLTE_NBIOT_NUM_RX_ANTENNAS; i++) { + q->rx_buffer[i] = srslte_vec_malloc(10 * SRSLTE_SF_LEN_PRB_NBIOT * sizeof(cf_t)); + if (!q->rx_buffer[i]) { + perror("malloc"); + goto clean_exit; + } + } + + // buffer to hold subframes for NSSS detection + q->nsss_buffer = srslte_vec_malloc(SRSLTE_NSSS_NUM_SF_DETECT * SRSLTE_SF_LEN_PRB_NBIOT * sizeof(cf_t)); + if (!q->nsss_buffer) { + perror("malloc"); + goto clean_exit; + } + + q->max_frames = max_frames_total; + q->nof_valid_frames = max_frames_total; + + ret = SRSLTE_SUCCESS; + } + +clean_exit: + if (ret == SRSLTE_ERROR) { + srslte_ue_cellsearch_nbiot_free(q); + } + return ret; +} + +void srslte_ue_cellsearch_nbiot_free(srslte_ue_cellsearch_nbiot_t* q) +{ + for (uint32_t i = 0; i < SRSLTE_NBIOT_NUM_RX_ANTENNAS; i++) { + if (q->rx_buffer[i] != NULL) { + free(q->rx_buffer[i]); + } + } + + srslte_nbiot_ue_sync_free(&q->ue_sync); + bzero(q, sizeof(srslte_ue_cellsearch_nbiot_t)); +} + +int srslte_ue_cellsearch_nbiot_set_nof_valid_frames(srslte_ue_cellsearch_nbiot_t* q, uint32_t nof_frames) +{ + if (nof_frames <= q->max_frames) { + q->nof_valid_frames = nof_frames; + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR; + } +} + +/** This function tries to receive one valid NSSS subframe for cell id detection. + * It stores subframe 9 of two consecutive frames in a buffer and returns. + */ +int srslte_ue_cellsearch_nbiot_scan(srslte_ue_cellsearch_nbiot_t* q) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + uint32_t nof_detected_frames = 0; + uint32_t nof_scanned_frames = 0; + + if (q != NULL) { + ret = SRSLTE_ERROR; + srslte_nbiot_ue_sync_reset(&q->ue_sync); + q->nsss_sf_counter = 0; + + do { + ret = srslte_nbiot_ue_sync_zerocopy_multi(&q->ue_sync, q->rx_buffer); + if (ret < 0) { + fprintf(stderr, "Error calling srslte_nbiot_ue_sync_get_buffer()\n"); + break; + } else if (ret == 1) { + // we are in sync, wait until we have received two full frames, store subframe 9 for both + DEBUG("In tracking state sf_idx=%d\n", srslte_nbiot_ue_sync_get_sfidx(&q->ue_sync)); + if (srslte_nbiot_ue_sync_get_sfidx(&q->ue_sync) == 9) { + // accumulate NSSS subframes for cell id detection + memcpy(&q->nsss_buffer[q->nsss_sf_counter * SRSLTE_SF_LEN_PRB_NBIOT], + q->rx_buffer[0], + SRSLTE_SF_LEN_PRB_NBIOT * sizeof(cf_t)); + q->nsss_sf_counter++; + if (q->nsss_sf_counter == SRSLTE_NSSS_NUM_SF_DETECT) { + DEBUG("Captured %d subframes for NSSS detection.\n", q->nsss_sf_counter); + return SRSLTE_SUCCESS; + } + } + } else if (ret == 0) { + //< This means a peak is not yet found and we are in find state, do nothing, just wait and increase + //nof_scanned_frames counter + nof_scanned_frames++; + } + } while (nof_scanned_frames < q->max_frames && nof_detected_frames < q->nof_valid_frames); + } + return SRSLTE_ERROR; +} + +int srslte_ue_cellsearch_nbiot_detect(srslte_ue_cellsearch_nbiot_t* q, srslte_nbiot_ue_cellsearch_result_t* found_cells) +{ + int ret = srslte_sync_nbiot_find_cell_id(&q->ue_sync.strack, q->nsss_buffer); + if (ret == SRSLTE_SUCCESS) { + int cell_id = srslte_sync_nbiot_get_cell_id(&q->ue_sync.strack); + if (srslte_cellid_isvalid(cell_id)) { + // save cell, CP and peak + found_cells[0].n_id_ncell = (uint32_t)cell_id; + found_cells[0].peak = q->ue_sync.strack.npss.peak_value; + found_cells[0].psr = srslte_sync_nbiot_get_peak_value(&q->ue_sync.strack); + found_cells[0].cfo = srslte_nbiot_ue_sync_get_cfo(&q->ue_sync); + INFO("CELL SEARCH: Found peak PSR=%.3f, Cell_id: %d\n", found_cells[0].psr, found_cells[0].n_id_ncell); + } + } + return ret; +} diff --git a/lib/src/phy/ue/ue_mib_nbiot.c b/lib/src/phy/ue/ue_mib_nbiot.c new file mode 100644 index 000000000..cf2e54f70 --- /dev/null +++ b/lib/src/phy/ue/ue_mib_nbiot.c @@ -0,0 +1,272 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * A copy of the GNU Affero 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 "srslte/phy/ue/ue_mib_nbiot.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" +#include +#include +#include + +int srslte_ue_mib_nbiot_init(srslte_ue_mib_nbiot_t* q, cf_t** in_buffer, uint32_t max_prb) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) { + ret = SRSLTE_ERROR; + bzero(q, sizeof(srslte_ue_mib_nbiot_t)); + + if (srslte_npbch_init(&q->npbch)) { + fprintf(stderr, "Error initiating NPBCH\n"); + goto clean_exit; + } + + q->sf_symbols = srslte_vec_malloc(SRSLTE_SF_LEN_RE(max_prb, SRSLTE_CP_NORM) * sizeof(cf_t)); + if (!q->sf_symbols) { + perror("malloc"); + goto clean_exit; + } + + for (uint32_t i = 0; i < SRSLTE_MAX_PORTS; i++) { + q->ce[i] = srslte_vec_malloc(SRSLTE_SF_LEN_RE(max_prb, SRSLTE_CP_NORM) * sizeof(cf_t)); + if (!q->ce[i]) { + perror("malloc"); + goto clean_exit; + } + } + + if (srslte_ofdm_rx_init(&q->fft, SRSLTE_CP_NORM, in_buffer[0], q->sf_symbols, max_prb)) { + fprintf(stderr, "Error initializing FFT\n"); + goto clean_exit; + } + srslte_ofdm_set_freq_shift(&q->fft, SRSLTE_NBIOT_FREQ_SHIFT_FACTOR); + + if (srslte_chest_dl_nbiot_init(&q->chest, max_prb)) { + fprintf(stderr, "Error initializing reference signal\n"); + goto clean_exit; + } + srslte_ue_mib_nbiot_reset(q); + + ret = SRSLTE_SUCCESS; + } + +clean_exit: + if (ret == SRSLTE_ERROR) { + srslte_ue_mib_nbiot_free(q); + } + return ret; +} + +void srslte_ue_mib_nbiot_free(srslte_ue_mib_nbiot_t* q) +{ + if (q->sf_symbols) { + free(q->sf_symbols); + } + for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { + if (q->ce[i]) { + free(q->ce[i]); + } + } + srslte_sync_nbiot_free(&q->sfind); + srslte_chest_dl_nbiot_free(&q->chest); + srslte_npbch_free(&q->npbch); + srslte_ofdm_rx_free(&q->fft); + + bzero(q, sizeof(srslte_ue_mib_nbiot_t)); +} + +int srslte_ue_mib_nbiot_set_cell(srslte_ue_mib_nbiot_t* q, srslte_nbiot_cell_t cell) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && cell.nof_ports <= SRSLTE_NBIOT_MAX_PORTS) { + ret = SRSLTE_ERROR; + + if (srslte_npbch_set_cell(&q->npbch, cell)) { + fprintf(stderr, "Error setting cell in NPBCH\n"); + goto clean_exit; + } + + if (cell.nof_ports == 0) { + cell.nof_ports = SRSLTE_NBIOT_MAX_PORTS; + } + + if (srslte_chest_dl_nbiot_set_cell(&q->chest, cell)) { + fprintf(stderr, "Error initializing reference signal\n"); + goto clean_exit; + } + srslte_ue_mib_nbiot_reset(q); + + ret = SRSLTE_SUCCESS; + } + +clean_exit: + if (ret == SRSLTE_ERROR) { + srslte_ue_mib_nbiot_free(q); + } + return ret; +} + +void srslte_ue_mib_nbiot_reset(srslte_ue_mib_nbiot_t* q) +{ + q->frame_cnt = 0; + srslte_npbch_decode_reset(&q->npbch); +} + +int srslte_ue_mib_nbiot_decode(srslte_ue_mib_nbiot_t* q, + cf_t* input, + uint8_t* bch_payload, + uint32_t* nof_tx_ports, + int* sfn_offset) +{ + // Run FFT for the symbols + srslte_ofdm_rx_sf(&q->fft); + + // Get channel estimates of sf idx #0 for each port + if (srslte_chest_dl_nbiot_estimate(&q->chest, q->sf_symbols, q->ce, 0) < 0) { + return SRSLTE_ERROR; + } + + // Reset decoder if we missed a NPBCH TTI + if (q->frame_cnt > SRSLTE_NPBCH_NUM_FRAMES) { + INFO("Resetting NPBCH decoder after %d frames\n", q->frame_cnt); + srslte_ue_mib_nbiot_reset(q); + return SRSLTE_UE_MIB_NBIOT_NOTFOUND; + } + + // Decode NPBCH + if (SRSLTE_SUCCESS == srslte_npbch_decode_nf(&q->npbch, + q->sf_symbols, + q->ce, + srslte_chest_dl_nbiot_get_noise_estimate(&q->chest), + bch_payload, + nof_tx_ports, + sfn_offset, + 0)) { + DEBUG("BCH decoded ok with offset %d\n", *sfn_offset); + if (memcmp(bch_payload, q->last_bch_payload, SRSLTE_MIB_NB_LEN) == 0) { + DEBUG("BCH content equals last BCH, new counter %d\n", q->frame_cnt); + } else { + // new BCH transmitted + if (q->frame_cnt != 0) { + INFO("MIB-NB decoded: %u with offset %d\n", q->frame_cnt, *sfn_offset); + if (*sfn_offset != 0) { + INFO("New BCH was decoded at block offset %d. SFN may be corrupted.", *sfn_offset); + } + srslte_ue_mib_nbiot_reset(q); + return SRSLTE_UE_MIB_NBIOT_FOUND; + } else { + // store new BCH + DEBUG("New BCH transmitted after %d frames\n", q->frame_cnt); + memcpy(q->last_bch_payload, bch_payload, SRSLTE_MIB_NB_LEN); + } + } + q->frame_cnt++; + } + return SRSLTE_UE_MIB_NBIOT_NOTFOUND; +} + +int srslte_ue_mib_sync_nbiot_init_multi( + srslte_ue_mib_sync_nbiot_t* q, + int(recv_callback)(void*, cf_t* [SRSLTE_MAX_PORTS], uint32_t, srslte_timestamp_t*), + uint32_t nof_rx_antennas, + void* stream_handler) +{ + // Allocate memory for time re-alignment and MIB detection + for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { + q->sf_buffer[i] = NULL; + } + for (int i = 0; i < nof_rx_antennas; i++) { + q->sf_buffer[i] = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_SF_LEN_PRB_NBIOT * 10); + } + q->nof_rx_antennas = nof_rx_antennas; + + if (srslte_ue_mib_nbiot_init(&q->ue_mib, q->sf_buffer, SRSLTE_NBIOT_MAX_PRB)) { + fprintf(stderr, "Error initiating ue_mib\n"); + return SRSLTE_ERROR; + } + if (srslte_nbiot_ue_sync_init_multi( + &q->ue_sync, SRSLTE_NBIOT_MAX_PRB, recv_callback, q->nof_rx_antennas, stream_handler)) { + fprintf(stderr, "Error initiating ue_sync\n"); + srslte_ue_mib_nbiot_free(&q->ue_mib); + return SRSLTE_ERROR; + } + return SRSLTE_SUCCESS; +} + +int srslte_ue_mib_sync_nbiot_set_cell(srslte_ue_mib_sync_nbiot_t* q, srslte_nbiot_cell_t cell) +{ + if (srslte_ue_mib_nbiot_set_cell(&q->ue_mib, cell)) { + fprintf(stderr, "Error initiating ue_mib\n"); + return SRSLTE_ERROR; + } + if (srslte_nbiot_ue_sync_set_cell(&q->ue_sync, cell)) { + fprintf(stderr, "Error initiating ue_sync\n"); + srslte_ue_mib_nbiot_free(&q->ue_mib); + return SRSLTE_ERROR; + } + return SRSLTE_SUCCESS; +} + +void srslte_ue_mib_sync_nbiot_free(srslte_ue_mib_sync_nbiot_t* q) +{ + srslte_ue_mib_nbiot_free(&q->ue_mib); + srslte_nbiot_ue_sync_free(&q->ue_sync); +} + +void srslte_ue_mib_sync_nbiot_reset(srslte_ue_mib_sync_nbiot_t* q) +{ + srslte_ue_mib_nbiot_reset(&q->ue_mib); + srslte_nbiot_ue_sync_reset(&q->ue_sync); +} + +int srslte_ue_mib_sync_nbiot_decode(srslte_ue_mib_sync_nbiot_t* q, + uint32_t max_frames_timeout, + uint8_t* bch_payload, + uint32_t* nof_tx_ports, + int* sfn_offset) +{ + int mib_ret = SRSLTE_UE_MIB_NBIOT_NOTFOUND; + + if (q != NULL) { + int ret = SRSLTE_SUCCESS; + uint32_t nof_frames = 0; + do { + mib_ret = SRSLTE_UE_MIB_NBIOT_NOTFOUND; + ret = srslte_nbiot_ue_sync_zerocopy_multi(&q->ue_sync, q->sf_buffer); + if (ret < 0) { + fprintf(stderr, "Error calling srslte_nbiot_ue_sync_zerocopy_multi()\n"); + break; + } else if (srslte_nbiot_ue_sync_get_sfidx(&q->ue_sync) == 0) { + mib_ret = srslte_ue_mib_nbiot_decode(&q->ue_mib, NULL, bch_payload, nof_tx_ports, sfn_offset); + if (mib_ret < 0) { + DEBUG("Resetting NPBCH decoder after %d frames\n", q->ue_mib.frame_cnt); + srslte_ue_mib_nbiot_reset(&q->ue_mib); + } + nof_frames++; + } + } while (mib_ret == SRSLTE_UE_MIB_NBIOT_NOTFOUND && ret >= 0 && nof_frames < max_frames_timeout); + if (mib_ret < 0) { + ret = mib_ret; + } + } + return mib_ret; +} diff --git a/lib/src/phy/ue/ue_sync_nbiot.c b/lib/src/phy/ue/ue_sync_nbiot.c new file mode 100644 index 000000000..61f185dc7 --- /dev/null +++ b/lib/src/phy/ue/ue_sync_nbiot.c @@ -0,0 +1,669 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * A copy of the GNU Affero 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 "srslte/phy/ue/ue_sync_nbiot.h" +#include "srslte/phy/io/filesource.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" +#include +#include +#include +#include +#include +#include + +#define REPEAT_FROM_FILE 0 +#define TIME_ALIGN_FROM_FILE 1 +#define MAX_TIME_OFFSET 128 +cf_t dummy[MAX_TIME_OFFSET]; + +#define TRACK_MAX_LOST 4 +#define TRACK_FRAME_SIZE 32 +#define FIND_NOF_AVG_FRAMES 4 +#define DEFAULT_SFO_EMA_COEFF 0.1 + +cf_t dummy_buffer_nbiot0[15 * 2048 / 2]; +cf_t dummy_buffer_nbiot1[15 * 2048 / 2]; + +// FIXME: this will break for 4 antennas!! +cf_t* dummy_offset_buffer_nbiot[SRSLTE_MAX_PORTS] = {dummy_buffer_nbiot0, dummy_buffer_nbiot1}; + +///< This is a list of CFO candidates that the sync object uses to pre-compensate the received signal +static const float cfo_cands[] = + {0.0, 1000.0, -1000.0, 2000.0, -2000.0, 3000.0, -3000.0, 4000.0, -4000.0, 5000.0, -5000.0}; + +int srslte_nbiot_ue_sync_init_file(srslte_nbiot_ue_sync_t* q, + srslte_nbiot_cell_t cell, + char* file_name, + int offset_time, + float offset_freq) +{ + return srslte_nbiot_ue_sync_init_file_multi(q, cell, file_name, offset_time, offset_freq, 1); +} + +int srslte_nbiot_ue_sync_init_file_multi(srslte_nbiot_ue_sync_t* q, + srslte_nbiot_cell_t cell, + char* file_name, + int offset_time, + float offset_freq, + uint32_t nof_rx_ant) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && file_name != NULL && srslte_nofprb_isvalid(cell.base.nof_prb)) { + ret = SRSLTE_ERROR; + bzero(q, sizeof(srslte_nbiot_ue_sync_t)); + q->file_mode = true; + q->sf_len = SRSLTE_SF_LEN(srslte_symbol_sz(cell.base.nof_prb)); + q->file_cfo = -offset_freq; + q->correct_cfo = true; + q->fft_size = srslte_symbol_sz(cell.base.nof_prb); + + if (nof_rx_ant != 1) { + fprintf(stderr, "With file input, only single Rx antenna is supported.\n"); + goto clean_exit; + } + q->nof_rx_antennas = nof_rx_ant; + + if (srslte_cfo_init(&q->file_cfo_correct, 2 * q->sf_len)) { + fprintf(stderr, "Error initiating CFO\n"); + goto clean_exit; + } + + if (srslte_filesource_init(&q->file_source, file_name, SRSLTE_COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", file_name); + goto clean_exit; + } + +#if TIME_ALIGN_FROM_FILE + q->frame_len = SRSLTE_NOF_SF_X_FRAME * q->sf_len; + if (srslte_sync_nbiot_init(&q->sfind, q->frame_len, q->frame_len, q->fft_size)) { + fprintf(stderr, "Error initiating sync find\n"); + goto clean_exit; + } + int n = srslte_filesource_read(&q->file_source, dummy_buffer_nbiot0, q->frame_len); + if (n != q->frame_len) { + fprintf(stderr, "Error reading frame from file.\n"); + exit(-1); + } + + // find NPSS and set offset time parameter to beginning of next frame + uint32_t peak_idx; + if (srslte_sync_nbiot_find(&q->sfind, dummy_buffer_nbiot0, 0, &peak_idx) == SRSLTE_SYNC_ERROR) { + fprintf(stderr, "Error finding NPSS peak\n"); + exit(-1); + } + offset_time = (peak_idx - SRSLTE_NPSS_CORR_OFFSET + q->frame_len / 2) % q->frame_len; + srslte_sync_nbiot_free(&q->sfind); +#endif + + INFO("Offseting input file by %d samples and %.1f kHz\n", offset_time, offset_freq / 1000); + + srslte_filesource_read(&q->file_source, dummy_buffer_nbiot0, offset_time); + srslte_nbiot_ue_sync_reset(q); + + ret = SRSLTE_SUCCESS; + } +clean_exit: + if (ret == SRSLTE_ERROR) { + srslte_nbiot_ue_sync_free(q); + } + return ret; +} + +int srslte_nbiot_ue_sync_start_agc(srslte_nbiot_ue_sync_t* q, + float(set_gain_callback)(void*, float), + float init_gain_value) +{ + uint32_t nframes; + if (q->nof_recv_sf == 1) { + nframes = 10; + } else { + nframes = 0; + } + int n = srslte_agc_init_uhd(&q->agc, SRSLTE_AGC_MODE_PEAK_AMPLITUDE, nframes, set_gain_callback, q->stream); + q->do_agc = n == 0 ? true : false; + if (q->do_agc) { + srslte_agc_set_gain(&q->agc, init_gain_value); + } + return n; +} + +int recv_callback_nbiot_multi_to_single(void* h, cf_t* x[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t* t) +{ + srslte_nbiot_ue_sync_t* q = (srslte_nbiot_ue_sync_t*)h; + return q->recv_callback_single(q->stream_single, (void*)x[0], nsamples, t); +} + +int srslte_nbiot_ue_sync_init(srslte_nbiot_ue_sync_t* q, + srslte_nbiot_cell_t cell, + int(recv_callback)(void*, void*, uint32_t, srslte_timestamp_t*), + void* stream_handler) +{ + int ret = srslte_nbiot_ue_sync_init_multi( + q, SRSLTE_NBIOT_MAX_PRB, recv_callback_nbiot_multi_to_single, SRSLTE_NBIOT_NUM_RX_ANTENNAS, (void*)q); + q->recv_callback_single = recv_callback; + q->stream_single = stream_handler; + return ret; +} + +int srslte_nbiot_ue_sync_init_multi(srslte_nbiot_ue_sync_t* q, + uint32_t max_prb, + int(recv_callback)(void*, cf_t* [SRSLTE_MAX_PORTS], uint32_t, srslte_timestamp_t*), + uint32_t nof_rx_antennas, + void* stream_handler) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && stream_handler != NULL && srslte_nofprb_isvalid(max_prb) && recv_callback != NULL) { + ret = SRSLTE_ERROR; + + bzero(q, sizeof(srslte_nbiot_ue_sync_t)); + + q->stream = stream_handler; + q->recv_callback = recv_callback; + q->fft_size = srslte_symbol_sz(max_prb); + q->sf_len = SRSLTE_SF_LEN(q->fft_size); + q->file_mode = false; + q->correct_cfo = true; + q->nof_rx_antennas = nof_rx_antennas; + q->agc_period = 0; + q->sample_offset_correct_period = DEFAULT_SAMPLE_OFFSET_CORRECT_PERIOD; + q->sfo_ema = DEFAULT_SFO_EMA_COEFF; + q->max_prb = max_prb; + + // we search for NPSS/NSSS in a full frame + q->nof_recv_sf = 10; + q->frame_len = q->nof_recv_sf * q->sf_len; + + if (srslte_sync_nbiot_init(&q->sfind, q->frame_len, q->frame_len, q->fft_size)) { + fprintf(stderr, "Error initiating sync find\n"); + goto clean_exit; + } + + // in tracking phase we only sample for one subframe but still use the entire + // subframe to run the correlation (FIXME: use only one symbol?) + if (srslte_sync_nbiot_init(&q->strack, q->sf_len, q->sf_len, q->fft_size)) { + fprintf(stderr, "Error initiating sync track\n"); + goto clean_exit; + } + + srslte_nbiot_ue_sync_reset(q); + + ret = SRSLTE_SUCCESS; + } + +clean_exit: + if (ret == SRSLTE_ERROR) { + srslte_nbiot_ue_sync_free(q); + } + return ret; +} + +uint32_t srslte_nbiot_ue_sync_sf_len(srslte_nbiot_ue_sync_t* q) +{ + return q->frame_len; +} + +void srslte_nbiot_ue_sync_free(srslte_nbiot_ue_sync_t* q) +{ + if (q->do_agc) { + srslte_agc_free(&q->agc); + } + if (!q->file_mode) { + srslte_sync_nbiot_free(&q->sfind); + srslte_sync_nbiot_free(&q->strack); + } else { + srslte_filesource_free(&q->file_source); + } + bzero(q, sizeof(srslte_nbiot_ue_sync_t)); +} + +int srslte_nbiot_ue_sync_set_cell(srslte_nbiot_ue_sync_t* q, srslte_nbiot_cell_t cell) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && srslte_nofprb_isvalid(cell.base.nof_prb)) { + ret = SRSLTE_ERROR; + + if (cell.base.nof_prb > q->max_prb) { + fprintf(stderr, "Error in nbiot_ue_sync_set_cell(): cell.base.nof_prb must be lower than initialized\n"); + return SRSLTE_ERROR; + } + + q->cell = cell; + q->fft_size = srslte_symbol_sz(q->cell.base.nof_prb); + q->sf_len = SRSLTE_SF_LEN(q->fft_size); + q->agc_period = 0; + + // we search for NPSS/NSSS in a full frame + q->nof_recv_sf = SRSLTE_NOF_SF_X_FRAME; + q->frame_len = q->nof_recv_sf * q->sf_len; + + if (srslte_sync_nbiot_resize(&q->sfind, q->frame_len, q->frame_len, q->fft_size)) { + fprintf(stderr, "Error resizing sync find\n"); + return SRSLTE_ERROR; + } + + if (srslte_sync_nbiot_resize(&q->strack, q->sf_len, q->sf_len, q->fft_size)) { + fprintf(stderr, "Error resizing sync find\n"); + return SRSLTE_ERROR; + } + + srslte_nbiot_ue_sync_reset(q); + + ret = SRSLTE_SUCCESS; + } + + return ret; +} + +void srslte_nbiot_ue_sync_get_last_timestamp(srslte_nbiot_ue_sync_t* q, srslte_timestamp_t* timestamp) +{ + memcpy(timestamp, &q->last_timestamp, sizeof(srslte_timestamp_t)); +} + +uint32_t srslte_nbiot_ue_sync_peak_idx(srslte_nbiot_ue_sync_t* q) +{ + return q->peak_idx; +} + +srslte_nbiot_ue_sync_state_t srslte_nbiot_ue_sync_get_state(srslte_nbiot_ue_sync_t* q) +{ + return q->state; +} +uint32_t srslte_nbiot_ue_sync_get_sfidx(srslte_nbiot_ue_sync_t* q) +{ + return q->sf_idx; +} + +void srslte_nbiot_ue_sync_set_cfo_enable(srslte_nbiot_ue_sync_t* q, bool enable) +{ + srslte_sync_nbiot_set_cfo_enable(&q->sfind, enable); + srslte_sync_nbiot_set_cfo_enable(&q->strack, enable); +} + +float srslte_nbiot_ue_sync_get_cfo(srslte_nbiot_ue_sync_t* q) +{ + return 15000 * (q->state == SF_TRACK ? srslte_sync_nbiot_get_cfo(&q->strack) : srslte_sync_nbiot_get_cfo(&q->sfind)); +} + +void srslte_nbiot_ue_sync_set_cfo(srslte_nbiot_ue_sync_t* q, float cfo) +{ + srslte_sync_nbiot_set_cfo(&q->sfind, cfo / 15000); + srslte_sync_nbiot_set_cfo(&q->strack, cfo / 15000); +} + +float srslte_nbiot_ue_sync_get_sfo(srslte_nbiot_ue_sync_t* q) +{ + return q->mean_sfo / 5e-3; +} + +int srslte_nbiot_ue_sync_get_last_sample_offset(srslte_nbiot_ue_sync_t* q) +{ + return q->last_sample_offset; +} + +void srslte_nbiot_ue_sync_set_sample_offset_correct_period(srslte_nbiot_ue_sync_t* q, uint32_t nof_subframes) +{ + q->sample_offset_correct_period = nof_subframes; +} + +void srslte_nbiot_ue_sync_set_cfo_ema(srslte_nbiot_ue_sync_t* q, float ema) +{ + srslte_sync_nbiot_set_cfo_ema_alpha(&q->sfind, ema); + srslte_sync_nbiot_set_cfo_ema_alpha(&q->strack, ema); +} + +void srslte_nbiot_ue_sync_set_cfo_tol(srslte_nbiot_ue_sync_t* q, float cfo_tol) +{ + srslte_sync_nbiot_set_cfo_tol(&q->sfind, cfo_tol); + srslte_sync_nbiot_set_cfo_tol(&q->strack, cfo_tol); +} + +void srslte_nbiot_ue_sync_set_sfo_ema(srslte_nbiot_ue_sync_t* q, float ema_coefficient) +{ + q->sfo_ema = ema_coefficient; +} + +void srslte_nbiot_ue_sync_set_agc_period(srslte_nbiot_ue_sync_t* q, uint32_t period) +{ + q->agc_period = period; +} + +static int find_peak_ok(srslte_nbiot_ue_sync_t* q, cf_t* input_buffer[SRSLTE_MAX_PORTS]) +{ + // set subframe idx to NPSS position + q->sf_idx = 5; + q->frame_find_cnt++; + DEBUG("Found peak %d at %d, value %.3f, n_id_ncell: %d\n", + q->frame_find_cnt, + q->peak_idx, + srslte_sync_nbiot_get_peak_value(&q->sfind), + q->cell.n_id_ncell); + + if (q->frame_find_cnt >= q->nof_avg_find_frames || q->peak_idx < 2 * q->fft_size) { + int num_drop = (q->peak_idx - SRSLTE_NPSS_CORR_OFFSET + q->frame_len / 2) % q->frame_len; + INFO("Realigning frame, reading %d samples\n", num_drop); + /* Receive the rest of the subframe so that we are subframe aligned */ + if (q->recv_callback(q->stream, input_buffer, num_drop, &q->last_timestamp) < 0) { + return SRSLTE_ERROR; + } + + ///< reset variables + q->frame_ok_cnt = 0; + q->frame_no_cnt = 0; + q->frame_total_cnt = 0; + q->frame_find_cnt = 0; + q->mean_sample_offset = 0; + q->sf_idx = 9; + + ///< adjust sampling parameters + q->nof_recv_sf = 1; + q->frame_len = q->nof_recv_sf * q->sf_len; + + ///< go to tracking state + q->state = SF_TRACK; + + ///< Initialize track state CFO + q->strack.mean_cfo = q->sfind.mean_cfo; + q->strack.cfo_i = q->sfind.cfo_i; + } + + return 0; +} + +static int track_peak_ok(srslte_nbiot_ue_sync_t* q, uint32_t track_idx) +{ + // Get sampling time offset + q->last_sample_offset = ((int)track_idx - SRSLTE_NPSS_CORR_OFFSET); + + // Adjust sampling time every q->sample_offset_correct_period subframes + uint32_t frame_idx = 0; + if (q->sample_offset_correct_period) { + frame_idx = q->frame_ok_cnt % q->sample_offset_correct_period; + q->mean_sample_offset += (float)q->last_sample_offset / q->sample_offset_correct_period; + } else { + q->mean_sample_offset = q->last_sample_offset; + } + + // Compute cumulative moving average time offset + if (!frame_idx) { + // Adjust RF sampling time based on the mean sampling offset + q->next_rf_sample_offset = (int)round(q->mean_sample_offset); + + // Reset PSS averaging if correcting every a period longer than 1 + if (q->sample_offset_correct_period > 1) { + srslte_sync_nbiot_reset(&q->strack); + } + + // Compute SFO based on mean sample offset + if (q->sample_offset_correct_period) { + q->mean_sample_offset /= q->sample_offset_correct_period; + } + q->mean_sfo = SRSLTE_VEC_EMA(q->mean_sample_offset, q->mean_sfo, q->sfo_ema); + + if (q->next_rf_sample_offset) { + INFO("Time offset adjustment: %d samples (%.2f), mean SFO: %.2f Hz, %.5f samples/10-sf, ema=%f, length=%d\n", + q->next_rf_sample_offset, + q->mean_sample_offset, + srslte_nbiot_ue_sync_get_sfo(q), + q->mean_sfo, + q->sfo_ema, + q->sample_offset_correct_period); + } + q->mean_sample_offset = 0; + } + + ///< If the NPSS peak is beyond the frame we sample too slow, discard the offseted samples to align next frame + if (q->next_rf_sample_offset > 0 && q->next_rf_sample_offset < MAX_TIME_OFFSET) { + DEBUG("Positive time offset %d samples.\n", q->next_rf_sample_offset); + if (q->recv_callback( + q->stream, &dummy_offset_buffer_nbiot[0], (uint32_t)q->next_rf_sample_offset, &q->last_timestamp) < 0) { + fprintf(stderr, "Error receiving from USRP\n"); + return SRSLTE_ERROR; + } + q->next_rf_sample_offset = 0; + } + + q->peak_idx = q->sf_len / 2 + q->last_sample_offset; + q->frame_ok_cnt++; + + return 1; +} + +static int track_peak_no(srslte_nbiot_ue_sync_t* q) +{ + ///< if we missed too many NPSS, we go back to FIND and consider this frame unsynchronized + q->frame_no_cnt++; + if (q->frame_no_cnt >= TRACK_MAX_LOST) { + INFO("%d frames lost. Going back to FIND\n", (int)q->frame_no_cnt); + q->nof_recv_sf = 10; + q->frame_len = q->nof_recv_sf * q->sf_len; + q->state = SF_FIND; + return 0; + } else { + INFO("Tracking peak not found. Peak %.3f, %d lost\n", + srslte_sync_nbiot_get_peak_value(&q->strack), + (int)q->frame_no_cnt); + /* + printf("Saving files: pss_corr (%d), input (%d)\n", q->strack.pss.frame_size, SRSLTE_SF_LEN_PRB(q->cell.nof_prb)); + srslte_vec_save_file("pss_corr", q->strack.pss.conv_output_avg, q->strack.pss.frame_size*sizeof(float)); + srslte_vec_save_file("input", q->input_buffer, SRSLTE_SF_LEN_PRB(q->cell.nof_prb)*sizeof(cf_t)); + exit(-1); + */ + return 1; + } +} + +static int receive_samples(srslte_nbiot_ue_sync_t* q, cf_t* input_buffer[SRSLTE_MAX_PORTS]) +{ + ///< A negative time offset means there are samples in our buffer for the next subframe, because we are sampling too + ///< fast + if (q->next_rf_sample_offset < 0) { + q->next_rf_sample_offset = -q->next_rf_sample_offset; + } + + ///< Get N subframes from the USRP getting more samples and keeping the previous samples, if any + cf_t* ptr[SRSLTE_MAX_PORTS] = {NULL, NULL, NULL, NULL}; + for (int i = 0; i < q->nof_rx_antennas; i++) { + ptr[i] = &input_buffer[i][q->next_rf_sample_offset]; + } + + if (q->recv_callback(q->stream, ptr, q->frame_len - q->next_rf_sample_offset, &q->last_timestamp) < 0) { + return SRSLTE_ERROR; + } + ///< reset time offset + q->next_rf_sample_offset = 0; + + return SRSLTE_SUCCESS; +} + +int srslte_nbiot_ue_sync_zerocopy(srslte_nbiot_ue_sync_t* q, cf_t* input_buffer) +{ + cf_t* _input_buffer[SRSLTE_MAX_PORTS] = {NULL}; + _input_buffer[0] = input_buffer; + return srslte_nbiot_ue_sync_zerocopy_multi(q, _input_buffer); +} + +/* Returns 1 if the subframe is synchronized in time, 0 otherwise */ +int srslte_nbiot_ue_sync_zerocopy_multi(srslte_nbiot_ue_sync_t* q, cf_t* input_buffer[SRSLTE_MAX_PORTS]) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) { + if (q->file_mode) { + int n = srslte_filesource_read(&q->file_source, input_buffer[0], q->sf_len); + if (n < q->sf_len) { + fprintf(stderr, "Error reading input file, read %d bytes\n", n); +#if REPEAT_FROM_FILE + srslte_filesource_seek(&q->file_source, 0); +#else + return SRSLTE_ERROR; +#endif + } + if (q->correct_cfo) { + for (int i = 0; i < q->nof_rx_antennas; i++) { + srslte_cfo_correct(&q->file_cfo_correct, input_buffer[i], input_buffer[i], q->file_cfo / 15000 / q->fft_size); + } + } + q->sf_idx++; + if (q->sf_idx == 10) { + q->sf_idx = 0; + } + DEBUG("Reading %d samples. sf_idx = %d\n", q->sf_len, q->sf_idx); + ret = 1; + } else { + if (receive_samples(q, input_buffer)) { + fprintf(stderr, "Error receiving samples\n"); + return SRSLTE_ERROR; + } + + switch (q->state) { + case SF_FIND: + switch (srslte_sync_nbiot_find(&q->sfind, input_buffer[0], 0, &q->peak_idx)) { + case SRSLTE_SYNC_ERROR: + ret = SRSLTE_ERROR; + fprintf(stderr, "Error finding correlation peak (%d)\n", ret); + return SRSLTE_ERROR; + case SRSLTE_SYNC_FOUND: + ret = find_peak_ok(q, input_buffer); + break; + case SRSLTE_SYNC_FOUND_NOSPACE: + /* If a peak was found but there is not enough space for SSS/CP detection, discard a few samples */ + printf("No space for SSS/CP detection. Realigning frame...\n"); + q->recv_callback(q->stream, dummy_offset_buffer_nbiot, q->frame_len / 2, NULL); + srslte_sync_nbiot_reset(&q->sfind); + ret = SRSLTE_SUCCESS; + break; + default: + ret = SRSLTE_SUCCESS; + break; + } + if (q->do_agc) { + srslte_agc_process(&q->agc, input_buffer[0], q->sf_len); + } + break; + case SF_TRACK: + ret = 1; + q->sf_idx = (q->sf_idx + q->nof_recv_sf) % SRSLTE_NOF_SF_X_FRAME; + + ///< Every SF idx 5, find peak around known position q->peak_idx + if (q->sf_idx == 5) { + if (q->do_agc && (q->agc_period == 0 || (q->agc_period && (q->frame_total_cnt % q->agc_period) == 0))) { + srslte_agc_process(&q->agc, input_buffer[0], q->sf_len); + } + +#ifdef MEASURE_EXEC_TIME + struct timeval t[3]; + gettimeofday(&t[1], NULL); +#endif + uint32_t track_idx = 0; + + // Track NPSS around the expected position + uint32_t find_offset = q->frame_len / 2 - q->strack.max_offset / 2; + switch (srslte_sync_nbiot_find(&q->strack, input_buffer[0], find_offset, &track_idx)) { + case SRSLTE_SYNC_ERROR: + ret = SRSLTE_ERROR; + fprintf(stderr, "Error tracking correlation peak\n"); + return SRSLTE_ERROR; + case SRSLTE_SYNC_FOUND: + ret = track_peak_ok(q, track_idx); + break; + case SRSLTE_SYNC_FOUND_NOSPACE: + // It's very very unlikely that we fall here because this event should happen at FIND phase only + ret = 0; + q->state = SF_FIND; + printf("Warning: No space for SSS/CP while in tracking phase\n"); + break; + case SRSLTE_SYNC_NOFOUND: + ret = track_peak_no(q); + break; + } + +#ifdef MEASURE_EXEC_TIME + gettimeofday(&t[2], NULL); + get_time_interval(t); + q->mean_exec_time = (float)SRSLTE_VEC_CMA((float)t[0].tv_usec, q->mean_exec_time, q->frame_total_cnt); +#endif + + if (ret == SRSLTE_ERROR) { + fprintf(stderr, "Error processing tracking peak\n"); + q->state = SF_FIND; + return SRSLTE_SUCCESS; + } + + q->frame_total_cnt++; + } else { + if (q->correct_cfo) { + for (int i = 0; i < q->nof_rx_antennas; i++) { + srslte_cfo_correct(&q->strack.cfocorr, + input_buffer[i], + input_buffer[i], + -srslte_sync_nbiot_get_cfo(&q->strack) / q->fft_size); + } + } + } + break; + } + } + } + return ret; +} + +void srslte_nbiot_ue_sync_reset(srslte_nbiot_ue_sync_t* q) +{ + ///< Set default params + srslte_sync_nbiot_set_cfo_enable(&q->sfind, true); + srslte_sync_nbiot_set_cfo_enable(&q->strack, true); + + srslte_sync_nbiot_set_cfo_ema_alpha(&q->sfind, 0.15); + srslte_sync_nbiot_set_cfo_ema_alpha(&q->strack, 0.01); + + ///< In find phase and if the cell is known, do not average NPSS correlation because we only capture 1 subframe and + ///< do not know where the peak is. + q->nof_avg_find_frames = 1; + srslte_sync_nbiot_set_npss_ema_alpha(&q->sfind, 1.0); + srslte_sync_nbiot_set_threshold(&q->sfind, 2.5); + + srslte_sync_nbiot_set_cfo_cand(&q->sfind, cfo_cands, sizeof(cfo_cands) / sizeof(float)); + srslte_sync_nbiot_set_cfo_cand_test_enable(&q->sfind, true); + + srslte_sync_nbiot_set_npss_ema_alpha(&q->strack, 0.1); + srslte_sync_nbiot_set_threshold(&q->strack, 1.2); + + if (!q->file_mode) { + srslte_sync_nbiot_reset(&q->sfind); + srslte_sync_nbiot_reset(&q->strack); + } else { + q->sf_idx = 9; + } + q->state = SF_FIND; + q->frame_ok_cnt = 0; + q->frame_no_cnt = 0; + q->frame_total_cnt = 0; + q->mean_sample_offset = 0.0; + q->next_rf_sample_offset = 0; + q->frame_find_cnt = 0; +#ifdef MEASURE_EXEC_TIME + q->mean_exec_time = 0; +#endif +}