adding NB-IoT sync code

master
Andre Puschmann 6 years ago
parent d887624e74
commit 95a5c2dcdb

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

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

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

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

@ -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";
}
}

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

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

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

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

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

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

@ -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 <math.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include "srslte/phy/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

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

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

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