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