diff --git a/lib/include/srslte/phy/common/phy_common.h b/lib/include/srslte/phy/common/phy_common.h index 920c44323..828015f67 100644 --- a/lib/include/srslte/phy/common/phy_common.h +++ b/lib/include/srslte/phy/common/phy_common.h @@ -258,6 +258,9 @@ typedef enum { SRSLTE_DCI_FORMAT2B, // SRSLTE_DCI_FORMAT3, // SRSLTE_DCI_FORMAT3A, + SRSLTE_DCI_FORMATN0, + SRSLTE_DCI_FORMATN1, + SRSLTE_DCI_FORMATN2, SRSLTE_DCI_NOF_FORMATS } srslte_dci_format_t; @@ -283,6 +286,43 @@ enum band_geographical_area { SRSLTE_BAND_GEO_AREA_NA }; +// NB-IoT specific structs +typedef enum { + SRSLTE_NBIOT_MODE_INBAND_SAME_PCI = 0, + SRSLTE_NBIOT_MODE_INBAND_DIFFERENT_PCI, + SRSLTE_NBIOT_MODE_GUARDBAND, + SRSLTE_NBIOT_MODE_STANDALONE, + SRSLTE_NBIOT_MODE_N_ITEMS, +} srslte_nbiot_mode_t; + +typedef struct SRSLTE_API { + srslte_cell_t base; // the umbrella or super cell + uint32_t nbiot_prb; // the index of the NB-IoT PRB within the cell + uint32_t n_id_ncell; + uint32_t nof_ports; // The number of antenna ports for NB-IoT + bool is_r14; // Whether the cell is a R14 cell + srslte_nbiot_mode_t mode; +} srslte_nbiot_cell_t; + +#define SRSLTE_NBIOT_MAX_PORTS 2 +#define SRSLTE_NBIOT_MAX_CODEWORDS SRSLTE_MAX_CODEWORDS + +#define SRSLTE_SF_LEN_PRB_NBIOT (SRSLTE_SF_LEN_PRB(1)) + +#define SRSLTE_SF_LEN_RE_NBIOT (SRSLTE_SF_LEN_RE(1, SRSLTE_CP_NORM)) + +#define SRSLTE_NBIOT_FFT_SIZE 128 +#define SRSLTE_NBIOT_FREQ_SHIFT_FACTOR ((float)-0.5) +#define SRSLTE_NBIOT_NUM_RX_ANTENNAS 1 +#define SRSLTE_NBIOT_MAX_PRB 1 + +#define SRSLTE_NBIOT_DEFAULT_NUM_PRB_BASECELL 1 +#define SRSLTE_NBIOT_DEFAULT_PRB_OFFSET 0 + +#define SRSLTE_DEFAULT_MAX_FRAMES_NPBCH 500 +#define SRSLTE_DEFAULT_MAX_FRAMES_NPSS 20 +#define SRSLTE_DEFAULT_NOF_VALID_NPSS_FRAMES 20 + SRSLTE_API bool srslte_cell_isvalid(srslte_cell_t *cell); SRSLTE_API void srslte_cell_fprint(FILE *stream, @@ -386,4 +426,10 @@ SRSLTE_API uint32_t srslte_tti_interval(uint32_t tti1, uint32_t tti2); SRSLTE_API uint32_t srslte_print_check(char* s, size_t max_len, uint32_t cur_len, const char* format, ...); +SRSLTE_API bool srslte_nbiot_cell_isvalid(srslte_nbiot_cell_t* cell); +SRSLTE_API bool srslte_nbiot_portid_isvalid(uint32_t port_id); +SRSLTE_API float srslte_band_fu_nbiot(uint32_t ul_earfcn, const float m_ul); + +SRSLTE_API char* srslte_nbiot_mode_string(srslte_nbiot_mode_t mode); + #endif // SRSLTE_PHY_COMMON_H diff --git a/lib/include/srslte/phy/sync/npss.h b/lib/include/srslte/phy/sync/npss.h new file mode 100644 index 000000000..58fa1dfc7 --- /dev/null +++ b/lib/include/srslte/phy/sync/npss.h @@ -0,0 +1,123 @@ +/* + * 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/. + * + */ + +/****************************************************************************** + * File: npss.h + * + * Description: Narrowband Primary synchronization signal (NPSS) generation and detection. + * + * The srslte_npss_synch_t object provides functions for fast + * computation of the crosscorrelation between the NPSS and received + * signal and CFO estimation. Also, the function srslte_npss_synch_tperiodic() + * is designed to be called periodically every subframe, taking + * care of the correct data alignment with respect to the NPSS sequence. + * + * The object is designed to work with signals sampled at ?.? Mhz + * centered at the carrier frequency. Thus, downsampling is required + * if the signal is sampled at higher frequencies. + * + * Reference: 3GPP TS 36.211 version 13.2.0 Release 13 Sec. 10.x.x + *****************************************************************************/ + +#ifndef SRSLTE_NPSS_H +#define SRSLTE_NPSS_H + +#include +#include + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/utils/convolution.h" + +#define CONVOLUTION_FFT + +#define SRSLTE_NPSS_RETURN_PSR + +#define SRSLTE_NPSS_LEN 11 +#define SRSLTE_NPSS_NUM_OFDM_SYMS 11 +#define SRSLTE_NPSS_TOT_LEN (SRSLTE_NPSS_LEN * SRSLTE_NPSS_NUM_OFDM_SYMS) + +#define SRSLTE_NPSS_CORR_FILTER_LEN \ + ((SRSLTE_NPSS_NUM_OFDM_SYMS * SRSLTE_NBIOT_FFT_SIZE) + \ + (SRSLTE_NPSS_NUM_OFDM_SYMS - 1) * SRSLTE_CP_LEN_NORM(1, SRSLTE_NBIOT_FFT_SIZE) + \ + SRSLTE_CP_LEN_NORM(0, SRSLTE_NBIOT_FFT_SIZE)) + +// The below value corresponds to the time-domain representation of the first +// three OFDM-symbols plus cyclic prefix that are not transmitted in the sub-frame +// carrying the NPSS +#define SRSLTE_NPSS_CORR_OFFSET (SRSLTE_SF_LEN(SRSLTE_NBIOT_FFT_SIZE) - SRSLTE_NPSS_CORR_FILTER_LEN) + +// CFO estimation based on the NPSS is done using the second slot of the sub-frame +#define SRSLTE_NPSS_CFO_OFFSET (SRSLTE_SF_LEN(SRSLTE_NBIOT_FFT_SIZE) / 2 - SRSLTE_NPSS_CORR_OFFSET) +#define SRSLTE_NPSS_CFO_NUM_SYMS 6 // number of symbols for CFO estimation +#define SRSLTE_NPSS_CFO_NUM_SAMPS \ + ((SRSLTE_NPSS_CFO_NUM_SYMS * SRSLTE_NBIOT_FFT_SIZE) + \ + (SRSLTE_NPSS_CFO_NUM_SYMS - 1) * SRSLTE_CP_LEN_NORM(1, SRSLTE_NBIOT_FFT_SIZE) + \ + SRSLTE_CP_LEN_NORM(0, SRSLTE_NBIOT_FFT_SIZE)) // resulting number of samples + +// NPSS processing options +#define SRSLTE_NPSS_ACCUMULATE_ABS // If enabled, accumulates the correlation absolute value on consecutive calls to + // srslte_pss_synch_find_pss + +#define SRSLTE_NPSS_ABS_SQUARE // If enabled, compute abs square, otherwise computes absolute value only + +#define SRSLTE_NPSS_RETURN_PSR // If enabled returns peak to side-lobe ratio, otherwise returns absolute peak value + +/* Low-level API */ +typedef struct SRSLTE_API { +#ifdef CONVOLUTION_FFT + srslte_conv_fft_cc_t conv_fft; +#endif + + uint32_t frame_size, max_frame_size; + uint32_t fft_size, max_fft_size; + + cf_t* npss_signal_time; + cf_t* tmp_input; + cf_t* conv_output; + float* conv_output_abs; + float ema_alpha; + float* conv_output_avg; + float peak_value; +} srslte_npss_synch_t; + +// Basic functionality +SRSLTE_API int srslte_npss_synch_init(srslte_npss_synch_t* q, uint32_t frame_size, uint32_t fft_size); + +SRSLTE_API void srslte_npss_synch_reset(srslte_npss_synch_t* q); + +SRSLTE_API int srslte_npss_synch_resize(srslte_npss_synch_t* q, uint32_t frame_size, uint32_t fft_size); + +SRSLTE_API void srslte_npss_synch_set_ema_alpha(srslte_npss_synch_t* q, float alpha); + +SRSLTE_API void srslte_npss_synch_free(srslte_npss_synch_t* q); + +SRSLTE_API int srslte_npss_sync_find(srslte_npss_synch_t* q, cf_t* input, float* corr_peak_value); + +// Internal functions +SRSLTE_API int srslte_npss_corr_init(cf_t* npss_signal_time, uint32_t fft_size, uint32_t frame_size); + +SRSLTE_API int srslte_npss_generate(cf_t* signal); + +SRSLTE_API void srslte_npss_put_subframe( + srslte_npss_synch_t* q, cf_t* npss_signal, cf_t* sf, const uint32_t nof_prb, const uint32_t nbiot_prb_offset); + +#endif // SRSLTE_NPSS_H diff --git a/lib/include/srslte/phy/sync/nsss.h b/lib/include/srslte/phy/sync/nsss.h new file mode 100644 index 000000000..fb7582ccc --- /dev/null +++ b/lib/include/srslte/phy/sync/nsss.h @@ -0,0 +1,116 @@ +/* + * 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/. + * + */ + +/****************************************************************************** + * File: nsss.h + * + * Description: Narrowband secondary synchronization signal (NSSS) + * generation and detection. + * + * + * Reference: 3GPP TS 36.211 version 13.2.0 Release 13 Sec. 10.2.7.2 + *****************************************************************************/ + +#ifndef SRSLTE_NSSS_H +#define SRSLTE_NSSS_H + +#include +#include +#include + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/dft/dft.h" +#include "srslte/phy/utils/convolution.h" + +#define SRSLTE_NSSS_NSYMB 11 +#define SRSLTE_NSSS_NSC 12 +#define SRSLTE_NSSS_LEN (SRSLTE_NSSS_NSYMB * SRSLTE_NSSS_NSC) +#define SRSLTE_NSSS_NUM_SEQ 4 +#define SRSLTE_NSSS_TOT_LEN (SRSLTE_NSSS_NUM_SEQ * SRSLTE_NSSS_LEN) + +#define SRSLTE_NSSS_CORR_FILTER_LEN 1508 +#define SRSLTE_NSSS_CORR_OFFSET 412 + +#define SRSLTE_NUM_PCI 504 + +#define SRSLTE_NSSS_PERIOD 2 +#define SRSLTE_NSSS_NUM_SF_DETECT (SRSLTE_NSSS_PERIOD) + +// b_q_m table from 3GPP TS 36.211 v13.2.0 table 10.2.7.2.1-1 +static const int b_q_m[SRSLTE_NSSS_NUM_SEQ][128] = { + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, + 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, + 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, + 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1}, + {1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, + -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, + -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, + -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, + 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1}, + {1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, + -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, + -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, + 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, + -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1}}; + +/* Low-level API */ +typedef struct SRSLTE_API { + uint32_t input_size; + uint32_t subframe_sz; + uint32_t fft_size, max_fft_size; + srslte_conv_fft_cc_t conv_fft; + + cf_t* nsss_signal_time[SRSLTE_NUM_PCI]; + cf_t* tmp_input; + cf_t* conv_output; + float* conv_output_abs; + float peak_values[SRSLTE_NUM_PCI]; + float corr_peak_threshold; +} srslte_nsss_synch_t; + +SRSLTE_API int srslte_nsss_synch_init(srslte_nsss_synch_t* q, uint32_t input_size, uint32_t fft_size); + +SRSLTE_API void srslte_nsss_synch_free(srslte_nsss_synch_t* q); + +SRSLTE_API int srslte_nsss_synch_resize(srslte_nsss_synch_t* q, uint32_t fft_size); + +SRSLTE_API int srslte_nsss_sync_find( + srslte_nsss_synch_t* q, cf_t* input, float* corr_peak_value, uint32_t* cell_id, uint32_t* sfn_partial); + +void srslte_nsss_sync_find_pci(srslte_nsss_synch_t* q, cf_t* input, uint32_t cell_id); + +SRSLTE_API int srslte_nsss_corr_init(srslte_nsss_synch_t* q); + +SRSLTE_API void srslte_nsss_generate(cf_t* signal, uint32_t cell_id); + +SRSLTE_API void srslte_nsss_put_subframe(srslte_nsss_synch_t* q, + cf_t* nsss, + cf_t* subframe, + const int nf, + const uint32_t nof_prb, + const uint32_t nbiot_prb_offset); + +#endif // SRSLTE_NSSS_H \ No newline at end of file diff --git a/lib/include/srslte/phy/sync/sync_nbiot.h b/lib/include/srslte/phy/sync/sync_nbiot.h new file mode 100644 index 000000000..e54e9a60d --- /dev/null +++ b/lib/include/srslte/phy/sync/sync_nbiot.h @@ -0,0 +1,125 @@ +/* + * 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/. + * + */ + +/****************************************************************************** + * File: sync_nbiot.h + * + * Description: Time and frequency synchronization using the NPSS and NSSS signals. + * + * The object is designed to work with signals sampled at 1.92 Mhz + * centered at the carrier frequency. Thus, downsampling is required + * if the signal is sampled at higher frequencies. + * + * Correlation peak is detected comparing the maximum at the output + * of the correlator with a threshold. + * + * Reference: 3GPP TS 36.211 version 13.2.0 Release 13 + *****************************************************************************/ + +#ifndef SRSLTE_SYNC_NBIOT_H +#define SRSLTE_SYNC_NBIOT_H + +#include +#include + +#include "srslte/config.h" +#include "srslte/phy/sync/npss.h" +#include "srslte/phy/sync/nsss.h" +#include "srslte/phy/sync/sync.h" +#include "srslte/phy/ue/ue_sync.h" + +#define MAX_NUM_CFO_CANDITATES 50 + +typedef struct SRSLTE_API { + srslte_npss_synch_t npss; + srslte_nsss_synch_t nsss; + srslte_cp_synch_t cp_synch; + uint32_t n_id_ncell; + + float threshold; + float peak_value; + uint32_t fft_size; + uint32_t frame_size; + uint32_t max_frame_size; + uint32_t max_offset; + bool enable_cfo_estimation; + bool enable_cfo_cand_test; + float cfo_cand[MAX_NUM_CFO_CANDITATES]; + int cfo_num_cand; + int cfo_cand_idx; + float mean_cfo; + float current_cfo_tol; + cf_t* shift_buffer; + cf_t* cfo_output; + int cfo_i; + bool find_cfo_i; + bool find_cfo_i_initiated; + float cfo_ema_alpha; + uint32_t nof_symbols; + uint32_t cp_len; + srslte_cfo_t cfocorr; + srslte_cp_t cp; +} srslte_sync_nbiot_t; + +SRSLTE_API int +srslte_sync_nbiot_init(srslte_sync_nbiot_t* q, uint32_t frame_size, uint32_t max_offset, uint32_t fft_size); + +SRSLTE_API void srslte_sync_nbiot_free(srslte_sync_nbiot_t* q); + +SRSLTE_API int +srslte_sync_nbiot_resize(srslte_sync_nbiot_t* q, uint32_t frame_size, uint32_t max_offset, uint32_t fft_size); + +SRSLTE_API srslte_sync_find_ret_t srslte_sync_nbiot_find(srslte_sync_nbiot_t* q, + cf_t* input, + uint32_t find_offset, + uint32_t* peak_position); + +SRSLTE_API float cfo_estimate_nbiot(srslte_sync_nbiot_t* q, cf_t* input); + +SRSLTE_API void srslte_sync_nbiot_set_threshold(srslte_sync_nbiot_t* q, float threshold); + +SRSLTE_API void srslte_sync_nbiot_set_cfo_enable(srslte_sync_nbiot_t* q, bool enable); + +SRSLTE_API void srslte_sync_nbiot_set_cfo_cand_test_enable(srslte_sync_nbiot_t* q, bool enable); + +SRSLTE_API int srslte_sync_nbiot_set_cfo_cand(srslte_sync_nbiot_t* q, float* cand, const int num); + +SRSLTE_API void srslte_sync_nbiot_set_cfo_tol(srslte_sync_nbiot_t* q, float tol); + +SRSLTE_API void srslte_sync_nbiot_set_cfo_ema_alpha(srslte_sync_nbiot_t* q, float alpha); + +SRSLTE_API void srslte_sync_nbiot_set_npss_ema_alpha(srslte_sync_nbiot_t* q, float alpha); + +SRSLTE_API int srslte_sync_nbiot_find_cell_id(srslte_sync_nbiot_t* q, cf_t* input); + +SRSLTE_API int srslte_sync_nbiot_get_cell_id(srslte_sync_nbiot_t* q); + +SRSLTE_API float srslte_sync_nbiot_get_cfo(srslte_sync_nbiot_t* q); + +SRSLTE_API void srslte_sync_nbiot_set_cfo(srslte_sync_nbiot_t* q, float cfo); + +SRSLTE_API bool srslte_sync_nbiot_nsss_detected(srslte_sync_nbiot_t* q); + +SRSLTE_API float srslte_sync_nbiot_get_peak_value(srslte_sync_nbiot_t* q); + +SRSLTE_API void srslte_sync_nbiot_reset(srslte_sync_nbiot_t* q); + +#endif // SRSLTE_SYNC_NBIOT_H diff --git a/lib/src/phy/common/phy_common.c b/lib/src/phy/common/phy_common.c index db563d275..24e9d1643 100644 --- a/lib/src/phy/common/phy_common.c +++ b/lib/src/phy/common/phy_common.c @@ -739,3 +739,42 @@ uint32_t srslte_print_check(char* s, size_t max_len, uint32_t cur_len, const cha } return cur_len; } + +bool srslte_nbiot_prb_isvalid(srslte_nbiot_cell_t* cell) +{ + if (cell->nbiot_prb <= cell->base.nof_prb) { + return true; + } + return false; +} + +bool srslte_nbiot_cell_isvalid(srslte_nbiot_cell_t* cell) +{ + return (srslte_cell_isvalid(&cell->base) && srslte_nbiot_portid_isvalid(cell->nof_ports) && + srslte_nbiot_prb_isvalid(cell) && srslte_cellid_isvalid(cell->n_id_ncell)); +} + +bool srslte_nbiot_portid_isvalid(uint32_t port_id) +{ + if (port_id <= SRSLTE_NBIOT_MAX_PORTS) { + return true; + } else { + return false; + } +} + +char* srslte_nbiot_mode_string(srslte_nbiot_mode_t mode) +{ + switch (mode) { + case SRSLTE_NBIOT_MODE_INBAND_SAME_PCI: + return "Inband (Same PCI)"; + case SRSLTE_NBIOT_MODE_INBAND_DIFFERENT_PCI: + return "Inband (Different PCI)"; + case SRSLTE_NBIOT_MODE_GUARDBAND: + return "Guardband"; + case SRSLTE_NBIOT_MODE_STANDALONE: + return "Standalone"; + default: + return "N/A"; + } +} \ No newline at end of file diff --git a/lib/src/phy/sync/npss.c b/lib/src/phy/sync/npss.c new file mode 100644 index 000000000..7b7c19a7c --- /dev/null +++ b/lib/src/phy/sync/npss.c @@ -0,0 +1,435 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/dft/dft.h" +#include "srslte/phy/io/filesink.h" +#include "srslte/phy/sync/npss.h" +#include "srslte/phy/utils/convolution.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + +#define PRINT_ERR(err) fprintf(stderr, "%s(): %s", __PRETTY_FUNCTION__, err) + +#define DUMP_SIGNALS 0 +#define DO_FREQ_SHIFT 1 + +const float factor_lut[SRSLTE_NPSS_LEN] = {1, 1, 1, 1, -1, -1, 1, 1, 1, -1, 1}; + +/* Initializes the NPSS synchronization object. + * + * It correlates a signal of frame_size samples with the NPSS sequence in the time domain + * The NPSS sequence is transformed using 11 * fft_size samples plus cyclic prefix. + */ +int srslte_npss_synch_init(srslte_npss_synch_t* q, uint32_t frame_size, uint32_t fft_size) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) { + bzero(q, sizeof(srslte_npss_synch_t)); + + q->fft_size = q->max_fft_size = fft_size; + q->frame_size = q->max_frame_size = frame_size; + q->ema_alpha = 0.2; + + uint32_t buffer_size = SRSLTE_NPSS_CORR_FILTER_LEN + frame_size + 1; + + q->tmp_input = srslte_vec_malloc(buffer_size * sizeof(cf_t)); + if (!q->tmp_input) { + PRINT_ERR("Error allocating memory\n"); + goto clean_and_exit; + } + bzero(q->tmp_input, buffer_size * sizeof(cf_t)); + + q->conv_output = srslte_vec_malloc(buffer_size * sizeof(cf_t)); + if (!q->conv_output) { + fprintf(stderr, "Error allocating memory\n"); + goto clean_and_exit; + } + bzero(q->conv_output, sizeof(cf_t) * buffer_size); + q->conv_output_avg = srslte_vec_malloc(buffer_size * sizeof(float)); + if (!q->conv_output_avg) { + fprintf(stderr, "Error allocating memory\n"); + goto clean_and_exit; + } + bzero(q->conv_output_avg, sizeof(float) * buffer_size); +#ifdef SRSLTE_NPSS_ACCUMULATE_ABS + q->conv_output_abs = srslte_vec_malloc(buffer_size * sizeof(float)); + if (!q->conv_output_abs) { + fprintf(stderr, "Error allocating memory\n"); + goto clean_and_exit; + } + bzero(q->conv_output_abs, sizeof(float) * buffer_size); +#endif + + q->npss_signal_time = srslte_vec_malloc(buffer_size * sizeof(cf_t)); + if (!q->npss_signal_time) { + fprintf(stderr, "Error allocating memory\n"); + goto clean_and_exit; + } + bzero(q->npss_signal_time, sizeof(cf_t) * buffer_size); + + // The NPSS is translated into the time domain + if (srslte_npss_corr_init(q->npss_signal_time, fft_size, q->frame_size)) { + fprintf(stderr, "Error initiating NPSS detector for fft_size=%d\n", fft_size); + goto clean_and_exit; + } + +#ifdef CONVOLUTION_FFT + if (srslte_conv_fft_cc_init(&q->conv_fft, frame_size, SRSLTE_NPSS_CORR_FILTER_LEN)) { + fprintf(stderr, "Error initiating convolution FFT\n"); + goto clean_and_exit; + } + // run convolution once to compute filter + srslte_corr_fft_cc_run(&q->conv_fft, q->tmp_input, q->npss_signal_time, q->conv_output); +#endif + + srslte_npss_synch_reset(q); + + ret = SRSLTE_SUCCESS; + } + +clean_and_exit: + if (ret == SRSLTE_ERROR) { + srslte_npss_synch_free(q); + } + return ret; +} + +int srslte_npss_synch_resize(srslte_npss_synch_t* q, uint32_t frame_size, uint32_t fft_size) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (q != NULL) { + ret = SRSLTE_ERROR; + + if (fft_size > q->max_fft_size || frame_size > q->max_frame_size) { + PRINT_ERR("fft_size and frame_size must be lower than initialized\n"); + return SRSLTE_ERROR; + } + + q->ema_alpha = 0.2; + q->fft_size = fft_size; + q->frame_size = frame_size; + + uint32_t buffer_size = SRSLTE_NPSS_CORR_FILTER_LEN + frame_size + 1; + bzero(q->tmp_input, buffer_size * sizeof(cf_t)); + bzero(q->conv_output, sizeof(cf_t) * buffer_size); + bzero(q->conv_output_avg, sizeof(float) * buffer_size); + +#ifdef SRSLTE_NPSS_ACCUMULATE_ABS + bzero(q->conv_output_abs, sizeof(float) * buffer_size); +#endif + + // Re-generate NPSS sequences for this FFT size + // bzero(q->npss_signal_time, sizeof(cf_t) * buffer_size); + if (srslte_npss_corr_init(q->npss_signal_time, fft_size, q->frame_size)) { + fprintf(stderr, "Error initiating NPSS detector for fft_size=%d\n", fft_size); + return SRSLTE_ERROR; + } + +#ifdef CONVOLUTION_FFT + if (srslte_conv_fft_cc_replan(&q->conv_fft, frame_size, SRSLTE_NPSS_CORR_FILTER_LEN)) { + fprintf(stderr, "Error initiating convolution FFT\n"); + return SRSLTE_ERROR; + } +#endif + + srslte_npss_synch_reset(q); + + ret = SRSLTE_SUCCESS; + } + return ret; +} + +int srslte_npss_corr_init(cf_t* npss_signal_time, uint32_t fft_size, uint32_t frame_size) +{ + srslte_dft_plan_t plan; + _Complex float npss_signal_pad[fft_size]; + _Complex float npss_signal[SRSLTE_NPSS_TOT_LEN]; + + // generate correlation sequence + srslte_npss_generate(npss_signal); +#if DUMP_SIGNALS + // srslte_vec_save_file("npss_corr_seq_freq.bin", npss_signal, SRSLTE_NPSS_TOT_LEN*sizeof(cf_t)); +#endif + + // zero buffers + bzero(npss_signal_time, (fft_size + frame_size) * sizeof(cf_t)); + bzero(npss_signal_pad, fft_size * sizeof(cf_t)); + + // construct dft plan and convert signal into the time domain + if (srslte_dft_plan(&plan, fft_size, SRSLTE_DFT_BACKWARD, SRSLTE_DFT_COMPLEX)) { + return SRSLTE_ERROR; + } + srslte_dft_plan_set_mirror(&plan, true); + srslte_dft_plan_set_dc(&plan, false); + srslte_dft_plan_set_norm(&plan, true); + + // one symbol at a time + cf_t* output = npss_signal_time; + int output_len = 0; + for (int i = 0; i < SRSLTE_NPSS_NUM_OFDM_SYMS; i++) { + // zero buffer, copy NPSS symbol to appr. pos and transform to time-domain + bzero(npss_signal_pad, fft_size * sizeof(cf_t)); + + // 5th NPSS symbol has CP length of 10 symbols + int cp_len = (i != 4) ? SRSLTE_CP_LEN_NORM(1, SRSLTE_NBIOT_FFT_SIZE) : SRSLTE_CP_LEN_NORM(0, SRSLTE_NBIOT_FFT_SIZE); + int k = (fft_size - SRSLTE_NRE) / 2; // place NPSS in the centre + + memcpy(&npss_signal_pad[k], &npss_signal[i * SRSLTE_NPSS_LEN], SRSLTE_NPSS_LEN * sizeof(cf_t)); + srslte_dft_run_c(&plan, npss_signal_pad, &output[cp_len]); + + // add CP + memcpy(output, &output[fft_size], cp_len * sizeof(cf_t)); + + // prepare next iteration + output += fft_size + cp_len; + output_len += fft_size + cp_len; + } + + assert(output_len == SRSLTE_NPSS_CORR_FILTER_LEN); + +#if DO_FREQ_SHIFT + // shift entire signal in frequency domain by half a subcarrier + cf_t shift_buffer[SRSLTE_SF_LEN(fft_size)]; + cf_t* ptr = shift_buffer; + for (uint32_t n = 0; n < 2; n++) { + for (uint32_t i = 0; i < 7; i++) { + uint32_t cplen = SRSLTE_CP_LEN_NORM(i, fft_size); + for (uint32_t t = 0; t < fft_size + cplen; t++) { + ptr[t] = cexpf(I * 2 * M_PI * ((float)t - (float)cplen) * -SRSLTE_NBIOT_FREQ_SHIFT_FACTOR / fft_size); + } + ptr += fft_size + cplen; + } + } + srslte_vec_prod_ccc( + npss_signal_time, &shift_buffer[SRSLTE_NPSS_CORR_OFFSET], npss_signal_time, SRSLTE_NPSS_CORR_FILTER_LEN); + srslte_vec_sc_prod_cfc(npss_signal_time, 1.0 / 3, npss_signal_time, output_len); +#endif + + srslte_dft_plan_free(&plan); + + return SRSLTE_SUCCESS; +} + +/** Performs time-domain NPSS correlation. + * Returns the index of the NPSS correlation peak in a subframe. + * The frame starts at corr_peak_pos-SRSLTE_NPSS_CORR_OFFSET+frame_size/2. + * The value of the correlation is stored in corr_peak_value. + * + * Input buffer must be subframe_size long. + */ +int srslte_npss_sync_find(srslte_npss_synch_t* q, cf_t* input, float* corr_peak_value) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && input != NULL) { + uint32_t corr_peak_pos; + uint32_t conv_output_len; + + // Correlate input with NPSS sequence + if (q->frame_size >= q->fft_size) { +#ifdef CONVOLUTION_FFT + memcpy(q->tmp_input, input, q->frame_size * sizeof(cf_t)); + conv_output_len = srslte_corr_fft_cc_run_opt(&q->conv_fft, q->tmp_input, q->npss_signal_time, q->conv_output); +#else + conv_output_len = + srslte_conv_cc(input, q->pss_signal_time[q->N_id_2], q->conv_output, q->frame_size, q->fft_size); +#endif + } else { + for (int i = 0; i < q->frame_size; i++) { + q->conv_output[i] = srslte_vec_dot_prod_ccc(q->npss_signal_time, &input[i], q->fft_size); + } + conv_output_len = q->frame_size; + } + +#ifdef SRSLTE_NPSS_ABS_SQUARE + srslte_vec_abs_square_cf(q->conv_output, q->conv_output_abs, conv_output_len - 1); +#else + srslte_vec_abs_cf(q->conv_output, q->conv_output_abs, conv_output_len - 1); +#endif + + if (q->ema_alpha < 1.0 && q->ema_alpha > 0.0) { + srslte_vec_sc_prod_fff(q->conv_output_abs, q->ema_alpha, q->conv_output_abs, conv_output_len - 1); + srslte_vec_sc_prod_fff(q->conv_output_avg, 1 - q->ema_alpha, q->conv_output_avg, conv_output_len - 1); + + srslte_vec_sum_fff(q->conv_output_abs, q->conv_output_avg, q->conv_output_avg, conv_output_len - 1); + } else { + memcpy(q->conv_output_avg, q->conv_output_abs, sizeof(float) * (conv_output_len - 1)); + } + + // Find maximum of the absolute value of the correlation + corr_peak_pos = srslte_vec_max_fi(q->conv_output_avg, conv_output_len - 1); + +#if DUMP_SIGNALS + printf("Dumping debug signals.\n"); + srslte_vec_save_file("npss_find_input.bin", input, q->frame_size * sizeof(cf_t)); + srslte_vec_save_file("npss_corr_seq_time.bin", q->npss_signal_time, SRSLTE_NPSS_CORR_FILTER_LEN * sizeof(cf_t)); + srslte_vec_save_file("npss_find_conv_output_abs.bin", q->conv_output_abs, conv_output_len * sizeof(float)); + srslte_vec_save_file("npss_find_conv_output_avg.bin", q->conv_output_avg, conv_output_len * sizeof(float)); +#endif + + // save absolute value + q->peak_value = q->conv_output_avg[corr_peak_pos]; + +#ifdef SRSLTE_NPSS_RETURN_PSR + // Find second side lobe + + // Find end of peak lobe to the right + int pl_ub = corr_peak_pos + 1; + while (q->conv_output_avg[pl_ub + 1] <= q->conv_output_avg[pl_ub] && pl_ub < conv_output_len) { + pl_ub++; + } + // Find end of peak lobe to the left + int pl_lb; + if (corr_peak_pos > 2) { + pl_lb = corr_peak_pos - 1; + while (q->conv_output_avg[pl_lb - 1] <= q->conv_output_avg[pl_lb] && pl_lb > 1) { + pl_lb--; + } + } else { + pl_lb = 0; + } + + int sl_distance_right = conv_output_len - 1 - pl_ub; + if (sl_distance_right < 0) { + sl_distance_right = 0; + } + int sl_distance_left = pl_lb; + + int sl_right = pl_ub + srslte_vec_max_fi(&q->conv_output_avg[pl_ub], sl_distance_right); + int sl_left = srslte_vec_max_fi(q->conv_output_avg, sl_distance_left); + float side_lobe_value = SRSLTE_MAX(q->conv_output_avg[sl_right], q->conv_output_avg[sl_left]); + if (corr_peak_value) { + *corr_peak_value = q->conv_output_avg[corr_peak_pos] / side_lobe_value; + + if (*corr_peak_value < 10) { + DEBUG("peak_pos=%2d, pl_ub=%2d, pl_lb=%2d, sl_right: %2d, sl_left: %2d, PSR: %.2f/%.2f=%.2f\n", + corr_peak_pos, + pl_ub, + pl_lb, + sl_right, + sl_left, + q->conv_output_avg[corr_peak_pos], + side_lobe_value, + *corr_peak_value); + } + } +#else + if (corr_peak_value) { + *corr_peak_value = q->conv_output_avg[corr_peak_pos]; + } +#endif + + ret = (int)corr_peak_pos; + } + return ret; +} + +void srslte_npss_synch_set_ema_alpha(srslte_npss_synch_t* q, float alpha) +{ + q->ema_alpha = alpha; +} + +void srslte_npss_synch_free(srslte_npss_synch_t* q) +{ + if (q) { + if (q->npss_signal_time) { + free(q->npss_signal_time); + } +#ifdef CONVOLUTION_FFT + srslte_conv_fft_cc_free(&q->conv_fft); +#endif + if (q->tmp_input) { + free(q->tmp_input); + } + if (q->conv_output) { + free(q->conv_output); + } + if (q->conv_output_abs) { + free(q->conv_output_abs); + } + if (q->conv_output_avg) { + free(q->conv_output_avg); + } + } +} + +void srslte_npss_synch_reset(srslte_npss_synch_t* q) +{ + if (q->conv_output_avg) { + uint32_t buffer_size = SRSLTE_NPSS_CORR_FILTER_LEN + q->max_frame_size + 1; + bzero(q->conv_output_avg, sizeof(float) * buffer_size); + } +} + +/** + * This function calculates the Zadoff-Chu sequence. + * 36.211 13.2.0 section 10.2.7.1.1 + * + * It produces SRSLTE_NPSS_LEN * SRSLTE_NPSS_NUM_SC = 11 * 11 = 121 samples. + * @param signal Output array. + */ +int srslte_npss_generate(cf_t* signal) +{ + float arg; + const float root_value = 5.0; + + int sign = -1; + int l = 0; + int n = 0; + + // iterate over symbol indices + for (l = 0; l < SRSLTE_CP_NORM_SF_NSYMB - 3; l++) { + // iterate over subcarriers, leave out last one + for (n = 0; n < SRSLTE_NRE - 1; n++) { + arg = (float)sign * M_PI * root_value * ((float)n * ((float)n + 1.0)) / 11.0; + __real__ signal[l * SRSLTE_NPSS_LEN + n] = cosf(arg); + __imag__ signal[l * SRSLTE_NPSS_LEN + n] = sinf(arg); + + signal[l * SRSLTE_NPSS_LEN + n] *= (float)factor_lut[l]; + } + } + + return 0; +} + +/** 36.211 10.3 section 6.11.1.2 + */ +void srslte_npss_put_subframe( + srslte_npss_synch_t* q, cf_t* npss_signal, cf_t* sf, const uint32_t nof_prb, const uint32_t nbiot_prb_offset) +{ + // skip first 3 OFDM symbols over all PRBs completely + int k = 3 * nof_prb * SRSLTE_NRE + nbiot_prb_offset * SRSLTE_NRE; + + // put NPSS in each of the 11 symbols of the subframe + for (int l = 0; l < SRSLTE_CP_NORM_SF_NSYMB - 3; l++) { + memcpy(&sf[k + SRSLTE_NPSS_LEN * l], &npss_signal[SRSLTE_NPSS_LEN * l], SRSLTE_NPSS_LEN * sizeof(cf_t)); + k += (nof_prb - 1) * SRSLTE_NRE + 1; // last SC of the PRB is also null + } +} diff --git a/lib/src/phy/sync/nsss.c b/lib/src/phy/sync/nsss.c new file mode 100644 index 000000000..22589b7c2 --- /dev/null +++ b/lib/src/phy/sync/nsss.c @@ -0,0 +1,398 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include + +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/sync/nsss.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + +#define PRINT_ERR(err) fprintf(stderr, "%s: %s", __PRETTY_FUNCTION__, err) + +#define DUMP_SIGNALS 0 +#define DO_FREQ_SHIFT 1 +#define SRSLTE_NSSS_RETURN_PSR 0 + +int srslte_nsss_synch_init(srslte_nsss_synch_t* q, uint32_t input_size, uint32_t fft_size) +{ + if (q != NULL && fft_size <= 2048) { + int ret = SRSLTE_ERROR; + bzero(q, sizeof(srslte_nsss_synch_t)); + + q->fft_size = q->max_fft_size = fft_size; + + q->input_size = input_size; + q->corr_peak_threshold = 2.0; + + uint32_t buffer_size = SRSLTE_NSSS_CORR_FILTER_LEN + q->input_size + 1; + DEBUG("NSSS buffer size is %d samples.\n", buffer_size); + q->tmp_input = srslte_vec_malloc(buffer_size * sizeof(cf_t)); + if (!q->tmp_input) { + fprintf(stderr, "Error allocating memory\n"); + goto clean_and_exit; + } + bzero(q->tmp_input, buffer_size * sizeof(cf_t)); + + q->conv_output = srslte_vec_malloc(buffer_size * sizeof(cf_t)); + if (!q->conv_output) { + fprintf(stderr, "Error allocating memory\n"); + goto clean_and_exit; + } + bzero(q->conv_output, sizeof(cf_t) * buffer_size); + + q->conv_output_abs = srslte_vec_malloc(buffer_size * sizeof(float)); + if (!q->conv_output_abs) { + fprintf(stderr, "Error allocating memory\n"); + goto clean_and_exit; + } + bzero(q->conv_output_abs, sizeof(float) * buffer_size); + + for (int i = 0; i < SRSLTE_NUM_PCI; i++) { + q->nsss_signal_time[i] = srslte_vec_malloc(buffer_size * sizeof(cf_t)); + if (!q->nsss_signal_time[i]) { + fprintf(stderr, "Error allocating memory\n"); + goto clean_and_exit; + } + bzero(q->nsss_signal_time[i], sizeof(cf_t) * buffer_size); + } + + // generate NSSS sequences + if (srslte_nsss_corr_init(q)) { + fprintf(stderr, "Error initiating NSSS detector for fft_size=%d\n", fft_size); + goto clean_and_exit; + } + + if (srslte_conv_fft_cc_init(&q->conv_fft, q->input_size, SRSLTE_NSSS_CORR_FILTER_LEN)) { + fprintf(stderr, "Error initiating convolution FFT\n"); + goto clean_and_exit; + } + + ret = SRSLTE_SUCCESS; + + clean_and_exit: + if (ret == SRSLTE_ERROR) { + srslte_nsss_synch_free(q); + } + return ret; + } + return SRSLTE_ERROR_INVALID_INPUTS; +} + +void srslte_nsss_synch_free(srslte_nsss_synch_t* q) +{ + if (q) { + for (int i = 0; i < SRSLTE_NUM_PCI; i++) { + if (q->nsss_signal_time[i]) { + free(q->nsss_signal_time[i]); + } + } + srslte_conv_fft_cc_free(&q->conv_fft); + if (q->tmp_input) { + free(q->tmp_input); + } + if (q->conv_output) { + free(q->conv_output); + } + if (q->conv_output_abs) { + free(q->conv_output_abs); + } + } +} + +int srslte_nsss_synch_resize(srslte_nsss_synch_t* q, uint32_t fft_size) +{ + if (q != NULL && fft_size <= 2048) { + if (fft_size > q->max_fft_size) { + PRINT_ERR("fft_size must be lower than initialized\n"); + return SRSLTE_ERROR; + } + + q->fft_size = fft_size; + + if (srslte_nsss_corr_init(q) != SRSLTE_SUCCESS) { + PRINT_ERR("Couldn't initialize NSSS sequence\n"); + return SRSLTE_ERROR; + } + + return SRSLTE_SUCCESS; + } + return SRSLTE_ERROR_INVALID_INPUTS; +} + +int srslte_nsss_corr_init(srslte_nsss_synch_t* q) +{ + srslte_dft_plan_t plan; + float complex nsss_signal_pad[q->fft_size]; + + // construct dft plan + if (srslte_dft_plan(&plan, q->fft_size, SRSLTE_DFT_BACKWARD, SRSLTE_DFT_COMPLEX)) { + return SRSLTE_ERROR; + } + srslte_dft_plan_set_mirror(&plan, true); + srslte_dft_plan_set_dc(&plan, false); + srslte_dft_plan_set_norm(&plan, true); + +#if DO_FREQ_SHIFT + // shift entire signal in frequency domain by half a subcarrier + cf_t shift_buffer[SRSLTE_SF_LEN(q->fft_size)]; + cf_t* ptr = shift_buffer; + for (uint32_t n = 0; n < 2; n++) { + for (uint32_t i = 0; i < 7; i++) { + uint32_t cplen = SRSLTE_CP_LEN_NORM(i, q->fft_size); + for (uint32_t t = 0; t < q->fft_size + cplen; t++) { + ptr[t] = cexpf(I * 2 * M_PI * ((float)t - (float)cplen) * -SRSLTE_NBIOT_FREQ_SHIFT_FACTOR / q->fft_size); + } + ptr += q->fft_size + cplen; + } + } +#endif + + // generate correlation sequences + DEBUG("Generating NSSS sequences\n"); + for (int i = 0; i < SRSLTE_NUM_PCI; i++) { + float complex nsss_signal[SRSLTE_NSSS_TOT_LEN]; + bzero(nsss_signal, SRSLTE_NSSS_TOT_LEN * sizeof(cf_t)); + srslte_nsss_generate(nsss_signal, i); + + // one symbol at a time + cf_t* output = q->nsss_signal_time[i]; + int output_len = 0; + for (int i = 0; i < SRSLTE_NSSS_NSYMB; i++) { + // zero buffer, copy NSSS symbol to appr. pos and transform to time-domain + bzero(nsss_signal_pad, q->fft_size * sizeof(cf_t)); + + // 5th NSSS symbol has CP length of 10 symbols + int cp_len = + (i != 4) ? SRSLTE_CP_LEN_NORM(1, SRSLTE_NBIOT_FFT_SIZE) : SRSLTE_CP_LEN_NORM(0, SRSLTE_NBIOT_FFT_SIZE); + int k = (q->fft_size - SRSLTE_NRE) / 2; // place seq in the centre + k = 57; + + // use generated sequence for theta_f = 0 + int theta_f = 0; + memcpy(&nsss_signal_pad[k], + &nsss_signal[(theta_f * SRSLTE_NSSS_LEN) + i * SRSLTE_NSSS_NSC], + SRSLTE_NSSS_NSC * sizeof(cf_t)); + srslte_dft_run_c(&plan, nsss_signal_pad, &output[cp_len]); + + // add CP + memcpy(output, &output[q->fft_size], cp_len * sizeof(cf_t)); + + // prepare next iteration + output += q->fft_size + cp_len; + output_len += q->fft_size + cp_len; + } + assert(output_len == SRSLTE_NSSS_CORR_FILTER_LEN); + +#if DO_FREQ_SHIFT + srslte_vec_prod_ccc(q->nsss_signal_time[i], + &shift_buffer[SRSLTE_NSSS_CORR_OFFSET], + q->nsss_signal_time[i], + SRSLTE_NSSS_CORR_FILTER_LEN); + // srslte_vec_sc_prod_cfc(npss_signal_time, 1.0/3, npss_signal_time, output_len); +#endif + +#if DUMP_SIGNALS +#define MAX_FNAME_LEN 40 + char fname[MAX_FNAME_LEN]; + snprintf(fname, MAX_FNAME_LEN, "nsss_corr_seq_time_id%d.bin", i); + srslte_vec_save_file(fname, q->nsss_signal_time[i], SRSLTE_NSSS_CORR_FILTER_LEN * sizeof(cf_t)); + + snprintf(fname, MAX_FNAME_LEN, "nsss_corr_seq_freq_id%d.bin", i); + srslte_vec_save_file(fname, nsss_signal, SRSLTE_NSSS_TOT_LEN * sizeof(cf_t)); +#endif + } + + srslte_dft_plan_free(&plan); + return SRSLTE_SUCCESS; +} + +int srslte_nsss_sync_find( + srslte_nsss_synch_t* q, cf_t* input, float* corr_peak_value, uint32_t* cell_id, uint32_t* sfn_partial) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && input != NULL && corr_peak_value != NULL && cell_id != NULL && sfn_partial != NULL) { + float peak_value; + ret = SRSLTE_ERROR; + + // save input + memcpy(q->tmp_input, input, q->input_size * sizeof(cf_t)); + + if (*cell_id == SRSLTE_CELL_ID_UNKNOWN) { + DEBUG("N_id_ncell is not set. Perform exhaustive search on input.\n"); + + // brute-force: correlate with all possible sequences until cell is found + for (int i = 0; i < SRSLTE_NUM_PCI; i++) { + srslte_nsss_sync_find_pci(q, q->tmp_input, i); + } + + // find maximum of all correlation maxima + uint32_t max_id = srslte_vec_max_fi(q->peak_values, SRSLTE_NUM_PCI); + if (q->peak_values[max_id] > q->corr_peak_threshold) { + // cell found, set return values + *cell_id = max_id; + ret = SRSLTE_SUCCESS; + } + peak_value = q->peak_values[max_id]; + + } else { + DEBUG("Current N_id_ncell is %d.\n", *cell_id); + + // run correlation only for given id + srslte_nsss_sync_find_pci(q, q->tmp_input, *cell_id); + + if (q->peak_values[*cell_id] > q->corr_peak_threshold) { + ret = SRSLTE_SUCCESS; + } + peak_value = q->peak_values[*cell_id]; + } + + // set remaining return values + if (sfn_partial) { + *sfn_partial = 0; // we only search for the first of the four possible shifts + } + if (corr_peak_value) { + *corr_peak_value = peak_value; + } + } + return ret; +} + +// Correlates input signal with the NSSS sequence for a given n_id_ncell +void srslte_nsss_sync_find_pci(srslte_nsss_synch_t* q, cf_t* input, uint32_t cell_id) +{ + // correlate input with NSSS sequences + uint32_t conv_output_len = srslte_corr_fft_cc_run(&q->conv_fft, input, q->nsss_signal_time[cell_id], q->conv_output); + srslte_vec_abs_cf(q->conv_output, q->conv_output_abs, conv_output_len - 1); + + // Find maximum of the absolute value of the correlation + uint32_t corr_peak_pos = srslte_vec_max_fi(q->conv_output_abs, conv_output_len - 1); + +#if DUMP_SIGNALS + printf("Dumping debug signals for cell-id %d.\n", i); + srslte_vec_save_file("nsss_find_input.bin", input, q->input_size * sizeof(cf_t)); + srslte_vec_save_file("nsss_corr_seq_time.bin", q->nsss_signal_time[i], SRSLTE_NSSS_CORR_FILTER_LEN * sizeof(cf_t)); + srslte_vec_save_file("nsss_find_conv_output_abs.bin", q->conv_output_abs, conv_output_len * sizeof(float)); +#endif + +#if SRSLTE_NSSS_RETURN_PSR + // Find second side lobe + + // Find end of peak lobe to the right + int pl_ub = corr_peak_pos + 1; + while (q->conv_output_abs[pl_ub + 1] <= q->conv_output_abs[pl_ub] && pl_ub < conv_output_len) { + pl_ub++; + } + // Find end of peak lobe to the left + int pl_lb; + if (corr_peak_pos > 2) { + pl_lb = corr_peak_pos - 1; + while (q->conv_output_abs[pl_lb - 1] <= q->conv_output_abs[pl_lb] && pl_lb > 1) { + pl_lb--; + } + } else { + pl_lb = 0; + } + + int sl_distance_right = conv_output_len - 1 - pl_ub; + if (sl_distance_right < 0) { + sl_distance_right = 0; + } + int sl_distance_left = pl_lb; + + int sl_right = pl_ub + srslte_vec_max_fi(&q->conv_output_abs[pl_ub], sl_distance_right); + int sl_left = srslte_vec_max_fi(q->conv_output_abs, sl_distance_left); + float side_lobe_value = SRSLTE_MAX(q->conv_output_abs[sl_right], q->conv_output_abs[sl_left]); + q->peak_values[cell_id] = q->conv_output_abs[corr_peak_pos] / side_lobe_value; + DEBUG("NSSS n_id_ncell=%d at peak_pos=%2d, pl_ub=%2d, pl_lb=%2d, sl_right: %2d, sl_left: %2d, PSR: %.2f/%.2f=%.2f\n", + cell_id, + corr_peak_pos, + pl_ub, + pl_lb, + sl_right, + sl_left, + q->conv_output_abs[corr_peak_pos], + side_lobe_value, + q->peak_values[cell_id]); +#else + // save max. absolute value + q->peak_values[cell_id] = q->conv_output_abs[corr_peak_pos]; + DEBUG("NSSS n_id_ncell=%d with peak=%f found at: %d\n", cell_id, q->peak_values[cell_id], corr_peak_pos); +#endif +} + +// generate the NSSS signal for each of 4 different cyclic shifts +// return 4 * 132 = 528 complex samples +void srslte_nsss_generate(cf_t* signal, uint32_t cell_id) +{ + if (srslte_cellid_isvalid(cell_id)) { + int u = cell_id % 126 + 3; + int q = floor(cell_id / 126.0); + int sign = -1; + + // iterate over all possible cyclic shifts + for (int theta_f = 0; theta_f < SRSLTE_NSSS_NUM_SEQ; theta_f++) { + for (int n = 0; n < SRSLTE_NSSS_LEN; n++) { + int n_prime = n % 131; + int m = n % 128; + + float arg = (float)sign * 2.0 * M_PI * ((float)theta_f) * ((float)n); + float complex tmp1; + __real__ tmp1 = cosf(arg); + __imag__ tmp1 = sinf(arg); + + arg = ((float)sign * M_PI * ((float)u) * (float)n_prime * ((float)n_prime + 1.0)) / 131.0; + float complex tmp2; + __real__ tmp2 = cosf(arg); + __imag__ tmp2 = sinf(arg); + + signal[theta_f * SRSLTE_NSSS_LEN + n] = b_q_m[q][m] * tmp1 * tmp2; + } + } + } else { + DEBUG("Invalid n_id_ncell %d\n", cell_id); + } +} + +void srslte_nsss_put_subframe(srslte_nsss_synch_t* q, + cf_t* nsss, + cf_t* subframe, + const int nf, + const uint32_t nof_prb, + const uint32_t nbiot_prb_offset) +{ + int theta_f = (int)floor(33 / 132.0 * (nf / 2.0)) % SRSLTE_NSSS_NUM_SEQ; + + // skip first 3 OFDM symbols over all PRBs completely + int k = 3 * nof_prb * SRSLTE_NRE + nbiot_prb_offset * SRSLTE_NRE; + + DEBUG("%d.9: Putting NSSS with theta_f=%d\n", nf, theta_f); + for (int l = 0; l < SRSLTE_CP_NORM_SF_NSYMB - 3; l++) { + memcpy(&subframe[k + SRSLTE_NSSS_NSC * l], + &nsss[(theta_f * SRSLTE_NSSS_LEN) + (l * SRSLTE_NSSS_NSC)], + SRSLTE_NSSS_NSC * sizeof(cf_t)); + k += (nof_prb - 1) * SRSLTE_NRE; + } +} diff --git a/lib/src/phy/sync/sync_nbiot.c b/lib/src/phy/sync/sync_nbiot.c new file mode 100644 index 000000000..1111232f5 --- /dev/null +++ b/lib/src/phy/sync/sync_nbiot.c @@ -0,0 +1,354 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include + +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/sync/cfo.h" +#include "srslte/phy/sync/sync_nbiot.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + +#define MEANPEAK_EMA_ALPHA 0.1 +#define CFO_EMA_ALPHA 0.1 +#define CP_EMA_ALPHA 0.1 +#define DEFAULT_CFO_TOL 50.0 // Hz + +/* We use the default LTE synch object internally for all the generic + * functions like CFO correction, etc. + * + */ +int srslte_sync_nbiot_init(srslte_sync_nbiot_t* q, uint32_t frame_size, uint32_t max_offset, uint32_t fft_size) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + q->n_id_ncell = SRSLTE_CELL_ID_UNKNOWN; + q->mean_cfo = 0; + q->cfo_ema_alpha = CFO_EMA_ALPHA; + q->fft_size = fft_size; + q->frame_size = frame_size; + q->max_frame_size = frame_size; + q->max_offset = max_offset; + q->threshold = 5.0; + q->enable_cfo_estimation = true; + + if (srslte_cfo_init(&q->cfocorr, q->frame_size)) { + fprintf(stderr, "Error initiating CFO\n"); + goto clean_exit; + } + + // Set default CFO tolerance + srslte_sync_nbiot_set_cfo_tol(q, DEFAULT_CFO_TOL); + + // initialize shift buffer for CFO estimation + q->shift_buffer = srslte_vec_malloc(SRSLTE_SF_LEN(q->fft_size) * sizeof(cf_t)); + if (!q->shift_buffer) { + perror("malloc"); + goto clean_exit; + } + srslte_cexptab_gen_sf(q->shift_buffer, -SRSLTE_NBIOT_FREQ_SHIFT_FACTOR, q->fft_size); + + // allocate memory for early CFO estimation + q->cfo_output = srslte_vec_malloc(10 * SRSLTE_SF_LEN(q->fft_size) * sizeof(cf_t)); + if (!q->cfo_output) { + perror("malloc"); + goto clean_exit; + } + + // configure CP + q->cp = SRSLTE_CP_NORM; + q->cp_len = SRSLTE_CP_LEN_NORM(1, q->fft_size); + if (q->frame_size < q->fft_size) { + q->nof_symbols = 1; + } else { + q->nof_symbols = q->frame_size / (q->fft_size + q->cp_len) - 1; + } + + if (srslte_npss_synch_init(&q->npss, frame_size, fft_size)) { + fprintf(stderr, "Error initializing NPSS object\n"); + return SRSLTE_ERROR; + } + + if (srslte_nsss_synch_init(&q->nsss, SRSLTE_NSSS_NUM_SF_DETECT * SRSLTE_SF_LEN_PRB_NBIOT, fft_size)) { + fprintf(stderr, "Error initializing NSSS object\n"); + exit(-1); + } + + if (srslte_cp_synch_init(&q->cp_synch, fft_size)) { + fprintf(stderr, "Error initiating CFO\n"); + goto clean_exit; + } + + ret = SRSLTE_SUCCESS; + +clean_exit: + if (ret == SRSLTE_ERROR) { + srslte_sync_nbiot_free(q); + } + return ret; +} + +void srslte_sync_nbiot_free(srslte_sync_nbiot_t* q) +{ + if (q) { + srslte_npss_synch_free(&q->npss); + srslte_nsss_synch_free(&q->nsss); + srslte_cfo_free(&q->cfocorr); + srslte_cp_synch_free(&q->cp_synch); + if (q->shift_buffer) { + free(q->shift_buffer); + } + if (q->cfo_output) { + free(q->cfo_output); + } + } +} + +int srslte_sync_nbiot_resize(srslte_sync_nbiot_t* q, uint32_t frame_size, uint32_t max_offset, uint32_t fft_size) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && frame_size <= 307200) { + ret = SRSLTE_ERROR; + + if (frame_size > q->max_frame_size) { + fprintf(stderr, "Error in srslte_sync_nbiot_resize(): frame_size must be lower than initialized\n"); + return SRSLTE_ERROR; + } + q->mean_cfo = 0; + q->cfo_i = 0; + q->find_cfo_i = false; + q->find_cfo_i_initiated = false; + q->cfo_ema_alpha = CFO_EMA_ALPHA; + q->fft_size = fft_size; + q->frame_size = frame_size; + q->max_offset = max_offset; + + if (srslte_npss_synch_resize(&q->npss, max_offset, fft_size)) { + fprintf(stderr, "Error resizing PSS object\n"); + return SRSLTE_ERROR; + } + if (srslte_nsss_synch_resize(&q->nsss, fft_size)) { + fprintf(stderr, "Error resizing SSS object\n"); + return SRSLTE_ERROR; + } + + if (srslte_cp_synch_resize(&q->cp_synch, fft_size)) { + fprintf(stderr, "Error resizing CFO\n"); + return SRSLTE_ERROR; + } + + if (srslte_cfo_resize(&q->cfocorr, q->frame_size)) { + fprintf(stderr, "Error resizing CFO\n"); + return SRSLTE_ERROR; + } + + // Update CFO tolerance + srslte_sync_nbiot_set_cfo_tol(q, q->current_cfo_tol); + + DEBUG("NBIOT SYNC init with frame_size=%d, max_offset=%d and fft_size=%d\n", frame_size, max_offset, fft_size); + + ret = SRSLTE_SUCCESS; + } else { + fprintf(stderr, "Invalid parameters frame_size: %d, fft_size: %d\n", frame_size, fft_size); + } + + return ret; +} + +/** Finds the NPSS sequence around the position find_offset in the buffer input. + * Returns 1 if the correlation peak exceeds the threshold set by srslte_sync_set_threshold() + * or 0 otherwise. Returns a negative number on error. + * + * The maximum of the correlation peak is always stored in *peak_position + */ +srslte_sync_find_ret_t +srslte_sync_nbiot_find(srslte_sync_nbiot_t* q, cf_t* input, uint32_t find_offset, uint32_t* peak_position) +{ + srslte_sync_find_ret_t ret = SRSLTE_SYNC_ERROR; + + int peak_pos = 0; + if (peak_position) { + *peak_position = 0; + } + + // Retrieve CFO from a set of candidates + if (q->enable_cfo_cand_test) { + q->mean_cfo = q->cfo_cand[q->cfo_cand_idx] / 15000; + q->cfo_cand_idx = (q->cfo_cand_idx + 1) % q->cfo_num_cand; + } + + // correct CFO using current estimate, store result in seperate buffer for NPSS detection + srslte_cfo_correct(&q->cfocorr, input, q->cfo_output, -q->mean_cfo / q->fft_size); + + peak_pos = srslte_npss_sync_find(&q->npss, &q->cfo_output[find_offset], &q->peak_value); + if (peak_pos < 0) { + fprintf(stderr, "Error calling finding NPSS sequence, peak pos: %d\n", peak_pos); + return SRSLTE_ERROR; + } + + if (peak_position) { + *peak_position = (uint32_t)peak_pos; + } + + /* If peak is over threshold return success */ + if (q->peak_value >= q->threshold) { + ret = SRSLTE_SYNC_FOUND; + } else { + ret = SRSLTE_SYNC_NOFOUND; + } + + // estimate CFO after NPSS has been detected + if (q->enable_cfo_estimation) { + // check if there are enough samples left + if (peak_pos + SRSLTE_NPSS_CFO_OFFSET + SRSLTE_NPSS_CFO_NUM_SAMPS + SRSLTE_NBIOT_FFT_SIZE < q->frame_size) { + // shift input signal + srslte_vec_prod_ccc(&q->shift_buffer[SRSLTE_SF_LEN(q->fft_size) / 2], + &input[peak_pos + SRSLTE_NPSS_CFO_OFFSET], + &input[peak_pos + SRSLTE_NPSS_CFO_OFFSET], + SRSLTE_NPSS_CFO_NUM_SAMPS); + + // use second slot of the NPSS for CFO estimation + float cfo = cfo_estimate_nbiot(q, &input[peak_pos + SRSLTE_NPSS_CFO_OFFSET]); + + // compute exponential moving average CFO + q->mean_cfo = SRSLTE_VEC_EMA(cfo, q->mean_cfo, q->cfo_ema_alpha); + DEBUG("CFO=%.4f, mean=%.4f (%.2f Hz), ema=%.2f\n", cfo, q->mean_cfo, q->mean_cfo * 15000, q->cfo_ema_alpha); + } else { + DEBUG("Not enough samples for CFO estimation. Skipping.\n"); + } + } + + DEBUG("sync_nbiot ret=%d find_offset=%d frame_len=%d, pos=%d peak=%.2f threshold=%.2f, CFO=%.3f kHz\n", + ret, + find_offset, + q->frame_size, + peak_pos, + q->peak_value, + q->threshold, + 15 * (q->mean_cfo)); + + return ret; +} + +// Use two OFDM symbols to estimate CFO +float cfo_estimate_nbiot(srslte_sync_nbiot_t* q, cf_t* input) +{ + uint32_t cp_offset = 0; + cp_offset = + srslte_cp_synch(&q->cp_synch, input, q->max_offset, SRSLTE_NPSS_CFO_NUM_SYMS, SRSLTE_CP_LEN_NORM(1, q->fft_size)); + cf_t cp_corr_max = srslte_cp_synch_corr_output(&q->cp_synch, cp_offset); + float cfo = -carg(cp_corr_max) / M_PI / 2; + return cfo; +} + +void srslte_sync_nbiot_set_threshold(srslte_sync_nbiot_t* q, float threshold) +{ + q->threshold = threshold; +} + +void srslte_sync_nbiot_set_cfo_enable(srslte_sync_nbiot_t* q, bool enable) +{ + q->enable_cfo_estimation = enable; +} + +void srslte_sync_nbiot_set_cfo_cand_test_enable(srslte_sync_nbiot_t* q, bool enable) +{ + q->enable_cfo_cand_test = enable; +} + +int srslte_sync_nbiot_set_cfo_cand(srslte_sync_nbiot_t* q, float* cand, const int num) +{ + if (num > MAX_NUM_CFO_CANDITATES) { + printf("Too many candidates, maximum is %d.\n", MAX_NUM_CFO_CANDITATES); + return SRSLTE_ERROR; + } + for (int i = 0; i < num; i++) { + q->cfo_cand[i] = cand[i]; + } + q->cfo_num_cand = num; + return SRSLTE_SUCCESS; +} + +void srslte_sync_nbiot_set_cfo_tol(srslte_sync_nbiot_t* q, float tol) +{ + srslte_cfo_set_tol(&q->cfocorr, tol / (15000.0 * q->fft_size)); +} + +void srslte_sync_nbiot_set_cfo_ema_alpha(srslte_sync_nbiot_t* q, float alpha) +{ + q->cfo_ema_alpha = alpha; +} + +void srslte_sync_nbiot_set_npss_ema_alpha(srslte_sync_nbiot_t* q, float alpha) +{ + srslte_npss_synch_set_ema_alpha(&q->npss, alpha); +} + +/** Determines the N_id_ncell using the samples in the buffer input. + * The function expects two subframes of samples provided as input which + * both contain subframe 9 of two consecutive frames. Either the first + * or the seconds contain the NSSS sequence. + * + * Returns 1 if the correlation peak exceeds the threshold or 0 otherwise. + * Returns a negative number on error. + * + */ +int srslte_sync_nbiot_find_cell_id(srslte_sync_nbiot_t* q, cf_t* input) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + float peak_value; + uint32_t sfn_partial; + + if (q != NULL && input != NULL && q->frame_size == SRSLTE_SF_LEN_PRB_NBIOT) { + ret = srslte_nsss_sync_find(&q->nsss, input, &peak_value, &q->n_id_ncell, &sfn_partial); + printf("NSSS with peak=%f, cell-id: %d, partial SFN: %x\n", peak_value, q->n_id_ncell, sfn_partial); + } + return ret; +} + +int srslte_sync_nbiot_get_cell_id(srslte_sync_nbiot_t* q) +{ + return q->n_id_ncell; +} + +float srslte_sync_nbiot_get_cfo(srslte_sync_nbiot_t* q) +{ + return q->mean_cfo + q->cfo_i; +} + +void srslte_sync_nbiot_set_cfo(srslte_sync_nbiot_t* q, float cfo) +{ + q->mean_cfo = cfo; +} + +float srslte_sync_nbiot_get_peak_value(srslte_sync_nbiot_t* q) +{ + return q->peak_value; +} + +void srslte_sync_nbiot_reset(srslte_sync_nbiot_t* q) +{ + srslte_npss_synch_reset(&q->npss); +} diff --git a/lib/src/phy/sync/test/CMakeLists.txt b/lib/src/phy/sync/test/CMakeLists.txt index 77df8cfd4..25035c18e 100644 --- a/lib/src/phy/sync/test/CMakeLists.txt +++ b/lib/src/phy/sync/test/CMakeLists.txt @@ -25,18 +25,29 @@ add_executable(pss_file pss_file.c) target_link_libraries(pss_file srslte_phy) -if(UHD_FOUND) +add_executable(npss_file npss_file.c) +target_link_libraries(npss_file srslte_phy) + +if(RF_FOUND) add_executable(pss_usrp pss_usrp.c) - target_link_libraries(pss_usrp srslte_phy srslte_rf) -endif(UHD_FOUND) + target_link_libraries(pss_usrp srslte_rf srslte_phy) + + add_executable(npss_usrp npss_usrp.c) + target_link_libraries(npss_usrp srslte_rf srslte_phy) + + add_executable(nsss_usrp nsss_usrp.c) + target_link_libraries(nsss_usrp srslte_rf srslte_phy) +endif(RF_FOUND) if(SRSGUI_FOUND) include_directories(${SRSGUI_INCLUDE_DIRS}) target_link_libraries(pss_file ${SRSGUI_LIBRARIES}) - if(UHD_FOUND) + if(RF_FOUND) target_link_libraries(pss_usrp ${SRSGUI_LIBRARIES}) - endif(UHD_FOUND) - add_definitions(-DENABLE_GUI) + target_link_libraries(npss_usrp ${SRSGUI_LIBRARIES}) + endif(RF_FOUND) +else(SRSGUI_FOUND) + add_definitions(-DDISABLE_GRAPHICS) endif(SRSGUI_FOUND) ######################################################################## @@ -46,6 +57,15 @@ endif(SRSGUI_FOUND) add_executable(sync_test sync_test.c) target_link_libraries(sync_test srslte_phy) +add_executable(sync_nbiot_test sync_nbiot_test.c) +target_link_libraries(sync_nbiot_test srslte_phy) + +add_executable(npss_test npss_test.c) +target_link_libraries(npss_test srslte_phy) + +add_executable(nsss_test nsss_test.c) +target_link_libraries(nsss_test srslte_phy) + add_test(sync_test_100 sync_test -o 100 -c 501) add_test(sync_test_400 sync_test -o 400 -c 2) add_test(sync_test_100_e sync_test -o 100 -e -c 150) @@ -56,6 +76,10 @@ add_test(sync_test_400 sync_test -o 400 -p 50 -c 500) add_test(sync_test_100_e sync_test -o 100 -e -p 50 -c 133) add_test(sync_test_400_e sync_test -o 400 -e -p 50 -c 123) +add_test(npss_test_nonoise npss_test) +add_test(nsss_test_nonoise_2 nsss_test -c 2) +add_test(nsss_test_nonoise_501 nsss_test -c 501) + ######################################################################## # CFO TEST ######################################################################## @@ -65,7 +89,3 @@ target_link_libraries(cfo_test srslte_phy) add_test(cfo_test_1 cfo_test -f 0.12345 -n 1000) add_test(cfo_test_2 cfo_test -f 0.99849 -n 1000) - - - - diff --git a/lib/src/phy/sync/test/npss_file.c b/lib/src/phy/sync/test/npss_file.c new file mode 100644 index 000000000..b53fcf156 --- /dev/null +++ b/lib/src/phy/sync/test/npss_file.c @@ -0,0 +1,307 @@ +/* + * 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/io/filesink.h" +#include "srslte/phy/io/filesource.h" +#include "srslte/phy/sync/npss.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define OUTPUT_FILENAME "npss_file.m" +void write_to_file(); + +srslte_nbiot_cell_t cell = { + .base = {.nof_prb = 1, .cp = SRSLTE_CP_NORM, .id = 0}, + .base.nof_ports = 1, + .base.nof_prb = 1, + .nbiot_prb = 0, +}; + +bool disable_plots = false; +char* input_file_name; +int cell_id = -1; +int nof_frames = 1; +uint32_t fft_size = 128; +float threshold = 0.4; +int N_id_2_sync = -1; +srslte_cp_t cp = SRSLTE_CP_NORM; +int file_offset = 0; +bool save_frame_to_file = false; + +#define FLEN (fft_size * 15 * 10) // for one entire frame + +void usage(char* prog) +{ + printf("Usage: %s [nlestodv] -i cell_id -f input_file_name\n", prog); + printf("\t-n nof_frames [Default %d]\n", nof_frames); + printf("\t-l N_id_2 to sync [Default use cell_id]\n"); + printf("\t-s Safe to aligned frame to file [Default %d]\n", save_frame_to_file); + printf("\t-t threshold [Default %.2f]\n", threshold); + printf("\t-o file read offset [Default %d]\n", file_offset); + printf("\t-v srslte_verbose\n"); +} + +void parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "nlstvof")) != -1) { + switch (opt) { + case 'f': + input_file_name = argv[optind]; + break; + case 't': + threshold = atof(argv[optind]); + break; + case 'o': + file_offset = atoi(argv[optind]); + break; + case 'l': + N_id_2_sync = atoi(argv[optind]); + break; + case 's': + save_frame_to_file = true; + break; + case 'n': + nof_frames = atoi(argv[optind]); + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} +float m0_value, m1_value; + +int main(int argc, char** argv) +{ + srslte_filesource_t fsrc; + cf_t* buffer; + int frame_cnt, n; + srslte_npss_synch_t npss; + int peak_idx, last_peak; + float peak_value; + float mean_peak; + uint32_t nof_det, nof_nodet, nof_nopeak, nof_nopeakdet; + + parse_args(argc, argv); + + buffer = malloc(sizeof(cf_t) * FLEN * 2); + if (!buffer) { + perror("malloc"); + exit(-1); + } + + if (srslte_npss_synch_init(&npss, FLEN, fft_size)) { + fprintf(stderr, "Error initializing NPSS object\n"); + exit(-1); + } + + printf("Opening file...\n"); + if (srslte_filesource_init(&fsrc, input_file_name, SRSLTE_COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", input_file_name); + exit(-1); + } + + printf("Frame length %d samples\n", FLEN); + printf("NPSS detection threshold: %.2f\n", threshold); + + nof_det = nof_nodet = nof_nopeak = nof_nopeakdet = 0; + frame_cnt = 0; + last_peak = 0; + mean_peak = 0; + int peak_offset = 0; + + n = srslte_filesource_read(&fsrc, buffer, file_offset); + + bool save_and_exit = false; + while (frame_cnt < nof_frames || nof_frames == -1) { + n = srslte_filesource_read(&fsrc, buffer, FLEN - peak_offset); + if (n < 0) { + fprintf(stderr, "Error reading samples\n"); + exit(-1); + } + if (n < FLEN / 10) { + fprintf(stdout, "End of file (n=%d, flen=%d, peak=%d)\n", n, FLEN, peak_offset); + break; + } + + if (save_frame_to_file && save_and_exit) { + char* filename = "frame_hyp.bin"; + printf("Saving entire frame to %s\n", filename); + srslte_vec_save_file(filename, buffer, FLEN * sizeof(cf_t)); + exit(-1); + } + + peak_idx = srslte_npss_sync_find(&npss, buffer, &peak_value); + if (peak_idx < 0) { + fprintf(stderr, "Error finding NPSS peak\n"); + exit(-1); + } + + mean_peak = SRSLTE_VEC_CMA(peak_value, mean_peak, frame_cnt); + + if (peak_value >= threshold) { + nof_det++; + + // try to align frame + if (save_frame_to_file && !save_and_exit) { + cf_t dummy[FLEN]; // full frame + printf("Peak_idx at %d\n", peak_idx); + int num_drop = peak_idx - SRSLTE_NPSS_CORR_OFFSET + FLEN / 2; + printf("Dropping %d samples!\n", num_drop); + + if (num_drop > FLEN) { + printf("wrapping num drop to %d\n", num_drop); + num_drop = num_drop % FLEN; + } + + srslte_filesource_read(&fsrc, dummy, num_drop); + save_and_exit = true; + } + + } else { + nof_nodet++; + } + + if (frame_cnt > 100) { + if (abs(last_peak - peak_idx) > 4) { + if (peak_value >= threshold) { + nof_nopeakdet++; + } + nof_nopeak++; + } + } + + frame_cnt++; + + printf("[%5d]: Pos: %5d, PSR: %4.1f (~%4.1f) Pdet: %4.2f, " + "FA: %4.2f\n", + frame_cnt, + (peak_value > threshold) ? peak_idx : 0, + peak_value, + mean_peak, + (float)nof_det / frame_cnt, + (float)nof_nopeakdet / frame_cnt); + + if (SRSLTE_VERBOSE_ISINFO()) { + printf("\n"); + } + + usleep(10000); + + last_peak = peak_idx; + } + + printf("NPSS detected #%d\n", nof_det); + + srslte_vec_save_file( + "npss_find_conv_output_abs.bin", npss.conv_output_abs, (FLEN + SRSLTE_NPSS_CORR_FILTER_LEN) * sizeof(float)); + srslte_vec_save_file("npss_corr_seq_time.bin", npss.npss_signal_time, SRSLTE_NPSS_CORR_FILTER_LEN * sizeof(cf_t)); + + write_to_file(); + + srslte_npss_synch_free(&npss); + free(buffer); + srslte_filesource_free(&fsrc); + + printf("Ok\n"); + exit(0); +} + +float tmp[1000000]; +void write_to_file() +{ + srslte_filesink_t debug_fsink; + char fname[] = OUTPUT_FILENAME; + if (srslte_filesink_init(&debug_fsink, fname, SRSLTE_TEXT)) { + fprintf(stderr, "Error opening file %s\n", fname); + exit(-1); + } + + fprintf(debug_fsink.f, "%% %s : auto-generated file\n", OUTPUT_FILENAME); + fprintf(debug_fsink.f, "clear all;\n"); + fprintf(debug_fsink.f, "close all;\n"); + fprintf(debug_fsink.f, "pkg load signal;\n\n"); + + // the correlation sequence + fprintf(debug_fsink.f, "len = %u;\n", SRSLTE_NPSS_CORR_FILTER_LEN); + fprintf(debug_fsink.f, "sig1=read_complex('npss_corr_seq_time.bin', len);\n"); + fprintf(debug_fsink.f, "figure;\n"); + fprintf(debug_fsink.f, "t=1:len;\n"); + fprintf(debug_fsink.f, "plot(t,real(sig1),t,imag(sig1));\n"); + fprintf(debug_fsink.f, "xlabel('sample index');\n"); + fprintf(debug_fsink.f, "title(\"Correlation sequence time-domain\");\n"); + fprintf(debug_fsink.f, "\n\n"); + + // the FFT of the first symbol after the frequency correction + fprintf(debug_fsink.f, "npss_sym0=sig1(10:137);\n"); + fprintf(debug_fsink.f, "figure;\n"); + fprintf(debug_fsink.f, "plot(real(fftshift(fft(npss_sym0.*exp(-2*pi*1i*(0:127)'*.5/128), 128))));\n"); + fprintf(debug_fsink.f, "title(\"FFT of first symbol after frequency correction\");\n"); + fprintf(debug_fsink.f, "xlabel('sample index');\n"); + fprintf(debug_fsink.f, "\n\n"); + + // the input signal + fprintf(debug_fsink.f, "len = %u;\n", FLEN); + fprintf(debug_fsink.f, "sig=read_complex('%s', len);\n", input_file_name); + fprintf(debug_fsink.f, "figure;\n"); + fprintf(debug_fsink.f, "t= 1:length(sig);\n"); + fprintf(debug_fsink.f, "plot(t,real(sig),t,imag(sig));\n"); + fprintf(debug_fsink.f, "xlabel('sample index');\n"); + fprintf(debug_fsink.f, "title(\"Subframe time-domain\");\n"); + fprintf(debug_fsink.f, "\n\n"); + + // the correlation output + fprintf(debug_fsink.f, "num_samples = %u;\n", SRSLTE_NPSS_CORR_FILTER_LEN + FLEN - 1); + fprintf(debug_fsink.f, "conv = read_real('npss_find_conv_output_abs.bin', num_samples);\n"); + fprintf(debug_fsink.f, "t=1:length(conv);\n"); + fprintf(debug_fsink.f, "\n\n"); + fprintf(debug_fsink.f, "figure;\n"); + fprintf(debug_fsink.f, "plot(t,conv);\n"); + fprintf(debug_fsink.f, "xlabel('sample index');\n"); + fprintf(debug_fsink.f, "title(\"Convolution output absolute\");\n"); + fprintf(debug_fsink.f, "ylabel('Correlation magnitude');\n"); + fprintf(debug_fsink.f, "\n\n"); + + // cross-correlation in octave + fprintf(debug_fsink.f, "[corr, lag] = xcorr(sig,sig1);\n"); + fprintf(debug_fsink.f, "\n\n"); + fprintf(debug_fsink.f, "figure;\n"); + fprintf(debug_fsink.f, "plot(abs(corr));\n"); + fprintf(debug_fsink.f, "xlabel('sample index');\n"); + fprintf(debug_fsink.f, "title(\"Correlation in Octave\");\n"); + fprintf(debug_fsink.f, "ylabel('Correlation magnitude');\n"); + fprintf(debug_fsink.f, "\n\n"); + + srslte_filesink_free(&debug_fsink); + printf("data written to %s\n", OUTPUT_FILENAME); +} diff --git a/lib/src/phy/sync/test/npss_test.c b/lib/src/phy/sync/test/npss_test.c new file mode 100644 index 000000000..06dfab29b --- /dev/null +++ b/lib/src/phy/sync/test/npss_test.c @@ -0,0 +1,220 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/sync/npss.h" +#include "srslte/srslte.h" + +#define OUTPUT_FILENAME "npss_test.m" +void write_to_file(); + +#define DUMP_SIGNALS 0 + +int input_len = SRSLTE_SF_LEN(SRSLTE_NBIOT_FFT_SIZE); + +void usage(char* prog) +{ + printf("Usage: %s [cpoev]\n", prog); + printf("\t-v srslte_verbose\n"); +} + +void parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "lv")) != -1) { + switch (opt) { + case 'l': + input_len = atoi(argv[optind]); + break; + case 'v': + srslte_verbose = SRSLTE_VERBOSE_DEBUG; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char** argv) +{ + cf_t* fft_buffer; + cf_t* input_buffer; + + srslte_npss_synch_t syncobj; + srslte_ofdm_t ifft; + struct timeval t[3]; + int fft_size; + int peak_pos; + float peak_value; + int ret = SRSLTE_ERROR; + + parse_args(argc, argv); + + if (input_len < SRSLTE_SF_LEN(SRSLTE_NBIOT_FFT_SIZE)) { + fprintf(stderr, "Input len too small (%d), must be at least one subframe\n", input_len); + exit(-1); + } + + fft_size = srslte_symbol_sz(SRSLTE_NBIOT_DEFAULT_NUM_PRB_BASECELL); + if (fft_size < 0) { + fprintf(stderr, "Invalid nof_prb=%d\n", SRSLTE_NBIOT_DEFAULT_NUM_PRB_BASECELL); + exit(-1); + } + + printf("Input buffer length is %d samples\n", input_len); + uint32_t buffer_len = input_len + SRSLTE_NPSS_CORR_FILTER_LEN + 1; + fft_buffer = malloc(sizeof(cf_t) * buffer_len); + if (!fft_buffer) { + perror("malloc"); + exit(-1); + } + bzero(fft_buffer, sizeof(cf_t) * buffer_len); + + input_buffer = malloc(sizeof(cf_t) * input_len); + if (!input_buffer) { + perror("malloc"); + exit(-1); + } + bzero(input_buffer, sizeof(cf_t) * input_len); + + if (srslte_ofdm_tx_init(&ifft, SRSLTE_CP_NORM, input_buffer, fft_buffer, SRSLTE_NBIOT_DEFAULT_NUM_PRB_BASECELL)) { + fprintf(stderr, "Error creating iFFT object\n"); + exit(-1); + } + srslte_ofdm_set_freq_shift(&ifft, -SRSLTE_NBIOT_FREQ_SHIFT_FACTOR); + + if (srslte_npss_synch_init(&syncobj, input_len, fft_size)) { + fprintf(stderr, "Error initializing NPSS object\n"); + return SRSLTE_ERROR; + } + + // generate NPSS/NSSS signals + _Complex float npss_signal[SRSLTE_NPSS_TOT_LEN]; + srslte_npss_generate(npss_signal); + srslte_npss_put_subframe( + &syncobj, npss_signal, input_buffer, SRSLTE_NBIOT_DEFAULT_NUM_PRB_BASECELL, SRSLTE_NBIOT_DEFAULT_PRB_OFFSET); + + // Transform to OFDM symbols + srslte_ofdm_tx_sf(&ifft); + + // look for NPSS signal + gettimeofday(&t[1], NULL); + peak_pos = srslte_npss_sync_find(&syncobj, fft_buffer, &peak_value); + gettimeofday(&t[2], NULL); + get_time_interval(t); + printf("NPPS with peak=%f found at: %d (in %.0f usec)\n", + peak_value, + peak_pos, + (int)t[0].tv_sec * 1e6 + (int)t[0].tv_usec); + + // write results to file +#if DUMP_SIGNALS + srslte_vec_save_file("npss_find_conv_output_abs.bin", syncobj.conv_output_abs, buffer_len * sizeof(float)); + srslte_vec_save_file("npss_sf_time.bin", fft_buffer, input_len * sizeof(cf_t)); + srslte_vec_save_file("npss_corr_seq_time.bin", syncobj.npss_signal_time, SRSLTE_NPSS_CORR_FILTER_LEN * sizeof(cf_t)); + write_to_file(); +#endif + + // cleanup + srslte_npss_synch_free(&syncobj); + free(fft_buffer); + free(input_buffer); + srslte_ofdm_tx_free(&ifft); + + if (peak_pos == SRSLTE_NPSS_CORR_OFFSET) { + printf("Ok\n"); + ret = SRSLTE_SUCCESS; + } else { + printf("Failed\n"); + } + + return ret; +} + +void write_to_file() +{ + srslte_filesink_t debug_fsink; + char fname[] = OUTPUT_FILENAME; + if (srslte_filesink_init(&debug_fsink, fname, SRSLTE_TEXT)) { + fprintf(stderr, "Error opening file %s\n", fname); + exit(-1); + } + + fprintf(debug_fsink.f, "%% %s : auto-generated file\n", OUTPUT_FILENAME); + fprintf(debug_fsink.f, "clear all;\n"); + fprintf(debug_fsink.f, "close all;\n"); + fprintf(debug_fsink.f, "pkg load signal;\n\n"); + + // the correlation sequence + fprintf(debug_fsink.f, "len = %u;\n", SRSLTE_NPSS_CORR_FILTER_LEN); + fprintf(debug_fsink.f, "sig1=read_complex('npss_corr_seq_time.bin', len);\n"); + fprintf(debug_fsink.f, "figure;\n"); + fprintf(debug_fsink.f, "t=1:len;\n"); + fprintf(debug_fsink.f, "plot(t,real(sig1),t,imag(sig1));\n"); + fprintf(debug_fsink.f, "xlabel('sample index');\n"); + fprintf(debug_fsink.f, "title('Correlation sequence time-domain');\n"); + fprintf(debug_fsink.f, "\n\n"); + + // the generated subframe + fprintf(debug_fsink.f, "len = %u;\n", input_len); + fprintf(debug_fsink.f, "sig=read_complex('npss_sf_time.bin', len);\n"); + fprintf(debug_fsink.f, "figure;\n"); + fprintf(debug_fsink.f, "t= 1:len;\n"); + fprintf(debug_fsink.f, "plot(t,real(sig),t,imag(sig));\n"); + fprintf(debug_fsink.f, "xlabel('sample index');\n"); + fprintf(debug_fsink.f, "title('Subframe time-domain');\n"); + fprintf(debug_fsink.f, "\n\n"); + + // the correlation output + fprintf(debug_fsink.f, "num_samples = %u;\n", SRSLTE_NPSS_CORR_FILTER_LEN + input_len - 1); + fprintf(debug_fsink.f, "sig = read_real('npss_find_conv_output_abs.bin', num_samples);\n"); + fprintf(debug_fsink.f, "t=1:num_samples;\n"); + fprintf(debug_fsink.f, "\n\n"); + fprintf(debug_fsink.f, "figure;\n"); + fprintf(debug_fsink.f, "plot(t,sig);\n"); + fprintf(debug_fsink.f, "xlabel('sample index');\n"); + fprintf(debug_fsink.f, "title('Convolution output absolute');\n"); + fprintf(debug_fsink.f, "ylabel('Correlation magnitude');\n"); + fprintf(debug_fsink.f, "\n\n"); + + // cross-correlation in octave + fprintf(debug_fsink.f, "[corr, lag] = xcorr(sig1,sig1);\n"); + fprintf(debug_fsink.f, "\n\n"); + fprintf(debug_fsink.f, "figure;\n"); + fprintf(debug_fsink.f, "plot(abs(corr));\n"); + fprintf(debug_fsink.f, "xlabel('sample index');\n"); + fprintf(debug_fsink.f, "title('Correlation in Octave');\n"); + fprintf(debug_fsink.f, "ylabel('Correlation magnitude');\n"); + fprintf(debug_fsink.f, "\n\n"); + + srslte_filesink_free(&debug_fsink); + printf("data written to %s\n", OUTPUT_FILENAME); +} diff --git a/lib/src/phy/sync/test/npss_usrp.c b/lib/src/phy/sync/test/npss_usrp.c new file mode 100644 index 000000000..190b2f687 --- /dev/null +++ b/lib/src/phy/sync/test/npss_usrp.c @@ -0,0 +1,320 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/rf/rf.h" +#include "srslte/phy/sync/npss.h" +#include "srslte/srslte.h" + +#ifndef DISABLE_GRAPHICS +void init_plots(); +void do_plots_npss(float* corr, float energy, uint32_t size); +#endif + +bool disable_plots = false; +char* rf_args = ""; +float rf_gain = 40.0, rf_freq = -1.0; +int nof_frames = -1; +uint32_t fft_size = 128; +float threshold = 0.4; +bool save_frame_to_file = false; +float cfo_fixed = 0.0; +bool has_cfo_corr = true; + +srslte_nbiot_cell_t cell = { + .base = {.nof_prb = 1, .cp = SRSLTE_CP_NORM, .nof_ports = 1, .id = 0}, + .nbiot_prb = 0, + .n_id_ncell = 0, +}; + +void usage(char* prog) +{ + printf("Usage: %s [adgtvnp] -f rx_frequency_hz -i cell_id\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-C Disable CFO correction [Default %s]\n", has_cfo_corr ? "Enabled" : "Disabled"); + printf("\t-c Manual CFO offset [Default %.0f Hz]\n", cfo_fixed); + printf("\t-n nof_frames [Default %d]\n", nof_frames); + printf("\t-s Save frame to file [Default %d]\n", save_frame_to_file); + 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, "aCcdgtvsfi")) != -1) { + switch (opt) { + case 'a': + rf_args = argv[optind]; + break; + case 'C': + has_cfo_corr = false; + break; + case 'c': + cfo_fixed = atof(argv[optind]); + break; + case 'g': + rf_gain = atof(argv[optind]); + break; + case 'f': + rf_freq = atof(argv[optind]); + break; + case 't': + threshold = atof(argv[optind]); + break; + case 'i': + cell.base.id = atoi(argv[optind]); + break; + case 's': + save_frame_to_file = true; + disable_plots = true; + break; + case 'n': + nof_frames = atoi(argv[optind]); + 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); + } +} + +float m0_value, m1_value; + +bool go_exit = false; +void sig_int_handler(int signo) +{ + printf("SIGINT received. Exiting...\n"); + if (signo == SIGINT) { + go_exit = true; + } +} + +int main(int argc, char** argv) +{ + cf_t* buffer; + int frame_cnt, n; + srslte_rf_t rf; + srslte_cfo_t cfocorr; + srslte_npss_synch_t npss; + int32_t flen; + int peak_idx; + float peak_value; + float mean_peak; + uint32_t nof_det, nof_nodet, nof_nopeak, nof_nopeakdet; + + parse_args(argc, argv); + +#ifndef DISABLE_GRAPHICS + if (!disable_plots) + init_plots(); +#endif + + signal(SIGINT, sig_int_handler); + + float srate = 15000.0 * fft_size; + flen = srate * 10 / 1000; + + printf("Frame length %d samples\n", flen); + printf("NPSS detection threshold: %.2f\n", threshold); + + if (cfo_fixed) { + printf("Manually compensating %.0f Hz CFO offset\n", cfo_fixed); + } + + if (srslte_cfo_init(&cfocorr, flen)) { + fprintf(stderr, "Error initiating CFO\n"); + exit(-1); + } + srslte_cfo_set_tol(&cfocorr, 50.0 / (15000.0 * fft_size)); + + printf("Opening RF device...\n"); + if (srslte_rf_open(&rf, rf_args)) { + fprintf(stderr, "Error opening rf\n"); + exit(-1); + } + + if (srate < 10e6) { + srslte_rf_set_master_clock_rate(&rf, 4 * srate); + } else { + srslte_rf_set_master_clock_rate(&rf, srate); + } + + 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_rf_rx_wait_lo_locked(&rf); + + buffer = malloc(sizeof(cf_t) * flen * 2); + if (!buffer) { + perror("malloc"); + exit(-1); + } + bzero(buffer, sizeof(cf_t) * flen * 2); + + if (srslte_npss_synch_init(&npss, flen, fft_size)) { + fprintf(stderr, "Error initializing NPSS object\n"); + exit(-1); + } + + srslte_rf_start_rx_stream(&rf, false); + + nof_det = nof_nodet = nof_nopeak = nof_nopeakdet = 0; + frame_cnt = 0; + mean_peak = 0; + peak_idx = 0; + int peak_offset = 0; + + bool save_and_exit = false; + while ((frame_cnt < nof_frames || nof_frames == -1) && !go_exit) { + n = srslte_rf_recv(&rf, buffer, flen - peak_offset, 1); + if (n < 0) { + fprintf(stderr, "Error receiving samples\n"); + exit(-1); + } + + frame_cnt++; + + if (save_frame_to_file && save_and_exit) { + char* filename = "frame_hyp.bin"; + printf("Saving entire frame to %s\n", filename); + srslte_vec_save_file(filename, buffer, flen * sizeof(cf_t)); + go_exit = true; + } + + // perform CFO correction + if (has_cfo_corr) { + srslte_cfo_correct(&cfocorr, buffer, buffer, -cfo_fixed / (15000 * fft_size)); + } + + peak_idx = srslte_npss_sync_find(&npss, buffer, &peak_value); + if (peak_idx < 0) { + fprintf(stderr, "Error finding NPSS peak\n"); + exit(-1); + } + + mean_peak = SRSLTE_VEC_CMA(peak_value, mean_peak, frame_cnt); + + if (peak_value >= threshold) { + nof_det++; + + // try to align frame + if (save_frame_to_file && !save_and_exit) { + cf_t dummy[flen]; // full frame + printf("Peak_idx at %d\n", peak_idx); + int num_drop = peak_idx - SRSLTE_NPSS_CORR_OFFSET + flen / 2; + printf("Dropping %d samples!\n", num_drop); + + if (num_drop > flen) { + printf("wrapping num drop to %d\n", num_drop); + num_drop = num_drop % flen; + } + + srslte_rf_recv(&rf, dummy, num_drop, 1); + save_and_exit = true; + } + } else { + nof_nodet++; + } + printf("[%5d]: Pos: %5d, PSR: %4.1f (~%4.1f) Pdet: %4.2f, " + "FA: %4.2f\r", + frame_cnt, + (peak_value > threshold) ? (peak_idx - SRSLTE_NPSS_CORR_OFFSET - flen / 10 / 2) : 0, + peak_value, + mean_peak, + (float)nof_det / frame_cnt, + (float)nof_nopeakdet / frame_cnt); + + if (SRSLTE_VERBOSE_ISINFO()) { + printf("\n"); + } + +#ifndef DISABLE_GRAPHICS + if (!disable_plots) { + int len = SRSLTE_NPSS_CORR_FILTER_LEN + npss.frame_size - 1; + do_plots_npss(npss.conv_output_avg, npss.conv_output_avg[peak_idx], len); + } +#endif + } + + printf("NPSS detected #%d\n", nof_det); + + srslte_npss_synch_free(&npss); + srslte_cfo_free(&cfocorr); + free(buffer); + srslte_rf_close(&rf); + + printf("Ok\n"); + exit(0); +} + +/********************************************************************** + * Plotting Functions + ***********************************************************************/ +#ifndef DISABLE_GRAPHICS + +#include "srsgui/srsgui.h" +plot_real_t pssout; +plot_real_t psss1; + +float tmp[1000000]; + +void init_plots() +{ + sdrgui_init(); + plot_real_init(&pssout); + plot_real_setTitle(&pssout, "NPSS xCorr"); + plot_real_setLabels(&pssout, "Index", "Absolute value"); + plot_real_setYAxisScale(&pssout, 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(&pssout, tmp, size); +} + +#endif diff --git a/lib/src/phy/sync/test/nsss_test.c b/lib/src/phy/sync/test/nsss_test.c new file mode 100644 index 000000000..d2ad94c0e --- /dev/null +++ b/lib/src/phy/sync/test/nsss_test.c @@ -0,0 +1,278 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/sync/nsss.h" +#include "srslte/srslte.h" + +#define OUTPUT_FILENAME "nsss_test.m" +void write_to_file(); + +int sfn = 0; +uint32_t n_id_ncell = SRSLTE_CELL_ID_UNKNOWN; +char* input_file_name; + +int max_num_sf = 20; + +#define NOF_PRB 1 + +#define DUMP_SIGNALS 0 +#define MAX_CORR_LEN 10000 +#define SFLEN (1 * SRSLTE_SF_LEN(SRSLTE_NBIOT_FFT_SIZE)) + +void usage(char* prog) +{ + printf("Usage: %s [fcpoev]\n", prog); + printf("\t-f file to read from\n"); + printf("\t-c cell ID\n"); + printf("\t-n SFN\n"); + printf("\t-r Maximum number of subframes to read from file [default: %d]\n", max_num_sf); + printf("\t-v srslte_verbose\n"); +} + +void parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "fcprnov")) != -1) { + switch (opt) { + case 'f': + input_file_name = argv[optind]; + break; + case 'c': + n_id_ncell = atoi(argv[optind]); + break; + case 'n': + sfn = atoi(argv[optind]); + break; + case 'r': + max_num_sf = atoi(argv[optind]); + break; + case 'v': + srslte_verbose = SRSLTE_VERBOSE_DEBUG; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char** argv) +{ + cf_t* fft_buffer; + cf_t* buffer; + srslte_nsss_synch_t syncobj; + srslte_ofdm_t ifft; + int fft_size; + float peak_value; + int num_sf = 1; + int ret = SRSLTE_ERROR; + + parse_args(argc, argv); + + buffer = malloc(sizeof(cf_t) * SFLEN * max_num_sf); + if (!buffer) { + perror("malloc"); + return ret; + } + memset(buffer, 0, sizeof(cf_t) * SFLEN * max_num_sf); + + fft_size = srslte_symbol_sz(NOF_PRB); + if (fft_size < 0) { + fprintf(stderr, "Invalid nof_prb=%d\n", NOF_PRB); + return ret; + } + + printf("SFLEN is %d samples\n", SFLEN); + fft_buffer = malloc(sizeof(cf_t) * SFLEN * max_num_sf); + if (!fft_buffer) { + perror("malloc"); + return ret; + } + memset(fft_buffer, 0, sizeof(cf_t) * SFLEN * max_num_sf); + + if (srslte_ofdm_tx_init(&ifft, SRSLTE_CP_NORM, buffer, fft_buffer, NOF_PRB)) { + fprintf(stderr, "Error creating iFFT object\n"); + return ret; + } + + if (input_file_name != NULL) { + srslte_filesource_t fsrc; + printf("Opening file %s\n", input_file_name); + if (srslte_filesource_init(&fsrc, input_file_name, SRSLTE_COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", input_file_name); + return ret; + } + num_sf = 0; + + // offset file + int file_offset = 0; + srslte_filesource_read(&fsrc, buffer, file_offset); + + // now read + while (num_sf < max_num_sf) { + int n = srslte_filesource_read(&fsrc, &fft_buffer[num_sf * SFLEN], SFLEN); + if (n < 0) { + fprintf(stderr, "Error reading samples\n"); + return ret; + } + if (n < SFLEN) { + fprintf(stdout, "End of file (n=%d, sflen=%d)\n", n, SFLEN); + break; + } + num_sf++; + } + srslte_filesource_free(&fsrc); + printf("Read %d sumbframes from file.\n", num_sf); + } + + // initialize NSSS object with actual input length + printf("Initializing NSSS synch with %dx%d samples.\n", num_sf, SFLEN); + if (srslte_nsss_synch_init(&syncobj, num_sf * SFLEN, fft_size)) { + fprintf(stderr, "Error initializing NSSS object\n"); + return ret; + } + + // write single NSSS sequence if not reading from input file + if (!input_file_name) { + // generate NPSS/NSSS signals + printf("Generating NSSS sequence for n_id_ncell=%d\n", n_id_ncell); + cf_t nsss_signals[SRSLTE_NSSS_TOT_LEN] = {}; + srslte_nsss_generate(nsss_signals, n_id_ncell == SRSLTE_CELL_ID_UNKNOWN ? 0 : n_id_ncell); + +#if DUMP_SIGNALS + srslte_vec_save_file("nsss_signal_freq.bin", nsss_signals, SRSLTE_NSSS_LEN * sizeof(cf_t)); +#endif + + srslte_nsss_put_subframe( + &syncobj, nsss_signals, buffer, sfn, SRSLTE_NBIOT_DEFAULT_NUM_PRB_BASECELL, SRSLTE_NBIOT_DEFAULT_PRB_OFFSET); + + // Transform to OFDM symbols + srslte_ofdm_tx_sf(&ifft); + } + + // look for NSSS signal + uint32_t n_id_ncell_detected = SRSLTE_CELL_ID_UNKNOWN; + uint32_t sfn_partial = 0; + srslte_nsss_sync_find(&syncobj, fft_buffer, &peak_value, &n_id_ncell_detected, &sfn_partial); + printf("NSSS with peak=%f, n_id_ncell: %d, partial SFN: %x\n", peak_value, n_id_ncell_detected, sfn_partial); + + if (n_id_ncell_detected == (n_id_ncell == SRSLTE_CELL_ID_UNKNOWN ? 0 : n_id_ncell)) { + printf("Ok\n"); + ret = SRSLTE_SUCCESS; + } else { + printf("Failed\n"); + } + +#if DUMP_SIGNALS +// dump signals +#define MAX_FNAME_LEN 40 + char fname[MAX_FNAME_LEN] = {}; + snprintf(fname, MAX_FNAME_LEN, "nsss_find_input.bin"); + printf("Saving entire sub-frame to %s\n", fname); + srslte_vec_save_file(fname, fft_buffer, num_sf * SFLEN * sizeof(cf_t)); + srslte_vec_save_file("nsss_corr_seq_time.bin", + syncobj.nsss_signal_time[n_id_ncell == SRSLTE_CELL_ID_UNKNOWN ? 0 : n_id_ncell], + SRSLTE_NSSS_CORR_FILTER_LEN * sizeof(cf_t)); + if (n_id_ncell_detected != SRSLTE_CELL_ID_UNKNOWN) { + // run correlation again with found cell to populate conv_output_abs + srslte_nsss_sync_find(&syncobj, fft_buffer, &peak_value, &n_id_ncell_detected, &sfn_partial); + } + srslte_vec_save_file("nsss_find_conv_output_abs.bin", + syncobj.conv_output_abs, + (SRSLTE_NSSS_CORR_FILTER_LEN + num_sf * SFLEN) * sizeof(float)); + + // write Octave script + write_to_file(); +#endif + + // cleanup + srslte_nsss_synch_free(&syncobj); + free(buffer); + free(fft_buffer); + srslte_ofdm_tx_free(&ifft); + return ret; +} + +void write_to_file() +{ + srslte_filesink_t debug_fsink; + char fname[] = OUTPUT_FILENAME; + if (srslte_filesink_init(&debug_fsink, fname, SRSLTE_TEXT)) { + fprintf(stderr, "Error opening file %s\n", fname); + exit(-1); + } + + fprintf(debug_fsink.f, "%% %s : auto-generated file\n", OUTPUT_FILENAME); + fprintf(debug_fsink.f, "clear all;\n"); + fprintf(debug_fsink.f, "close all;\n"); + fprintf(debug_fsink.f, "set(0,'DefaultFigureWindowStyle','docked');\n\n"); + fprintf(debug_fsink.f, "max_len = 1920 * 100;\n"); + + // the correlation sequence + fprintf(debug_fsink.f, "corr_seq=read_complex('nsss_corr_seq_time.bin', max_len);\n"); + fprintf(debug_fsink.f, "figure;\n"); + fprintf(debug_fsink.f, "t=1:length(corr_seq);\n"); + fprintf(debug_fsink.f, "plot(t,real(corr_seq),t,imag(corr_seq));\n"); + fprintf(debug_fsink.f, "xlabel('sample index');\n"); + fprintf(debug_fsink.f, "title('Correlation sequence time-domain');\n"); + fprintf(debug_fsink.f, "\n\n"); + + // the generated subframe + fprintf(debug_fsink.f, "input=read_complex('nsss_find_input.bin', max_len);\n"); + fprintf(debug_fsink.f, "figure;\n"); + fprintf(debug_fsink.f, "t=1:length(input);\n"); + fprintf(debug_fsink.f, "plot(t,real(input),t,imag(input));\n"); + fprintf(debug_fsink.f, "xlabel('sample index');\n"); + fprintf(debug_fsink.f, "title('Subframe time-domain');\n"); + fprintf(debug_fsink.f, "\n\n"); + + // the correlation output + fprintf(debug_fsink.f, "corr_srslte = read_real('nsss_find_conv_output_abs.bin', max_len);\n"); + fprintf(debug_fsink.f, "t=1:length(corr_srslte);\n"); + fprintf(debug_fsink.f, "figure;\n"); + fprintf(debug_fsink.f, "plot(t,corr_srslte);\n"); + fprintf(debug_fsink.f, "xlabel('sample index');\n"); + fprintf(debug_fsink.f, "title('Convolution output absolute');\n"); + fprintf(debug_fsink.f, "ylabel('Auto-correlation magnitude');\n"); + fprintf(debug_fsink.f, "\n\n"); + + // correlation in octave + fprintf(debug_fsink.f, "[corr_oct, lag] = xcorr(input,corr_seq);\n"); + fprintf(debug_fsink.f, "figure;\n"); + fprintf(debug_fsink.f, "plot(abs(corr_oct));\n"); + fprintf(debug_fsink.f, "xlabel('sample index');\n"); + fprintf(debug_fsink.f, "title('Correlation in Octave');\n"); + fprintf(debug_fsink.f, "ylabel('Correlation magnitude');\n"); + fprintf(debug_fsink.f, "\n\n"); + + srslte_filesink_free(&debug_fsink); + printf("data written to %s\n", OUTPUT_FILENAME); +} diff --git a/lib/src/phy/sync/test/nsss_usrp.c b/lib/src/phy/sync/test/nsss_usrp.c new file mode 100644 index 000000000..644994fd9 --- /dev/null +++ b/lib/src/phy/sync/test/nsss_usrp.c @@ -0,0 +1,196 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/io/filesink.h" +#include "srslte/phy/io/filesource.h" +#include "srslte/phy/rf/rf.h" +#include "srslte/phy/sync/cfo.h" +#include "srslte/phy/sync/nsss.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + +char* rf_args = ""; +float rf_gain = 40.0, rf_freq = -1.0; +int nof_frames = -1; +uint32_t fft_size = 128; +float threshold = 0.4; +bool has_cfo_corr = true; +float cfo_fixed = 0.0; + +srslte_nbiot_cell_t cell = { + .base = {.nof_prb = 1, .cp = SRSLTE_CP_NORM, .nof_ports = 1, .id = 0}, + .nbiot_prb = 0, + .n_id_ncell = 0, +}; + +void usage(char* prog) +{ + printf("Usage: %s [adgtvnp] -f rx_frequency_hz -i cell_id\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-C Disable CFO correction [Default %s]\n", has_cfo_corr ? "Enabled" : "Disabled"); + printf("\t-c Manual CFO offset [Default %.0f Hz]\n", cfo_fixed); + printf("\t-n nof_frames [Default %d]\n", nof_frames); + 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, "agCctvfi")) != -1) { + switch (opt) { + case 'a': + rf_args = argv[optind]; + break; + case 'C': + has_cfo_corr = false; + break; + case 'c': + cfo_fixed = atof(argv[optind]); + break; + case 'g': + rf_gain = atof(argv[optind]); + break; + case 'f': + rf_freq = atof(argv[optind]); + break; + case 't': + threshold = atof(argv[optind]); + break; + case 'i': + cell.n_id_ncell = atoi(argv[optind]); + break; + case 'n': + nof_frames = atoi(argv[optind]); + 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 main(int argc, char** argv) +{ + cf_t* buffer; + int n; + srslte_rf_t rf; + srslte_nsss_synch_t nsss; + float nsss_peak_value; + parse_args(argc, argv); + + signal(SIGINT, sig_int_handler); + + float srate = 15000.0 * fft_size; + int input_len = srate * 10 / 1000 * 2; // capture two full frames to make sure we have one NSSS + + printf("Input length %d samples\n", input_len); + + printf("Opening RF device...\n"); + if (srslte_rf_open(&rf, rf_args)) { + fprintf(stderr, "Error opening rf\n"); + exit(-1); + } + + if (srate < 10e6) { + srslte_rf_set_master_clock_rate(&rf, 4 * srate); + } else { + srslte_rf_set_master_clock_rate(&rf, srate); + } + + 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_rf_rx_wait_lo_locked(&rf); + + buffer = malloc(sizeof(cf_t) * input_len * 2); + if (!buffer) { + perror("malloc"); + exit(-1); + } + + if (srslte_nsss_synch_init(&nsss, input_len, fft_size)) { + fprintf(stderr, "Error initializing NSSS object\n"); + exit(-1); + } + + srslte_rf_start_rx_stream(&rf, false); + + printf("Receiving two full frames ..\n"); + n = srslte_rf_recv(&rf, buffer, input_len, 1); + if (n != input_len) { + fprintf(stderr, "Error receiving samples\n"); + exit(-1); + } + srslte_rf_close(&rf); + + // perform CFO correction + if (has_cfo_corr) { + srslte_cfo_t cfocorr; + if (srslte_cfo_init(&cfocorr, input_len)) { + fprintf(stderr, "Error initiating CFO\n"); + exit(-1); + } + srslte_cfo_set_tol(&cfocorr, 50.0 / (15000.0 * fft_size)); + srslte_cfo_correct(&cfocorr, buffer, buffer, -cfo_fixed / (15000 * fft_size)); + srslte_cfo_free(&cfocorr); + } + + // try to find NSSS + printf("Detecting cell id ..\n"); + uint32_t cell_id = SRSLTE_CELL_ID_UNKNOWN; + uint32_t sfn_partial; + srslte_nsss_sync_find(&nsss, buffer, &nsss_peak_value, &cell_id, &sfn_partial); + printf("Cell id: %d, peak_value=%f\n", cell_id, nsss_peak_value); + + srslte_nsss_synch_free(&nsss); + free(buffer); + + printf("Ok\n"); + exit(0); +} diff --git a/lib/src/phy/sync/test/sync_nbiot_test.c b/lib/src/phy/sync/test/sync_nbiot_test.c new file mode 100644 index 000000000..a77e98112 --- /dev/null +++ b/lib/src/phy/sync/test/sync_nbiot_test.c @@ -0,0 +1,266 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "srslte/phy/sync/sync_nbiot.h" +#include "srslte/srslte.h" + +int offset = 0; +float cfo = 0.0; +float snr = -1.0; + +#define OUTPUT_FILENAME "sync_nbiot_test.m" +void write_to_file(); +char* input_file_name; + +#define DUMP_SIGNALS 1 +#define MAX_CORR_LEN 10000 +#define SFLEN (10 * SRSLTE_SF_LEN(128)) + +void usage(char* prog) +{ + printf("Usage: %s [cgofv] -f input_file_name\n", prog); + printf("\t-c add CFO [Default %f]\n", cfo); + printf("\t-g add AWGN with target SNR [Default off]\n"); + printf("\t-o offset [Default %d]\n", offset); + printf("\t-v srslte_verbose\n"); +} + +void parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "cgofv")) != -1) { + switch (opt) { + case 'f': + input_file_name = argv[optind]; + break; + case 'c': + cfo = atof(argv[optind]); + break; + case 'g': + snr = atof(argv[optind]); + break; + case 'o': + offset = atoi(argv[optind]); + break; + case 'v': + srslte_verbose = SRSLTE_VERBOSE_DEBUG; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char** argv) +{ + int sf_idx = 0; + cf_t* fft_buffer; + _Complex float buffer[SFLEN]; // FLEN + fft_size + + srslte_filesource_t fsrc; + uint32_t find_idx = 0; + srslte_sync_nbiot_t syncobj; + srslte_ofdm_t ifft; + srslte_cfo_t cfocorr; + int fft_size; + + input_file_name = NULL; + + parse_args(argc, argv); + + if (input_file_name != NULL) { + printf("Opening file...\n"); + if (srslte_filesource_init(&fsrc, input_file_name, SRSLTE_COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", input_file_name); + exit(-1); + } + } + + fft_size = srslte_symbol_sz(SRSLTE_NBIOT_DEFAULT_NUM_PRB_BASECELL); + if (fft_size < 0) { + fprintf(stderr, "Invalid nof_prb=%d\n", SRSLTE_NBIOT_DEFAULT_NUM_PRB_BASECELL); + exit(-1); + } + + printf("SFLEN is %d samples\n", SFLEN); + fft_buffer = malloc(sizeof(cf_t) * SFLEN * 2); + if (!fft_buffer) { + perror("malloc"); + exit(-1); + } + + memset(buffer, 0, sizeof(cf_t) * SFLEN); + + if (srslte_cfo_init(&cfocorr, SFLEN)) { + fprintf(stderr, "Error initiating CFO\n"); + return -1; + } + // Set a CFO tolerance of approx 100 Hz + srslte_cfo_set_tol(&cfocorr, 100.0 / (15000.0 * fft_size)); + + // init synch object for a maximum SFLEN samples + if (srslte_sync_nbiot_init(&syncobj, SFLEN, SFLEN, fft_size)) { + fprintf(stderr, "Error initiating NPSS/NSSS\n"); + return -1; + } + srslte_sync_nbiot_set_cfo_enable(&syncobj, true); + + if (input_file_name == NULL) { + // generating test sequence + if (srslte_ofdm_tx_init( + &ifft, SRSLTE_CP_NORM, buffer, &fft_buffer[offset], SRSLTE_NBIOT_DEFAULT_NUM_PRB_BASECELL)) { + fprintf(stderr, "Error creating iFFT object\n"); + exit(-1); + } + srslte_ofdm_set_normalize(&ifft, true); + srslte_ofdm_set_freq_shift(&ifft, -SRSLTE_NBIOT_FREQ_SHIFT_FACTOR); + + // generate NPSS/NSSS signals + cf_t npss_signal[SRSLTE_NPSS_TOT_LEN]; + srslte_npss_generate(npss_signal); + srslte_npss_put_subframe( + &syncobj.npss, npss_signal, buffer, SRSLTE_NBIOT_DEFAULT_NUM_PRB_BASECELL, SRSLTE_NBIOT_DEFAULT_PRB_OFFSET); + + // Transform to OFDM symbols + memset(fft_buffer, 0, sizeof(cf_t) * SFLEN * 2); + srslte_ofdm_tx_sf(&ifft); + + srslte_ofdm_tx_free(&ifft); + } else { + // read samples from file + printf("Reading %d samples from file.\n", SFLEN); + int n = srslte_filesource_read(&fsrc, fft_buffer, SFLEN); + if (n < 0) { + fprintf(stderr, "Error reading samples\n"); + exit(-1); + } + } + +#ifdef DUMP_SIGNALS + srslte_vec_save_file( + "npss_corr_seq_time.bin", syncobj.npss.npss_signal_time, SRSLTE_NPSS_CORR_FILTER_LEN * sizeof(cf_t)); + srslte_vec_save_file("npss_sf_time.bin", fft_buffer, SFLEN * sizeof(cf_t)); +#endif + + if (cfo > 0.0) { + float delta_freq = cfo / 15000 / SRSLTE_NBIOT_FFT_SIZE; + printf("Adding CFO with target: %.4f\n", delta_freq); + printf("WARNING: not working at the moment!\n"); + srslte_cfo_correct(&cfocorr, fft_buffer, fft_buffer, delta_freq); + } + + // add some noise to the signal + if (snr != -1.0) { + snr -= 10.0; + printf("Adding AWGN with target SNR: %.2fdB\n", snr); + float nstd = powf(10.0f, -snr / 20.0f); + srslte_ch_awgn_c(fft_buffer, fft_buffer, nstd, SFLEN); + } + + // look for NPSS signal + if (srslte_sync_nbiot_find(&syncobj, fft_buffer, 0, &find_idx) < 0) { + fprintf(stderr, "Error running srslte_sync_nbiot_find()\n"); + exit(-1); + } + + printf("NPPS with peak=%f found at: %d, offset: %d, SF starts at %d\n", syncobj.peak_value, find_idx, offset, sf_idx); + + // write results to file + write_to_file(); + +#ifdef DUMP_SIGNALS + srslte_vec_save_file("npss_find_conv_output_abs.bin", + syncobj.npss.conv_output_abs, + (SFLEN + SRSLTE_NPSS_CORR_FILTER_LEN - 1) * sizeof(float)); +#endif + + // cleanup + if (input_file_name != NULL) + srslte_filesource_free(&fsrc); + srslte_sync_nbiot_free(&syncobj); + free(fft_buffer); + srslte_cfo_free(&cfocorr); + + printf("Ok\n"); + exit(0); +} + +void write_to_file() +{ + srslte_filesink_t debug_fsink; + char fname[] = OUTPUT_FILENAME; + if (srslte_filesink_init(&debug_fsink, fname, SRSLTE_TEXT)) { + fprintf(stderr, "Error opening file %s\n", fname); + exit(-1); + } + + fprintf(debug_fsink.f, "%% %s : auto-generated file\n", OUTPUT_FILENAME); + fprintf(debug_fsink.f, "clear all;\n"); + fprintf(debug_fsink.f, "close all;\n"); + fprintf(debug_fsink.f, "set(0,'DefaultFigureWindowStyle','docked');\n\n"); + + // the correlation sequence + fprintf(debug_fsink.f, "len = %u;\n", SRSLTE_NPSS_CORR_FILTER_LEN); + fprintf(debug_fsink.f, "sig1=read_complex('npss_corr_seq_time.bin', len);\n"); + fprintf(debug_fsink.f, "figure;\n"); + fprintf(debug_fsink.f, "t=1:len;\n"); + fprintf(debug_fsink.f, "plot(t,real(sig1),t,imag(sig1));\n"); + fprintf(debug_fsink.f, "xlabel('sample index');\n"); + fprintf(debug_fsink.f, "title('Correlation sequence time-domain');\n"); + fprintf(debug_fsink.f, "\n\n"); + + // the generated subframe + fprintf(debug_fsink.f, "len = %u;\n", SFLEN); + fprintf(debug_fsink.f, "sig=read_complex('npss_sf_time.bin', len);\n"); + fprintf(debug_fsink.f, "figure;\n"); + fprintf(debug_fsink.f, "t= 1:len;\n"); + fprintf(debug_fsink.f, "plot(t,real(sig),t,imag(sig));\n"); + fprintf(debug_fsink.f, "xlabel('sample index');\n"); + fprintf(debug_fsink.f, "title('Subframe time-domain');\n"); + fprintf(debug_fsink.f, "\n\n"); + + // the correlation output + fprintf(debug_fsink.f, "num_samples = %u;\n", SRSLTE_NPSS_CORR_FILTER_LEN + SFLEN - 1); + fprintf(debug_fsink.f, "sig = read_real('npss_find_conv_output_abs.bin', num_samples);\n"); + fprintf(debug_fsink.f, "t=1:num_samples;\n"); + fprintf(debug_fsink.f, "\n\n"); + fprintf(debug_fsink.f, "figure;\n"); + fprintf(debug_fsink.f, "plot(t,sig);\n"); + fprintf(debug_fsink.f, "xlabel('sample index');\n"); + fprintf(debug_fsink.f, "title('Convolution output absolute');\n"); + fprintf(debug_fsink.f, "ylabel('Auto-correlation magnitude');\n"); + fprintf(debug_fsink.f, "\n\n"); + + srslte_filesink_free(&debug_fsink); + printf("data written to %s\n", OUTPUT_FILENAME); +}