mirror of https://github.com/PentHertz/srsLTE.git
Implemented PBCH blind decode
This commit is contained in:
parent
a31f3d42ce
commit
5181a9d64c
|
@ -21,7 +21,7 @@
|
||||||
typedef struct SRSRAN_API {
|
typedef struct SRSRAN_API {
|
||||||
uint32_t N_id; ///< Physical cell identifier
|
uint32_t N_id; ///< Physical cell identifier
|
||||||
uint32_t n_hf; ///< Number of half radio frame, 0 or 1
|
uint32_t n_hf; ///< Number of half radio frame, 0 or 1
|
||||||
uint32_t ssb_idx; ///< SSB candidate index
|
uint32_t ssb_idx; ///< SSB candidate index, up to 3 LSB are significant
|
||||||
uint32_t L_max; ///< Number of SSB opportunities in half radio frame
|
uint32_t L_max; ///< Number of SSB opportunities in half radio frame
|
||||||
float beta; ///< Power allocation specified in TS 38.213
|
float beta; ///< Power allocation specified in TS 38.213
|
||||||
srsran_subcarrier_spacing_t scs; ///< SSB configured subcarrier spacing
|
srsran_subcarrier_spacing_t scs; ///< SSB configured subcarrier spacing
|
||||||
|
@ -46,17 +46,26 @@ typedef struct SRSRAN_API {
|
||||||
*/
|
*/
|
||||||
SRSRAN_API int srsran_dmrs_pbch_put(const srsran_dmrs_pbch_cfg_t* cfg, cf_t ssb_grid[SRSRAN_SSB_NOF_RE]);
|
SRSRAN_API int srsran_dmrs_pbch_put(const srsran_dmrs_pbch_cfg_t* cfg, cf_t ssb_grid[SRSRAN_SSB_NOF_RE]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Measures NR PBCH DMRS
|
||||||
|
* @param cfg PBCH DMRS configuration
|
||||||
|
* @param ssb_grid SSB resource grid
|
||||||
|
* @param[out] meas Measurement
|
||||||
|
* @return SRSRAN_SUCCESS if the inputs and configuration are valid, SRSRAN_ERROR code otherwise
|
||||||
|
*/
|
||||||
|
SRSRAN_API int srsran_dmrs_pbch_measure(const srsran_dmrs_pbch_cfg_t* cfg,
|
||||||
|
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
|
||||||
|
srsran_dmrs_pbch_meas_t* meas);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Estimates NR PBCH DMRS
|
* @brief Estimates NR PBCH DMRS
|
||||||
* @param cfg PBCH DMRS configuration
|
* @param cfg PBCH DMRS configuration
|
||||||
* @param ssb_grid Demodulated SSB resource grid
|
* @param ssb_grid Demodulated SSB resource grid
|
||||||
* @param[out] ce Estimated channel
|
* @param[out] ce Estimated channel
|
||||||
* @param[out] meas Estimated channel measurements
|
|
||||||
* @return SRSRAN_SUCCESS if the inputs and configuration are valid, SRSRAN_ERROR code otherwise
|
* @return SRSRAN_SUCCESS if the inputs and configuration are valid, SRSRAN_ERROR code otherwise
|
||||||
*/
|
*/
|
||||||
SRSRAN_API int srsran_dmrs_pbch_estimate(const srsran_dmrs_pbch_cfg_t* cfg,
|
SRSRAN_API int srsran_dmrs_pbch_estimate(const srsran_dmrs_pbch_cfg_t* cfg,
|
||||||
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
|
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
|
||||||
cf_t ce[SRSRAN_SSB_NOF_RE],
|
cf_t ce[SRSRAN_SSB_NOF_RE]);
|
||||||
srsran_dmrs_pbch_meas_t* meas);
|
|
||||||
|
|
||||||
#endif // SRSRAN_DMRS_PBCH_H
|
#endif // SRSRAN_DMRS_PBCH_H
|
||||||
|
|
|
@ -39,11 +39,11 @@ typedef struct SRSRAN_API {
|
||||||
* @brief Describes the NR PBCH configuration
|
* @brief Describes the NR PBCH configuration
|
||||||
*/
|
*/
|
||||||
typedef struct SRSRAN_API {
|
typedef struct SRSRAN_API {
|
||||||
uint32_t N_id; ///< Physical cell identifier
|
uint32_t N_id; ///< Physical cell identifier
|
||||||
srsran_subcarrier_spacing_t ssb_scs; ///< SSB Subcarrier spacing
|
uint32_t n_hf; ///< Number of half radio frame, 0 or 1
|
||||||
uint32_t Lmax; ///< Number of SSB opportunities, described in TS 38.213 4.1 ...
|
uint32_t ssb_idx; ///< SSB candidate index, up to 4 LSB significant
|
||||||
float beta; ///< Scaling factor for PBCH symbols, set to zero for default
|
uint32_t Lmax; ///< Number of SSB opportunities, described in TS 38.213 4.1 ...
|
||||||
float beta_dmrs; ///< Scaling factor for PBCH DM-RS, set to zero for default
|
float beta; ///< Scaling factor for PBCH symbols, set to zero for default
|
||||||
} srsran_pbch_nr_cfg_t;
|
} srsran_pbch_nr_cfg_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -102,15 +102,12 @@ SRSRAN_API int srsran_pbch_nr_encode(srsran_pbch_nr_t* q,
|
||||||
* @brief Decodes an NR PBCH message in the SSB resource grid
|
* @brief Decodes an NR PBCH message in the SSB resource grid
|
||||||
* @param q NR PBCH object
|
* @param q NR PBCH object
|
||||||
* @param cfg NR PBCH configuration
|
* @param cfg NR PBCH configuration
|
||||||
* @param ssb_idx SSB candidate index
|
|
||||||
* @param[in] ssb_grid SSB resource grid
|
|
||||||
* @param[in] ce Channel estimates for the SSB resource grid
|
* @param[in] ce Channel estimates for the SSB resource grid
|
||||||
* @param msg NR PBCH message received
|
* @param msg NR PBCH message received
|
||||||
* @return SRSRAN_SUCCESS if decoding is successful, SRSRAN_ERROR code otherwise
|
* @return SRSRAN_SUCCESS if decoding is successful, SRSRAN_ERROR code otherwise
|
||||||
*/
|
*/
|
||||||
SRSRAN_API int srsran_pbch_nr_decode(srsran_pbch_nr_t* q,
|
SRSRAN_API int srsran_pbch_nr_decode(srsran_pbch_nr_t* q,
|
||||||
const srsran_pbch_nr_cfg_t* cfg,
|
const srsran_pbch_nr_cfg_t* cfg,
|
||||||
uint32_t ssb_idx,
|
|
||||||
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
|
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
|
||||||
const cf_t ce[SRSRAN_SSB_NOF_RE],
|
const cf_t ce[SRSRAN_SSB_NOF_RE],
|
||||||
srsran_pbch_msg_nr_t* msg);
|
srsran_pbch_msg_nr_t* msg);
|
||||||
|
|
|
@ -105,6 +105,16 @@ typedef struct SRSRAN_API {
|
||||||
cf_t* pss_seq[SRSRAN_NOF_NID_2_NR]; ///< Possible frequency domain PSS for find
|
cf_t* pss_seq[SRSRAN_NOF_NID_2_NR]; ///< Possible frequency domain PSS for find
|
||||||
} srsran_ssb_t;
|
} srsran_ssb_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Describes an SSB search result
|
||||||
|
* @note if pbch.crc is true, SSB transmission is found and decoded. Otherwise, no SSB transmission has been decoded
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint32_t N_id; ///< Most suitable physical cell identifier
|
||||||
|
uint32_t t_offset; ///< Time offset in the input samples
|
||||||
|
srsran_pbch_msg_nr_t pbch_msg; ///< Physical broadcast channel message of the most suitable SSB candidate
|
||||||
|
} srsran_ssb_search_res_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialises configures NR SSB with the given arguments
|
* @brief Initialises configures NR SSB with the given arguments
|
||||||
* @param q SSB object
|
* @param q SSB object
|
||||||
|
@ -131,18 +141,28 @@ SRSRAN_API int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg);
|
||||||
* @note It currently expects an input buffer of half radio frame
|
* @note It currently expects an input buffer of half radio frame
|
||||||
* @param q SSB object
|
* @param q SSB object
|
||||||
* @param N_id Physical Cell Identifier
|
* @param N_id Physical Cell Identifier
|
||||||
* @param ssb_idx SSB candidate index
|
|
||||||
* @param n_hf Number of hald radio frame, 0 or 1
|
* @param n_hf Number of hald radio frame, 0 or 1
|
||||||
|
* @param ssb_idx SSB candidate index
|
||||||
* @param in Input baseband buffer
|
* @param in Input baseband buffer
|
||||||
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
|
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
|
||||||
*/
|
*/
|
||||||
SRSRAN_API int srsran_ssb_decode_pbch(srsran_ssb_t* q,
|
SRSRAN_API int srsran_ssb_decode_pbch(srsran_ssb_t* q,
|
||||||
uint32_t N_id,
|
uint32_t N_id,
|
||||||
uint32_t ssb_idx,
|
|
||||||
uint32_t n_hf,
|
uint32_t n_hf,
|
||||||
|
uint32_t ssb_idx,
|
||||||
const cf_t* in,
|
const cf_t* in,
|
||||||
srsran_pbch_msg_nr_t* msg);
|
srsran_pbch_msg_nr_t* msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Searches for an SSB transmission and decodes the PBCH message
|
||||||
|
* @param q SSB object
|
||||||
|
* @param in Input baseband buffer
|
||||||
|
* @param nof_samples Number of samples available in the buffer
|
||||||
|
* @param res SSB Search result
|
||||||
|
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
|
||||||
|
*/
|
||||||
|
SRSRAN_API int srsran_ssb_search(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, srsran_ssb_search_res_t* res);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Decides if the SSB object is configured and a given subframe is configured for SSB transmission
|
* @brief Decides if the SSB object is configured and a given subframe is configured for SSB transmission
|
||||||
* @param q SSB object
|
* @param q SSB object
|
||||||
|
@ -155,16 +175,11 @@ SRSRAN_API bool srsran_ssb_send(srsran_ssb_t* q, uint32_t sf_idx);
|
||||||
* @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 N_id Physical Cell Identifier
|
||||||
* @param ssb_idx SSB candidate index
|
|
||||||
* @param msg NR PBCH message to transmit
|
* @param msg NR PBCH message to transmit
|
||||||
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
|
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
|
||||||
*/
|
*/
|
||||||
SRSRAN_API int srsran_ssb_add(srsran_ssb_t* q,
|
SRSRAN_API int
|
||||||
uint32_t N_id,
|
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);
|
||||||
uint32_t ssb_idx,
|
|
||||||
const srsran_pbch_msg_nr_t* msg,
|
|
||||||
const cf_t* in,
|
|
||||||
cf_t* out);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Perform cell search and measurement
|
* @brief Perform cell search and measurement
|
||||||
|
|
|
@ -137,16 +137,11 @@ int dmrs_pbch_extract_lse(const srsran_dmrs_pbch_cfg_t* cfg,
|
||||||
return SRSRAN_SUCCESS;
|
return SRSRAN_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int srsran_dmrs_pbch_estimate(const srsran_dmrs_pbch_cfg_t* cfg,
|
static int dmrs_pbch_meas_estimate(const srsran_dmrs_pbch_cfg_t* cfg,
|
||||||
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
|
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
|
||||||
cf_t ce[SRSRAN_SSB_NOF_RE],
|
cf_t ce[SRSRAN_SSB_NOF_RE],
|
||||||
srsran_dmrs_pbch_meas_t* meas)
|
srsran_dmrs_pbch_meas_t* meas)
|
||||||
{
|
{
|
||||||
// Validate inputs
|
|
||||||
if (cfg == NULL || ssb_grid == NULL || ce == NULL || meas == NULL) {
|
|
||||||
return SRSRAN_ERROR_INVALID_INPUTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract least square estimates
|
// Extract least square estimates
|
||||||
cf_t lse[DMRS_PBCH_NOF_RE];
|
cf_t lse[DMRS_PBCH_NOF_RE];
|
||||||
if (dmrs_pbch_extract_lse(cfg, ssb_grid, lse) < SRSRAN_SUCCESS) {
|
if (dmrs_pbch_extract_lse(cfg, ssb_grid, lse) < SRSRAN_SUCCESS) {
|
||||||
|
@ -201,18 +196,47 @@ int srsran_dmrs_pbch_estimate(const srsran_dmrs_pbch_cfg_t* cfg,
|
||||||
float epre = srsran_vec_avg_power_cf(lse, DMRS_PBCH_NOF_RE);
|
float epre = srsran_vec_avg_power_cf(lse, DMRS_PBCH_NOF_RE);
|
||||||
|
|
||||||
// Write measurements
|
// Write measurements
|
||||||
meas->corr = rsrp / epre;
|
if (meas != NULL) {
|
||||||
meas->epre = epre;
|
meas->corr = rsrp / epre;
|
||||||
meas->rsrp = rsrp;
|
meas->epre = epre;
|
||||||
meas->cfo_hz = cfo_hz;
|
meas->rsrp = rsrp;
|
||||||
meas->avg_delay_us = avg_delay_us;
|
meas->cfo_hz = cfo_hz;
|
||||||
|
meas->avg_delay_us = avg_delay_us;
|
||||||
|
}
|
||||||
|
|
||||||
// Compute channel estimates
|
// Generate estimated grid
|
||||||
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
|
if (ce != NULL) {
|
||||||
float t_s = srsran_symbol_offset_s(l, cfg->scs);
|
// Compute channel estimates
|
||||||
cf_t symbol_wideband_gain = cexpf(-I * 2.0f * M_PI * cfo_hz * t_s) * wideband_gain;
|
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
|
||||||
srsran_vec_gen_sine(symbol_wideband_gain, -avg_delay_norm, &ce[l * SRSRAN_SSB_BW_SUBC], SRSRAN_SSB_BW_SUBC);
|
float t_s = srsran_symbol_offset_s(l, cfg->scs);
|
||||||
|
cf_t symbol_wideband_gain = cexpf(-I * 2.0f * M_PI * cfo_hz * t_s) * wideband_gain;
|
||||||
|
srsran_vec_gen_sine(symbol_wideband_gain, -avg_delay_norm, &ce[l * SRSRAN_SSB_BW_SUBC], SRSRAN_SSB_BW_SUBC);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return SRSRAN_SUCCESS;
|
return SRSRAN_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int srsran_dmrs_pbch_measure(const srsran_dmrs_pbch_cfg_t* cfg,
|
||||||
|
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
|
||||||
|
srsran_dmrs_pbch_meas_t* meas)
|
||||||
|
{
|
||||||
|
// Validate inputs
|
||||||
|
if (cfg == NULL || ssb_grid == NULL || meas == NULL) {
|
||||||
|
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dmrs_pbch_meas_estimate(cfg, ssb_grid, NULL, meas);
|
||||||
|
}
|
||||||
|
|
||||||
|
int srsran_dmrs_pbch_estimate(const srsran_dmrs_pbch_cfg_t* cfg,
|
||||||
|
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
|
||||||
|
cf_t ce[SRSRAN_SSB_NOF_RE])
|
||||||
|
{
|
||||||
|
// Validate inputs
|
||||||
|
if (cfg == NULL || ssb_grid == NULL || ce == NULL) {
|
||||||
|
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dmrs_pbch_meas_estimate(cfg, ssb_grid, ce, NULL);
|
||||||
|
}
|
|
@ -245,6 +245,7 @@ pbch_nr_pbch_msg_unpack(const srsran_pbch_nr_cfg_t* cfg, const uint8_t a[PBCH_NR
|
||||||
msg->hrf = (a[G[10]] == 1);
|
msg->hrf = (a[G[10]] == 1);
|
||||||
|
|
||||||
// Put SSB related in a_hat[A_hat + 5] to a_hat[A_hat + 7]
|
// Put SSB related in a_hat[A_hat + 5] to a_hat[A_hat + 7]
|
||||||
|
msg->ssb_idx = cfg->ssb_idx; // Load 4 LSB
|
||||||
if (cfg->Lmax == 64) {
|
if (cfg->Lmax == 64) {
|
||||||
msg->ssb_idx = msg->ssb_idx & 0b111;
|
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[11]] << 5U); // 6th bit of SSB index
|
||||||
|
@ -427,9 +428,9 @@ static void pbch_nr_scramble_rx(const srsran_pbch_nr_cfg_t* cfg,
|
||||||
uint32_t M_bit = PBCH_NR_E;
|
uint32_t M_bit = PBCH_NR_E;
|
||||||
|
|
||||||
// Select value v
|
// Select value v
|
||||||
uint32_t v = (ssb_idx & 0x7U);
|
uint32_t v = (ssb_idx & 0b111U);
|
||||||
if (cfg->Lmax == 4) {
|
if (cfg->Lmax == 4) {
|
||||||
v = ssb_idx & 0x3U;
|
v = ssb_idx & 0b11U;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advance sequence
|
// Advance sequence
|
||||||
|
@ -594,7 +595,6 @@ int srsran_pbch_nr_encode(srsran_pbch_nr_t* q,
|
||||||
|
|
||||||
int srsran_pbch_nr_decode(srsran_pbch_nr_t* q,
|
int srsran_pbch_nr_decode(srsran_pbch_nr_t* q,
|
||||||
const srsran_pbch_nr_cfg_t* cfg,
|
const srsran_pbch_nr_cfg_t* cfg,
|
||||||
uint32_t ssb_idx,
|
|
||||||
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
|
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
|
||||||
const cf_t ce_grid[SRSRAN_SSB_NOF_RE],
|
const cf_t ce_grid[SRSRAN_SSB_NOF_RE],
|
||||||
srsran_pbch_msg_nr_t* msg)
|
srsran_pbch_msg_nr_t* msg)
|
||||||
|
@ -623,7 +623,7 @@ int srsran_pbch_nr_decode(srsran_pbch_nr_t* q,
|
||||||
|
|
||||||
// TS 38.211 7.3.3 Physical broadcast channel
|
// TS 38.211 7.3.3 Physical broadcast channel
|
||||||
// 7.3.3.1 Scrambling
|
// 7.3.3.1 Scrambling
|
||||||
pbch_nr_scramble_rx(cfg, ssb_idx, llr, llr);
|
pbch_nr_scramble_rx(cfg, cfg->ssb_idx, llr, llr);
|
||||||
|
|
||||||
// 7.1.5 Rate matching
|
// 7.1.5 Rate matching
|
||||||
int8_t d[PBCH_NR_N];
|
int8_t d[PBCH_NR_N];
|
||||||
|
|
|
@ -510,12 +510,7 @@ bool srsran_ssb_send(srsran_ssb_t* q, uint32_t sf_idx)
|
||||||
return (sf_idx % q->cfg.periodicity_ms == 0);
|
return (sf_idx % q->cfg.periodicity_ms == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int srsran_ssb_add(srsran_ssb_t* q,
|
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)
|
||||||
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
|
// Verify input parameters
|
||||||
if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || msg == NULL || in == NULL || out == NULL) {
|
if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || msg == NULL || in == NULL || out == NULL) {
|
||||||
|
@ -558,7 +553,6 @@ int srsran_ssb_add(srsran_ssb_t* q,
|
||||||
// Put PBCH payload
|
// Put PBCH payload
|
||||||
srsran_pbch_nr_cfg_t pbch_cfg = {};
|
srsran_pbch_nr_cfg_t pbch_cfg = {};
|
||||||
pbch_cfg.N_id = N_id;
|
pbch_cfg.N_id = N_id;
|
||||||
pbch_cfg.ssb_scs = q->cfg.scs;
|
|
||||||
pbch_cfg.Lmax = q->Lmax;
|
pbch_cfg.Lmax = q->Lmax;
|
||||||
if (srsran_pbch_nr_encode(&q->pbch, &pbch_cfg, msg, ssb_grid) < SRSRAN_SUCCESS) {
|
if (srsran_pbch_nr_encode(&q->pbch, &pbch_cfg, msg, ssb_grid) < SRSRAN_SUCCESS) {
|
||||||
ERROR("Error encoding PBCH");
|
ERROR("Error encoding PBCH");
|
||||||
|
@ -566,7 +560,7 @@ int srsran_ssb_add(srsran_ssb_t* q,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select start symbol from SSB candidate index
|
// Select start symbol from SSB candidate index
|
||||||
int t_offset = ssb_get_t_offset(q, ssb_idx);
|
int t_offset = ssb_get_t_offset(q, msg->ssb_idx);
|
||||||
if (t_offset < SRSRAN_SUCCESS) {
|
if (t_offset < SRSRAN_SUCCESS) {
|
||||||
ERROR("Invalid SSB candidate index");
|
ERROR("Invalid SSB candidate index");
|
||||||
return SRSRAN_ERROR;
|
return SRSRAN_ERROR;
|
||||||
|
@ -905,10 +899,100 @@ int srsran_ssb_csi_measure(srsran_ssb_t* q,
|
||||||
return SRSRAN_SUCCESS;
|
return SRSRAN_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ssb_select_pbch(srsran_ssb_t* q,
|
||||||
|
uint32_t N_id,
|
||||||
|
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
|
||||||
|
uint32_t* found_n_hf,
|
||||||
|
uint32_t* found_ssb_idx_4lsb)
|
||||||
|
{
|
||||||
|
// Prepare PBCH DMRS configuration
|
||||||
|
srsran_dmrs_pbch_cfg_t pbch_dmrs_cfg = {};
|
||||||
|
pbch_dmrs_cfg.N_id = N_id;
|
||||||
|
pbch_dmrs_cfg.n_hf = 0; // Parameter to guess
|
||||||
|
pbch_dmrs_cfg.ssb_idx = 0; // Parameter to guess
|
||||||
|
pbch_dmrs_cfg.L_max = q->Lmax;
|
||||||
|
pbch_dmrs_cfg.beta = 0.0f;
|
||||||
|
pbch_dmrs_cfg.scs = q->cfg.scs;
|
||||||
|
|
||||||
|
// Initialise best values
|
||||||
|
srsran_dmrs_pbch_meas_t best_meas = {};
|
||||||
|
uint32_t best_n_hf = 0;
|
||||||
|
uint32_t best_ssb_idx = 0;
|
||||||
|
|
||||||
|
// Iterate over all the parameters to guess and select the most suitable
|
||||||
|
for (uint32_t n_hf = 0; n_hf < 2; n_hf++) {
|
||||||
|
for (uint32_t ssb_idx = 0; ssb_idx < SRSRAN_MIN(8, q->Lmax); ssb_idx++) {
|
||||||
|
// Set parameters
|
||||||
|
pbch_dmrs_cfg.n_hf = n_hf;
|
||||||
|
pbch_dmrs_cfg.ssb_idx = ssb_idx;
|
||||||
|
|
||||||
|
// Measure
|
||||||
|
srsran_dmrs_pbch_meas_t meas = {};
|
||||||
|
if (srsran_dmrs_pbch_measure(&pbch_dmrs_cfg, ssb_grid, &meas) < SRSRAN_SUCCESS) {
|
||||||
|
ERROR("Error measure for n_hf=%d ssb_idx=%d", n_hf, ssb_idx);
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select the result with highest correlation (most suitable)
|
||||||
|
if (meas.corr > best_meas.corr) {
|
||||||
|
best_meas = meas;
|
||||||
|
best_n_hf = n_hf;
|
||||||
|
best_ssb_idx = ssb_idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save findings
|
||||||
|
*found_n_hf = best_n_hf;
|
||||||
|
*found_ssb_idx_4lsb = best_ssb_idx;
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ssb_decode_pbch(srsran_ssb_t* q,
|
||||||
|
uint32_t N_id,
|
||||||
|
uint32_t n_hf,
|
||||||
|
uint32_t ssb_idx,
|
||||||
|
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
|
||||||
|
srsran_pbch_msg_nr_t* msg)
|
||||||
|
{
|
||||||
|
// Prepare PBCH DMRS configuration
|
||||||
|
srsran_dmrs_pbch_cfg_t pbch_dmrs_cfg = {};
|
||||||
|
pbch_dmrs_cfg.N_id = N_id;
|
||||||
|
pbch_dmrs_cfg.n_hf = n_hf;
|
||||||
|
pbch_dmrs_cfg.ssb_idx = ssb_idx;
|
||||||
|
pbch_dmrs_cfg.L_max = q->Lmax;
|
||||||
|
pbch_dmrs_cfg.beta = 0.0f;
|
||||||
|
pbch_dmrs_cfg.scs = q->cfg.scs;
|
||||||
|
|
||||||
|
// Compute PBCH channel estimates
|
||||||
|
cf_t ce[SRSRAN_SSB_NOF_RE] = {};
|
||||||
|
if (srsran_dmrs_pbch_estimate(&pbch_dmrs_cfg, ssb_grid, ce) < SRSRAN_SUCCESS) {
|
||||||
|
ERROR("Error estimating channel");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare PBCH configuration
|
||||||
|
srsran_pbch_nr_cfg_t pbch_cfg = {};
|
||||||
|
pbch_cfg.N_id = N_id;
|
||||||
|
pbch_cfg.n_hf = n_hf;
|
||||||
|
pbch_cfg.ssb_idx = ssb_idx;
|
||||||
|
pbch_cfg.Lmax = q->Lmax;
|
||||||
|
pbch_cfg.beta = 0.0f;
|
||||||
|
|
||||||
|
// Decode
|
||||||
|
if (srsran_pbch_nr_decode(&q->pbch, &pbch_cfg, ssb_grid, ce, msg) < SRSRAN_SUCCESS) {
|
||||||
|
ERROR("Error decoding PBCH");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
int srsran_ssb_decode_pbch(srsran_ssb_t* q,
|
int srsran_ssb_decode_pbch(srsran_ssb_t* q,
|
||||||
uint32_t N_id,
|
uint32_t N_id,
|
||||||
uint32_t ssb_idx,
|
|
||||||
uint32_t n_hf,
|
uint32_t n_hf,
|
||||||
|
uint32_t ssb_idx,
|
||||||
const cf_t* in,
|
const cf_t* in,
|
||||||
srsran_pbch_msg_nr_t* msg)
|
srsran_pbch_msg_nr_t* msg)
|
||||||
{
|
{
|
||||||
|
@ -935,40 +1019,81 @@ int srsran_ssb_decode_pbch(srsran_ssb_t* q,
|
||||||
return SRSRAN_ERROR;
|
return SRSRAN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare PBCH DMRS configuration
|
// Decode PBCH
|
||||||
srsran_dmrs_pbch_cfg_t pbch_dmrs_cfg = {};
|
if (ssb_decode_pbch(q, N_id, n_hf, ssb_idx, ssb_grid, msg) < SRSRAN_SUCCESS) {
|
||||||
pbch_dmrs_cfg.N_id = N_id;
|
ERROR("Error decoding");
|
||||||
pbch_dmrs_cfg.n_hf = n_hf;
|
|
||||||
pbch_dmrs_cfg.ssb_idx = ssb_idx;
|
|
||||||
pbch_dmrs_cfg.L_max = q->Lmax;
|
|
||||||
pbch_dmrs_cfg.beta = 0.0f;
|
|
||||||
pbch_dmrs_cfg.scs = q->cfg.scs;
|
|
||||||
|
|
||||||
// Compute PBCH channel estimates
|
|
||||||
srsran_dmrs_pbch_meas_t meas = {};
|
|
||||||
cf_t ce[SRSRAN_SSB_NOF_RE] = {};
|
|
||||||
if (srsran_dmrs_pbch_estimate(&pbch_dmrs_cfg, ssb_grid, ce, &meas) < SRSRAN_SUCCESS) {
|
|
||||||
ERROR("Error estimating channel");
|
|
||||||
return SRSRAN_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare measurement with threshold
|
|
||||||
if (meas.corr < q->args.pbch_dmrs_thr) {
|
|
||||||
msg->crc = false;
|
|
||||||
return SRSRAN_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare PBCH 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, ce, msg) < SRSRAN_SUCCESS) {
|
|
||||||
ERROR("Error decoding PBCH");
|
|
||||||
return SRSRAN_ERROR;
|
return SRSRAN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return SRSRAN_SUCCESS;
|
return SRSRAN_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int srsran_ssb_search(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, srsran_ssb_search_res_t* res)
|
||||||
|
{
|
||||||
|
// Verify inputs
|
||||||
|
if (q == NULL || in == NULL || res == NULL || !isnormal(q->scs_hz)) {
|
||||||
|
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!q->args.enable_search || !q->args.enable_decode) {
|
||||||
|
ERROR("SSB is not configured to search (%c) and decode (%c)",
|
||||||
|
q->args.enable_search ? 'y' : 'n',
|
||||||
|
q->args.enable_decode ? 'y' : 'n');
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for PSS in time domain
|
||||||
|
uint32_t N_id_2 = 0;
|
||||||
|
uint32_t t_offset = 0;
|
||||||
|
if (ssb_pss_search(q, in, nof_samples, &N_id_2, &t_offset) < SRSRAN_SUCCESS) {
|
||||||
|
ERROR("Error searching for N_id_2");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove CP offset prior demodulation
|
||||||
|
if (t_offset >= q->cp_sz) {
|
||||||
|
t_offset -= q->cp_sz;
|
||||||
|
} else {
|
||||||
|
t_offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Demodulate
|
||||||
|
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
|
||||||
|
if (ssb_demodulate(q, in, t_offset, ssb_grid) < SRSRAN_SUCCESS) {
|
||||||
|
ERROR("Error demodulating");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find best N_id_1
|
||||||
|
uint32_t N_id_1 = 0;
|
||||||
|
float sss_corr = 0.0f;
|
||||||
|
if (srsran_sss_nr_find(ssb_grid, N_id_2, &sss_corr, &N_id_1) < SRSRAN_SUCCESS) {
|
||||||
|
ERROR("Error searching for N_id_2");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select N_id
|
||||||
|
uint32_t N_id = SRSRAN_NID_NR(N_id_1, N_id_2);
|
||||||
|
|
||||||
|
// Select the most suitable SSB candidate
|
||||||
|
uint32_t n_hf = 0;
|
||||||
|
uint32_t ssb_idx = 0;
|
||||||
|
if (ssb_select_pbch(q, N_id, ssb_grid, &n_hf, &ssb_idx) < SRSRAN_SUCCESS) {
|
||||||
|
ERROR("Error selecting PBCH");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute PBCH channel estimates
|
||||||
|
srsran_pbch_msg_nr_t pbch_msg = {};
|
||||||
|
if (ssb_decode_pbch(q, N_id, n_hf, ssb_idx, ssb_grid, &pbch_msg) < SRSRAN_SUCCESS) {
|
||||||
|
ERROR("Error decoding PBCH");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save result
|
||||||
|
res->N_id = N_id;
|
||||||
|
res->t_offset = t_offset;
|
||||||
|
res->pbch_msg = pbch_msg;
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
|
@ -85,6 +85,7 @@ static void gen_pbch_msg(srsran_pbch_msg_nr_t* pbch_msg, uint32_t ssb_idx)
|
||||||
srsran_random_bit_vector(random_gen, pbch_msg->payload, SRSRAN_PBCH_NR_PAYLOAD_SZ);
|
srsran_random_bit_vector(random_gen, pbch_msg->payload, SRSRAN_PBCH_NR_PAYLOAD_SZ);
|
||||||
|
|
||||||
pbch_msg->ssb_idx = ssb_idx;
|
pbch_msg->ssb_idx = ssb_idx;
|
||||||
|
pbch_msg->crc = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int test_case_1(srsran_ssb_t* ssb)
|
static int test_case_1(srsran_ssb_t* ssb)
|
||||||
|
@ -92,6 +93,7 @@ static int test_case_1(srsran_ssb_t* ssb)
|
||||||
// For benchmarking purposes
|
// For benchmarking purposes
|
||||||
uint64_t t_encode_usec = 0;
|
uint64_t t_encode_usec = 0;
|
||||||
uint64_t t_decode_usec = 0;
|
uint64_t t_decode_usec = 0;
|
||||||
|
uint64_t t_search_usec = 0;
|
||||||
|
|
||||||
// SSB configuration
|
// SSB configuration
|
||||||
srsran_ssb_cfg_t ssb_cfg = {};
|
srsran_ssb_cfg_t ssb_cfg = {};
|
||||||
|
@ -123,7 +125,7 @@ static int test_case_1(srsran_ssb_t* ssb)
|
||||||
|
|
||||||
// Add the SSB base-band
|
// Add the SSB base-band
|
||||||
gettimeofday(&t[1], NULL);
|
gettimeofday(&t[1], NULL);
|
||||||
TESTASSERT(srsran_ssb_add(ssb, pci, ssb_idx, &pbch_msg_tx, buffer, buffer) == SRSRAN_SUCCESS);
|
TESTASSERT(srsran_ssb_add(ssb, pci, &pbch_msg_tx, buffer, buffer) == SRSRAN_SUCCESS);
|
||||||
gettimeofday(&t[2], NULL);
|
gettimeofday(&t[2], NULL);
|
||||||
get_time_interval(t);
|
get_time_interval(t);
|
||||||
t_encode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
|
t_encode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
|
||||||
|
@ -134,7 +136,8 @@ static int test_case_1(srsran_ssb_t* ssb)
|
||||||
// Decode
|
// Decode
|
||||||
gettimeofday(&t[1], NULL);
|
gettimeofday(&t[1], NULL);
|
||||||
srsran_pbch_msg_nr_t pbch_msg_rx = {};
|
srsran_pbch_msg_nr_t pbch_msg_rx = {};
|
||||||
TESTASSERT(srsran_ssb_decode_pbch(ssb, pci, ssb_idx, 0, buffer, &pbch_msg_rx) == SRSRAN_SUCCESS);
|
TESTASSERT(srsran_ssb_decode_pbch(ssb, pci, pbch_msg_tx.hrf, pbch_msg_tx.ssb_idx, buffer, &pbch_msg_rx) ==
|
||||||
|
SRSRAN_SUCCESS);
|
||||||
gettimeofday(&t[2], NULL);
|
gettimeofday(&t[2], NULL);
|
||||||
get_time_interval(t);
|
get_time_interval(t);
|
||||||
t_decode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
|
t_decode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
|
||||||
|
@ -145,12 +148,30 @@ static int test_case_1(srsran_ssb_t* ssb)
|
||||||
|
|
||||||
// Assert PBCH message CRC
|
// Assert PBCH message CRC
|
||||||
TESTASSERT(pbch_msg_rx.crc);
|
TESTASSERT(pbch_msg_rx.crc);
|
||||||
|
TESTASSERT(memcmp(&pbch_msg_rx, &pbch_msg_tx, sizeof(srsran_pbch_msg_nr_t)) == 0);
|
||||||
|
|
||||||
|
// Search
|
||||||
|
srsran_ssb_search_res_t res = {};
|
||||||
|
gettimeofday(&t[1], NULL);
|
||||||
|
TESTASSERT(srsran_ssb_search(ssb, buffer, hf_len, &res) == SRSRAN_SUCCESS);
|
||||||
|
gettimeofday(&t[2], NULL);
|
||||||
|
get_time_interval(t);
|
||||||
|
t_search_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
|
||||||
|
|
||||||
|
// Print decoded PBCH message
|
||||||
|
srsran_pbch_msg_info(&res.pbch_msg, str, sizeof(str));
|
||||||
|
INFO("test_case_1 - found pci=%d %s crc=%s", res.N_id, str, res.pbch_msg.crc ? "OK" : "KO");
|
||||||
|
|
||||||
|
// Assert PBCH message CRC
|
||||||
|
TESTASSERT(res.pbch_msg.crc);
|
||||||
|
TESTASSERT(memcmp(&res.pbch_msg, &pbch_msg_tx, sizeof(srsran_pbch_msg_nr_t)) == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
INFO("test_case_1 - %.1f usec/encode; %.1f usec/decode;",
|
INFO("test_case_1 - %.1f usec/encode; %.1f usec/decode; %.1f usec/decode;",
|
||||||
(double)t_encode_usec / (double)(count),
|
(double)t_encode_usec / (double)(count),
|
||||||
(double)t_decode_usec / (double)(count));
|
(double)t_decode_usec / (double)(count),
|
||||||
|
(double)t_search_usec / (double)(count));
|
||||||
|
|
||||||
return SRSRAN_SUCCESS;
|
return SRSRAN_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -169,6 +190,7 @@ int main(int argc, char** argv)
|
||||||
srsran_ssb_args_t ssb_args = {};
|
srsran_ssb_args_t ssb_args = {};
|
||||||
ssb_args.enable_encode = true;
|
ssb_args.enable_encode = true;
|
||||||
ssb_args.enable_decode = true;
|
ssb_args.enable_decode = true;
|
||||||
|
ssb_args.enable_search = true;
|
||||||
|
|
||||||
if (buffer == NULL) {
|
if (buffer == NULL) {
|
||||||
ERROR("Malloc");
|
ERROR("Malloc");
|
||||||
|
|
|
@ -116,7 +116,7 @@ static int test_case_1(srsran_ssb_t* ssb)
|
||||||
|
|
||||||
// Add the SSB base-band
|
// Add the SSB base-band
|
||||||
gettimeofday(&t[1], NULL);
|
gettimeofday(&t[1], NULL);
|
||||||
TESTASSERT(srsran_ssb_add(ssb, pci, 0, &pbch_msg, buffer, buffer) == SRSRAN_SUCCESS);
|
TESTASSERT(srsran_ssb_add(ssb, pci, &pbch_msg, buffer, buffer) == SRSRAN_SUCCESS);
|
||||||
gettimeofday(&t[2], NULL);
|
gettimeofday(&t[2], NULL);
|
||||||
get_time_interval(t);
|
get_time_interval(t);
|
||||||
t_add_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
|
t_add_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
|
||||||
|
|
|
@ -105,7 +105,7 @@ public:
|
||||||
srsran_pbch_msg_nr_t msg = {};
|
srsran_pbch_msg_nr_t msg = {};
|
||||||
|
|
||||||
// Add SSB
|
// Add SSB
|
||||||
if (srsran_ssb_add(&ssb, pci, 0, &msg, buffer.data(), buffer.data()) < SRSRAN_SUCCESS) {
|
if (srsran_ssb_add(&ssb, pci, &msg, buffer.data(), buffer.data()) < SRSRAN_SUCCESS) {
|
||||||
logger.error("Error adding SSB");
|
logger.error("Error adding SSB");
|
||||||
return SRSRAN_ERROR;
|
return SRSRAN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue