Added SSB encode and decode from grid, plus unit test

This commit is contained in:
Xavier Arteaga 2022-01-28 19:21:19 +01:00 committed by Xavier Arteaga
parent d448eac941
commit a74fdb84c9
4 changed files with 339 additions and 31 deletions

View File

@ -89,7 +89,7 @@ typedef struct SRSRAN_API {
uint32_t corr_sz; ///< Correlation size
uint32_t corr_window; ///< Correlation window length
uint32_t ssb_sz; ///< SSB size in samples at the configured sampling rate
int32_t f_offset; ///< Current SSB integer frequency offset (multiple of SCS)
int32_t f_offset; ///< SSB integer frequency offset (multiple of SCS) between DC and the SSB center
uint32_t cp_sz; ///< CP length for the given symbol size
/// Other parameters
@ -143,22 +143,6 @@ SRSRAN_API void srsran_ssb_free(srsran_ssb_t* q);
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/
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
* @note It currently expects an input buffer of half radio frame
* @param q SSB object
* @param N_id Physical Cell Identifier
* @param n_hf Number of hald radio frame, 0 or 1
* @param ssb_idx SSB candidate index
* @param in Input baseband buffer
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int srsran_ssb_decode_pbch(srsran_ssb_t* q,
uint32_t N_id,
uint32_t n_hf,
uint32_t ssb_idx,
const cf_t* in,
srsran_pbch_msg_nr_t* msg);
/**
* @brief Searches for an SSB transmission and decodes the PBCH message
@ -178,6 +162,21 @@ SRSRAN_API int srsran_ssb_search(srsran_ssb_t* q, const cf_t* in, uint32_t nof_s
*/
SRSRAN_API bool srsran_ssb_send(srsran_ssb_t* q, uint32_t sf_idx);
/**
*
* @param q SSB object
* @param N_id Physical Cell Identifier
* @param msg NR PBCH message to transmit
* @param re_grid Resource grid pointer
* @param grid_sz_rb Resource grid bandwidth in subcarriers
* @return SRSRAN_SUCCES if inputs and data are correct, otherwise SRSRAN_ERROR code
*/
SRSRAN_API int srsran_ssb_put_grid(srsran_ssb_t* q,
uint32_t N_id,
const srsran_pbch_msg_nr_t* msg,
cf_t* re_grid,
uint32_t grid_bw_sc);
/**
* @brief Adds SSB to a given signal in time domain
* @param q SSB object
@ -188,6 +187,41 @@ SRSRAN_API bool srsran_ssb_send(srsran_ssb_t* q, uint32_t sf_idx);
SRSRAN_API int
srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, const cf_t* in, cf_t* out);
/**
* @brief Decodes PBCH in the given time domain signal
* @note It currently expects an input buffer of half radio frame
* @param q SSB object
* @param N_id Physical Cell Identifier
* @param n_hf Number of hald radio frame, 0 or 1
* @param ssb_idx SSB candidate index
* @param in Input baseband buffer
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int srsran_ssb_decode_pbch(srsran_ssb_t* q,
uint32_t N_id,
uint32_t n_hf,
uint32_t ssb_idx,
const cf_t* grid,
srsran_pbch_msg_nr_t* msg);
/**
* @brief Decodes PBCH in the given resource grid
* @param q SSB object
* @param N_id Physical Cell Identifier
* @param n_hf Number of hald radio frame, 0 or 1
* @param ssb_idx SSB candidate index
* @param grid Input resource grid buffer
* @param grid_sz_rb Resource grid bandwidth in subcarriers
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int srsran_ssb_decode_grid(srsran_ssb_t* q,
uint32_t N_id,
uint32_t n_hf,
uint32_t ssb_idx,
const cf_t* in,
uint32_t grid_sz_rb,
srsran_pbch_msg_nr_t* msg);
/**
* @brief Perform cell search and measurement
* @note This function assumes the SSB transmission is aligned with the input base-band signal

View File

@ -527,21 +527,10 @@ 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)
static int ssb_encode(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, cf_t ssb_grid[SRSRAN_SSB_NOF_RE])
{
// 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] = {};
uint32_t N_id_1 = SRSRAN_NID_1_NR(N_id);
uint32_t N_id_2 = SRSRAN_NID_2_NR(N_id);
// Put PSS
if (srsran_pss_nr_put(ssb_grid, N_id_2, q->cfg.beta_pss) < SRSRAN_SUCCESS) {
@ -580,6 +569,64 @@ int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* m
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
SRSRAN_API int
srsran_ssb_put_grid(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, cf_t* re_grid, uint32_t grid_bw_sc)
{
// Verify input parameters
if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || msg == NULL || re_grid == NULL ||
grid_bw_sc * SRSRAN_NRE < SRSRAN_SSB_BW_SUBC) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (!q->args.enable_encode) {
ERROR("SSB is not configured for encode");
return SRSRAN_ERROR;
}
// Put signals in SSB grid
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
if (ssb_encode(q, N_id, msg, ssb_grid) < SRSRAN_SUCCESS) {
ERROR("Putting SSB in grid");
return SRSRAN_ERROR;
}
// First symbol in the half frame
uint32_t l_first = q->l_first[msg->ssb_idx];
// Frequency offset fom the bottom of the grid
uint32_t f_offset = grid_bw_sc / 2 + q->f_offset - SRSRAN_SSB_BW_SUBC / 2;
// Put SSB grid in the actual resource grid
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
srsran_vec_cf_copy(
&re_grid[grid_bw_sc * (l_first + l) + f_offset], &ssb_grid[SRSRAN_SSB_BW_SUBC * l], SRSRAN_SSB_BW_SUBC);
}
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;
}
// Put signals in SSB grid
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
if (ssb_encode(q, N_id, msg, ssb_grid) < SRSRAN_SUCCESS) {
ERROR("Putting SSB in grid");
return SRSRAN_ERROR;
}
// Select start symbol from SSB candidate index
int t_offset = ssb_get_t_offset(q, msg->ssb_idx);
if (t_offset < SRSRAN_SUCCESS) {
@ -1132,6 +1179,46 @@ static int ssb_decode_pbch(srsran_ssb_t* q,
return SRSRAN_SUCCESS;
}
int srsran_ssb_decode_grid(srsran_ssb_t* q,
uint32_t N_id,
uint32_t n_hf,
uint32_t ssb_idx,
const cf_t* re_grid,
uint32_t grid_bw_sc,
srsran_pbch_msg_nr_t* msg)
{
// Verify input parameters
if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || msg == NULL || re_grid == NULL || grid_bw_sc < SRSRAN_SSB_BW_SUBC) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (!q->args.enable_encode) {
ERROR("SSB is not configured for encode");
return SRSRAN_ERROR;
}
// First symbol in the half frame
uint32_t l_first = q->l_first[ssb_idx];
// Frequency offset fom the bottom of the grid
uint32_t f_offset = grid_bw_sc / 2 + q->f_offset - SRSRAN_SSB_BW_SUBC / 2;
// Get SSB grid from resource grid
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
srsran_vec_cf_copy(
&ssb_grid[SRSRAN_SSB_BW_SUBC * l], &re_grid[grid_bw_sc * (l_first + l) + f_offset], SRSRAN_SSB_BW_SUBC);
}
// Decode PBCH
if (ssb_decode_pbch(q, N_id, n_hf, ssb_idx, ssb_grid, msg) < SRSRAN_SUCCESS) {
ERROR("Error decoding");
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
int srsran_ssb_decode_pbch(srsran_ssb_t* q,
uint32_t N_id,
uint32_t n_hf,

View File

@ -133,6 +133,9 @@ target_link_libraries(ssb_measure_test srsran_phy)
add_executable(ssb_decode_test ssb_decode_test.c)
target_link_libraries(ssb_decode_test srsran_phy)
add_executable(ssb_grid_test ssb_grid_test.c)
target_link_libraries(ssb_grid_test srsran_phy)
# For 1.0 GHz and 3.5 GHz Center frequencies
foreach (CELL_FREQ 1000000 3500000)
# For each supported Cell/Carrier subcarrier spacing
@ -153,6 +156,10 @@ foreach (CELL_FREQ 1000000 3500000)
# Test SSB PBCH decoding
add_nr_test(ssb_decode_test_${CELL_FREQ}000_${CELL_SCS}_${SSB_FREQ}000_${SSB_SCS}_${SSB_PATTERN} ssb_decode_test
-F ${CELL_FREQ}000 -S ${CELL_SCS} -f ${SSB_FREQ}000 -s ${SSB_SCS} -P ${SSB_PATTERN})
# Test SSB grid put/get decoding
add_nr_test(ssb_grid_test_${CELL_FREQ}000_${CELL_SCS}_${SSB_FREQ}000_${SSB_SCS}_${SSB_PATTERN} ssb_grid_test
-F ${CELL_FREQ}000 -S ${CELL_SCS} -f ${SSB_FREQ}000 -s ${SSB_SCS} -P ${SSB_PATTERN})
endforeach ()
endforeach ()
endforeach ()

View File

@ -0,0 +1,180 @@
/**
*
* \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 <complex.h>
#include <getopt.h>
#include <srsran/phy/utils/random.h>
#include <stdlib.h>
// NR parameters
static srsran_subcarrier_spacing_t carrier_scs = srsran_subcarrier_spacing_15kHz;
static double carrier_freq_hz = 3.5e9 + 960e3;
static srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_30kHz;
static double ssb_freq_hz = 3.5e9;
static srsran_ssb_patern_t ssb_pattern = SRSRAN_SSB_PATTERN_A;
static uint32_t ssb_idx = 0; // SSB candidate index to test
static uint32_t pci = 123; // N_id
// Test context
static srsran_random_t random_gen = NULL;
static double srate_hz = 0.0f; // Base-band sampling rate
static cf_t* grid = NULL; // Resource grid
static uint32_t grid_bw_sc = 52 * SRSRAN_NRE; // Resource grid bandwidth in subcarriers
static void usage(char* prog)
{
printf("Usage: %s [v]\n", prog);
printf("\t-s SSB subcarrier spacing [default, %s kHz]\n", srsran_subcarrier_spacing_to_str(ssb_scs));
printf("\t-f SSB center frequency [default, %.3f MHz]\n", ssb_freq_hz / 1e6);
printf("\t-S cell/carrier subcarrier spacing [default, %s kHz]\n", srsran_subcarrier_spacing_to_str(carrier_scs));
printf("\t-F cell/carrier center frequency in Hz [default, %.3f MHz]\n", carrier_freq_hz / 1e6);
printf("\t-P SSB pattern [default, %s]\n", srsran_ssb_pattern_to_str(ssb_pattern));
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, "SsFfPv")) != -1) {
switch (opt) {
case 's':
ssb_scs = srsran_subcarrier_spacing_from_str(argv[optind]);
if (ssb_scs == srsran_subcarrier_spacing_invalid) {
ERROR("Invalid SSB subcarrier spacing %s\n", argv[optind]);
exit(-1);
}
break;
case 'f':
ssb_freq_hz = strtod(argv[optind], NULL);
break;
case 'S':
carrier_scs = srsran_subcarrier_spacing_from_str(argv[optind]);
if (carrier_scs == srsran_subcarrier_spacing_invalid) {
ERROR("Invalid Cell/Carrier subcarrier spacing %s\n", argv[optind]);
exit(-1);
}
break;
case 'F':
carrier_freq_hz = strtod(argv[optind], NULL);
break;
case 'P':
ssb_pattern = srsran_ssb_pattern_fom_str(argv[optind]);
break;
case 'v':
increase_srsran_verbose_level();
break;
default:
usage(argv[0]);
exit(-1);
}
}
}
static void gen_pbch_msg(srsran_pbch_msg_nr_t* pbch_msg)
{
// 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_MSG_NR_SZ);
pbch_msg->ssb_idx = ssb_idx;
pbch_msg->crc = true;
}
static int test_case(srsran_ssb_t* ssb)
{
// SSB configuration
srsran_ssb_cfg_t ssb_cfg = {};
ssb_cfg.srate_hz = srate_hz;
ssb_cfg.center_freq_hz = carrier_freq_hz;
ssb_cfg.ssb_freq_hz = ssb_freq_hz;
ssb_cfg.scs = ssb_scs;
ssb_cfg.pattern = ssb_pattern;
TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS);
// Build PBCH message
srsran_pbch_msg_nr_t pbch_msg_tx = {};
gen_pbch_msg(&pbch_msg_tx);
// Print encoded PBCH message
char str[512] = {};
srsran_pbch_msg_info(&pbch_msg_tx, str, sizeof(str));
INFO("test_case - encoded pci=%d %s", pci, str);
// Add the SSB base-band
TESTASSERT(srsran_ssb_put_grid(ssb, pci, &pbch_msg_tx, grid, grid_bw_sc) == SRSRAN_SUCCESS);
// Decode
srsran_pbch_msg_nr_t pbch_msg_rx = {};
TESTASSERT(srsran_ssb_decode_grid(ssb, pci, pbch_msg_tx.hrf, pbch_msg_tx.ssb_idx, grid, grid_bw_sc, &pbch_msg_rx) ==
SRSRAN_SUCCESS);
// Print decoded PBCH message
srsran_pbch_msg_info(&pbch_msg_rx, str, sizeof(str));
INFO("test_case - decoded pci=%d %s crc=%s", pci, str, pbch_msg_rx.crc ? "OK" : "KO");
// Assert PBCH message CRC
TESTASSERT(pbch_msg_rx.crc);
TESTASSERT(memcmp(&pbch_msg_rx, &pbch_msg_tx, sizeof(srsran_pbch_msg_nr_t)) == 0);
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(grid_bw_sc / SRSRAN_NRE);
grid = srsran_vec_cf_malloc(grid_bw_sc * SRSRAN_NSYMB_PER_SLOT_NR);
srsran_ssb_t ssb = {};
srsran_ssb_args_t ssb_args = {};
ssb_args.enable_encode = true;
ssb_args.enable_decode = true;
ssb_args.enable_search = true;
if (grid == NULL) {
ERROR("Malloc");
goto clean_exit;
}
if (srsran_ssb_init(&ssb, &ssb_args) < SRSRAN_SUCCESS) {
ERROR("Init");
goto clean_exit;
}
if (test_case(&ssb) != SRSRAN_SUCCESS) {
ERROR("test case failed");
goto clean_exit;
}
ret = SRSRAN_SUCCESS;
clean_exit:
srsran_random_free(random_gen);
srsran_ssb_free(&ssb);
if (grid) {
free(grid);
}
return ret;
}