Implemented initial NR cell search

master
Xavier Arteaga 4 years ago committed by Xavier Arteaga
parent 2f9c7e1345
commit 758fd3c1ba

@ -181,6 +181,11 @@ extern "C" {
*/ */
#define SRSRAN_NID_2_NR(N_ID) ((N_ID) % SRSRAN_NOF_NID_2_NR) #define SRSRAN_NID_2_NR(N_ID) ((N_ID) % SRSRAN_NOF_NID_2_NR)
/**
* @brief Compute Physical Cell Identifier (PCI) N_id from N_id_1 and N_id_2
*/
#define SRSRAN_NID_NR(NID_1, NID_2) (SRSRAN_NOF_NID_2_NR * (NID_1) + (NID_2))
/** /**
* @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
* block * block

@ -46,4 +46,14 @@ SRSRAN_API int srsran_pss_nr_put(cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id
*/ */
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]); 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]);
/**
* @brief Find the best PSS sequence given 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 norm_corr Normalised correlation of the best found sequence
* @param found_N_id_2 The N_id_2 of the best sequence
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise
*/
SRSRAN_API int srsran_pss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], float* norm_corr, uint32_t* found_N_id_2);
#endif // SRSRAN_PSS_NR_H #endif // SRSRAN_PSS_NR_H

@ -43,7 +43,7 @@ typedef struct SRSRAN_API {
bool enable_correlate; ///< Enables PSS/SSS correlation and peak search (UE cell search) bool enable_correlate; ///< Enables PSS/SSS correlation and peak search (UE cell search)
bool enable_encode; ///< Enables PBCH Encoder (intended for gNb) bool enable_encode; ///< Enables PBCH Encoder (intended for gNb)
bool enable_decode; ///< Enables PBCH Decoder (intented for UE) bool enable_decode; ///< Enables PBCH Decoder (intented for UE)
bool enable_measure; ///< Enables PSS/SSS CSI measurements bool enable_measure; ///< Enables PSS/SSS CSI measurements and frequency domain search
} srsran_ssb_args_t; } srsran_ssb_args_t;
/** /**
@ -121,6 +121,18 @@ SRSRAN_API int srsran_ssb_decode_pbch(srsran_ssb_t* q, const cf_t* in, srsran_pb
SRSRAN_API int 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_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 cell search and measurement
* @note This function assumes the SSB transmission is aligned with the input base-band signal
* @param q NR PSS object
* @param in Base-band signal buffer
* @param N_id Physical Cell Identifier of the most suitable cell identifier
* @param meas SSB-based CSI measurement of the most suitable cell identifier
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise
*/
SRSRAN_API int
srsran_ssb_csi_search(srsran_ssb_t* q, const cf_t* in, uint32_t* N_id, srsran_csi_trs_measurements_t* meas);
/** /**
* @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

@ -94,3 +94,52 @@ int srsran_pss_nr_extract_lse(const cf_t* ssb_grid, uint32_t N_id_2, cf_t lse[SR
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
int srsran_pss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], float* norm_corr, uint32_t* found_N_id_2)
{
// Verify inputs
if (ssb_grid == NULL || norm_corr == NULL || found_N_id_2 == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
const cf_t* pss_ptr = &ssb_grid[SRSRAN_PSS_NR_SYMBOL_IDX * SRSRAN_SSB_BW_SUBC + PSS_NR_SUBC_BEGIN];
// Measure PSS region average power
float avg_power = srsran_vec_avg_power_cf(pss_ptr, SRSRAN_PSS_NR_LEN);
// If no energy detected or invalid, consider zero correlation
if (!isnormal(avg_power)) {
*norm_corr = 0.0f;
*found_N_id_2 = 0;
return SRSRAN_SUCCESS;
}
// Search state
float max_corr = -INFINITY; //< Stores best correlation
uint32_t N_id_2 = 0; //< Best N_id_2
// Iterate over all possible N_id_2
for (uint32_t N_id_2_candidate = 0; N_id_2_candidate < SRSRAN_NOF_NID_2_NR; N_id_2_candidate++) {
uint32_t m = PSS_NR_SEQUENCE_M(N_id_2_candidate);
cf_t acc = 0.0f;
// Correlate d sequence fist part
acc += srsran_vec_dot_prod_ccc(&pss_nr_d[m], &pss_ptr[0], SRSRAN_PSS_NR_LEN - m);
// Correlate d sequence second part
acc += srsran_vec_dot_prod_ccc(&pss_nr_d[0], &pss_ptr[SRSRAN_PSS_NR_LEN - m], m);
// Correlate
float corr = SRSRAN_CSQABS(acc);
if (corr > max_corr) {
N_id_2 = N_id_2_candidate;
max_corr = corr;
}
}
// Copy found result
*norm_corr = max_corr / avg_power / SRSRAN_PSS_NR_LEN;
*found_N_id_2 = N_id_2;
return SRSRAN_SUCCESS;
}

@ -241,23 +241,8 @@ int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* m
return SRSRAN_SUCCESS; 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) static int ssb_demodulate(srsran_ssb_t* q, const cf_t* in, cf_t ssb_grid[SRSRAN_SSB_NOF_RE])
{ {
// Verify inputs
if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || in == NULL || meas == NULL || !isnormal(q->scs_hz)) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (!q->args.enable_measure) {
ERROR("SSB is not configured for measure");
return SRSRAN_ERROR;
}
uint32_t N_id_1 = SRSRAN_NID_1_NR(N_id);
uint32_t N_id_2 = SRSRAN_NID_2_NR(N_id);
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
// Demodulate
const cf_t* in_ptr = in; const cf_t* in_ptr = in;
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
// Get CP length // Get CP length
@ -297,6 +282,15 @@ int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsra
} }
} }
return SRSRAN_SUCCESS;
}
static int
ssb_measure(srsran_ssb_t* q, const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id, srsran_csi_trs_measurements_t* meas)
{
uint32_t N_id_1 = SRSRAN_NID_1_NR(N_id);
uint32_t N_id_2 = SRSRAN_NID_2_NR(N_id);
// Extract PSS LSE // Extract PSS LSE
cf_t pss_lse[SRSRAN_PSS_NR_LEN]; cf_t pss_lse[SRSRAN_PSS_NR_LEN];
cf_t sss_lse[SRSRAN_SSS_NR_LEN]; cf_t sss_lse[SRSRAN_SSS_NR_LEN];
@ -313,14 +307,17 @@ int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsra
float delay_avg_us = 1e6f * delay_avg_norm / q->scs_hz; float delay_avg_us = 1e6f * delay_avg_norm / q->scs_hz;
// Pre-compensate delay // Pre-compensate delay
cf_t ssb_grid_corrected[SRSRAN_SSB_NOF_RE];
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
srsran_vec_apply_cfo( srsran_vec_apply_cfo(&ssb_grid[SRSRAN_SSB_BW_SUBC * l],
&ssb_grid[SRSRAN_SSB_BW_SUBC * l], delay_avg_norm, &ssb_grid[SRSRAN_SSB_BW_SUBC * l], SRSRAN_SSB_BW_SUBC); delay_avg_norm,
&ssb_grid_corrected[SRSRAN_SSB_BW_SUBC * l],
SRSRAN_SSB_BW_SUBC);
} }
// Extract LSE again // Extract LSE again
if (srsran_pss_nr_extract_lse(ssb_grid, N_id_2, pss_lse) < SRSRAN_SUCCESS || if (srsran_pss_nr_extract_lse(ssb_grid_corrected, N_id_2, pss_lse) < SRSRAN_SUCCESS ||
srsran_sss_nr_extract_lse(ssb_grid, N_id_1, N_id_2, sss_lse) < SRSRAN_SUCCESS) { srsran_sss_nr_extract_lse(ssb_grid_corrected, N_id_1, N_id_2, sss_lse) < SRSRAN_SUCCESS) {
ERROR("Error extracting LSE"); ERROR("Error extracting LSE");
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
@ -363,3 +360,79 @@ int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsra
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
int srsran_ssb_csi_search(srsran_ssb_t* q, const cf_t* in, uint32_t* N_id, srsran_csi_trs_measurements_t* meas)
{
// Verify inputs
if (q == NULL || in == NULL || N_id == NULL || meas == NULL || !isnormal(q->scs_hz)) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (!q->args.enable_measure) {
ERROR("SSB is not configured for search");
return SRSRAN_ERROR;
}
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
// Demodulate
if (ssb_demodulate(q, in, ssb_grid) < SRSRAN_SUCCESS) {
ERROR("Error demodulating");
return SRSRAN_ERROR;
}
// Find best N_id_2
uint32_t N_id_2 = 0;
float pss_corr = 0.0f;
if (srsran_pss_nr_find(ssb_grid, &pss_corr, &N_id_2) < SRSRAN_SUCCESS) {
ERROR("Error searching for N_id_2");
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
*N_id = SRSRAN_NID_NR(N_id_1, N_id_2);
// Measure selected N_id
if (ssb_measure(q, ssb_grid, *N_id, meas)) {
ERROR("Error measuring");
return SRSRAN_ERROR;
}
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
if (ssb_demodulate(q, in, ssb_grid) < SRSRAN_SUCCESS) {
ERROR("Error demodulating");
return SRSRAN_ERROR;
}
// Actual measurement
if (ssb_measure(q, ssb_grid, N_id, meas)) {
ERROR("Error measuring");
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}

@ -142,7 +142,7 @@ int srsran_sss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
uint32_t* found_N_id_1) uint32_t* found_N_id_1)
{ {
// Verify inputs // Verify inputs
if (ssb_grid == NULL || N_id_2 >= SRSRAN_NOF_NID_2_NR) { if (ssb_grid == NULL || N_id_2 >= SRSRAN_NOF_NID_2_NR || norm_corr == NULL || found_N_id_1 == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS; return SRSRAN_ERROR_INVALID_INPUTS;
} }
@ -154,9 +154,8 @@ int srsran_sss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
// If the measured power is invalid or zero, consider no SSS signal // If the measured power is invalid or zero, consider no SSS signal
if (!isnormal(avg_power)) { if (!isnormal(avg_power)) {
if (norm_corr) { *norm_corr = 0.0f;
*norm_corr = 0.0f; *found_N_id_1 = 0;
}
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
@ -176,19 +175,18 @@ int srsran_sss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
srsran_vec_prod_ccc(&sss_ptr[SRSRAN_SSS_NR_LEN - m1], &sss_nr_d1[0], &sss_seq_m1[SRSRAN_SSS_NR_LEN - m1], m1); 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 // 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) { for (uint32_t N_id_1_blind = m1; N_id_1_blind < SRSRAN_NOF_NID_1_NR; N_id_1_blind += SSS_NR_NOF_M1) {
uint32_t m0 = SSS_NR_SEQUENCE_M0(N_id_1_blind, N_id_2); uint32_t m0 = SSS_NR_SEQUENCE_M0(N_id_1_blind, N_id_2);
cf_t acc = 0.0f;
cf_t sss_seq_m0[SRSRAN_SSS_NR_LEN];
// Apply d0 sequence fist part // Correlate 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); acc += srsran_vec_dot_prod_ccc(&sss_seq_m1[0], &sss_nr_d0[m0], SRSRAN_SSS_NR_LEN - m0);
// Apply d0 sequence second part // Correlate 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); acc += srsran_vec_dot_prod_ccc(&sss_seq_m1[SRSRAN_SSS_NR_LEN - m0], &sss_nr_d0[0], m0);
// Correlate // Normalise
float corr = SRSRAN_CSQABS(srsran_vec_acc_cc(sss_seq_m0, SRSRAN_SSS_NR_LEN)) / avg_power; float corr = SRSRAN_CSQABS(acc);
if (corr > max_corr) { if (corr > max_corr) {
N_id_1 = N_id_1_blind; N_id_1 = N_id_1_blind;
max_corr = corr; max_corr = corr;
@ -196,13 +194,9 @@ int srsran_sss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
} }
} }
if (norm_corr) { // Copy found result
*norm_corr = max_corr; *norm_corr = max_corr / avg_power / SRSRAN_SSS_NR_LEN;
} *found_N_id_1 = N_id_1;
if (found_N_id_1) {
*found_N_id_1 = N_id_1;
}
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }

@ -77,13 +77,25 @@ static void run_channel()
srsran_channel_awgn_run_c(&awgn, buffer, buffer, sf_len); srsran_channel_awgn_run_c(&awgn, buffer, buffer, sf_len);
} }
static int assert_measure(const srsran_csi_trs_measurements_t* meas)
{
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);
return SRSRAN_SUCCESS;
}
static int test_case_1(srsran_ssb_t* ssb) static int test_case_1(srsran_ssb_t* ssb)
{ {
uint64_t t_usec = 0; uint64_t t_find_usec = 0;
srsran_ssb_cfg_t ssb_cfg = {}; uint64_t t_meas_usec = 0;
ssb_cfg.srate_hz = srate_hz; srsran_ssb_cfg_t ssb_cfg = {};
ssb_cfg.freq_offset_hz = 0.0; ssb_cfg.srate_hz = srate_hz;
ssb_cfg.scs = ssb_scs; ssb_cfg.freq_offset_hz = 0.0;
ssb_cfg.scs = ssb_scs;
TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS); TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS);
@ -102,13 +114,26 @@ static int test_case_1(srsran_ssb_t* ssb)
// Run channel // Run channel
run_channel(); run_channel();
// Find
gettimeofday(&t[1], NULL);
uint32_t N_id_found = 0;
srsran_csi_trs_measurements_t meas_search = {};
TESTASSERT(srsran_ssb_csi_search(ssb, buffer, &N_id_found, &meas_search) == SRSRAN_SUCCESS);
gettimeofday(&t[2], NULL);
get_time_interval(t);
t_find_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
// Assert find and measurements
TESTASSERT(N_id_found == pci);
TESTASSERT(assert_measure(&meas_search) == SRSRAN_SUCCESS);
// Measure // Measure
gettimeofday(&t[1], NULL); gettimeofday(&t[1], NULL);
srsran_csi_trs_measurements_t meas = {}; srsran_csi_trs_measurements_t meas = {};
TESTASSERT(srsran_ssb_csi_measure(ssb, pci, buffer, &meas) == SRSRAN_SUCCESS); TESTASSERT(srsran_ssb_csi_measure(ssb, pci, buffer, &meas) == SRSRAN_SUCCESS);
gettimeofday(&t[2], NULL); gettimeofday(&t[2], NULL);
get_time_interval(t); get_time_interval(t);
t_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; t_meas_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
// Print measurement // Print measurement
char str[512]; char str[512];
@ -116,15 +141,12 @@ static int test_case_1(srsran_ssb_t* ssb)
INFO("test_case_1 - pci=%d %s", pci, str); INFO("test_case_1 - pci=%d %s", pci, str);
// Assert measurements // Assert measurements
TESTASSERT(fabsf(meas.rsrp_dB - 0.0f) < RSRP_MAX_ERROR); TESTASSERT(assert_measure(&meas) == SRSRAN_SUCCESS);
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); INFO("test_case_1 - %.1f usec/search; %.1f usec/measurement",
(double)t_find_usec / (double)SRSRAN_NOF_NID_NR,
(double)t_meas_usec / (double)SRSRAN_NOF_NID_NR);
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }

Loading…
Cancel
Save