mirror of https://github.com/pvnis/srsRAN_4G.git
add cell search and USRP capture examples for NB-IoT
parent
e53910010e
commit
4dbbcaa668
@ -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
|
@ -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…
Reference in New Issue