Initial SSB measurement implementation

This commit is contained in:
Xavier Arteaga 2021-05-06 18:49:19 +02:00 committed by Xavier Arteaga
parent 5c31f4335f
commit 60d1708b80
13 changed files with 1115 additions and 56 deletions

View File

@ -39,26 +39,6 @@
*/ */
#define SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_OTHER 6 #define SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_OTHER 6
/**
* @brief Describes a measurement for NZP-CSI-RS
* @note Used for fine tracking RSRP, SNR, CFO, SFO, and so on
* @note srsran_csi_channel_measurements_t is used for CSI report generation
*/
typedef struct SRSRAN_API {
float rsrp; ///< Linear scale RSRP
float rsrp_dB; ///< Logarithm scale RSRP relative to full-scale
float epre; ///< Linear scale EPRE
float epre_dB; ///< Logarithm scale EPRE relative to full-scale
float n0; ///< Linear noise level
float n0_dB; ///< Logarithm scale noise level relative to full-scale
float snr_dB; ///< Signal to noise ratio in decibels
float cfo_hz; ///< Carrier frequency offset in Hz. Only set if more than 2 symbols are available in a TRS set
float cfo_hz_max; ///< Maximum CFO in Hz that can be measured. It is set to 0 if CFO cannot be estimated
float delay_us; ///< Average measured delay in microseconds
uint32_t nof_re; ///< Number of available RE for the measurement, it can be used for weighting among different
///< measurements
} srsran_csi_trs_measurements_t;
/** /**
* @brief Calculates if the given periodicity implies a CSI-RS transmission in the given slot * @brief Calculates if the given periodicity implies a CSI-RS transmission in the given slot
* @remark Described in TS 36.211 section 7.4.1.5.3 Mapping to physical resources * @remark Described in TS 36.211 section 7.4.1.5.3 Mapping to physical resources

View File

@ -155,25 +155,31 @@ extern "C" {
* @brief Number of NR N_id_1 Physical Cell Identifier (PCI) as described in TS 38.211 section 7.4.2.1 Physical-layer * @brief Number of NR N_id_1 Physical Cell Identifier (PCI) as described in TS 38.211 section 7.4.2.1 Physical-layer
* cell identities * cell identities
*/ */
#define SRSRAN_NOF_N_ID_1 336 #define SRSRAN_NOF_NID_1_NR 336
/** /**
* @brief Number of NR N_id_2 Physical Cell Identifier (PCI) as described in TS 38.211 section 7.4.2.1 Physical-layer * @brief Number of NR N_id_2 Physical Cell Identifier (PCI) as described in TS 38.211 section 7.4.2.1 Physical-layer
* cell identities * cell identities
*/ */
#define SRSRAN_NOF_N_ID_2 3 #define SRSRAN_NOF_NID_2_NR 3
/**
* @brief Number of NR N_id Physical Cell Identifier (PCI) as described in TS 38.211 section 7.4.2.1 Physical-layer
* cell identities
*/
#define SRSRAN_NOF_NID_NR (SRSRAN_NOF_NID_1_NR * SRSRAN_NOF_NID_2_NR)
/** /**
* @brief Compute N_id_1 from the Physical Cell Identifier (PCI) as described in TS 38.211 section 7.4.2.1 * @brief Compute N_id_1 from the Physical Cell Identifier (PCI) as described in TS 38.211 section 7.4.2.1
* Physical-layer cell identities * Physical-layer cell identities
*/ */
#define SRSRAN_N_ID_1(N_ID) ((N_ID) / SRSRAN_NOF_N_ID_2) #define SRSRAN_NID_1_NR(N_ID) ((N_ID) / SRSRAN_NOF_NID_2_NR)
/** /**
* @brief Compute N_id_2 from the Physical Cell Identifier (PCI) as described in TS 38.211 section 7.4.2.1 * @brief Compute N_id_2 from the Physical Cell Identifier (PCI) as described in TS 38.211 section 7.4.2.1
* Physical-layer cell identities * Physical-layer cell identities
*/ */
#define SRSRAN_N_ID_2(N_ID) ((N_ID) % SRSRAN_NOF_N_ID_2) #define SRSRAN_NID_2_NR(N_ID) ((N_ID) % SRSRAN_NOF_NID_2_NR)
/** /**
* @brief SSB number of resource elements, described in TS 38.211 section 7.4.3.1 Time-frequency structure of an SS/PBCH * @brief SSB number of resource elements, described in TS 38.211 section 7.4.3.1 Time-frequency structure of an SS/PBCH
@ -417,6 +423,26 @@ typedef struct SRSRAN_API {
srsran_tdd_pattern_t pattern2; srsran_tdd_pattern_t pattern2;
} srsran_tdd_config_nr_t; } srsran_tdd_config_nr_t;
/**
* @brief Describes a measurement based on NZP-CSI-RS or SSB-CSI
* @note Used for tracking RSRP, SNR, CFO, SFO, and so on
* @note srsran_csi_channel_measurements_t is used for CSI report generation
*/
typedef struct SRSRAN_API {
float rsrp; ///< Linear scale RSRP
float rsrp_dB; ///< Logarithm scale RSRP relative to full-scale
float epre; ///< Linear scale EPRE
float epre_dB; ///< Logarithm scale EPRE relative to full-scale
float n0; ///< Linear noise level
float n0_dB; ///< Logarithm scale noise level relative to full-scale
float snr_dB; ///< Signal to noise ratio in decibels
float cfo_hz; ///< Carrier frequency offset in Hz. Only set if more than 2 symbols are available in a TRS set
float cfo_hz_max; ///< Maximum CFO in Hz that can be measured. It is set to 0 if CFO cannot be estimated
float delay_us; ///< Average measured delay in microseconds
uint32_t nof_re; ///< Number of available RE for the measurement, it can be used for weighting among different
///< measurements
} srsran_csi_trs_measurements_t;
/** /**
* @brief Get the RNTI type name for NR * @brief Get the RNTI type name for NR
* @param rnti_type RNTI type name * @param rnti_type RNTI type name
@ -490,10 +516,10 @@ SRSRAN_API uint32_t srsran_min_symbol_sz_rb(uint32_t nof_prb);
* @remark All symbol size reference and values are taken from TS 38.211 section 5.3 OFDM baseband signal generation * @remark All symbol size reference and values are taken from TS 38.211 section 5.3 OFDM baseband signal generation
* @param l0 First symbol index within the slot * @param l0 First symbol index within the slot
* @param l1 Second symbol index within the slot * @param l1 Second symbol index within the slot
* @param numerology NR Carrier numerology * @param scs Subcarrier spacing
* @return Returns the time in seconds between the two symbols if the condition above is satisfied, 0 seconds otherwise * @return Returns the time in seconds between the two symbols if the condition above is satisfied, 0 seconds otherwise
*/ */
SRSRAN_API float srsran_symbol_distance_s(uint32_t l0, uint32_t l1, uint32_t numerology); SRSRAN_API float srsran_symbol_distance_s(uint32_t l0, uint32_t l1, srsran_subcarrier_spacing_t scs);
/** /**
* @brief Decides whether a given slot is configured as Downlink * @brief Decides whether a given slot is configured as Downlink
@ -515,6 +541,15 @@ SRSRAN_API bool srsran_tdd_nr_is_ul(const srsran_tdd_config_nr_t* cfg, uint32_t
SRSRAN_API int srsran_carrier_to_cell(const srsran_carrier_nr_t* carrier, srsran_cell_t* cell); SRSRAN_API int srsran_carrier_to_cell(const srsran_carrier_nr_t* carrier, srsran_cell_t* cell);
/**
* @brief Writes Channel State Information measurement into a string
* @param meas Provides the measurement
* @param str Provides string
* @param str_len Maximum string length
* @return The number of writen characters
*/
SRSRAN_API uint32_t srsran_csi_meas_info(const srsran_csi_trs_measurements_t* meas, char* str, uint32_t str_len);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

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

View File

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

View File

@ -16,6 +16,7 @@
#include "srsran/config.h" #include "srsran/config.h"
#include "srsran/phy/common/phy_common_nr.h" #include "srsran/phy/common/phy_common_nr.h"
#include "srsran/phy/dft/dft.h" #include "srsran/phy/dft/dft.h"
#include "srsran/phy/phch/pbch_nr.h"
#include <inttypes.h> #include <inttypes.h>
/** /**
@ -23,24 +24,39 @@
*/ */
#define SRSRAN_SSB_DEFAULT_MAX_SRATE_HZ 61.44e6 #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 initializatoion arguments * @brief Describes SSB object initializatoion arguments
*/ */
typedef struct SRSRAN_API { typedef struct SRSRAN_API {
double srate_hz; ///< Maximum sampling rate in Hz (common for gNb and UE), set to zero to use default double max_srate_hz; ///< Maximum sampling rate in Hz (common for gNb and UE), set to zero to use default
bool enable_correlate; ///< Enables PSS/SSS correlation and peak search (UE cell search) srsran_subcarrier_spacing_t min_scs; ///< Minimum subcarrier spacing
bool enable_pbch_encode; ///< Enables PBCH Encoder (intended for gNb) bool enable_correlate; ///< Enables PSS/SSS correlation and peak search (UE cell search)
bool enable_pbch_decode; ///< Enables PBCH Decoder (intented for UE) bool enable_encode; ///< Enables PBCH Encoder (intended for gNb)
bool enable_measure; ///< Enables PSS/SSS CSI measurements bool enable_decode; ///< Enables PBCH Decoder (intented for UE)
bool enable_measure; ///< Enables PSS/SSS CSI measurements
} srsran_ssb_args_t; } srsran_ssb_args_t;
/** /**
* @brief Describes SSB configuration arguments * @brief Describes SSB configuration arguments
*/ */
typedef struct SRSRAN_API { typedef struct SRSRAN_API {
double srate_hz; ///< Current sampling rate in Hz double srate_hz; ///< Current sampling rate in Hz
double ssb_freq_offset_hz; ///< SSB base-band frequency offset double freq_offset_hz; ///< SSB base-band frequency offset
srsran_subcarrier_spacing_t ssb_scs; ///< SSB configured Subcarrier spacing 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; } srsran_ssb_cfg_t;
/** /**
@ -51,9 +67,12 @@ typedef struct SRSRAN_API {
srsran_ssb_cfg_t cfg; ///< Stores last configuration srsran_ssb_cfg_t cfg; ///< Stores last configuration
/// Sampling rate dependent parameters /// Sampling rate dependent parameters
uint32_t symbol_sz; ///< Current symbol size float scs_hz; ///< Subcarrier spacing in Hz
uint32_t cp0_sz; ///< First symbol cyclic prefix size uint32_t max_symbol_sz; ///< Maximum symbol size given the minimum supported SCS and sampling rate
uint32_t cp_sz; ///< Other symbol cyclic prefix size 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 /// Internal Objects
// srsran_pbch_nr_t pbch; ///< PBCH object for encoding/decoding // srsran_pbch_nr_t pbch; ///< PBCH object for encoding/decoding
@ -61,14 +80,15 @@ typedef struct SRSRAN_API {
srsran_dft_plan_t ifft; ///< IFFT object for modulating the SSB srsran_dft_plan_t ifft; ///< IFFT object for modulating the SSB
srsran_dft_plan_t fft; ///< FFT object for demodulate the SSB. srsran_dft_plan_t fft; ///< FFT object for demodulate the SSB.
/// Frequency domain temporal data /// Frequency/Time domain temporal data
cf_t ssb_grid[SRSRAN_SSB_NOF_RE]; ///< SSB resource grid cf_t* tmp_freq;
cf_t* tmp_time;
/// Time domain sequences /// Time domain sequences
cf_t* pss[SRSRAN_NOF_N_ID_2]; ///< PSS signal for each possible N_id_2 // cf_t* pss[SRSRAN_NOF_NID_2_NR]; ///< PSS signal for each possible N_id_2
cf_t* sss[SRSRAN_NOF_N_ID_1]; ///< SSS signal for each possible N_id_1 // cf_t* sss[SRSRAN_NOF_NID_1_NR]; ///< SSS signal for each possible N_id_1
} srsran_ssb_nr_t; } srsran_ssb_t;
/** /**
* @brief Initialises configures NR SSB with the given arguments * @brief Initialises configures NR SSB with the given arguments
@ -76,13 +96,13 @@ typedef struct SRSRAN_API {
* @param args NR PSS initialization arguments * @param args NR PSS initialization arguments
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise * @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise
*/ */
SRSRAN_API int srsran_ssb_init(srsran_ssb_nr_t* q, const srsran_ssb_args_t* args); SRSRAN_API int srsran_ssb_init(srsran_ssb_t* q, const srsran_ssb_args_t* args);
/** /**
* @brief Frees NR SSB object * @brief Frees NR SSB object
* @param q SSB object * @param q SSB object
*/ */
SRSRAN_API void srsran_ssb_free(srsran_ssb_nr_t* q); SRSRAN_API void srsran_ssb_free(srsran_ssb_t* q);
/** /**
* @brief Sets SSB configuration with the current SSB configuration * @brief Sets SSB configuration with the current SSB configuration
@ -90,27 +110,33 @@ SRSRAN_API void srsran_ssb_free(srsran_ssb_nr_t* q);
* @param cfg Current SSB configuration * @param cfg Current SSB configuration
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise * @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise
*/ */
SRSRAN_API int srsran_ssb_set_cfg(srsran_ssb_nr_t* q, const srsran_ssb_cfg_t* cfg); 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 * @brief Decodes PBCH in the given time domain signal
* @param q SSB object * @param q SSB object
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise * @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise
*/ */
SRSRAN_API int srsran_ssb_decode_pbch(srsran_ssb_nr_t* q, const cf_t* in, srsran_pbch_msg_t* msg); 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 * @brief Adds SSB to a given signal in time domain
* @param q SSB object * @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 * @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise
*/ */
SRSRAN_API int srsran_ssb_add(srsran_ssb_nr_t* q, const srsran_pbch_msg_t* msg, const cf_t* in, cf_t* out); 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 * @brief Perform Channel State Information (CSI) measurement from the SSB
* @param q NR PSS object * @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 * @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise
*/ */
SRSRAN_API int SRSRAN_API int
srsran_ssb_csi_measure(srsran_ssb_nr_t* q, const cf_t* in, srsran_csi_channel_measurements_t* measurement); 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 #endif // SRSRAN_SSB_H

View File

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

View File

@ -174,8 +174,10 @@ float srsran_symbol_distance_s(uint32_t l0, uint32_t l1, srsran_subcarrier_spaci
// Compute reference FFT size // Compute reference FFT size
uint32_t N = (2048 + 144) * count + extra_cp; uint32_t N = (2048 + 144) * count + extra_cp;
float TS = SRSRAN_LTE_TS / (float)(1U << (uint32_t)scs);
// Return symbol distance in microseconds // Return symbol distance in microseconds
return (N << (uint32_t)scs) * SRSRAN_LTE_TS; return (float)N * TS;
} }
bool srsran_tdd_nr_is_dl(const srsran_tdd_config_nr_t* cfg, uint32_t numerology, uint32_t slot_idx) bool srsran_tdd_nr_is_dl(const srsran_tdd_config_nr_t* cfg, uint32_t numerology, uint32_t slot_idx)
@ -267,3 +269,21 @@ int srsran_carrier_to_cell(const srsran_carrier_nr_t* carrier, srsran_cell_t* ce
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
uint32_t srsran_csi_meas_info(const srsran_csi_trs_measurements_t* meas, char* str, uint32_t str_len)
{
if (meas == NULL || str == NULL || str_len == 0) {
return 0;
}
return srsran_print_check(str,
str_len,
0,
"rsrp=%+.1f epre=%+.1f n0=%+.1f snr=%+.1f cfo=%+.1f delay=%+.1f",
meas->rsrp_dB,
meas->epre_dB,
meas->n0_dB,
meas->snr_dB,
meas->cfo_hz,
meas->delay_us);
}

96
lib/src/phy/sync/pss_nr.c Normal file
View File

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

363
lib/src/phy/sync/ssb.c Normal file
View File

@ -0,0 +1,363 @@
/**
*
* \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;
}
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, SRSRAN_NID_2_NR(N_id), pss_lse) < SRSRAN_SUCCESS ||
srsran_sss_nr_extract_lse(ssb_grid, SRSRAN_NID_1_NR(N_id), SRSRAN_NID_2_NR(N_id), 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, SRSRAN_NID_2_NR(N_id), pss_lse) < SRSRAN_SUCCESS ||
srsran_sss_nr_extract_lse(ssb_grid, SRSRAN_NID_1_NR(N_id), SRSRAN_NID_2_NR(N_id), 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;
}

208
lib/src/phy/sync/sss_nr.c Normal file
View File

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

View File

@ -121,3 +121,13 @@ target_link_libraries(cfo_test srsran_phy)
add_test(cfo_test_1 cfo_test -f 0.12345 -n 1000) add_test(cfo_test_1 cfo_test -f 0.12345 -n 1000)
add_test(cfo_test_2 cfo_test -f 0.99849 -n 1000) add_test(cfo_test_2 cfo_test -f 0.99849 -n 1000)
########################################################################
# NE TEST
########################################################################
add_executable(ssb_measure_test ssb_measure_test.c)
target_link_libraries(ssb_measure_test srsran_phy)
add_test(ssb_measure_test ssb_measure_test)

View File

@ -0,0 +1,185 @@
/**
*
* \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/sync/ssb.h"
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/vector.h"
#include <getopt.h>
#include <srsran/phy/channel/ch_awgn.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
srsran_csi_trs_measurements_t meas = {};
TESTASSERT(srsran_ssb_csi_measure(ssb, pci, buffer, &meas) == SRSRAN_SUCCESS);
gettimeofday(&t[1], NULL);
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;
}

View File

@ -321,13 +321,11 @@ bool cc_worker::measure_csi()
continue; continue;
} }
logger.info("NZP-CSI-RS (TRS): id=%d rsrp=%+.1f epre=%+.1f snr=%+.1f cfo=%+.1f delay=%.1f", if (logger.info.enabled()) {
resource_set_id, std::array<char, 512> str = {};
trs_measurements.rsrp_dB, srsran_csi_meas_info(&trs_measurements, str.data(), (uint32_t)str.size());
trs_measurements.epre_dB, logger.info("NZP-CSI-RS (TRS): id=%d %s", resource_set_id, str.data());
trs_measurements.snr_dB, }
trs_measurements.cfo_hz,
trs_measurements.delay_us);
// Compute channel metrics and push it // Compute channel metrics and push it
ch_metrics_t ch_metrics = {}; ch_metrics_t ch_metrics = {};