mirror of https://github.com/pvnis/srsRAN_4G.git
Merge branch 'next' into agpl_next
commit
f3da533f6b
@ -0,0 +1,92 @@
|
|||||||
|
#
|
||||||
|
# Copyright 2013-2021 Software Radio Systems Limited
|
||||||
|
#
|
||||||
|
# By using this file, you agree to the terms and conditions set
|
||||||
|
# forth in the LICENSE file which can be found at the top level of
|
||||||
|
# the distribution.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Adopted from https://github.com/pothosware/SoapyRTLSDR
|
||||||
|
# Copyright: 2015, Charles J. Cliffe
|
||||||
|
# License: MIT
|
||||||
|
|
||||||
|
# - Try to find if atomics need -latomic linking
|
||||||
|
# Once done this will define
|
||||||
|
# HAVE_CXX_ATOMICS_WITHOUT_LIB - Wether atomic types work without -latomic
|
||||||
|
# HAVE_CXX_ATOMICS64_WITHOUT_LIB - Wether 64 bit atomic types work without -latomic
|
||||||
|
|
||||||
|
INCLUDE(CheckCXXSourceCompiles)
|
||||||
|
INCLUDE(CheckLibraryExists)
|
||||||
|
|
||||||
|
# Sometimes linking against libatomic is required for atomic ops, if
|
||||||
|
# the platform doesn't support lock-free atomics.
|
||||||
|
|
||||||
|
function(check_working_cxx_atomics varname)
|
||||||
|
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++11")
|
||||||
|
CHECK_CXX_SOURCE_COMPILES("
|
||||||
|
#include <atomic>
|
||||||
|
std::atomic<int> x;
|
||||||
|
int main() {
|
||||||
|
return std::atomic_is_lock_free(&x);
|
||||||
|
}
|
||||||
|
" ${varname})
|
||||||
|
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||||
|
endfunction(check_working_cxx_atomics)
|
||||||
|
|
||||||
|
function(check_working_cxx_atomics64 varname)
|
||||||
|
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "-std=c++11 ${CMAKE_REQUIRED_FLAGS}")
|
||||||
|
CHECK_CXX_SOURCE_COMPILES("
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstdint>
|
||||||
|
std::atomic<uint64_t> x (0);
|
||||||
|
int main() {
|
||||||
|
uint64_t i = x.load(std::memory_order_relaxed);
|
||||||
|
return std::atomic_is_lock_free(&x);
|
||||||
|
}
|
||||||
|
" ${varname})
|
||||||
|
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||||
|
endfunction(check_working_cxx_atomics64)
|
||||||
|
|
||||||
|
# Check for atomic operations.
|
||||||
|
if(MSVC)
|
||||||
|
# This isn't necessary on MSVC.
|
||||||
|
set(HAVE_CXX_ATOMICS_WITHOUT_LIB True)
|
||||||
|
else()
|
||||||
|
# First check if atomics work without the library.
|
||||||
|
check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# If not, check if the library exists, and atomics work with it.
|
||||||
|
if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB)
|
||||||
|
check_library_exists(atomic __atomic_fetch_add_4 "" HAVE_LIBATOMIC)
|
||||||
|
if(NOT HAVE_LIBATOMIC)
|
||||||
|
message(STATUS "Host compiler appears to require libatomic, but cannot locate it.")
|
||||||
|
endif()
|
||||||
|
list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
|
||||||
|
check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB)
|
||||||
|
if (NOT HAVE_CXX_ATOMICS_WITH_LIB)
|
||||||
|
message(FATAL_ERROR "Host compiler must support std::atomic!")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Check for 64 bit atomic operations.
|
||||||
|
if(MSVC)
|
||||||
|
set(HAVE_CXX_ATOMICS64_WITHOUT_LIB True)
|
||||||
|
else()
|
||||||
|
check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITHOUT_LIB)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# If not, check if the library exists, and atomics work with it.
|
||||||
|
if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)
|
||||||
|
check_library_exists(atomic __atomic_load_8 "" HAVE_LIBATOMIC64)
|
||||||
|
if(NOT HAVE_LIBATOMIC64)
|
||||||
|
message(STATUS "Host compiler appears to require libatomic, but cannot locate it.")
|
||||||
|
endif()
|
||||||
|
list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
|
||||||
|
check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB)
|
||||||
|
if (NOT HAVE_CXX_ATOMICS64_WITH_LIB)
|
||||||
|
message(FATAL_ERROR "Host compiler must support std::atomic!")
|
||||||
|
endif()
|
||||||
|
endif()
|
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRSRAN_PBCH_NR_H
|
||||||
|
#define SRSRAN_PBCH_NR_H
|
||||||
|
|
||||||
|
#include "srsran/config.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Descibes the NR PBCH message
|
||||||
|
*/
|
||||||
|
typedef struct SRSRAN_API {
|
||||||
|
// TBD
|
||||||
|
} srsran_pbch_msg_nr_t;
|
||||||
|
|
||||||
|
#endif // SRSRAN_PBCH_NR_H
|
@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRSRAN_PSS_NR_H
|
||||||
|
#define SRSRAN_PSS_NR_H
|
||||||
|
|
||||||
|
#include "srsran/config.h"
|
||||||
|
#include "srsran/phy/common/phy_common_nr.h"
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief NR PSS sequence length in frequency domain
|
||||||
|
*/
|
||||||
|
#define SRSRAN_PSS_NR_LEN 127
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief NR PSS Symbol number
|
||||||
|
*/
|
||||||
|
#define SRSRAN_PSS_NR_SYMBOL_IDX 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Put NR PSS sequence in the SSB grid
|
||||||
|
* @remark Described in TS 38.211 section 7.4.2.2 Primary synchronization signal
|
||||||
|
* @param ssb_grid SSB resource grid
|
||||||
|
* @param N_id_2 Physical cell ID 2
|
||||||
|
* @param beta PSS power allocation
|
||||||
|
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise
|
||||||
|
*/
|
||||||
|
SRSRAN_API int srsran_pss_nr_put(cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id_2, float beta);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Extracts the NR PSS Least Square Estimates (LSE) from the SSB grid
|
||||||
|
* @param ssb_grid received SSB resource grid
|
||||||
|
* @param N_id_2 Physical cell ID 2
|
||||||
|
* @param lse Provides LSE pointer
|
||||||
|
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise
|
||||||
|
*/
|
||||||
|
SRSRAN_API int srsran_pss_nr_extract_lse(const cf_t* ssb_grid, uint32_t N_id_2, cf_t lse[SRSRAN_PSS_NR_LEN]);
|
||||||
|
|
||||||
|
#endif // SRSRAN_PSS_NR_H
|
@ -0,0 +1,135 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRSRAN_SSB_H
|
||||||
|
#define SRSRAN_SSB_H
|
||||||
|
|
||||||
|
#include "srsran/config.h"
|
||||||
|
#include "srsran/phy/common/phy_common_nr.h"
|
||||||
|
#include "srsran/phy/dft/dft.h"
|
||||||
|
#include "srsran/phy/phch/pbch_nr.h"
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default SSB maximum sampling rate
|
||||||
|
*/
|
||||||
|
#define SRSRAN_SSB_DEFAULT_MAX_SRATE_HZ 61.44e6
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default SSB minimum subcarrier spacing
|
||||||
|
*/
|
||||||
|
#define SRSRAN_SSB_DEFAULT_MIN_SCS srsran_subcarrier_spacing_15kHz
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default beta value, used in case they are set to zero
|
||||||
|
*/
|
||||||
|
#define SRSRAN_SSB_DEFAULT_BETA 1.0f
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Describes SSB object initialization arguments
|
||||||
|
*/
|
||||||
|
typedef struct SRSRAN_API {
|
||||||
|
double max_srate_hz; ///< Maximum sampling rate in Hz (common for gNb and UE), set to zero to use default
|
||||||
|
srsran_subcarrier_spacing_t min_scs; ///< Minimum subcarrier spacing
|
||||||
|
bool enable_correlate; ///< Enables PSS/SSS correlation and peak search (UE cell search)
|
||||||
|
bool enable_encode; ///< Enables PBCH Encoder (intended for gNb)
|
||||||
|
bool enable_decode; ///< Enables PBCH Decoder (intented for UE)
|
||||||
|
bool enable_measure; ///< Enables PSS/SSS CSI measurements
|
||||||
|
} srsran_ssb_args_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Describes SSB configuration arguments
|
||||||
|
*/
|
||||||
|
typedef struct SRSRAN_API {
|
||||||
|
double srate_hz; ///< Current sampling rate in Hz
|
||||||
|
double freq_offset_hz; ///< SSB base-band frequency offset
|
||||||
|
srsran_subcarrier_spacing_t scs; ///< SSB configured Subcarrier spacing
|
||||||
|
float beta_pss; ////< PSS power allocation
|
||||||
|
float beta_sss; ////< SSS power allocation
|
||||||
|
float beta_pbch; ////< PBCH power allocation
|
||||||
|
float beta_pbch_dmrs; ////< PBCH DMRS power allocation
|
||||||
|
} srsran_ssb_cfg_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Describes SSB object
|
||||||
|
*/
|
||||||
|
typedef struct SRSRAN_API {
|
||||||
|
srsran_ssb_args_t args; ///< Stores initialization arguments
|
||||||
|
srsran_ssb_cfg_t cfg; ///< Stores last configuration
|
||||||
|
|
||||||
|
/// Sampling rate dependent parameters
|
||||||
|
float scs_hz; ///< Subcarrier spacing in Hz
|
||||||
|
uint32_t max_symbol_sz; ///< Maximum symbol size given the minimum supported SCS and sampling rate
|
||||||
|
uint32_t symbol_sz; ///< Current SSB symbol size (for the given base-band sampling rate)
|
||||||
|
int32_t offset; ///< Current SSB integer offset (multiple of SCS)
|
||||||
|
uint32_t cp0_sz; ///< First symbol cyclic prefix size
|
||||||
|
uint32_t cp_sz; ///< Other symbol cyclic prefix size
|
||||||
|
|
||||||
|
/// Internal Objects
|
||||||
|
srsran_dft_plan_t ifft; ///< IFFT object for modulating the SSB
|
||||||
|
srsran_dft_plan_t fft; ///< FFT object for demodulate the SSB.
|
||||||
|
|
||||||
|
/// Frequency/Time domain temporal data
|
||||||
|
cf_t* tmp_freq;
|
||||||
|
cf_t* tmp_time;
|
||||||
|
} srsran_ssb_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialises configures NR SSB with the given arguments
|
||||||
|
* @param q SSB object
|
||||||
|
* @param args NR PSS initialization arguments
|
||||||
|
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise
|
||||||
|
*/
|
||||||
|
SRSRAN_API int srsran_ssb_init(srsran_ssb_t* q, const srsran_ssb_args_t* args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Frees NR SSB object
|
||||||
|
* @param q SSB object
|
||||||
|
*/
|
||||||
|
SRSRAN_API void srsran_ssb_free(srsran_ssb_t* q);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets SSB configuration with the current SSB configuration
|
||||||
|
* @param q SSB object
|
||||||
|
* @param cfg Current SSB configuration
|
||||||
|
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise
|
||||||
|
*/
|
||||||
|
SRSRAN_API int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg);
|
||||||
|
/**
|
||||||
|
* @brief Decodes PBCH in the given time domain signal
|
||||||
|
* @param q SSB object
|
||||||
|
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise
|
||||||
|
*/
|
||||||
|
SRSRAN_API int srsran_ssb_decode_pbch(srsran_ssb_t* q, const cf_t* in, srsran_pbch_msg_nr_t* msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds SSB to a given signal in time domain
|
||||||
|
* @param q SSB object
|
||||||
|
* @param N_id Physical Cell Identifier
|
||||||
|
* @param msg NR PBCH message to transmit
|
||||||
|
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise
|
||||||
|
*/
|
||||||
|
SRSRAN_API int
|
||||||
|
srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, const cf_t* in, cf_t* out);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Perform Channel State Information (CSI) measurement from the SSB
|
||||||
|
* @param q NR PSS object
|
||||||
|
* @param N_id Physical Cell Identifier
|
||||||
|
* @param in Base-band signal
|
||||||
|
* @param meas SSB-based CSI measurement
|
||||||
|
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise
|
||||||
|
*/
|
||||||
|
SRSRAN_API int
|
||||||
|
srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsran_csi_trs_measurements_t* meas);
|
||||||
|
|
||||||
|
#endif // SRSRAN_SSB_H
|
@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRSRAN_SSS_NR_H
|
||||||
|
#define SRSRAN_SSS_NR_H
|
||||||
|
|
||||||
|
#include "srsran/config.h"
|
||||||
|
#include "srsran/phy/common/phy_common_nr.h"
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief NR SSS sequence length in frequency domain
|
||||||
|
*/
|
||||||
|
#define SRSRAN_SSS_NR_LEN 127
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief NR SSS Symbol number
|
||||||
|
*/
|
||||||
|
#define SRSRAN_SSS_NR_SYMBOL_IDX 2
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Put NR SSS sequence in the SSB grid
|
||||||
|
* @remark Described in TS 38.211 section 7.4.2.3 Secondary synchronization signal
|
||||||
|
* @param ssb_grid SSB resource grid
|
||||||
|
* @param N_id_1 Physical cell ID 1
|
||||||
|
* @param N_id_2 Physical cell ID 2
|
||||||
|
* @param beta SSS power allocation
|
||||||
|
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise
|
||||||
|
*/
|
||||||
|
SRSRAN_API int srsran_sss_nr_put(cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id_1, uint32_t N_id_2, float beta);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Extracts the NR SSS Least Square Estimates (LSE) from the SSB grid
|
||||||
|
* @param ssb_grid received SSB resource grid
|
||||||
|
* @param N_id_1 Physical cell ID 1
|
||||||
|
* @param N_id_2 Physical cell ID 2
|
||||||
|
* @param lse Provides LSE pointer
|
||||||
|
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise
|
||||||
|
*/
|
||||||
|
SRSRAN_API int
|
||||||
|
srsran_sss_nr_extract_lse(const cf_t* ssb_grid, uint32_t N_id_1, uint32_t N_id_2, cf_t lse[SRSRAN_SSS_NR_LEN]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find the best SSS sequence given the N_id_2 and the SSB resource grid
|
||||||
|
* @attention Assumes the SSB is synchronized and the average delay is pre-compensated
|
||||||
|
* @param ssb_grid The SSB resource grid to search
|
||||||
|
* @param N_id_2 Fix N_id_2 to search, it reduces the search space 1/3
|
||||||
|
* @param norm_corr Normalised correlation of the best found sequence
|
||||||
|
* @param found_N_id_1 The N_id_1 of the best sequence
|
||||||
|
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise
|
||||||
|
*/
|
||||||
|
SRSRAN_API int
|
||||||
|
srsran_sss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id_2, float* norm_corr, uint32_t* found_N_id_1);
|
||||||
|
|
||||||
|
#endif // SRSRAN_SSS_NR_H
|
@ -0,0 +1,96 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srsran/phy/sync/pss_nr.h"
|
||||||
|
#include "srsran/phy/utils/vector.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NR PSS First Subcarrier index
|
||||||
|
*/
|
||||||
|
#define PSS_NR_SUBC_BEGIN 56
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates Sequence circular offset
|
||||||
|
*/
|
||||||
|
#define PSS_NR_SEQUENCE_M(N_id_2) ((43U * (N_id_2)) % SRSRAN_PSS_NR_LEN)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pregenerated modulated sequence
|
||||||
|
*/
|
||||||
|
static cf_t pss_nr_d[SRSRAN_PSS_NR_LEN] = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sequence generation as described in TS 38.211 clause 7.4.2.2.1
|
||||||
|
*/
|
||||||
|
__attribute__((constructor)) __attribute__((unused)) static void pss_nr_pregen()
|
||||||
|
{
|
||||||
|
// Initialise M sequence x
|
||||||
|
uint32_t x[SRSRAN_PSS_NR_LEN + 7];
|
||||||
|
x[6] = 1;
|
||||||
|
x[5] = 1;
|
||||||
|
x[4] = 1;
|
||||||
|
x[3] = 0;
|
||||||
|
x[2] = 1;
|
||||||
|
x[1] = 1;
|
||||||
|
x[0] = 0;
|
||||||
|
|
||||||
|
// Generate M sequence x
|
||||||
|
for (uint32_t i = 0; i < SRSRAN_PSS_NR_LEN; i++) {
|
||||||
|
x[i + 7] = (x[i + 4] + x[i]) % 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modulate M sequence d
|
||||||
|
for (uint32_t i = 0; i < SRSRAN_PSS_NR_LEN; i++) {
|
||||||
|
pss_nr_d[i] = 1.0f - 2.0f * (float)x[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int srsran_pss_nr_put(cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id_2, float beta)
|
||||||
|
{
|
||||||
|
// Verify inputs
|
||||||
|
if (ssb_grid == NULL || N_id_2 >= SRSRAN_NOF_NID_2_NR) {
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate generation parameters
|
||||||
|
uint32_t m = PSS_NR_SEQUENCE_M(N_id_2);
|
||||||
|
uint32_t copy_sz_1 = SRSRAN_PSS_NR_LEN - m;
|
||||||
|
uint32_t grid_idx_1 = SRSRAN_PSS_NR_SYMBOL_IDX * SRSRAN_SSB_BW_SUBC + PSS_NR_SUBC_BEGIN;
|
||||||
|
uint32_t grid_idx_2 = grid_idx_1 + copy_sz_1;
|
||||||
|
|
||||||
|
// Copy sequence from offset to the end
|
||||||
|
srsran_vec_sc_prod_cfc(&pss_nr_d[m], beta, &ssb_grid[grid_idx_1], copy_sz_1);
|
||||||
|
|
||||||
|
// Copy sequence from 0 to offset
|
||||||
|
srsran_vec_sc_prod_cfc(&pss_nr_d[0], beta, &ssb_grid[grid_idx_2], m);
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int srsran_pss_nr_extract_lse(const cf_t* ssb_grid, uint32_t N_id_2, cf_t lse[SRSRAN_PSS_NR_LEN])
|
||||||
|
{
|
||||||
|
// Verify inputs
|
||||||
|
if (ssb_grid == NULL || N_id_2 >= SRSRAN_NOF_NID_2_NR || lse == NULL) {
|
||||||
|
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract PSS
|
||||||
|
srsran_vec_cf_copy(
|
||||||
|
lse, &ssb_grid[SRSRAN_PSS_NR_SYMBOL_IDX * SRSRAN_SSB_BW_SUBC + PSS_NR_SUBC_BEGIN], SRSRAN_PSS_NR_LEN);
|
||||||
|
|
||||||
|
// Estimate
|
||||||
|
uint32_t m = PSS_NR_SEQUENCE_M(N_id_2);
|
||||||
|
srsran_vec_prod_ccc(&pss_nr_d[m], lse, lse, SRSRAN_PSS_NR_LEN - m);
|
||||||
|
srsran_vec_prod_ccc(&pss_nr_d[0], &lse[SRSRAN_PSS_NR_LEN - m], &lse[SRSRAN_PSS_NR_LEN - m], m);
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
@ -0,0 +1,365 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srsran/phy/sync/ssb.h"
|
||||||
|
#include "srsran/phy/sync/pss_nr.h"
|
||||||
|
#include "srsran/phy/sync/sss_nr.h"
|
||||||
|
#include "srsran/phy/utils/debug.h"
|
||||||
|
#include "srsran/phy/utils/vector.h"
|
||||||
|
#include <complex.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Maximum allowed maximum sampling rate error in Hz
|
||||||
|
*/
|
||||||
|
#define SSB_SRATE_MAX_ERROR_HZ 0.01
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Maximum allowed maximum frequency error offset in Hz
|
||||||
|
*/
|
||||||
|
#define SSB_FREQ_OFFSET_MAX_ERROR_HZ 0.01
|
||||||
|
|
||||||
|
int srsran_ssb_init(srsran_ssb_t* q, const srsran_ssb_args_t* args)
|
||||||
|
{
|
||||||
|
// Verify input parameters
|
||||||
|
if (q == NULL || args == NULL) {
|
||||||
|
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy arguments
|
||||||
|
q->args = *args;
|
||||||
|
|
||||||
|
// Check if the maximum sampling rate is in range, force default otherwise
|
||||||
|
if (!isnormal(q->args.max_srate_hz) || q->args.max_srate_hz < 0.0) {
|
||||||
|
q->args.max_srate_hz = SRSRAN_SSB_DEFAULT_MAX_SRATE_HZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
q->scs_hz = (float)SRSRAN_SUBC_SPACING_NR(q->args.min_scs);
|
||||||
|
q->max_symbol_sz = (uint32_t)round(q->args.max_srate_hz / q->scs_hz);
|
||||||
|
|
||||||
|
// Allocate temporal data
|
||||||
|
q->tmp_time = srsran_vec_cf_malloc(q->max_symbol_sz);
|
||||||
|
q->tmp_freq = srsran_vec_cf_malloc(q->max_symbol_sz);
|
||||||
|
if (q->tmp_time == NULL || q->tmp_time == NULL) {
|
||||||
|
ERROR("Malloc");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void srsran_ssb_free(srsran_ssb_t* q)
|
||||||
|
{
|
||||||
|
if (q == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q->tmp_time != NULL) {
|
||||||
|
free(q->tmp_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q->tmp_freq != NULL) {
|
||||||
|
free(q->tmp_freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
srsran_dft_plan_free(&q->ifft);
|
||||||
|
srsran_dft_plan_free(&q->fft);
|
||||||
|
|
||||||
|
SRSRAN_MEM_ZERO(q, srsran_ssb_t, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg)
|
||||||
|
{
|
||||||
|
// Verify input parameters
|
||||||
|
if (q == NULL || cfg == NULL) {
|
||||||
|
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate subcarrier spacing in Hz
|
||||||
|
q->scs_hz = (double)SRSRAN_SUBC_SPACING_NR(cfg->scs);
|
||||||
|
|
||||||
|
// Calculate SSB symbol size and integer offset
|
||||||
|
uint32_t symbol_sz = (uint32_t)round(cfg->srate_hz / q->scs_hz);
|
||||||
|
q->offset = (uint32_t)(cfg->freq_offset_hz / q->scs_hz);
|
||||||
|
q->cp0_sz = (160U * symbol_sz) / 2048U;
|
||||||
|
q->cp_sz = (144U * symbol_sz) / 2048U;
|
||||||
|
|
||||||
|
// Calculate SSB sampling error and check
|
||||||
|
double ssb_srate_error_Hz = ((double)symbol_sz * q->scs_hz) - cfg->srate_hz;
|
||||||
|
if (fabs(ssb_srate_error_Hz) > SSB_SRATE_MAX_ERROR_HZ) {
|
||||||
|
ERROR("Invalid sampling rate (%.2f MHz)", cfg->srate_hz / 1e6);
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate SSB offset error and check
|
||||||
|
double ssb_offset_error_Hz = ((double)q->offset * q->scs_hz) - cfg->freq_offset_hz;
|
||||||
|
if (fabs(ssb_offset_error_Hz) > SSB_FREQ_OFFSET_MAX_ERROR_HZ) {
|
||||||
|
ERROR("SSB Offset error exceeds maximum allowed");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify symbol size
|
||||||
|
if (q->max_symbol_sz < symbol_sz) {
|
||||||
|
ERROR("New symbol size (%d) exceeds maximum symbol size (%d)", symbol_sz, q->max_symbol_sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replan iFFT
|
||||||
|
if ((q->args.enable_encode) && q->symbol_sz != symbol_sz) {
|
||||||
|
// free the current IFFT, it internally checks if the plan was created
|
||||||
|
srsran_dft_plan_free(&q->ifft);
|
||||||
|
|
||||||
|
// Creates DFT plan
|
||||||
|
if (srsran_dft_plan_guru_c(&q->ifft, (int)symbol_sz, SRSRAN_DFT_BACKWARD, q->tmp_freq, q->tmp_time, 1, 1, 1, 1, 1) <
|
||||||
|
SRSRAN_SUCCESS) {
|
||||||
|
ERROR("Error creating iDFT");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replan FFT
|
||||||
|
if ((q->args.enable_measure || q->args.enable_decode) && q->symbol_sz != symbol_sz) {
|
||||||
|
// free the current FFT, it internally checks if the plan was created
|
||||||
|
srsran_dft_plan_free(&q->fft);
|
||||||
|
|
||||||
|
// Creates DFT plan
|
||||||
|
if (srsran_dft_plan_guru_c(&q->fft, (int)symbol_sz, SRSRAN_DFT_FORWARD, q->tmp_time, q->tmp_freq, 1, 1, 1, 1, 1) <
|
||||||
|
SRSRAN_SUCCESS) {
|
||||||
|
ERROR("Error creating iDFT");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, copy configuration
|
||||||
|
q->cfg = *cfg;
|
||||||
|
q->symbol_sz = symbol_sz;
|
||||||
|
|
||||||
|
if (!isnormal(q->cfg.beta_pss)) {
|
||||||
|
q->cfg.beta_pss = SRSRAN_SSB_DEFAULT_BETA;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isnormal(q->cfg.beta_sss)) {
|
||||||
|
q->cfg.beta_sss = SRSRAN_SSB_DEFAULT_BETA;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isnormal(q->cfg.beta_pbch)) {
|
||||||
|
q->cfg.beta_pbch = SRSRAN_SSB_DEFAULT_BETA;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isnormal(q->cfg.beta_pbch_dmrs)) {
|
||||||
|
q->cfg.beta_pbch = SRSRAN_SSB_DEFAULT_BETA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, const cf_t* in, cf_t* out)
|
||||||
|
{
|
||||||
|
// Verify input parameters
|
||||||
|
if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || msg == NULL || in == NULL || out == NULL) {
|
||||||
|
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!q->args.enable_encode) {
|
||||||
|
ERROR("SSB is not configured for encode");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t N_id_1 = SRSRAN_NID_1_NR(N_id);
|
||||||
|
uint32_t N_id_2 = SRSRAN_NID_2_NR(N_id);
|
||||||
|
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
|
||||||
|
|
||||||
|
// Put PSS
|
||||||
|
if (srsran_pss_nr_put(ssb_grid, N_id_2, q->cfg.beta_pss) < SRSRAN_SUCCESS) {
|
||||||
|
ERROR("Error putting PSS");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put SSS
|
||||||
|
if (srsran_sss_nr_put(ssb_grid, N_id_1, N_id_2, q->cfg.beta_sss) < SRSRAN_SUCCESS) {
|
||||||
|
ERROR("Error putting PSS");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put PBCH DMRS
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Put PBCH payload
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Initialise frequency domain
|
||||||
|
srsran_vec_cf_zero(q->tmp_freq, q->symbol_sz);
|
||||||
|
|
||||||
|
// Modulate
|
||||||
|
const cf_t* in_ptr = in;
|
||||||
|
cf_t* out_ptr = out;
|
||||||
|
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
|
||||||
|
// Get CP length
|
||||||
|
uint32_t cp_len = (l == 0) ? q->cp0_sz : q->cp_sz;
|
||||||
|
|
||||||
|
// Select symbol in grid
|
||||||
|
cf_t* ptr = &ssb_grid[l * SRSRAN_SSB_BW_SUBC];
|
||||||
|
|
||||||
|
// Map grid into frequency domain symbol
|
||||||
|
if (q->offset >= SRSRAN_SSB_BW_SUBC / 2) {
|
||||||
|
srsran_vec_cf_copy(&q->tmp_freq[q->offset - SRSRAN_SSB_BW_SUBC / 2], ptr, SRSRAN_SSB_BW_SUBC);
|
||||||
|
} else if (q->offset <= -SRSRAN_SSB_BW_SUBC / 2) {
|
||||||
|
srsran_vec_cf_copy(&q->tmp_freq[q->symbol_sz + q->offset - SRSRAN_SSB_BW_SUBC / 2], ptr, SRSRAN_SSB_BW_SUBC);
|
||||||
|
} else {
|
||||||
|
srsran_vec_cf_copy(&q->tmp_freq[0], &ptr[SRSRAN_SSB_BW_SUBC / 2 - q->offset], SRSRAN_SSB_BW_SUBC / 2 + q->offset);
|
||||||
|
srsran_vec_cf_copy(
|
||||||
|
&q->tmp_freq[q->symbol_sz - SRSRAN_SSB_BW_SUBC / 2 + q->offset], &ptr[0], SRSRAN_SSB_BW_SUBC / 2 - q->offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to time domain
|
||||||
|
srsran_dft_run_guru_c(&q->ifft);
|
||||||
|
|
||||||
|
// Normalise output
|
||||||
|
float norm = sqrtf((float)q->symbol_sz);
|
||||||
|
if (isnormal(norm)) {
|
||||||
|
srsran_vec_sc_prod_cfc(q->tmp_time, 1.0f / norm, q->tmp_time, q->symbol_sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add cyclic prefix to input;
|
||||||
|
srsran_vec_sum_ccc(in_ptr, &q->tmp_time[q->symbol_sz - cp_len], out_ptr, cp_len);
|
||||||
|
in_ptr += cp_len;
|
||||||
|
out_ptr += cp_len;
|
||||||
|
|
||||||
|
// Add symbol to the input baseband
|
||||||
|
srsran_vec_sum_ccc(in_ptr, q->tmp_time, out_ptr, q->symbol_sz);
|
||||||
|
in_ptr += q->symbol_sz;
|
||||||
|
out_ptr += q->symbol_sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsran_csi_trs_measurements_t* meas)
|
||||||
|
{
|
||||||
|
// Verify inputs
|
||||||
|
if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || in == NULL || meas == NULL || !isnormal(q->scs_hz)) {
|
||||||
|
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!q->args.enable_measure) {
|
||||||
|
ERROR("SSB is not configured for measure");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t N_id_1 = SRSRAN_NID_1_NR(N_id);
|
||||||
|
uint32_t N_id_2 = SRSRAN_NID_2_NR(N_id);
|
||||||
|
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
|
||||||
|
|
||||||
|
// Demodulate
|
||||||
|
const cf_t* in_ptr = in;
|
||||||
|
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
|
||||||
|
// Get CP length
|
||||||
|
uint32_t cp_len = (l == 0) ? q->cp0_sz : q->cp_sz;
|
||||||
|
|
||||||
|
// Advance half CP, to avoid inter symbol interference
|
||||||
|
in_ptr += SRSRAN_FLOOR(cp_len, 2);
|
||||||
|
|
||||||
|
// Copy FFT window in temporal time domain buffer
|
||||||
|
srsran_vec_cf_copy(q->tmp_time, in_ptr, q->symbol_sz);
|
||||||
|
in_ptr += q->symbol_sz + SRSRAN_CEIL(cp_len, 2);
|
||||||
|
|
||||||
|
// Convert to frequency domain
|
||||||
|
srsran_dft_run_guru_c(&q->fft);
|
||||||
|
|
||||||
|
// Compensate half CP delay
|
||||||
|
srsran_vec_apply_cfo(q->tmp_freq, SRSRAN_CEIL(cp_len, 2) / (float)(q->symbol_sz), q->tmp_freq, q->symbol_sz);
|
||||||
|
|
||||||
|
// Select symbol in grid
|
||||||
|
cf_t* ptr = &ssb_grid[l * SRSRAN_SSB_BW_SUBC];
|
||||||
|
|
||||||
|
// Map frequency domain symbol into the SSB grid
|
||||||
|
if (q->offset >= SRSRAN_SSB_BW_SUBC / 2) {
|
||||||
|
srsran_vec_cf_copy(ptr, &q->tmp_freq[q->offset - SRSRAN_SSB_BW_SUBC / 2], SRSRAN_SSB_BW_SUBC);
|
||||||
|
} else if (q->offset <= -SRSRAN_SSB_BW_SUBC / 2) {
|
||||||
|
srsran_vec_cf_copy(ptr, &q->tmp_freq[q->symbol_sz + q->offset - SRSRAN_SSB_BW_SUBC / 2], SRSRAN_SSB_BW_SUBC);
|
||||||
|
} else {
|
||||||
|
srsran_vec_cf_copy(&ptr[SRSRAN_SSB_BW_SUBC / 2 - q->offset], &q->tmp_freq[0], SRSRAN_SSB_BW_SUBC / 2 + q->offset);
|
||||||
|
srsran_vec_cf_copy(
|
||||||
|
&ptr[0], &q->tmp_freq[q->symbol_sz - SRSRAN_SSB_BW_SUBC / 2 + q->offset], SRSRAN_SSB_BW_SUBC / 2 - q->offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize
|
||||||
|
float norm = sqrtf((float)q->symbol_sz);
|
||||||
|
if (isnormal(norm)) {
|
||||||
|
srsran_vec_sc_prod_cfc(ptr, 1.0f / norm, ptr, SRSRAN_SSB_BW_SUBC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract PSS LSE
|
||||||
|
cf_t pss_lse[SRSRAN_PSS_NR_LEN];
|
||||||
|
cf_t sss_lse[SRSRAN_SSS_NR_LEN];
|
||||||
|
if (srsran_pss_nr_extract_lse(ssb_grid, N_id_2, pss_lse) < SRSRAN_SUCCESS ||
|
||||||
|
srsran_sss_nr_extract_lse(ssb_grid, N_id_1, N_id_2, sss_lse) < SRSRAN_SUCCESS) {
|
||||||
|
ERROR("Error extracting LSE");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estimate average delay
|
||||||
|
float delay_pss_norm = srsran_vec_estimate_frequency(pss_lse, SRSRAN_PSS_NR_LEN);
|
||||||
|
float delay_sss_norm = srsran_vec_estimate_frequency(sss_lse, SRSRAN_SSS_NR_LEN);
|
||||||
|
float delay_avg_norm = (delay_pss_norm + delay_sss_norm) / 2.0f;
|
||||||
|
float delay_avg_us = 1e6f * delay_avg_norm / q->scs_hz;
|
||||||
|
|
||||||
|
// Pre-compensate delay
|
||||||
|
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
|
||||||
|
srsran_vec_apply_cfo(
|
||||||
|
&ssb_grid[SRSRAN_SSB_BW_SUBC * l], delay_avg_norm, &ssb_grid[SRSRAN_SSB_BW_SUBC * l], SRSRAN_SSB_BW_SUBC);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract LSE again
|
||||||
|
if (srsran_pss_nr_extract_lse(ssb_grid, N_id_2, pss_lse) < SRSRAN_SUCCESS ||
|
||||||
|
srsran_sss_nr_extract_lse(ssb_grid, N_id_1, N_id_2, sss_lse) < SRSRAN_SUCCESS) {
|
||||||
|
ERROR("Error extracting LSE");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estimate average EPRE
|
||||||
|
float epre_pss = srsran_vec_avg_power_cf(pss_lse, SRSRAN_PSS_NR_LEN);
|
||||||
|
float epre_sss = srsran_vec_avg_power_cf(sss_lse, SRSRAN_SSS_NR_LEN);
|
||||||
|
float epre = (epre_pss + epre_sss) / 2.0f;
|
||||||
|
|
||||||
|
// Compute correlation
|
||||||
|
cf_t corr_pss = srsran_vec_acc_cc(pss_lse, SRSRAN_PSS_NR_LEN) / SRSRAN_PSS_NR_LEN;
|
||||||
|
cf_t corr_sss = srsran_vec_acc_cc(sss_lse, SRSRAN_SSS_NR_LEN) / SRSRAN_SSS_NR_LEN;
|
||||||
|
|
||||||
|
// Compute CFO in Hz
|
||||||
|
float distance_s = srsran_symbol_distance_s(SRSRAN_PSS_NR_SYMBOL_IDX, SRSRAN_SSS_NR_SYMBOL_IDX, q->cfg.scs);
|
||||||
|
float cfo_hz_max = 1.0f / distance_s;
|
||||||
|
float cfo_hz = cargf(corr_pss * conjf(corr_sss)) / (2.0f * M_PI) * cfo_hz_max;
|
||||||
|
|
||||||
|
// Compute average RSRP
|
||||||
|
float rsrp = (SRSRAN_CSQABS(corr_pss) + SRSRAN_CSQABS(corr_sss)) / 2.0f;
|
||||||
|
|
||||||
|
// Compute Noise
|
||||||
|
float n0 = 1e-9; // Almost 0
|
||||||
|
if (epre > rsrp) {
|
||||||
|
n0 = epre - rsrp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put measurements together
|
||||||
|
meas->epre = epre;
|
||||||
|
meas->epre_dB = srsran_convert_power_to_dB(epre);
|
||||||
|
meas->rsrp = rsrp;
|
||||||
|
meas->epre_dB = srsran_convert_power_to_dB(rsrp);
|
||||||
|
meas->n0 = n0;
|
||||||
|
meas->n0_dB = srsran_convert_power_to_dB(n0);
|
||||||
|
meas->snr_dB = meas->rsrp_dB - meas->n0_dB;
|
||||||
|
meas->cfo_hz = cfo_hz;
|
||||||
|
meas->cfo_hz_max = cfo_hz_max;
|
||||||
|
meas->delay_us = delay_avg_us; // Convert the delay to microseconds
|
||||||
|
meas->nof_re = SRSRAN_PSS_NR_LEN + SRSRAN_SSS_NR_LEN;
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
@ -0,0 +1,208 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srsran/phy/sync/sss_nr.h"
|
||||||
|
#include "srsran/phy/utils/vector.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NR SSS First Subcarrier index
|
||||||
|
*/
|
||||||
|
#define SSS_NR_SUBC_BEGIN 56
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of possible M1 shifts
|
||||||
|
*/
|
||||||
|
#define SSS_NR_NOF_M1 112U
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of possible M0 shifts
|
||||||
|
*/
|
||||||
|
#define SSS_NR_NOF_M0 SRSRAN_FLOOR(SRSRAN_NOF_NID_1_NR, SSS_NR_NOF_M1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates Sequence circular offset M0 value
|
||||||
|
*/
|
||||||
|
#define SSS_NR_SEQUENCE_M0(N_id_1, N_id_2) \
|
||||||
|
((15U * SRSRAN_FLOOR(N_id_1, SSS_NR_NOF_M1) + 5 * (N_id_2)) % SRSRAN_SSS_NR_LEN)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates Sequence circular offset M1 value
|
||||||
|
*/
|
||||||
|
#define SSS_NR_SEQUENCE_M1(N_id_1) (N_id_1 % SSS_NR_NOF_M1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pregenerated modulated sequences
|
||||||
|
*/
|
||||||
|
static cf_t sss_nr_d0[SRSRAN_SSS_NR_LEN] = {};
|
||||||
|
static cf_t sss_nr_d1[SRSRAN_SSS_NR_LEN] = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sequence generation as described in TS 38.211 clause 7.4.2.2.1
|
||||||
|
*/
|
||||||
|
__attribute__((constructor)) __attribute__((unused)) static void sss_nr_pregen()
|
||||||
|
{
|
||||||
|
// Initialise M sequence x0
|
||||||
|
uint32_t x0[SRSRAN_SSS_NR_LEN + 7];
|
||||||
|
x0[6] = 0;
|
||||||
|
x0[5] = 0;
|
||||||
|
x0[4] = 0;
|
||||||
|
x0[3] = 0;
|
||||||
|
x0[2] = 0;
|
||||||
|
x0[1] = 0;
|
||||||
|
x0[0] = 1;
|
||||||
|
|
||||||
|
// Initialise M sequence x1
|
||||||
|
uint32_t x1[SRSRAN_SSS_NR_LEN + 7];
|
||||||
|
x1[6] = 0;
|
||||||
|
x1[5] = 0;
|
||||||
|
x1[4] = 0;
|
||||||
|
x1[3] = 0;
|
||||||
|
x1[2] = 0;
|
||||||
|
x1[1] = 0;
|
||||||
|
x1[0] = 1;
|
||||||
|
|
||||||
|
// Generate M sequence x
|
||||||
|
for (uint32_t i = 0; i < SRSRAN_SSS_NR_LEN; i++) {
|
||||||
|
x0[i + 7] = (x0[i + 4] + x0[i]) % 2;
|
||||||
|
x1[i + 7] = (x1[i + 1] + x1[i]) % 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modulate M sequence d
|
||||||
|
for (uint32_t i = 0; i < SRSRAN_SSS_NR_LEN; i++) {
|
||||||
|
sss_nr_d0[i] = 1.0f - 2.0f * (float)x0[i];
|
||||||
|
sss_nr_d1[i] = 1.0f - 2.0f * (float)x1[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int srsran_sss_nr_put(cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id_1, uint32_t N_id_2, float beta)
|
||||||
|
{
|
||||||
|
// Verify inputs
|
||||||
|
if (ssb_grid == NULL || N_id_1 >= SRSRAN_NOF_NID_1_NR || N_id_2 >= SRSRAN_NOF_NID_2_NR) {
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate generation parameters
|
||||||
|
uint32_t m0 = SSS_NR_SEQUENCE_M0(N_id_1, N_id_2);
|
||||||
|
uint32_t m1 = SSS_NR_SEQUENCE_M1(N_id_1);
|
||||||
|
uint32_t grid_idx_m0_1 = SRSRAN_SSS_NR_SYMBOL_IDX * SRSRAN_SSB_BW_SUBC + SSS_NR_SUBC_BEGIN;
|
||||||
|
uint32_t grid_idx_m0_2 = grid_idx_m0_1 + (SRSRAN_SSS_NR_LEN - m0);
|
||||||
|
uint32_t grid_idx_m1_1 = SRSRAN_SSS_NR_SYMBOL_IDX * SRSRAN_SSB_BW_SUBC + SSS_NR_SUBC_BEGIN;
|
||||||
|
uint32_t grid_idx_m1_2 = grid_idx_m1_1 + (SRSRAN_SSS_NR_LEN - m1);
|
||||||
|
|
||||||
|
// Copy d0 sequence first part from m0 to the end
|
||||||
|
srsran_vec_sc_prod_cfc(&sss_nr_d0[m0], beta, &ssb_grid[grid_idx_m0_1], SRSRAN_SSS_NR_LEN - m0);
|
||||||
|
|
||||||
|
// Copy d0 sequence second part from 0 to m0
|
||||||
|
srsran_vec_sc_prod_cfc(&sss_nr_d0[0], beta, &ssb_grid[grid_idx_m0_2], m0);
|
||||||
|
|
||||||
|
// Multiply d1 sequence first part from m1 to the end
|
||||||
|
srsran_vec_prod_ccc(&ssb_grid[grid_idx_m1_1], &sss_nr_d1[m1], &ssb_grid[grid_idx_m1_1], SRSRAN_SSS_NR_LEN - m1);
|
||||||
|
|
||||||
|
// Multiply d1 sequence second part from 0 to m1
|
||||||
|
srsran_vec_prod_ccc(&ssb_grid[grid_idx_m1_2], &sss_nr_d1[0], &ssb_grid[grid_idx_m1_2], m1);
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int srsran_sss_nr_extract_lse(const cf_t* ssb_grid, uint32_t N_id_1, uint32_t N_id_2, cf_t lse[SRSRAN_SSS_NR_LEN])
|
||||||
|
{
|
||||||
|
// Verify inputs
|
||||||
|
if (ssb_grid == NULL || N_id_2 >= SRSRAN_NOF_NID_2_NR || lse == NULL) {
|
||||||
|
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract SSS
|
||||||
|
srsran_vec_cf_copy(
|
||||||
|
lse, &ssb_grid[SRSRAN_SSS_NR_SYMBOL_IDX * SRSRAN_SSB_BW_SUBC + SSS_NR_SUBC_BEGIN], SRSRAN_SSS_NR_LEN);
|
||||||
|
|
||||||
|
// Estimate
|
||||||
|
uint32_t m0 = SSS_NR_SEQUENCE_M0(N_id_1, N_id_2);
|
||||||
|
srsran_vec_prod_ccc(&sss_nr_d0[m0], lse, lse, SRSRAN_SSS_NR_LEN - m0);
|
||||||
|
srsran_vec_prod_ccc(&sss_nr_d0[0], &lse[SRSRAN_SSS_NR_LEN - m0], &lse[SRSRAN_SSS_NR_LEN - m0], m0);
|
||||||
|
|
||||||
|
uint32_t m1 = SSS_NR_SEQUENCE_M1(N_id_1);
|
||||||
|
srsran_vec_prod_ccc(&sss_nr_d1[m1], lse, lse, SRSRAN_SSS_NR_LEN - m1);
|
||||||
|
srsran_vec_prod_ccc(&sss_nr_d1[0], &lse[SRSRAN_SSS_NR_LEN - m1], &lse[SRSRAN_SSS_NR_LEN - m1], m1);
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int srsran_sss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
|
||||||
|
uint32_t N_id_2,
|
||||||
|
float* norm_corr,
|
||||||
|
uint32_t* found_N_id_1)
|
||||||
|
{
|
||||||
|
// Verify inputs
|
||||||
|
if (ssb_grid == NULL || N_id_2 >= SRSRAN_NOF_NID_2_NR) {
|
||||||
|
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract SSS ptr
|
||||||
|
const cf_t* sss_ptr = &ssb_grid[SRSRAN_SSS_NR_SYMBOL_IDX * SRSRAN_SSB_BW_SUBC + SSS_NR_SUBC_BEGIN];
|
||||||
|
|
||||||
|
// Measure SSS average power
|
||||||
|
float avg_power = srsran_vec_avg_power_cf(sss_ptr, SRSRAN_SSS_NR_LEN);
|
||||||
|
|
||||||
|
// If the measured power is invalid or zero, consider no SSS signal
|
||||||
|
if (!isnormal(avg_power)) {
|
||||||
|
if (norm_corr) {
|
||||||
|
*norm_corr = 0.0f;
|
||||||
|
}
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search state
|
||||||
|
float max_corr = -INFINITY; //< Stores best correlation
|
||||||
|
uint32_t N_id_1 = 0; //< Best N_id_1
|
||||||
|
|
||||||
|
// Iterate over all M1 shifts
|
||||||
|
for (uint32_t m1 = 0; m1 < SSS_NR_NOF_M1; m1++) {
|
||||||
|
// Temporal storage of SSS after applying d1 sequence
|
||||||
|
cf_t sss_seq_m1[SRSRAN_SSS_NR_LEN];
|
||||||
|
|
||||||
|
// Apply d1 sequence fist part
|
||||||
|
srsran_vec_prod_ccc(&sss_ptr[0], &sss_nr_d1[m1], &sss_seq_m1[0], SRSRAN_SSS_NR_LEN - m1);
|
||||||
|
|
||||||
|
// Apply d1 sequence second part
|
||||||
|
srsran_vec_prod_ccc(&sss_ptr[SRSRAN_SSS_NR_LEN - m1], &sss_nr_d1[0], &sss_seq_m1[SRSRAN_SSS_NR_LEN - m1], m1);
|
||||||
|
|
||||||
|
// Iterate over all N_id_1 with the given m1 sequence
|
||||||
|
for (uint32_t N_id_1_blind = m1; N_id_1_blind < SRSRAN_NOF_NID_1; N_id_1_blind += SSS_NR_NOF_M1) {
|
||||||
|
uint32_t m0 = SSS_NR_SEQUENCE_M0(N_id_1_blind, N_id_2);
|
||||||
|
|
||||||
|
cf_t sss_seq_m0[SRSRAN_SSS_NR_LEN];
|
||||||
|
|
||||||
|
// Apply d0 sequence fist part
|
||||||
|
srsran_vec_prod_ccc(&sss_seq_m1[0], &sss_nr_d0[m0], &sss_seq_m0[0], SRSRAN_SSS_NR_LEN - m0);
|
||||||
|
|
||||||
|
// Apply d0 sequence second part
|
||||||
|
srsran_vec_prod_ccc(&sss_seq_m1[SRSRAN_SSS_NR_LEN - m0], &sss_nr_d0[0], &sss_seq_m0[SRSRAN_SSS_NR_LEN - m0], m0);
|
||||||
|
|
||||||
|
// Correlate
|
||||||
|
float corr = SRSRAN_CSQABS(srsran_vec_acc_cc(sss_seq_m0, SRSRAN_SSS_NR_LEN)) / avg_power;
|
||||||
|
if (corr > max_corr) {
|
||||||
|
N_id_1 = N_id_1_blind;
|
||||||
|
max_corr = corr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (norm_corr) {
|
||||||
|
*norm_corr = max_corr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found_N_id_1) {
|
||||||
|
*found_N_id_1 = N_id_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
@ -0,0 +1,183 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srsran/common/test_common.h"
|
||||||
|
#include "srsran/phy/channel/ch_awgn.h"
|
||||||
|
#include "srsran/phy/sync/ssb.h"
|
||||||
|
#include "srsran/phy/utils/debug.h"
|
||||||
|
#include "srsran/phy/utils/vector.h"
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// NR parameters
|
||||||
|
static uint32_t carrier_nof_prb = 52;
|
||||||
|
static srsran_subcarrier_spacing_t carrier_scs = srsran_subcarrier_spacing_15kHz;
|
||||||
|
static srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_30kHz;
|
||||||
|
|
||||||
|
// Channel parameters
|
||||||
|
static int32_t delay_n = 1;
|
||||||
|
static float cfo_hz = 100.0f;
|
||||||
|
static float n0_dB = -30.0f;
|
||||||
|
|
||||||
|
// Test context
|
||||||
|
static srsran_channel_awgn_t awgn = {};
|
||||||
|
static double srate_hz = 0.0f; // Base-band sampling rate
|
||||||
|
static float delay_us = 0.0f; // Base-band sampling rate
|
||||||
|
static uint32_t sf_len = 0; // Subframe length
|
||||||
|
static cf_t* buffer = NULL; // Base-band buffer
|
||||||
|
|
||||||
|
#define RSRP_MAX_ERROR 1.0f
|
||||||
|
#define EPRE_MAX_ERROR 1.0f
|
||||||
|
#define N0_MAX_ERROR 2.0f
|
||||||
|
#define SNR_MAX_ERROR 2.0f
|
||||||
|
#define CFO_MAX_ERROR (cfo_hz * 0.3f)
|
||||||
|
#define DELAY_MAX_ERROR (delay_us * 0.1f)
|
||||||
|
|
||||||
|
static void usage(char* prog)
|
||||||
|
{
|
||||||
|
printf("Usage: %s [v]\n", prog);
|
||||||
|
printf("\t-v [set srsran_verbose to debug, default none]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_args(int argc, char** argv)
|
||||||
|
{
|
||||||
|
int opt;
|
||||||
|
while ((opt = getopt(argc, argv, "v")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'v':
|
||||||
|
srsran_verbose++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage(argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run_channel()
|
||||||
|
{
|
||||||
|
// Delay
|
||||||
|
for (uint32_t i = 0; i < sf_len; i++) {
|
||||||
|
buffer[i] = buffer[(i + delay_n) % sf_len];
|
||||||
|
}
|
||||||
|
|
||||||
|
// CFO
|
||||||
|
srsran_vec_apply_cfo(buffer, -cfo_hz / srate_hz, buffer, sf_len);
|
||||||
|
|
||||||
|
// AWGN
|
||||||
|
srsran_channel_awgn_run_c(&awgn, buffer, buffer, sf_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_case_1(srsran_ssb_t* ssb)
|
||||||
|
{
|
||||||
|
uint64_t t_usec = 0;
|
||||||
|
srsran_ssb_cfg_t ssb_cfg = {};
|
||||||
|
ssb_cfg.srate_hz = srate_hz;
|
||||||
|
ssb_cfg.freq_offset_hz = 0.0;
|
||||||
|
ssb_cfg.scs = ssb_scs;
|
||||||
|
|
||||||
|
TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS);
|
||||||
|
|
||||||
|
// Build PBCH message
|
||||||
|
srsran_pbch_msg_nr_t pbch_msg = {};
|
||||||
|
|
||||||
|
for (uint32_t pci = 0; pci < SRSRAN_NOF_NID_NR; pci++) {
|
||||||
|
struct timeval t[3] = {};
|
||||||
|
|
||||||
|
// Initialise baseband
|
||||||
|
srsran_vec_cf_zero(buffer, sf_len);
|
||||||
|
|
||||||
|
// Add the SSB base-band
|
||||||
|
TESTASSERT(srsran_ssb_add(ssb, pci, &pbch_msg, buffer, buffer) == SRSRAN_SUCCESS);
|
||||||
|
|
||||||
|
// Run channel
|
||||||
|
run_channel();
|
||||||
|
|
||||||
|
// Measure
|
||||||
|
gettimeofday(&t[1], NULL);
|
||||||
|
srsran_csi_trs_measurements_t meas = {};
|
||||||
|
TESTASSERT(srsran_ssb_csi_measure(ssb, pci, buffer, &meas) == SRSRAN_SUCCESS);
|
||||||
|
gettimeofday(&t[2], NULL);
|
||||||
|
get_time_interval(t);
|
||||||
|
t_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
|
||||||
|
|
||||||
|
// Print measurement
|
||||||
|
char str[512];
|
||||||
|
srsran_csi_meas_info(&meas, str, sizeof(str));
|
||||||
|
INFO("test_case_1 - pci=%d %s", pci, str);
|
||||||
|
|
||||||
|
// Assert measurements
|
||||||
|
TESTASSERT(fabsf(meas.rsrp_dB - 0.0f) < RSRP_MAX_ERROR);
|
||||||
|
TESTASSERT(fabsf(meas.epre_dB - 0.0f) < EPRE_MAX_ERROR);
|
||||||
|
TESTASSERT(fabsf(meas.n0_dB - n0_dB) < N0_MAX_ERROR);
|
||||||
|
TESTASSERT(fabsf(meas.snr_dB + n0_dB) < SNR_MAX_ERROR);
|
||||||
|
TESTASSERT(fabsf(meas.cfo_hz - cfo_hz) < CFO_MAX_ERROR);
|
||||||
|
TESTASSERT(fabsf(meas.delay_us + delay_us) < DELAY_MAX_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO("test_case_1 - %.1f usec/measurement", (double)t_usec / (double)SRSRAN_NOF_NID_NR);
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
int ret = SRSRAN_ERROR;
|
||||||
|
parse_args(argc, argv);
|
||||||
|
|
||||||
|
srate_hz = (double)SRSRAN_SUBC_SPACING_NR(carrier_scs) * srsran_min_symbol_sz_rb(carrier_nof_prb);
|
||||||
|
delay_us = 1e6f * delay_n / (float)srate_hz;
|
||||||
|
sf_len = (uint32_t)ceil(srate_hz / 1000.0);
|
||||||
|
buffer = srsran_vec_cf_malloc(sf_len);
|
||||||
|
|
||||||
|
srsran_ssb_t ssb = {};
|
||||||
|
srsran_ssb_args_t ssb_args = {};
|
||||||
|
ssb_args.enable_encode = true;
|
||||||
|
ssb_args.enable_measure = true;
|
||||||
|
|
||||||
|
if (buffer == NULL) {
|
||||||
|
ERROR("Malloc");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srsran_channel_awgn_init(&awgn, 0x0) < SRSRAN_SUCCESS) {
|
||||||
|
ERROR("AWGN");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srsran_channel_awgn_set_n0(&awgn, n0_dB) < SRSRAN_SUCCESS) {
|
||||||
|
ERROR("AWGN");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srsran_ssb_init(&ssb, &ssb_args) < SRSRAN_SUCCESS) {
|
||||||
|
ERROR("Init");
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (test_case_1(&ssb) != SRSRAN_SUCCESS) {
|
||||||
|
ERROR("test case failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = SRSRAN_SUCCESS;
|
||||||
|
|
||||||
|
clean_exit:
|
||||||
|
srsran_ssb_free(&ssb);
|
||||||
|
|
||||||
|
srsran_channel_awgn_free(&awgn);
|
||||||
|
|
||||||
|
if (buffer) {
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
Loading…
Reference in New Issue