add cell search and USRP capture examples for NB-IoT

master
Andre Puschmann 5 years ago
parent e53910010e
commit 4dbbcaa668

@ -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)
@ -80,5 +85,3 @@ if(RF_FOUND)
else(RF_FOUND)
message(STATUS " examples will NOT BE INSTALLED.")
endif(RF_FOUND)

@ -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 <math.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/time.h>
#include <unistd.h>
#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);
}

@ -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 <math.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#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);
}

@ -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

@ -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

@ -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 <stdbool.h>
#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

@ -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)

@ -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 <math.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#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;
}

@ -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 <math.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#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

@ -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 <assert.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#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;
}

@ -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 <stdlib.h>
#include <string.h>
#include <strings.h>
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;
}

@ -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 <assert.h>
#include <srslte/srslte.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#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
}
Loading…
Cancel
Save