mirror of https://github.com/PentHertz/srsLTE.git
Added SSB encode and decode from grid, plus unit test
This commit is contained in:
parent
d448eac941
commit
a74fdb84c9
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 ()
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue