mirror of https://github.com/PentHertz/srsLTE.git
Implemented initial PBCH decoder and refactored SSB candidate selection
This commit is contained in:
parent
de1b25558f
commit
48e0fc3c99
|
@ -29,9 +29,9 @@ struct phy_cfg_nr_t {
|
|||
* SSB configuration
|
||||
*/
|
||||
struct ssb_cfg_t {
|
||||
uint32_t periodicity_ms;
|
||||
std::array<bool, SRSRAN_SSB_NOF_POSITION> position_in_burst;
|
||||
srsran_subcarrier_spacing_t scs;
|
||||
uint32_t periodicity_ms;
|
||||
std::array<bool, SRSRAN_SSB_NOF_CANDIDATES> position_in_burst;
|
||||
srsran_subcarrier_spacing_t scs;
|
||||
};
|
||||
|
||||
srsran_tdd_config_nr_t tdd = {};
|
||||
|
|
|
@ -63,12 +63,12 @@ typedef struct SRSRAN_API {
|
|||
* @brief Describes the PBCH message
|
||||
*/
|
||||
typedef struct SRSRAN_API {
|
||||
uint8_t payload[SRSRAN_PBCH_NR_PAYLOAD_SZ]; ///< Actual PBCH payload provided by higher layers
|
||||
uint32_t sfn_4lsb; ///< SFN 4 LSB
|
||||
uint32_t ssb_idx; ///< SS/PBCH blocks index described in TS 38.213 4.1
|
||||
uint32_t k_ssb_msb; ///< Subcarrier offset MSB described in TS 38.211 7.4.3.1
|
||||
uint32_t hrf; ///< Half Radio Frame bit
|
||||
bool crc; ///< Decoder only, it is true only if the received CRC matches
|
||||
uint8_t payload[SRSRAN_PBCH_NR_PAYLOAD_SZ]; ///< Actual PBCH payload provided by higher layers
|
||||
uint8_t sfn_4lsb; ///< SFN 4 LSB
|
||||
uint8_t ssb_idx; ///< SS/PBCH blocks index described in TS 38.213 4.1
|
||||
uint8_t k_ssb_msb; ///< Subcarrier offset MSB described in TS 38.211 7.4.3.1
|
||||
bool hrf; ///< Half Radio Frame bit
|
||||
bool crc; ///< Decoder only, it is true only if the received CRC matches
|
||||
} srsran_pbch_msg_nr_t;
|
||||
|
||||
/**
|
||||
|
@ -102,13 +102,17 @@ SRSRAN_API int srsran_pbch_nr_encode(srsran_pbch_nr_t* q,
|
|||
* @brief Decodes an NR PBCH message in the SSB resource grid
|
||||
* @param q NR PBCH object
|
||||
* @param cfg NR PBCH configuration
|
||||
* @param ssb_idx SSB candidate index
|
||||
* @param[in] ssb_grid SSB resource grid
|
||||
* @param msg NR PBCH message received
|
||||
* @return SRSRAN_SUCCESS if decoding is successful, SRSLTE_ERROR code otherwise
|
||||
*/
|
||||
SRSRAN_API int srsran_pbch_nr_decode(srsran_pbch_nr_t* q,
|
||||
const srsran_pbch_nr_cfg_t* cfg,
|
||||
uint32_t ssb_idx,
|
||||
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
|
||||
srsran_pbch_msg_nr_t* msg);
|
||||
|
||||
SRSRAN_API uint32_t srsran_pbch_msg_info(const srsran_pbch_msg_nr_t* msg, char* str, uint32_t str_len);
|
||||
|
||||
#endif // SRSRAN_PBCH_NR_H
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
/**
|
||||
* @brief Maximum number of SSB positions in burst. Defined in TS 38.331 ServingCellConfigCommon, ssb-PositionsInBurst
|
||||
*/
|
||||
#define SRSRAN_SSB_NOF_POSITION 64
|
||||
#define SRSRAN_SSB_NOF_CANDIDATES 64
|
||||
|
||||
/**
|
||||
* @brief Describes SSB object initialization arguments
|
||||
|
@ -61,13 +61,12 @@ typedef struct SRSRAN_API {
|
|||
double ssb_freq_hz; ///< SSB center frequency
|
||||
srsran_subcarrier_spacing_t scs; ///< SSB configured Subcarrier spacing
|
||||
srsran_ssb_patern_t pattern; ///< SSB pattern as defined in TS 38.313 section 4.1 Cell search
|
||||
bool position[SRSRAN_SSB_NOF_POSITION]; ///< Indicates the time domain positions of the transmitted SS-blocks
|
||||
srsran_duplex_mode_t duplex_mode; ///< Set to true if the spectrum is paired (FDD)
|
||||
uint32_t periodicity_ms; ///< SSB periodicity in ms
|
||||
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_duplex_mode_t duplex_mode; ///< Set to true if the spectrum is paired (FDD)
|
||||
uint32_t periodicity_ms; ///< SSB periodicity in ms
|
||||
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;
|
||||
|
||||
/**
|
||||
|
@ -78,18 +77,18 @@ typedef struct SRSRAN_API {
|
|||
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 max_corr_sz; ///< Maximum correlation size
|
||||
uint32_t symbol_sz; ///< Current SSB symbol size (for the given base-band sampling rate)
|
||||
uint32_t corr_sz; ///< Correlation size
|
||||
uint32_t corr_window; ///< Correlation window length
|
||||
int32_t f_offset; ///< Current SSB integer frequency offset (multiple of SCS)
|
||||
uint32_t t_offset; ///< Current SSB integer time offset (number of samples)
|
||||
uint32_t cp_sz[SRSRAN_SSB_DURATION_NSYMB]; ///< CP length for each SSB symbol
|
||||
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 max_corr_sz; ///< Maximum correlation size
|
||||
uint32_t symbol_sz; ///< Current SSB symbol size (for the given base-band sampling rate)
|
||||
uint32_t corr_sz; ///< Correlation size
|
||||
uint32_t corr_window; ///< Correlation window length
|
||||
int32_t f_offset; ///< Current SSB integer frequency offset (multiple of SCS)
|
||||
uint32_t cp_sz; ///< CP length for the given symbol size
|
||||
|
||||
/// Other parameters
|
||||
uint32_t Lmax; ///< Number of SSB candidates
|
||||
uint32_t l_first[SRSRAN_SSB_NOF_CANDIDATES]; ///< Start symbol for each SSB candidate in half radio frame
|
||||
uint32_t Lmax; ///< Number of SSB candidates
|
||||
|
||||
/// Internal Objects
|
||||
srsran_dft_plan_t ifft; ///< IFFT object for modulating the SSB
|
||||
|
@ -129,9 +128,12 @@ 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
|
||||
* @param N_id Physical Cell Identifier
|
||||
* @param ssb_idx SSB candidate index
|
||||
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
|
||||
*/
|
||||
SRSRAN_API int srsran_ssb_decode_pbch(srsran_ssb_t* q, const cf_t* in, srsran_pbch_msg_nr_t* msg);
|
||||
SRSRAN_API int
|
||||
srsran_ssb_decode_pbch(srsran_ssb_t* q, uint32_t N_id, uint32_t ssb_idx, const cf_t* in, srsran_pbch_msg_nr_t* msg);
|
||||
|
||||
/**
|
||||
* @brief Decides if the SSB object is configured and a given subframe is configured for SSB transmission
|
||||
|
@ -145,11 +147,16 @@ SRSRAN_API bool srsran_ssb_send(srsran_ssb_t* q, uint32_t sf_idx);
|
|||
* @brief Adds SSB to a given signal in time domain
|
||||
* @param q SSB object
|
||||
* @param N_id Physical Cell Identifier
|
||||
* @param ssb_idx SSB candidate index
|
||||
* @param msg NR PBCH message to transmit
|
||||
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_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);
|
||||
SRSRAN_API int srsran_ssb_add(srsran_ssb_t* q,
|
||||
uint32_t N_id,
|
||||
uint32_t ssb_idx,
|
||||
const srsran_pbch_msg_nr_t* msg,
|
||||
const cf_t* in,
|
||||
cf_t* out);
|
||||
|
||||
/**
|
||||
* @brief Perform cell search and measurement
|
||||
|
@ -170,11 +177,15 @@ SRSRAN_API int srsran_ssb_csi_search(srsran_ssb_t* q,
|
|||
* @brief Perform Channel State Information (CSI) measurement from the SSB
|
||||
* @param q NR PSS object
|
||||
* @param N_id Physical Cell Identifier
|
||||
* @param ssb_idx SSB candidate index
|
||||
* @param in Base-band signal
|
||||
* @param meas SSB-based CSI measurement
|
||||
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_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);
|
||||
SRSRAN_API int srsran_ssb_csi_measure(srsran_ssb_t* q,
|
||||
uint32_t N_id,
|
||||
uint32_t ssb_idx,
|
||||
const cf_t* in,
|
||||
srsran_csi_trs_measurements_t* meas);
|
||||
|
||||
#endif // SRSRAN_SSB_H
|
||||
|
|
|
@ -1300,10 +1300,10 @@ bool make_phy_carrier_cfg(const freq_info_dl_s& asn1_freq_info_dl, srsran_carrie
|
|||
}
|
||||
|
||||
template <class bitstring_t>
|
||||
static inline void make_ssb_positions_in_burst(const bitstring_t& ans1_position_in_burst,
|
||||
std::array<bool, SRSRAN_SSB_NOF_POSITION>& position_in_burst)
|
||||
static inline void make_ssb_positions_in_burst(const bitstring_t& ans1_position_in_burst,
|
||||
std::array<bool, SRSRAN_SSB_NOF_CANDIDATES>& position_in_burst)
|
||||
{
|
||||
for (uint32_t i = 0; i < SRSRAN_SSB_NOF_POSITION; i++) {
|
||||
for (uint32_t i = 0; i < SRSRAN_SSB_NOF_CANDIDATES; i++) {
|
||||
if (i < ans1_position_in_burst.length()) {
|
||||
position_in_burst[i] = ans1_position_in_burst.get(ans1_position_in_burst.length() - 1 - i);
|
||||
} else {
|
||||
|
|
|
@ -13,10 +13,15 @@
|
|||
#include "srsran/phy/phch/pbch_nr.h"
|
||||
#include "srsran/phy/common/sequence.h"
|
||||
#include "srsran/phy/fec/polar/polar_chanalloc.h"
|
||||
#include "srsran/phy/fec/polar/polar_interleaver.h"
|
||||
#include "srsran/phy/modem/demod_soft.h"
|
||||
#include "srsran/phy/modem/mod.h"
|
||||
#include "srsran/phy/utils/debug.h"
|
||||
#include "srsran/phy/utils/vector.h"
|
||||
|
||||
#define PBCH_NR_DEBUG_TX(...) DEBUG("PBCH-NR Tx: " __VA_ARGS__)
|
||||
#define PBCH_NR_DEBUG_RX(...) DEBUG("PBCH-NR Rx: " __VA_ARGS__)
|
||||
|
||||
/*
|
||||
* CRC Parameters
|
||||
*/
|
||||
|
@ -31,7 +36,7 @@
|
|||
/*
|
||||
* Polar rate matching I_BIL
|
||||
*/
|
||||
#define pbch_nr_polar_rm_tx_IBIL 0
|
||||
#define PBCH_NR_POLAR_RM_IBIL 0
|
||||
|
||||
/*
|
||||
* Number of generated payload bits, called A
|
||||
|
@ -58,11 +63,6 @@
|
|||
*/
|
||||
#define PBCH_NR_M (PBCH_NR_E / 2)
|
||||
|
||||
/*
|
||||
* Number of DMRS
|
||||
*/
|
||||
#define PBCH_NR_NOF_DMRS (143)
|
||||
|
||||
static int pbch_nr_init_encoder(srsran_pbch_nr_t* q, const srsran_pbch_nr_args_t* args)
|
||||
{
|
||||
// Skip encoder init if not requested
|
||||
|
@ -214,6 +214,53 @@ pbch_nr_pbch_msg_pack(const srsran_pbch_nr_cfg_t* cfg, const srsran_pbch_msg_nr_
|
|||
a[G[j_other++]] = msg->payload[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
|
||||
PBCH_NR_DEBUG_TX("Packed PBCH bits: ");
|
||||
srsran_vec_fprint_byte(stdout, a, PBCH_NR_A);
|
||||
}
|
||||
}
|
||||
static void
|
||||
pbch_nr_pbch_msg_unpack(const srsran_pbch_nr_cfg_t* cfg, const uint8_t a[PBCH_NR_A], srsran_pbch_msg_nr_t* msg)
|
||||
{
|
||||
if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
|
||||
PBCH_NR_DEBUG_RX("Packed PBCH bits: ");
|
||||
srsran_vec_fprint_byte(stdout, a, PBCH_NR_A);
|
||||
}
|
||||
|
||||
// Extract actual payload size
|
||||
uint32_t A_hat = SRSRAN_PBCH_NR_PAYLOAD_SZ;
|
||||
|
||||
// Put SFN in a_hat[A_hat] to a_hat[A_hat + 3]
|
||||
uint32_t j_sfn = 0;
|
||||
msg->sfn_4lsb = 0;
|
||||
msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 3U); // 4th LSB of SFN
|
||||
msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 2U); // 3th LSB of SFN
|
||||
msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 1U); // 2th LSB of SFN
|
||||
msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 0U); // 1th LSB of SFN
|
||||
|
||||
// Put HRF in a_hat[A_hat + 4]
|
||||
msg->hrf = (a[G[10]] == 1);
|
||||
|
||||
// Put SSB related in a_hat[A_hat + 5] to a_hat[A_hat + 7]
|
||||
if (cfg->Lmax == 64) {
|
||||
msg->ssb_idx = msg->ssb_idx & 0b111;
|
||||
msg->ssb_idx |= (uint8_t)(a[G[11]] << 5U); // 6th bit of SSB index
|
||||
msg->ssb_idx |= (uint8_t)(a[G[12]] << 4U); // 5th bit of SSB index
|
||||
msg->ssb_idx |= (uint8_t)(a[G[13]] << 3U); // 4th bit of SSB index
|
||||
} else {
|
||||
msg->k_ssb_msb = a[G[11]];
|
||||
}
|
||||
|
||||
// Put actual payload
|
||||
uint32_t j_other = 14;
|
||||
for (uint32_t i = 0; i < A_hat; i++) {
|
||||
if (i > 0 && i < 7) {
|
||||
msg->payload[i] = a[G[j_sfn++]];
|
||||
} else {
|
||||
msg->payload[i] = a[G[j_other++]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pbch_nr_scramble(const srsran_pbch_nr_cfg_t* cfg, const uint8_t a[PBCH_NR_A], uint8_t a_prime[PBCH_NR_A])
|
||||
|
@ -258,27 +305,85 @@ static void pbch_nr_scramble(const srsran_pbch_nr_cfg_t* cfg, const uint8_t a[PB
|
|||
}
|
||||
}
|
||||
|
||||
static int pbch_nr_polar_encode(srsran_pbch_nr_t* q, const uint8_t c[PBCH_NR_K], uint8_t d[PBCH_NR_K])
|
||||
static int pbch_nr_polar_encode(srsran_pbch_nr_t* q, const uint8_t c[PBCH_NR_K], uint8_t d[PBCH_NR_N])
|
||||
{
|
||||
// Interleave
|
||||
uint8_t c_prime[SRSRAN_POLAR_INTERLEAVER_K_MAX_IL];
|
||||
srsran_polar_interleaver_run_u8(c, c_prime, PBCH_NR_K, true);
|
||||
|
||||
// Allocate channel
|
||||
uint8_t allocated[PBCH_NR_N];
|
||||
srsran_polar_chanalloc_tx(c, allocated, q->code.N, q->code.K, q->code.nPC, q->code.K_set, q->code.PC_set);
|
||||
srsran_polar_chanalloc_tx(c_prime, allocated, q->code.N, q->code.K, q->code.nPC, q->code.K_set, q->code.PC_set);
|
||||
|
||||
// Encode bits
|
||||
if (srsran_polar_encoder_encode(&q->polar_encoder, allocated, d, q->code.n) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
|
||||
PBCH_NR_DEBUG_TX("Allocated: ");
|
||||
srsran_vec_fprint_byte(stdout, allocated, PBCH_NR_N);
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
static int pbch_nr_polar_rm_tx(srsran_pbch_nr_t* q, const uint8_t d[PBCH_NR_K], uint8_t o[PBCH_NR_E])
|
||||
static int pbch_nr_polar_decode(srsran_pbch_nr_t* q, const int8_t d[PBCH_NR_N], uint8_t c[PBCH_NR_K])
|
||||
{
|
||||
if (srsran_polar_rm_tx(&q->polar_rm_tx, d, o, q->code.n, PBCH_NR_E, PBCH_NR_K, pbch_nr_polar_rm_tx_IBIL) <
|
||||
// Decode bits
|
||||
uint8_t allocated[PBCH_NR_N];
|
||||
if (srsran_polar_decoder_decode_c(&q->polar_decoder, d, allocated, q->code.n, q->code.F_set, q->code.F_set_size) <
|
||||
SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
|
||||
PBCH_NR_DEBUG_RX("Allocated: ");
|
||||
srsran_vec_fprint_byte(stdout, allocated, PBCH_NR_N);
|
||||
}
|
||||
|
||||
// Allocate channel
|
||||
uint8_t c_prime[SRSRAN_POLAR_INTERLEAVER_K_MAX_IL];
|
||||
srsran_polar_chanalloc_rx(allocated, c_prime, q->code.K, q->code.nPC, q->code.K_set, q->code.PC_set);
|
||||
|
||||
// Interleave
|
||||
srsran_polar_interleaver_run_u8(c_prime, c, PBCH_NR_K, false);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
static int pbch_nr_polar_rm_tx(srsran_pbch_nr_t* q, const uint8_t d[PBCH_NR_N], uint8_t o[PBCH_NR_E])
|
||||
{
|
||||
if (srsran_polar_rm_tx(&q->polar_rm_tx, d, o, q->code.n, PBCH_NR_E, PBCH_NR_K, PBCH_NR_POLAR_RM_IBIL) <
|
||||
SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
|
||||
PBCH_NR_DEBUG_TX("d: ");
|
||||
srsran_vec_fprint_byte(stdout, d, PBCH_NR_N);
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
static int pbch_nr_polar_rm_rx(srsran_pbch_nr_t* q, const int8_t llr[PBCH_NR_E], int8_t d[PBCH_NR_N])
|
||||
{
|
||||
if (srsran_polar_rm_rx_c(&q->polar_rm_rx, llr, d, PBCH_NR_E, q->code.n, PBCH_NR_K, PBCH_NR_POLAR_RM_IBIL) <
|
||||
SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Negate all LLR
|
||||
for (uint32_t i = 0; i < PBCH_NR_N; i++) {
|
||||
d[i] *= -1;
|
||||
}
|
||||
|
||||
if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
|
||||
PBCH_NR_DEBUG_RX("d: ");
|
||||
srsran_vec_fprint_bs(stdout, d, PBCH_NR_N);
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -307,8 +412,33 @@ static void pbch_nr_scramble_tx(const srsran_pbch_nr_cfg_t* cfg,
|
|||
srsran_sequence_state_apply_bit(&sequence_state, b, b_hat, PBCH_NR_E);
|
||||
}
|
||||
|
||||
static void pbch_nr_scramble_rx(const srsran_pbch_nr_cfg_t* cfg,
|
||||
uint32_t ssb_idx,
|
||||
const int8_t b_hat[PBCH_NR_E],
|
||||
int8_t b[PBCH_NR_E])
|
||||
{
|
||||
// Initialise sequence
|
||||
srsran_sequence_state_t sequence_state = {};
|
||||
srsran_sequence_state_init(&sequence_state, SRSRAN_SEQUENCE_MOD(cfg->N_id));
|
||||
|
||||
// Select value M
|
||||
uint32_t M_bit = PBCH_NR_E;
|
||||
|
||||
// Select value v
|
||||
uint32_t v = (ssb_idx & 0x7U);
|
||||
if (cfg->Lmax == 4) {
|
||||
v = ssb_idx & 0x3U;
|
||||
}
|
||||
|
||||
// Advance sequence
|
||||
srsran_sequence_state_advance(&sequence_state, v * M_bit);
|
||||
|
||||
// Apply sequence
|
||||
srsran_sequence_state_apply_c(&sequence_state, b_hat, b, PBCH_NR_E);
|
||||
}
|
||||
|
||||
static void
|
||||
pbch_nr_mapping(const srsran_pbch_nr_cfg_t* cfg, const cf_t symbols[PBCH_NR_E], cf_t ssb_grid[SRSRAN_SSB_NOF_RE])
|
||||
pbch_nr_mapping(const srsran_pbch_nr_cfg_t* cfg, const cf_t symbols[PBCH_NR_M], cf_t ssb_grid[SRSRAN_SSB_NOF_RE])
|
||||
{
|
||||
uint32_t count = 0;
|
||||
|
||||
|
@ -352,6 +482,63 @@ pbch_nr_mapping(const srsran_pbch_nr_cfg_t* cfg, const cf_t symbols[PBCH_NR_E],
|
|||
|
||||
ssb_grid[3 * SRSRAN_SSB_BW_SUBC + k] = symbols[count++];
|
||||
}
|
||||
|
||||
// if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
|
||||
// PBCH_NR_DEBUG_TX("Symbols: ");
|
||||
// srsran_vec_fprint_c(stdout, symbols, PBCH_NR_M);
|
||||
// }
|
||||
}
|
||||
|
||||
static void
|
||||
pbch_nr_demapping(const srsran_pbch_nr_cfg_t* cfg, const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], cf_t symbols[PBCH_NR_M])
|
||||
{
|
||||
uint32_t count = 0;
|
||||
|
||||
// PBCH DMRS shift
|
||||
uint32_t v = cfg->N_id % 4;
|
||||
|
||||
// Symbol 1
|
||||
for (uint32_t k = 0; k < SRSRAN_SSB_BW_SUBC; k++) {
|
||||
// Skip DMRS
|
||||
if (k % 4 == v) {
|
||||
continue;
|
||||
}
|
||||
|
||||
symbols[count++] = ssb_grid[1 * SRSRAN_SSB_BW_SUBC + k];
|
||||
}
|
||||
|
||||
// Symbol 2
|
||||
for (uint32_t k = 0; k < 48; k++) {
|
||||
// Skip DMRS
|
||||
if (k % 4 == v) {
|
||||
continue;
|
||||
}
|
||||
|
||||
symbols[count++] = ssb_grid[2 * SRSRAN_SSB_BW_SUBC + k];
|
||||
}
|
||||
for (uint32_t k = 192; k < SRSRAN_SSB_BW_SUBC; k++) {
|
||||
// Skip DMRS
|
||||
if (k % 4 == v) {
|
||||
continue;
|
||||
}
|
||||
|
||||
symbols[count++] = ssb_grid[2 * SRSRAN_SSB_BW_SUBC + k];
|
||||
}
|
||||
|
||||
// Symbol 3
|
||||
for (uint32_t k = 0; k < SRSRAN_SSB_BW_SUBC; k++) {
|
||||
// Skip DMRS
|
||||
if (k % 4 == v) {
|
||||
continue;
|
||||
}
|
||||
|
||||
symbols[count++] = ssb_grid[3 * SRSRAN_SSB_BW_SUBC + k];
|
||||
}
|
||||
|
||||
// if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
|
||||
// PBCH_NR_DEBUG_RX("Symbols: ");
|
||||
// srsran_vec_fprint_c(stdout, symbols, PBCH_NR_M);
|
||||
// }
|
||||
}
|
||||
|
||||
int srsran_pbch_nr_encode(srsran_pbch_nr_t* q,
|
||||
|
@ -374,10 +561,10 @@ int srsran_pbch_nr_encode(srsran_pbch_nr_t* q,
|
|||
|
||||
// 7.1.3 Transport block CRC attachment
|
||||
uint32_t checksum = srsran_crc_attach(&q->crc, b, PBCH_NR_A);
|
||||
INFO("NR-PBCH: checksum=%06x", checksum);
|
||||
PBCH_NR_DEBUG_TX("checksum=%06x", checksum);
|
||||
|
||||
// 7.1.4 Channel coding
|
||||
uint8_t d[PBCH_NR_K];
|
||||
uint8_t d[PBCH_NR_N];
|
||||
if (pbch_nr_polar_encode(q, b, d) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
@ -402,3 +589,77 @@ int srsran_pbch_nr_encode(srsran_pbch_nr_t* q,
|
|||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int srsran_pbch_nr_decode(srsran_pbch_nr_t* q,
|
||||
const srsran_pbch_nr_cfg_t* cfg,
|
||||
uint32_t ssb_idx,
|
||||
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
|
||||
srsran_pbch_msg_nr_t* msg)
|
||||
{
|
||||
if (q == NULL || cfg == NULL || msg == NULL || ssb_grid == NULL) {
|
||||
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
|
||||
// 7.3.3.3 Mapping to physical resources
|
||||
// 7.4.3.1.3 Mapping of PBCH and DM-RS within an SS/PBCH block
|
||||
cf_t symbols[PBCH_NR_M];
|
||||
pbch_nr_demapping(cfg, ssb_grid, symbols);
|
||||
|
||||
// 7.3.3.2 Modulation
|
||||
int8_t llr[PBCH_NR_E];
|
||||
srsran_demod_soft_demodulate_b(SRSRAN_MOD_QPSK, symbols, llr, PBCH_NR_M);
|
||||
|
||||
// TS 38.211 7.3.3 Physical broadcast channel
|
||||
// 7.3.3.1 Scrambling
|
||||
pbch_nr_scramble_rx(cfg, ssb_idx, llr, llr);
|
||||
|
||||
// 7.1.5 Rate matching
|
||||
int8_t d[PBCH_NR_N];
|
||||
if (pbch_nr_polar_rm_rx(q, llr, d) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// TS 38.212 7.1 Broadcast channel
|
||||
// 7.1.4 Channel coding
|
||||
uint8_t b[PBCH_NR_K];
|
||||
if (pbch_nr_polar_decode(q, d, b) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// 7.1.3 Transport block CRC attachment
|
||||
msg->crc = srsran_crc_match(&q->crc, b, PBCH_NR_A);
|
||||
PBCH_NR_DEBUG_RX("crc=%s", msg->crc ? "OK" : "KO");
|
||||
|
||||
// 7.1.2 Scrambling
|
||||
uint8_t a[PBCH_NR_A];
|
||||
pbch_nr_scramble(cfg, b, a);
|
||||
|
||||
// 7.1.1 PBCH payload generation
|
||||
pbch_nr_pbch_msg_unpack(cfg, a, msg);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t srsran_pbch_msg_info(const srsran_pbch_msg_nr_t* msg, char* str, uint32_t str_len)
|
||||
{
|
||||
if (msg == NULL || str == NULL || str_len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t len = 0;
|
||||
|
||||
len = srsran_print_check(str, str_len, len, "payload=");
|
||||
|
||||
len += srsran_vec_sprint_hex(&str[len], str_len - len, (uint8_t*)msg->payload, SRSRAN_PBCH_NR_PAYLOAD_SZ);
|
||||
|
||||
len = srsran_print_check(str,
|
||||
str_len,
|
||||
len,
|
||||
" sfn_lsb=%d ssb_idx=%d k_ssb_msb=%d hrf=%d ",
|
||||
msg->sfn_4lsb,
|
||||
msg->ssb_idx,
|
||||
msg->k_ssb_msb,
|
||||
msg->hrf);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
|
|
@ -147,7 +147,7 @@ void srsran_ssb_free(srsran_ssb_t* q)
|
|||
SRSRAN_MEM_ZERO(q, srsran_ssb_t, 1);
|
||||
}
|
||||
|
||||
static uint32_t ssb_first_symbol_caseA(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION])
|
||||
static uint32_t ssb_first_symbol_caseA(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES])
|
||||
{
|
||||
// Case A - 15 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes of { 2 , 8 } + 14 ⋅ n . For
|
||||
// carrier frequencies smaller than or equal to 3 GHz, n = 0 , 1 . For carrier frequencies within FR1 larger than 3
|
||||
|
@ -169,7 +169,7 @@ static uint32_t ssb_first_symbol_caseA(const srsran_ssb_cfg_t* cfg, uint32_t ind
|
|||
return count;
|
||||
}
|
||||
|
||||
static uint32_t ssb_first_symbol_caseB(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION])
|
||||
static uint32_t ssb_first_symbol_caseB(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES])
|
||||
{
|
||||
// Case B - 30 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes { 4 , 8 , 16 , 20 } + 28 ⋅ n .
|
||||
// For carrier frequencies smaller than or equal to 3 GHz, n = 0 . For carrier frequencies within FR1 larger than 3
|
||||
|
@ -191,7 +191,7 @@ static uint32_t ssb_first_symbol_caseB(const srsran_ssb_cfg_t* cfg, uint32_t ind
|
|||
return count;
|
||||
}
|
||||
|
||||
static uint32_t ssb_first_symbol_caseC(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION])
|
||||
static uint32_t ssb_first_symbol_caseC(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES])
|
||||
{
|
||||
// Case C - 30 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes { 2 , 8 } +14 ⋅ n .
|
||||
// - For paired spectrum operation
|
||||
|
@ -218,7 +218,7 @@ static uint32_t ssb_first_symbol_caseC(const srsran_ssb_cfg_t* cfg, uint32_t ind
|
|||
return count;
|
||||
}
|
||||
|
||||
static uint32_t ssb_first_symbol_caseD(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION])
|
||||
static uint32_t ssb_first_symbol_caseD(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES])
|
||||
{
|
||||
// Case D - 120 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes { 4 , 8 , 16 , 20 } + 28 ⋅ n .
|
||||
// For carrier frequencies within FR2, n = 0 , 1 , 2 , 3 , 5 , 6 , 7 , 8 , 10 , 11 , 12 , 13 , 15 , 16 , 17 , 18 .
|
||||
|
@ -235,7 +235,7 @@ static uint32_t ssb_first_symbol_caseD(const srsran_ssb_cfg_t* cfg, uint32_t ind
|
|||
return count;
|
||||
}
|
||||
|
||||
static uint32_t ssb_first_symbol_caseE(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION])
|
||||
static uint32_t ssb_first_symbol_caseE(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES])
|
||||
{
|
||||
// Case E - 240 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes
|
||||
//{ 8 , 12 , 16 , 20 , 32 , 36 , 40 , 44 } + 56 ⋅ n . For carrier frequencies within FR2, n = 0 , 1 , 2 , 3 , 5 , 6 ,
|
||||
|
@ -253,7 +253,7 @@ static uint32_t ssb_first_symbol_caseE(const srsran_ssb_cfg_t* cfg, uint32_t ind
|
|||
return count;
|
||||
}
|
||||
|
||||
static uint32_t ssb_candidates(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION])
|
||||
static uint32_t ssb_first_symbol(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES])
|
||||
{
|
||||
uint32_t Lmax = 0;
|
||||
|
||||
|
@ -279,27 +279,6 @@ static uint32_t ssb_candidates(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRS
|
|||
return Lmax;
|
||||
}
|
||||
|
||||
static int ssb_first_symbol(const srsran_ssb_cfg_t* cfg, uint32_t ssb_i, uint32_t* Lmax)
|
||||
{
|
||||
uint32_t indexes[SRSRAN_SSB_NOF_POSITION];
|
||||
*Lmax = ssb_candidates(cfg, indexes);
|
||||
|
||||
uint32_t ssb_count = 0;
|
||||
for (uint32_t i = 0; i < *Lmax; i++) {
|
||||
// There is a SSB transmission opportunity
|
||||
if (cfg->position[i]) {
|
||||
// Return the SSB transmission in burst
|
||||
if (ssb_i == ssb_count) {
|
||||
return (int)indexes[i];
|
||||
}
|
||||
|
||||
ssb_count++;
|
||||
}
|
||||
}
|
||||
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Modulates a given symbol l and stores the time domain signal in q->tmp_time
|
||||
static void ssb_modulate_symbol(srsran_ssb_t* q, cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t l)
|
||||
{
|
||||
|
@ -398,6 +377,23 @@ static int ssb_setup_corr(srsran_ssb_t* q)
|
|||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
static inline int ssb_get_t_offset(srsran_ssb_t* q, uint32_t ssb_idx)
|
||||
{
|
||||
// Get baseband time offset from the begining of the half radio frame to the first symbol
|
||||
if (ssb_idx >= SRSRAN_SSB_NOF_CANDIDATES) {
|
||||
ERROR("Invalid SSB candidate index (%d)", ssb_idx);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
float t_offset_s = srsran_symbol_offset_s(q->l_first[ssb_idx], q->cfg.scs);
|
||||
if (isnan(t_offset_s) || isinf(t_offset_s) || t_offset_s < 0.0f) {
|
||||
ERROR("Invalid first symbol (l_first=%d)", q->l_first[ssb_idx]);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
return (int)round(t_offset_s * q->cfg.srate_hz);
|
||||
}
|
||||
|
||||
int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg)
|
||||
{
|
||||
// Verify input parameters
|
||||
|
@ -409,34 +405,15 @@ int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg)
|
|||
q->scs_hz = (float)SRSRAN_SUBC_SPACING_NR(cfg->scs);
|
||||
|
||||
// Get first symbol
|
||||
int l_begin = ssb_first_symbol(cfg, 0, &q->Lmax);
|
||||
if (l_begin < SRSRAN_SUCCESS) {
|
||||
// set it to 2 in case it is not selected
|
||||
l_begin = 2;
|
||||
}
|
||||
q->Lmax = ssb_first_symbol(cfg, q->l_first);
|
||||
|
||||
float t_offset_s = srsran_symbol_offset_s((uint32_t)l_begin, cfg->scs);
|
||||
if (isnan(t_offset_s) || isinf(t_offset_s) || t_offset_s < 0.0f) {
|
||||
ERROR("Invalid first symbol (l_first=%d)", l_begin);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Calculate SSB symbol size and integer offset
|
||||
// Calculate SSB symbol size and integer frequency offset
|
||||
double freq_offset_hz = cfg->ssb_freq_hz - cfg->center_freq_hz;
|
||||
uint32_t symbol_sz = (uint32_t)round(cfg->srate_hz / q->scs_hz);
|
||||
q->f_offset = (int32_t)round(freq_offset_hz / q->scs_hz);
|
||||
q->t_offset = (uint32_t)round(t_offset_s * cfg->srate_hz);
|
||||
|
||||
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
|
||||
uint32_t l_real = l + (uint32_t)l_begin;
|
||||
|
||||
uint32_t ref_cp_sz = 144U;
|
||||
if (l_real == 0 || l_real == SRSRAN_EXT_CP_SYMBOL(cfg->scs)) {
|
||||
ref_cp_sz = 160U;
|
||||
}
|
||||
|
||||
q->cp_sz[l] = (ref_cp_sz * symbol_sz) / 2048U;
|
||||
}
|
||||
// Calculate cyclic prefix
|
||||
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;
|
||||
|
@ -528,7 +505,12 @@ bool srsran_ssb_send(srsran_ssb_t* q, uint32_t sf_idx)
|
|||
return (sf_idx % q->cfg.periodicity_ms == 0);
|
||||
}
|
||||
|
||||
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)
|
||||
int srsran_ssb_add(srsran_ssb_t* q,
|
||||
uint32_t N_id,
|
||||
uint32_t ssb_idx,
|
||||
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) {
|
||||
|
@ -569,22 +551,26 @@ int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* m
|
|||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Select start symbol from SSB candidate index
|
||||
int t_offset = ssb_get_t_offset(q, ssb_idx);
|
||||
if (t_offset < SRSRAN_SUCCESS) {
|
||||
ERROR("Invalid SSB candidate index");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Select input/ouput pointers considering the time offset in the slot
|
||||
const cf_t* in_ptr = &in[q->t_offset];
|
||||
cf_t* out_ptr = &out[q->t_offset];
|
||||
const cf_t* in_ptr = &in[t_offset];
|
||||
cf_t* out_ptr = &out[t_offset];
|
||||
|
||||
// For each SSB symbol, modulate
|
||||
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
|
||||
// Get CP length
|
||||
uint32_t cp_len = q->cp_sz[l];
|
||||
|
||||
// Map SSB in resource grid and perform IFFT
|
||||
ssb_modulate_symbol(q, ssb_grid, l);
|
||||
|
||||
// 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;
|
||||
srsran_vec_sum_ccc(in_ptr, &q->tmp_time[q->symbol_sz - q->cp_sz], out_ptr, q->cp_sz);
|
||||
in_ptr += q->cp_sz;
|
||||
out_ptr += q->cp_sz;
|
||||
|
||||
// Add symbol to the input baseband
|
||||
srsran_vec_sum_ccc(in_ptr, q->tmp_time, out_ptr, q->symbol_sz);
|
||||
|
@ -599,21 +585,18 @@ static int ssb_demodulate(srsran_ssb_t* q, const cf_t* in, uint32_t t_offset, cf
|
|||
{
|
||||
const cf_t* in_ptr = &in[t_offset];
|
||||
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
|
||||
// Get CP length
|
||||
uint32_t cp_len = q->cp_sz[l];
|
||||
|
||||
// Advance half CP, to avoid inter symbol interference
|
||||
in_ptr += SRSRAN_FLOOR(cp_len, 2);
|
||||
in_ptr += SRSRAN_FLOOR(q->cp_sz, 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);
|
||||
in_ptr += q->symbol_sz + SRSRAN_CEIL(q->cp_sz, 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);
|
||||
srsran_vec_apply_cfo(q->tmp_freq, SRSRAN_CEIL(q->cp_sz, 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];
|
||||
|
@ -825,8 +808,8 @@ int srsran_ssb_csi_search(srsran_ssb_t* q,
|
|||
}
|
||||
|
||||
// Remove CP offset prior demodulation
|
||||
if (t_offset >= q->cp_sz[0]) {
|
||||
t_offset -= q->cp_sz[0];
|
||||
if (t_offset >= q->cp_sz) {
|
||||
t_offset -= q->cp_sz;
|
||||
} else {
|
||||
t_offset = 0;
|
||||
}
|
||||
|
@ -861,7 +844,11 @@ int srsran_ssb_csi_search(srsran_ssb_t* q,
|
|||
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)
|
||||
int srsran_ssb_csi_measure(srsran_ssb_t* q,
|
||||
uint32_t N_id,
|
||||
uint32_t ssb_idx,
|
||||
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)) {
|
||||
|
@ -869,14 +856,19 @@ int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsra
|
|||
}
|
||||
|
||||
if (!q->args.enable_measure) {
|
||||
ERROR("SSB is not configured for measure");
|
||||
ERROR("SSB is not configured to measure");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
|
||||
// Select start symbol from SSB candidate index
|
||||
int t_offset = ssb_get_t_offset(q, ssb_idx);
|
||||
if (t_offset < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Demodulate
|
||||
if (ssb_demodulate(q, in, q->t_offset, ssb_grid) < SRSRAN_SUCCESS) {
|
||||
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
|
||||
if (ssb_demodulate(q, in, (uint32_t)t_offset, ssb_grid) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error demodulating");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
@ -889,3 +881,43 @@ int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsra
|
|||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int srsran_ssb_decode_pbch(srsran_ssb_t* q, uint32_t N_id, uint32_t ssb_idx, const cf_t* in, srsran_pbch_msg_nr_t* msg)
|
||||
{
|
||||
// Verify inputs
|
||||
if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || in == NULL || msg == NULL || !isnormal(q->scs_hz)) {
|
||||
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
|
||||
if (!q->args.enable_decode) {
|
||||
ERROR("SSB is not configured to decode");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Select start symbol from SSB candidate index
|
||||
int t_offset = ssb_get_t_offset(q, ssb_idx);
|
||||
if (t_offset < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Demodulate
|
||||
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
|
||||
if (ssb_demodulate(q, in, (uint32_t)t_offset, ssb_grid) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error demodulating");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Prepare configuration
|
||||
srsran_pbch_nr_cfg_t pbch_cfg = {};
|
||||
pbch_cfg.N_id = N_id;
|
||||
pbch_cfg.ssb_scs = q->cfg.scs;
|
||||
pbch_cfg.Lmax = q->Lmax;
|
||||
|
||||
// Decode
|
||||
if (srsran_pbch_nr_decode(&q->pbch, &pbch_cfg, ssb_idx, ssb_grid, msg) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error decoding PBCH");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -124,10 +124,13 @@ add_test(cfo_test_2 cfo_test -f 0.99849 -n 1000)
|
|||
|
||||
|
||||
########################################################################
|
||||
# NE TEST
|
||||
# NR TEST
|
||||
########################################################################
|
||||
|
||||
add_executable(ssb_measure_test ssb_measure_test.c)
|
||||
target_link_libraries(ssb_measure_test srsran_phy)
|
||||
add_nr_test(ssb_measure_test ssb_measure_test)
|
||||
|
||||
add_test(ssb_measure_test ssb_measure_test)
|
||||
add_executable(ssb_decode_test ssb_decode_test.c)
|
||||
target_link_libraries(ssb_decode_test srsran_phy)
|
||||
add_nr_test(ssb_decode_test ssb_decode_test)
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
/**
|
||||
*
|
||||
* \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 <srsran/phy/utils/random.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 = 0;
|
||||
static float cfo_hz = 0.0f;
|
||||
static float n0_dB = -300.0f;
|
||||
|
||||
// Test context
|
||||
static srsran_random_t random_gen = NULL;
|
||||
static srsran_channel_awgn_t awgn = {};
|
||||
static double srate_hz = 0.0f; // Base-band sampling rate
|
||||
static uint32_t hf_len = 0; // Half-frame length
|
||||
static cf_t* buffer = NULL; // Base-band buffer
|
||||
|
||||
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 < hf_len; i++) {
|
||||
buffer[i] = buffer[(i + delay_n) % hf_len];
|
||||
}
|
||||
|
||||
// CFO
|
||||
srsran_vec_apply_cfo(buffer, -cfo_hz / srate_hz, buffer, hf_len);
|
||||
|
||||
// AWGN
|
||||
srsran_channel_awgn_run_c(&awgn, buffer, buffer, hf_len);
|
||||
}
|
||||
|
||||
static void gen_pbch_msg(srsran_pbch_msg_nr_t* pbch_msg, uint32_t ssb_idx)
|
||||
{
|
||||
// Default all to zero
|
||||
SRSRAN_MEM_ZERO(pbch_msg, srsran_pbch_msg_nr_t, 1);
|
||||
|
||||
// Generate payload
|
||||
srsran_random_bit_vector(random_gen, pbch_msg->payload, SRSRAN_PBCH_NR_PAYLOAD_SZ);
|
||||
|
||||
pbch_msg->ssb_idx = ssb_idx;
|
||||
}
|
||||
|
||||
static int test_case_1(srsran_ssb_t* ssb)
|
||||
{
|
||||
// For benchmarking purposes
|
||||
uint64_t t_encode_usec = 0;
|
||||
uint64_t t_decode_usec = 0;
|
||||
|
||||
// SSB configuration
|
||||
srsran_ssb_cfg_t ssb_cfg = {};
|
||||
ssb_cfg.srate_hz = srate_hz;
|
||||
ssb_cfg.center_freq_hz = 3.5e9;
|
||||
ssb_cfg.ssb_freq_hz = 3.5e9 - 960e3;
|
||||
ssb_cfg.scs = ssb_scs;
|
||||
ssb_cfg.pattern = SRSRAN_SSB_PATTERN_C;
|
||||
|
||||
TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS);
|
||||
|
||||
// For each PCI...
|
||||
uint64_t count = 0;
|
||||
for (uint32_t pci = 0; pci < SRSRAN_NOF_NID_NR; pci += 23) {
|
||||
for (uint32_t ssb_idx = 0; ssb_idx < ssb->Lmax; ssb_idx++, count++) {
|
||||
struct timeval t[3] = {};
|
||||
|
||||
// Build PBCH message
|
||||
srsran_pbch_msg_nr_t pbch_msg_tx = {};
|
||||
gen_pbch_msg(&pbch_msg_tx, ssb_idx);
|
||||
|
||||
// Print encoded PBCH message
|
||||
char str[512] = {};
|
||||
srsran_pbch_msg_info(&pbch_msg_tx, str, sizeof(str));
|
||||
INFO("test_case_1 - encoded pci=%d %s", pci, str);
|
||||
|
||||
// Initialise baseband
|
||||
srsran_vec_cf_zero(buffer, hf_len);
|
||||
|
||||
// Add the SSB base-band
|
||||
gettimeofday(&t[1], NULL);
|
||||
TESTASSERT(srsran_ssb_add(ssb, pci, ssb_idx, &pbch_msg_tx, buffer, buffer) == SRSRAN_SUCCESS);
|
||||
gettimeofday(&t[2], NULL);
|
||||
get_time_interval(t);
|
||||
t_encode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
|
||||
|
||||
// Run channel
|
||||
run_channel();
|
||||
|
||||
// Decode
|
||||
gettimeofday(&t[1], NULL);
|
||||
srsran_pbch_msg_nr_t pbch_msg_rx = {};
|
||||
TESTASSERT(srsran_ssb_decode_pbch(ssb, pci, ssb_idx, buffer, &pbch_msg_rx) == SRSRAN_SUCCESS);
|
||||
gettimeofday(&t[2], NULL);
|
||||
get_time_interval(t);
|
||||
t_decode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
|
||||
|
||||
// Print decoded PBCH message
|
||||
srsran_pbch_msg_info(&pbch_msg_rx, str, sizeof(str));
|
||||
INFO("test_case_1 - decoded pci=%d %s crc=%s", pci, str, pbch_msg_rx.crc ? "OK" : "KO");
|
||||
|
||||
// Assert PBCH message CRC
|
||||
TESTASSERT(pbch_msg_rx.crc);
|
||||
}
|
||||
}
|
||||
|
||||
INFO("test_case_1 - %.1f usec/encode; %.1f usec/decode;",
|
||||
(double)t_encode_usec / (double)(count),
|
||||
(double)t_decode_usec / (double)(count));
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int ret = SRSRAN_ERROR;
|
||||
parse_args(argc, argv);
|
||||
|
||||
random_gen = srsran_random_init(1234);
|
||||
srate_hz = (double)SRSRAN_SUBC_SPACING_NR(carrier_scs) * srsran_min_symbol_sz_rb(carrier_nof_prb);
|
||||
hf_len = (uint32_t)ceil(srate_hz * (5.0 / 1000.0));
|
||||
buffer = srsran_vec_cf_malloc(hf_len);
|
||||
|
||||
srsran_ssb_t ssb = {};
|
||||
srsran_ssb_args_t ssb_args = {};
|
||||
ssb_args.enable_encode = true;
|
||||
ssb_args.enable_decode = 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_random_free(random_gen);
|
||||
srsran_ssb_free(&ssb);
|
||||
|
||||
srsran_channel_awgn_free(&awgn);
|
||||
|
||||
if (buffer) {
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -102,7 +102,6 @@ static int test_case_1(srsran_ssb_t* ssb)
|
|||
ssb_cfg.ssb_freq_hz = 3.5e9 - 960e3;
|
||||
ssb_cfg.scs = ssb_scs;
|
||||
ssb_cfg.pattern = SRSRAN_SSB_PATTERN_C;
|
||||
ssb_cfg.position[0] = true; // Rest to false
|
||||
|
||||
TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS);
|
||||
|
||||
|
@ -117,7 +116,7 @@ static int test_case_1(srsran_ssb_t* ssb)
|
|||
|
||||
// Add the SSB base-band
|
||||
gettimeofday(&t[1], NULL);
|
||||
TESTASSERT(srsran_ssb_add(ssb, pci, &pbch_msg, buffer, buffer) == SRSRAN_SUCCESS);
|
||||
TESTASSERT(srsran_ssb_add(ssb, pci, 0, &pbch_msg, buffer, buffer) == SRSRAN_SUCCESS);
|
||||
gettimeofday(&t[2], NULL);
|
||||
get_time_interval(t);
|
||||
t_add_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
|
||||
|
@ -145,7 +144,7 @@ static int test_case_1(srsran_ssb_t* ssb)
|
|||
// Measure
|
||||
gettimeofday(&t[1], NULL);
|
||||
srsran_csi_trs_measurements_t meas = {};
|
||||
TESTASSERT(srsran_ssb_csi_measure(ssb, pci, buffer, &meas) == SRSRAN_SUCCESS);
|
||||
TESTASSERT(srsran_ssb_csi_measure(ssb, pci, 0, buffer, &meas) == SRSRAN_SUCCESS);
|
||||
gettimeofday(&t[2], NULL);
|
||||
get_time_interval(t);
|
||||
t_meas_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
|
||||
|
|
|
@ -97,7 +97,6 @@ bool cc_worker::update_cfg()
|
|||
ssb_cfg.ssb_freq_hz = abs_freq_ssb_freq;
|
||||
ssb_cfg.scs = phy->cfg.ssb.scs;
|
||||
ssb_cfg.pattern = srsran::srsran_band_helper().get_ssb_pattern(band, phy->cfg.ssb.scs);
|
||||
memcpy(ssb_cfg.position, phy->cfg.ssb.position_in_burst.data(), sizeof(bool) * SRSRAN_SSB_NOF_POSITION);
|
||||
ssb_cfg.duplex_mode = srsran::srsran_band_helper().get_duplex_mode(band);
|
||||
ssb_cfg.periodicity_ms = phy->cfg.ssb.periodicity_ms;
|
||||
|
||||
|
@ -334,31 +333,44 @@ bool cc_worker::measure_csi()
|
|||
if (srsran_ssb_send(&ssb, dl_slot_cfg.idx)) {
|
||||
srsran_csi_trs_measurements_t meas = {};
|
||||
|
||||
if (srsran_ssb_csi_measure(&ssb, phy->cfg.carrier.pci, rx_buffer[0], &meas) < SRSRAN_SUCCESS) {
|
||||
logger.error("Error measuring SSB");
|
||||
return false;
|
||||
// Iterate all possible candidates
|
||||
const std::array<bool, SRSRAN_SSB_NOF_CANDIDATES> position_in_burst = phy->cfg.ssb.position_in_burst;
|
||||
for (uint32_t ssb_idx = 0; ssb_idx < SRSRAN_SSB_NOF_CANDIDATES; ssb_idx++) {
|
||||
// Skip SSB candidate if not enabled
|
||||
if (not position_in_burst[ssb_idx]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Measure SSB candidate
|
||||
if (srsran_ssb_csi_measure(&ssb, phy->cfg.carrier.pci, ssb_idx, rx_buffer[0], &meas) < SRSRAN_SUCCESS) {
|
||||
logger.error("Error measuring SSB");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (logger.debug.enabled()) {
|
||||
std::array<char, 512> str = {};
|
||||
srsran_csi_meas_info(&meas, str.data(), (uint32_t)str.size());
|
||||
logger.debug("SSB-CSI: %s", str.data());
|
||||
}
|
||||
|
||||
// Compute channel metrics and push it
|
||||
ch_metrics_t ch_metrics = {};
|
||||
ch_metrics.sinr = meas.snr_dB;
|
||||
ch_metrics.rsrp = meas.rsrp_dB;
|
||||
ch_metrics.rsrq = 0.0f; // Not supported
|
||||
ch_metrics.rssi = 0.0f; // Not supported
|
||||
ch_metrics.sync_err =
|
||||
meas.delay_us / (float)(ue_dl.fft->fft_plan.size * SRSRAN_SUBC_SPACING_NR(phy->cfg.carrier.scs));
|
||||
phy->set_channel_metrics(ch_metrics);
|
||||
|
||||
// Compute synch metrics and report it to the PHY state
|
||||
sync_metrics_t sync_metrics = {};
|
||||
sync_metrics.cfo = meas.cfo_hz;
|
||||
phy->set_sync_metrics(sync_metrics);
|
||||
|
||||
// Report SSB candidate channel measurement to the PHY state
|
||||
// ...
|
||||
}
|
||||
|
||||
if (logger.debug.enabled()) {
|
||||
std::array<char, 512> str = {};
|
||||
srsran_csi_meas_info(&meas, str.data(), (uint32_t)str.size());
|
||||
logger.debug("SSB-CSI: %s", str.data());
|
||||
}
|
||||
|
||||
// Compute channel metrics and push it
|
||||
ch_metrics_t ch_metrics = {};
|
||||
ch_metrics.sinr = meas.snr_dB;
|
||||
ch_metrics.rsrp = meas.rsrp_dB;
|
||||
ch_metrics.rsrq = 0.0f; // Not supported
|
||||
ch_metrics.rssi = 0.0f; // Not supported
|
||||
ch_metrics.sync_err =
|
||||
meas.delay_us / (float)(ue_dl.fft->fft_plan.size * SRSRAN_SUBC_SPACING_NR(phy->cfg.carrier.scs));
|
||||
phy->set_channel_metrics(ch_metrics);
|
||||
|
||||
// Compute synch metrics and report it to the PHY state
|
||||
sync_metrics_t sync_metrics = {};
|
||||
sync_metrics.cfo = meas.cfo_hz;
|
||||
phy->set_sync_metrics(sync_metrics);
|
||||
}
|
||||
|
||||
// Iterate all NZP-CSI-RS marked as TRS and perform channel measurements
|
||||
|
|
|
@ -81,7 +81,6 @@ public:
|
|||
ssb_cfg.ssb_freq_hz = args.ssb_freq_hz;
|
||||
ssb_cfg.scs = args.ssb_scs;
|
||||
ssb_cfg.pattern = args.get_ssb_pattern();
|
||||
ssb_cfg.position[0] = true;
|
||||
ssb_cfg.duplex_mode = args.get_duplex_mode();
|
||||
ssb_cfg.periodicity_ms = args.ssb_period_ms;
|
||||
if (srsran_ssb_set_cfg(&ssb, &ssb_cfg) < SRSRAN_SUCCESS) {
|
||||
|
@ -106,7 +105,7 @@ public:
|
|||
srsran_pbch_msg_nr_t msg = {};
|
||||
|
||||
// Add SSB
|
||||
if (srsran_ssb_add(&ssb, pci, &msg, buffer.data(), buffer.data()) < SRSRAN_SUCCESS) {
|
||||
if (srsran_ssb_add(&ssb, pci, 0, &msg, buffer.data(), buffer.data()) < SRSRAN_SUCCESS) {
|
||||
logger.error("Error adding SSB");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue