mirror of https://github.com/pvnis/srsRAN_4G.git
adding NB-IoT sync code
parent
d887624e74
commit
95a5c2dcdb
@ -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
|
@ -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);
|
||||||
|
}
|
@ -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…
Reference in New Issue