mirror of https://github.com/pvnis/srsRAN_4G.git
Merge branch 'next' into agpl_next
commit
73a61b884e
@ -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_TSAN_OPTIONS_H
|
||||
#define SRSRAN_TSAN_OPTIONS_H
|
||||
|
||||
// Options taken from Mozilla project
|
||||
// abort_on_error=1 - Causes TSan to abort instead of using exit().
|
||||
// halt_on_error=1 - Causes TSan to stop on the first race detected.
|
||||
//
|
||||
// report_signal_unsafe=0 - Required to avoid TSan deadlocks when
|
||||
// receiving external signals (e.g. SIGINT manually on console).
|
||||
//
|
||||
// allocator_may_return_null=1 - Tell TSan to return NULL when an allocation
|
||||
// fails instead of aborting the program. This allows us to handle failing
|
||||
// allocations the same way we would handle them with a regular allocator and
|
||||
// also uncovers potential bugs that might occur in these situations.
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
const char* __tsan_default_options()
|
||||
{
|
||||
return "halt_on_error=1:abort_on_error=1:report_signal_unsafe=0"
|
||||
":allocator_may_return_null=1";
|
||||
}
|
||||
|
||||
const char* __tsan_default_suppressions()
|
||||
{
|
||||
// External uninstrumented libraries
|
||||
return "called_from_lib:libzmq.so\n"
|
||||
"called_from_lib:libpgm-5.2.so\n";
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // SRSRAN_TSAN_OPTIONS_H
|
@ -0,0 +1,62 @@
|
||||
/**
|
||||
*
|
||||
* \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_DMRS_PBCH_H
|
||||
#define SRSRAN_DMRS_PBCH_H
|
||||
|
||||
#include "srsran/phy/common/phy_common_nr.h"
|
||||
|
||||
/**
|
||||
* @brief Describes the DeModulation Reference Signals (DMRS) for NR PBCH configuration
|
||||
*/
|
||||
typedef struct SRSRAN_API {
|
||||
uint32_t N_id; ///< Physical cell identifier
|
||||
uint32_t n_hf; ///< Number of half radio frame, 0 or 1
|
||||
uint32_t ssb_idx; ///< SSB candidate index
|
||||
uint32_t L_max; ///< Number of SSB opportunities in half radio frame
|
||||
float beta; ///< Power allocation specified in TS 38.213
|
||||
srsran_subcarrier_spacing_t scs; ///< SSB configured subcarrier spacing
|
||||
} srsran_dmrs_pbch_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Describes an NR PBCH DMRS based measurement
|
||||
*/
|
||||
typedef struct SRSRAN_API {
|
||||
float corr; ///< Normalised correlation
|
||||
float epre; ///< Linear energy per resource element
|
||||
float rsrp; ///< Linear RSRP
|
||||
float cfo_hz; ///< CFO in Hz
|
||||
float avg_delay_us; ///< Average delay in us
|
||||
} srsran_dmrs_pbch_meas_t;
|
||||
|
||||
/**
|
||||
* @brief Put NR PBCH DMRS in the SSB resource grid
|
||||
* @param cfg PBCH DMRS configuration
|
||||
* @param[out] ssb_grid SSB resource grid
|
||||
* @return SRSRAN_SUCCESS if the inputs and configuration are valid, SRSRAN_ERROR code otherwise
|
||||
*/
|
||||
SRSRAN_API int srsran_dmrs_pbch_put(const srsran_dmrs_pbch_cfg_t* cfg, cf_t ssb_grid[SRSRAN_SSB_NOF_RE]);
|
||||
|
||||
/**
|
||||
* @brief Estimates NR PBCH DMRS
|
||||
* @param cfg PBCH DMRS configuration
|
||||
* @param ssb_grid Demodulated SSB resource grid
|
||||
* @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
|
||||
*/
|
||||
SRSRAN_API 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],
|
||||
srsran_dmrs_pbch_meas_t* meas);
|
||||
|
||||
#endif // SRSRAN_DMRS_PBCH_H
|
@ -0,0 +1,218 @@
|
||||
/**
|
||||
*
|
||||
* \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/ch_estimation/dmrs_pbch.h"
|
||||
#include "srsran/phy/common/sequence.h"
|
||||
#include "srsran/phy/utils/debug.h"
|
||||
#include "srsran/phy/utils/vector.h"
|
||||
#include <complex.h>
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Number of NR PBCH DMRS resource elements present in an SSB resource grid
|
||||
*/
|
||||
#define DMRS_PBCH_NOF_RE 144
|
||||
|
||||
static uint32_t dmrs_pbch_cinit(const srsran_dmrs_pbch_cfg_t* cfg)
|
||||
{
|
||||
// Default values for L_max == 4
|
||||
uint64_t i_ssb = (cfg->ssb_idx & 0b11U) + 4UL * cfg->n_hf; // Least 2 significant bits
|
||||
|
||||
if (cfg->L_max == 8 || cfg->L_max == 64) {
|
||||
i_ssb = cfg->ssb_idx & 0b111U; // Least 3 significant bits
|
||||
}
|
||||
|
||||
return SRSRAN_SEQUENCE_MOD(((i_ssb + 1UL) * (SRSRAN_FLOOR(cfg->N_id, 4UL) + 1UL) << 11UL) + ((i_ssb + 1UL) << 6UL) +
|
||||
(cfg->N_id % 4));
|
||||
}
|
||||
|
||||
int srsran_dmrs_pbch_put(const srsran_dmrs_pbch_cfg_t* cfg, cf_t ssb_grid[SRSRAN_SSB_NOF_RE])
|
||||
{
|
||||
// Validate inputs
|
||||
if (cfg == NULL || ssb_grid == NULL) {
|
||||
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
|
||||
// Calculate index shift
|
||||
uint32_t v = cfg->N_id % 4;
|
||||
|
||||
// Calculate power allocation
|
||||
float beta = M_SQRT1_2;
|
||||
if (isnormal(cfg->beta)) {
|
||||
beta = cfg->beta;
|
||||
}
|
||||
|
||||
// Initialise sequence
|
||||
uint32_t cinit = dmrs_pbch_cinit(cfg);
|
||||
srsran_sequence_state_t sequence_state = {};
|
||||
srsran_sequence_state_init(&sequence_state, cinit);
|
||||
|
||||
// Generate sequence
|
||||
cf_t r[DMRS_PBCH_NOF_RE];
|
||||
srsran_sequence_state_gen_f(&sequence_state, beta, (float*)r, DMRS_PBCH_NOF_RE * 2);
|
||||
|
||||
// r sequence read index
|
||||
uint32_t r_idx = 0;
|
||||
|
||||
// Put sequence in symbol 1
|
||||
for (uint32_t k = v; k < SRSRAN_SSB_BW_SUBC; k += 4) {
|
||||
ssb_grid[SRSRAN_SSB_BW_SUBC * 1 + k] = r[r_idx++];
|
||||
}
|
||||
|
||||
// Put sequence in symbol 2, lower section
|
||||
for (uint32_t k = v; k < 48; k += 4) {
|
||||
ssb_grid[SRSRAN_SSB_BW_SUBC * 2 + k] = r[r_idx++];
|
||||
}
|
||||
|
||||
// Put sequence in symbol 2, upper section
|
||||
for (uint32_t k = 192 + v; k < SRSRAN_SSB_BW_SUBC; k += 4) {
|
||||
ssb_grid[SRSRAN_SSB_BW_SUBC * 2 + k] = r[r_idx++];
|
||||
}
|
||||
|
||||
// Put sequence in symbol 3
|
||||
for (uint32_t k = v; k < SRSRAN_SSB_BW_SUBC; k += 4) {
|
||||
ssb_grid[SRSRAN_SSB_BW_SUBC * 3 + k] = r[r_idx++];
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int dmrs_pbch_extract_lse(const srsran_dmrs_pbch_cfg_t* cfg,
|
||||
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
|
||||
cf_t lse[DMRS_PBCH_NOF_RE])
|
||||
{
|
||||
// Calculate index shift
|
||||
uint32_t v = cfg->N_id % 4;
|
||||
|
||||
// Calculate power allocation
|
||||
float beta = M_SQRT1_2;
|
||||
if (isnormal(cfg->beta)) {
|
||||
beta = cfg->beta;
|
||||
}
|
||||
|
||||
// Initialise sequence
|
||||
uint32_t cinit = dmrs_pbch_cinit(cfg);
|
||||
srsran_sequence_state_t sequence_state = {};
|
||||
srsran_sequence_state_init(&sequence_state, cinit);
|
||||
|
||||
// Generate sequence
|
||||
cf_t r[DMRS_PBCH_NOF_RE];
|
||||
srsran_sequence_state_gen_f(&sequence_state, beta, (float*)r, DMRS_PBCH_NOF_RE * 2);
|
||||
|
||||
// r sequence read index
|
||||
uint32_t r_idx = 0;
|
||||
|
||||
// Put sequence in symbol 1
|
||||
for (uint32_t k = v; k < SRSRAN_SSB_BW_SUBC; k += 4) {
|
||||
lse[r_idx++] = ssb_grid[SRSRAN_SSB_BW_SUBC * 1 + k];
|
||||
}
|
||||
|
||||
// Put sequence in symbol 2, lower section
|
||||
for (uint32_t k = v; k < 48; k += 4) {
|
||||
lse[r_idx++] = ssb_grid[SRSRAN_SSB_BW_SUBC * 2 + k];
|
||||
}
|
||||
|
||||
// Put sequence in symbol 2, upper section
|
||||
for (uint32_t k = 192 + v; k < SRSRAN_SSB_BW_SUBC; k += 4) {
|
||||
lse[r_idx++] = ssb_grid[SRSRAN_SSB_BW_SUBC * 2 + k];
|
||||
}
|
||||
|
||||
// Put sequence in symbol 3
|
||||
for (uint32_t k = v; k < SRSRAN_SSB_BW_SUBC; k += 4) {
|
||||
lse[r_idx++] = ssb_grid[SRSRAN_SSB_BW_SUBC * 3 + k];
|
||||
}
|
||||
|
||||
// Calculate actual least square estimates
|
||||
srsran_vec_prod_conj_ccc(lse, r, lse, DMRS_PBCH_NOF_RE);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
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],
|
||||
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
|
||||
cf_t lse[DMRS_PBCH_NOF_RE];
|
||||
if (dmrs_pbch_extract_lse(cfg, ssb_grid, lse) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
float scs_hz = SRSRAN_SUBC_SPACING_NR(cfg->scs);
|
||||
if (!isnormal(scs_hz)) {
|
||||
ERROR("Invalid SCS");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Compute average delay in microseconds from the symbols 1 and 3 (symbol 2 does not carry PBCH in all the grid)
|
||||
float avg_delay1_norm = srsran_vec_estimate_frequency(&lse[0], 60) / 4.0f;
|
||||
float avg_delay3_norm = srsran_vec_estimate_frequency(&lse[84], 60) / 4.0f;
|
||||
float avg_delay_norm = (avg_delay1_norm + avg_delay3_norm) / 2.0f;
|
||||
float avg_delay_us = avg_delay_norm / scs_hz;
|
||||
|
||||
// Generate a second SSB grid with the corrected average delay
|
||||
cf_t ssb_grid_corrected[SRSRAN_SSB_NOF_RE];
|
||||
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
|
||||
srsran_vec_apply_cfo(&ssb_grid[SRSRAN_SSB_BW_SUBC * l],
|
||||
avg_delay_norm,
|
||||
&ssb_grid_corrected[SRSRAN_SSB_BW_SUBC * l],
|
||||
SRSRAN_SSB_BW_SUBC);
|
||||
}
|
||||
|
||||
// Extract LSE from corrected grid
|
||||
if (dmrs_pbch_extract_lse(cfg, ssb_grid_corrected, lse) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Compute correlation of symbols 1 and 3
|
||||
cf_t corr1 = srsran_vec_acc_cc(&lse[0], 60) / 60.0f;
|
||||
cf_t corr3 = srsran_vec_acc_cc(&lse[84], 60) / 60.0f;
|
||||
|
||||
// Estimate CFO from correlation
|
||||
float distance_s = srsran_symbol_distance_s(1, 3, cfg->scs);
|
||||
float cfo_hz = 0.0f;
|
||||
if (isnormal(distance_s)) {
|
||||
cfo_hz = cargf(corr1 * conjf(corr3)) / (2.0f * (float)M_PI * distance_s);
|
||||
}
|
||||
|
||||
// Estimate wideband gain at symbol 0
|
||||
cf_t wideband_gain = (srsran_vec_acc_cc(lse, DMRS_PBCH_NOF_RE) / DMRS_PBCH_NOF_RE) *
|
||||
cexpf(I * 2.0f * M_PI * srsran_symbol_offset_s(2, cfg->scs) * cfo_hz);
|
||||
|
||||
// Compute RSRP from correlation
|
||||
float rsrp = SRSRAN_CSQABS((corr1 + corr3) / 2.0f);
|
||||
|
||||
// Compute EPRE
|
||||
float epre = srsran_vec_avg_power_cf(lse, DMRS_PBCH_NOF_RE);
|
||||
|
||||
// Write measurements
|
||||
meas->corr = rsrp / epre;
|
||||
meas->epre = epre;
|
||||
meas->rsrp = rsrp;
|
||||
meas->cfo_hz = cfo_hz;
|
||||
meas->avg_delay_us = avg_delay_us;
|
||||
|
||||
// Compute channel estimates
|
||||
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
|
||||
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;
|
||||
}
|
@ -0,0 +1,677 @@
|
||||
/**
|
||||
*
|
||||
* \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/phch/pbch_nr.h"
|
||||
#include "srsran/phy/common/sequence.h"
|
||||
#include "srsran/phy/fec/polar/polar_chanalloc.h"
|
||||
#include "srsran/phy/fec/polar/polar_interleaver.h"
|
||||
#include "srsran/phy/mimo/precoding.h"
|
||||
#include "srsran/phy/modem/demod_soft.h"
|
||||
#include "srsran/phy/modem/mod.h"
|
||||
#include "srsran/phy/utils/debug.h"
|
||||
#include "srsran/phy/utils/simd.h"
|
||||
#include "srsran/phy/utils/vector.h"
|
||||
|
||||
#define PBCH_NR_DEBUG_TX(...) DEBUG("PBCH-NR Tx: " __VA_ARGS__)
|
||||
#define PBCH_NR_DEBUG_RX(...) DEBUG("PBCH-NR Rx: " __VA_ARGS__)
|
||||
|
||||
/*
|
||||
* CRC Parameters
|
||||
*/
|
||||
#define PBCH_NR_CRC SRSRAN_LTE_CRC24C
|
||||
#define PBCH_NR_CRC_LEN 24
|
||||
|
||||
/*
|
||||
* Polar code N_max
|
||||
*/
|
||||
#define PBCH_NR_POLAR_N_MAX 9U
|
||||
|
||||
/*
|
||||
* Polar rate matching I_BIL
|
||||
*/
|
||||
#define PBCH_NR_POLAR_RM_IBIL 0
|
||||
|
||||
/*
|
||||
* Number of generated payload bits, called A
|
||||
*/
|
||||
#define PBCH_NR_A (SRSRAN_PBCH_NR_PAYLOAD_SZ + 8)
|
||||
|
||||
/*
|
||||
* Number of payload bits plus CRC
|
||||
*/
|
||||
#define PBCH_NR_K (PBCH_NR_A + PBCH_NR_CRC_LEN)
|
||||
|
||||
/*
|
||||
* Number of Polar encoded bits
|
||||
*/
|
||||
#define PBCH_NR_N (1U << PBCH_NR_POLAR_N_MAX)
|
||||
|
||||
/*
|
||||
* Number of RM bits
|
||||
*/
|
||||
#define PBCH_NR_E (864)
|
||||
|
||||
/*
|
||||
* Number of symbols
|
||||
*/
|
||||
#define PBCH_NR_M (PBCH_NR_E / 2)
|
||||
|
||||
static int pbch_nr_init_encoder(srsran_pbch_nr_t* q, const srsran_pbch_nr_args_t* args)
|
||||
{
|
||||
// Skip encoder init if not requested
|
||||
if (!args->enable_encode) {
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
srsran_polar_encoder_type_t encoder_type = SRSRAN_POLAR_ENCODER_PIPELINED;
|
||||
|
||||
#ifdef LV_HAVE_AVX2
|
||||
if (!args->disable_simd) {
|
||||
encoder_type = SRSRAN_POLAR_ENCODER_AVX2;
|
||||
}
|
||||
#endif /* LV_HAVE_AVX2 */
|
||||
|
||||
if (srsran_polar_encoder_init(&q->polar_encoder, encoder_type, PBCH_NR_POLAR_N_MAX) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error initiating polar encoder");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (srsran_polar_rm_tx_init(&q->polar_rm_tx) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error initiating polar RM");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (srsran_modem_table_lte(&q->qpsk, SRSRAN_MOD_QPSK) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
static int pbch_nr_init_decoder(srsran_pbch_nr_t* q, const srsran_pbch_nr_args_t* args)
|
||||
{
|
||||
// Skip decoder init if not requested
|
||||
if (!args->enable_decode) {
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
srsran_polar_decoder_type_t decoder_type = SRSRAN_POLAR_DECODER_SSC_C;
|
||||
|
||||
#ifdef LV_HAVE_AVX2
|
||||
if (!args->disable_simd) {
|
||||
decoder_type = SRSRAN_POLAR_DECODER_SSC_C_AVX2;
|
||||
}
|
||||
#endif /* LV_HAVE_AVX2 */
|
||||
|
||||
if (srsran_polar_decoder_init(&q->polar_decoder, decoder_type, PBCH_NR_POLAR_N_MAX) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error initiating polar decoder");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (srsran_polar_rm_rx_init_c(&q->polar_rm_rx) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error initiating polar RM");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int srsran_pbch_nr_init(srsran_pbch_nr_t* q, const srsran_pbch_nr_args_t* args)
|
||||
{
|
||||
if (q == NULL || args == NULL) {
|
||||
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
|
||||
if (!args->enable_encode && !args->enable_decode) {
|
||||
ERROR("Encoder and decoder are disabled, at least one of them shall be active");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (pbch_nr_init_encoder(q, args) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (pbch_nr_init_decoder(q, args) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (srsran_crc_init(&q->crc, PBCH_NR_CRC, PBCH_NR_CRC_LEN) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (srsran_polar_code_init(&q->code) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (srsran_polar_code_get(&q->code, PBCH_NR_K, PBCH_NR_E, PBCH_NR_POLAR_N_MAX) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error Getting polar code");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
void srsran_pbch_nr_free(srsran_pbch_nr_t* q)
|
||||
{
|
||||
if (q == NULL) {
|
||||
return;
|
||||
}
|
||||
srsran_polar_encoder_free(&q->polar_encoder);
|
||||
srsran_polar_decoder_free(&q->polar_decoder);
|
||||
srsran_polar_rm_rx_free_c(&q->polar_rm_rx);
|
||||
srsran_polar_rm_tx_free(&q->polar_rm_tx);
|
||||
srsran_polar_code_free(&q->code);
|
||||
srsran_modem_table_free(&q->qpsk);
|
||||
SRSRAN_MEM_ZERO(q, srsran_pbch_nr_t, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements TS 38.212 Table 7.1.1-1: Value of PBCH payload interleaver pattern G ( j )
|
||||
*/
|
||||
static const uint32_t G[PBCH_NR_A] = {16, 23, 18, 17, 8, 30, 10, 6, 24, 7, 0, 5, 3, 2, 1, 4,
|
||||
9, 11, 12, 13, 14, 15, 19, 20, 21, 22, 25, 26, 27, 28, 29, 31};
|
||||
|
||||
static void
|
||||
pbch_nr_pbch_msg_pack(const srsran_pbch_nr_cfg_t* cfg, const srsran_pbch_msg_nr_t* msg, uint8_t a[PBCH_NR_A])
|
||||
{
|
||||
// Extract actual payload size
|
||||
uint32_t A_hat = SRSRAN_PBCH_NR_PAYLOAD_SZ;
|
||||
|
||||
// Put SFN in a_hat[A_hat] to a_hat[A_hat + 3]
|
||||
uint32_t j_sfn = 0;
|
||||
a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 3U) & 1U); // 4th LSB of SFN
|
||||
a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 2U) & 1U); // 3th LSB of SFN
|
||||
a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 1U) & 1U); // 2th LSB of SFN
|
||||
a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 0U) & 1U); // 1th LSB of SFN
|
||||
|
||||
// Put HRF in a_hat[A_hat + 4]
|
||||
a[G[10]] = (msg->hrf ? 1 : 0);
|
||||
|
||||
// Put SSB related in a_hat[A_hat + 5] to a_hat[A_hat + 7]
|
||||
if (cfg->Lmax == 64) {
|
||||
a[G[11]] = (uint8_t)((msg->ssb_idx >> 5U) & 1U); // 6th bit of SSB index
|
||||
a[G[12]] = (uint8_t)((msg->ssb_idx >> 4U) & 1U); // 5th bit of SSB index
|
||||
a[G[13]] = (uint8_t)((msg->ssb_idx >> 3U) & 1U); // 4th bit of SSB index
|
||||
} else {
|
||||
a[G[11]] = (uint8_t)msg->k_ssb_msb; // 6th bit of SSB index
|
||||
a[G[12]] = 0; // Reserved
|
||||
a[G[13]] = 0; // Reserved
|
||||
}
|
||||
|
||||
// Put actual payload
|
||||
uint32_t j_other = 14;
|
||||
for (uint32_t i = 0; i < A_hat; i++) {
|
||||
if (i > 0 && i < 7) {
|
||||
a[G[j_sfn++]] = msg->payload[i];
|
||||
} else {
|
||||
a[G[j_other++]] = msg->payload[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
|
||||
PBCH_NR_DEBUG_TX("Packed PBCH bits: ");
|
||||
srsran_vec_fprint_byte(stdout, a, PBCH_NR_A);
|
||||
}
|
||||
}
|
||||
static void
|
||||
pbch_nr_pbch_msg_unpack(const srsran_pbch_nr_cfg_t* cfg, const uint8_t a[PBCH_NR_A], srsran_pbch_msg_nr_t* msg)
|
||||
{
|
||||
if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
|
||||
PBCH_NR_DEBUG_RX("Packed PBCH bits: ");
|
||||
srsran_vec_fprint_byte(stdout, a, PBCH_NR_A);
|
||||
}
|
||||
|
||||
// Extract actual payload size
|
||||
uint32_t A_hat = SRSRAN_PBCH_NR_PAYLOAD_SZ;
|
||||
|
||||
// Put SFN in a_hat[A_hat] to a_hat[A_hat + 3]
|
||||
uint32_t j_sfn = 0;
|
||||
msg->sfn_4lsb = 0;
|
||||
msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 3U); // 4th LSB of SFN
|
||||
msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 2U); // 3th LSB of SFN
|
||||
msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 1U); // 2th LSB of SFN
|
||||
msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 0U); // 1th LSB of SFN
|
||||
|
||||
// Put HRF in a_hat[A_hat + 4]
|
||||
msg->hrf = (a[G[10]] == 1);
|
||||
|
||||
// Put SSB related in a_hat[A_hat + 5] to a_hat[A_hat + 7]
|
||||
if (cfg->Lmax == 64) {
|
||||
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[12]] << 4U); // 5th bit of SSB index
|
||||
msg->ssb_idx |= (uint8_t)(a[G[13]] << 3U); // 4th bit of SSB index
|
||||
} else {
|
||||
msg->k_ssb_msb = a[G[11]];
|
||||
}
|
||||
|
||||
// Put actual payload
|
||||
uint32_t j_other = 14;
|
||||
for (uint32_t i = 0; i < A_hat; i++) {
|
||||
if (i > 0 && i < 7) {
|
||||
msg->payload[i] = a[G[j_sfn++]];
|
||||
} else {
|
||||
msg->payload[i] = a[G[j_other++]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pbch_nr_scramble(const srsran_pbch_nr_cfg_t* cfg, const uint8_t a[PBCH_NR_A], uint8_t a_prime[PBCH_NR_A])
|
||||
{
|
||||
uint32_t i = 0;
|
||||
uint32_t j = 0;
|
||||
|
||||
// Initialise sequence
|
||||
srsran_sequence_state_t sequence_state = {};
|
||||
srsran_sequence_state_init(&sequence_state, SRSRAN_SEQUENCE_MOD(cfg->N_id));
|
||||
|
||||
// Select value M
|
||||
uint32_t M = PBCH_NR_A - 3;
|
||||
if (cfg->Lmax == 64) {
|
||||
M = PBCH_NR_A - 6;
|
||||
}
|
||||
|
||||
// Select value v
|
||||
uint32_t v = 2 * a[G[1]] + a[G[2]];
|
||||
|
||||
// Advance sequence
|
||||
srsran_sequence_state_advance(&sequence_state, M * v);
|
||||
|
||||
// Generate actual sequence
|
||||
uint8_t c[PBCH_NR_A] = {};
|
||||
srsran_sequence_state_apply_bit(&sequence_state, c, c, PBCH_NR_A);
|
||||
|
||||
while (i < PBCH_NR_A) {
|
||||
// a i corresponds to any one of the bits belonging to the SS/PBCH block index, the half frame index, and 2 nd and 3
|
||||
// rd least significant bits of the system frame number
|
||||
uint8_t s_i = c[j];
|
||||
|
||||
// else
|
||||
if (i == G[11] || i == G[12] || i == G[13] || i == G[10] || i == G[1] || i == G[2]) {
|
||||
s_i = 0;
|
||||
} else {
|
||||
j++;
|
||||
}
|
||||
|
||||
a_prime[i] = a[i] ^ s_i;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
static int pbch_nr_polar_encode(srsran_pbch_nr_t* q, const uint8_t c[PBCH_NR_K], uint8_t d[PBCH_NR_N])
|
||||
{
|
||||
// Interleave
|
||||
uint8_t c_prime[SRSRAN_POLAR_INTERLEAVER_K_MAX_IL];
|
||||
srsran_polar_interleaver_run_u8(c, c_prime, PBCH_NR_K, true);
|
||||
|
||||
// Allocate channel
|
||||
uint8_t allocated[PBCH_NR_N];
|
||||
srsran_polar_chanalloc_tx(c_prime, allocated, q->code.N, q->code.K, q->code.nPC, q->code.K_set, q->code.PC_set);
|
||||
|
||||
// Encode bits
|
||||
if (srsran_polar_encoder_encode(&q->polar_encoder, allocated, d, q->code.n) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
|
||||
PBCH_NR_DEBUG_TX("Allocated: ");
|
||||
srsran_vec_fprint_byte(stdout, allocated, PBCH_NR_N);
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
static int pbch_nr_polar_decode(srsran_pbch_nr_t* q, const int8_t d[PBCH_NR_N], uint8_t c[PBCH_NR_K])
|
||||
{
|
||||
// Decode bits
|
||||
uint8_t allocated[PBCH_NR_N];
|
||||
if (srsran_polar_decoder_decode_c(&q->polar_decoder, d, allocated, q->code.n, q->code.F_set, q->code.F_set_size) <
|
||||
SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
|
||||
PBCH_NR_DEBUG_RX("Allocated: ");
|
||||
srsran_vec_fprint_byte(stdout, allocated, PBCH_NR_N);
|
||||
}
|
||||
|
||||
// Allocate channel
|
||||
uint8_t c_prime[SRSRAN_POLAR_INTERLEAVER_K_MAX_IL];
|
||||
srsran_polar_chanalloc_rx(allocated, c_prime, q->code.K, q->code.nPC, q->code.K_set, q->code.PC_set);
|
||||
|
||||
// Interleave
|
||||
srsran_polar_interleaver_run_u8(c_prime, c, PBCH_NR_K, false);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
static int pbch_nr_polar_rm_tx(srsran_pbch_nr_t* q, const uint8_t d[PBCH_NR_N], uint8_t o[PBCH_NR_E])
|
||||
{
|
||||
if (srsran_polar_rm_tx(&q->polar_rm_tx, d, o, q->code.n, PBCH_NR_E, PBCH_NR_K, PBCH_NR_POLAR_RM_IBIL) <
|
||||
SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
|
||||
PBCH_NR_DEBUG_TX("d: ");
|
||||
srsran_vec_fprint_byte(stdout, d, PBCH_NR_N);
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
static int pbch_nr_polar_rm_rx(srsran_pbch_nr_t* q, const int8_t llr[PBCH_NR_E], int8_t d[PBCH_NR_N])
|
||||
{
|
||||
if (srsran_polar_rm_rx_c(&q->polar_rm_rx, llr, d, PBCH_NR_E, q->code.n, PBCH_NR_K, PBCH_NR_POLAR_RM_IBIL) <
|
||||
SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Negate all LLR
|
||||
for (uint32_t i = 0; i < PBCH_NR_N; i++) {
|
||||
d[i] *= -1;
|
||||
}
|
||||
|
||||
if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
|
||||
PBCH_NR_DEBUG_RX("d: ");
|
||||
srsran_vec_fprint_bs(stdout, d, PBCH_NR_N);
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
static void pbch_nr_scramble_tx(const srsran_pbch_nr_cfg_t* cfg,
|
||||
uint32_t ssb_idx,
|
||||
const uint8_t b[PBCH_NR_E],
|
||||
uint8_t b_hat[PBCH_NR_E])
|
||||
{
|
||||
// Initialise sequence
|
||||
srsran_sequence_state_t sequence_state = {};
|
||||
srsran_sequence_state_init(&sequence_state, SRSRAN_SEQUENCE_MOD(cfg->N_id));
|
||||
|
||||
// Select value M
|
||||
uint32_t M_bit = PBCH_NR_E;
|
||||
|
||||
// Select value v
|
||||
uint32_t v = (ssb_idx & 0x7U);
|
||||
if (cfg->Lmax == 4) {
|
||||
v = ssb_idx & 0x3U;
|
||||
}
|
||||
|
||||
// Advance sequence
|
||||
srsran_sequence_state_advance(&sequence_state, v * M_bit);
|
||||
|
||||
// Apply sequence
|
||||
srsran_sequence_state_apply_bit(&sequence_state, b, b_hat, PBCH_NR_E);
|
||||
}
|
||||
|
||||
static void pbch_nr_scramble_rx(const srsran_pbch_nr_cfg_t* cfg,
|
||||
uint32_t ssb_idx,
|
||||
const int8_t b_hat[PBCH_NR_E],
|
||||
int8_t b[PBCH_NR_E])
|
||||
{
|
||||
// Initialise sequence
|
||||
srsran_sequence_state_t sequence_state = {};
|
||||
srsran_sequence_state_init(&sequence_state, SRSRAN_SEQUENCE_MOD(cfg->N_id));
|
||||
|
||||
// Select value M
|
||||
uint32_t M_bit = PBCH_NR_E;
|
||||
|
||||
// Select value v
|
||||
uint32_t v = (ssb_idx & 0x7U);
|
||||
if (cfg->Lmax == 4) {
|
||||
v = ssb_idx & 0x3U;
|
||||
}
|
||||
|
||||
// Advance sequence
|
||||
srsran_sequence_state_advance(&sequence_state, v * M_bit);
|
||||
|
||||
// Apply sequence
|
||||
srsran_sequence_state_apply_c(&sequence_state, b_hat, b, PBCH_NR_E);
|
||||
}
|
||||
|
||||
static void
|
||||
pbch_nr_mapping(const srsran_pbch_nr_cfg_t* cfg, const cf_t symbols[PBCH_NR_M], cf_t ssb_grid[SRSRAN_SSB_NOF_RE])
|
||||
{
|
||||
uint32_t count = 0;
|
||||
|
||||
// PBCH DMRS shift
|
||||
uint32_t v = cfg->N_id % 4;
|
||||
|
||||
// Symbol 1
|
||||
for (uint32_t k = 0; k < SRSRAN_SSB_BW_SUBC; k++) {
|
||||
// Skip DMRS
|
||||
if (k % 4 == v) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ssb_grid[1 * SRSRAN_SSB_BW_SUBC + k] = symbols[count++];
|
||||
}
|
||||
|
||||
// Symbol 2
|
||||
for (uint32_t k = 0; k < 48; k++) {
|
||||
// Skip DMRS
|
||||
if (k % 4 == v) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ssb_grid[2 * SRSRAN_SSB_BW_SUBC + k] = symbols[count++];
|
||||
}
|
||||
for (uint32_t k = 192; k < SRSRAN_SSB_BW_SUBC; k++) {
|
||||
// Skip DMRS
|
||||
if (k % 4 == v) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ssb_grid[2 * SRSRAN_SSB_BW_SUBC + k] = symbols[count++];
|
||||
}
|
||||
|
||||
// Symbol 3
|
||||
for (uint32_t k = 0; k < SRSRAN_SSB_BW_SUBC; k++) {
|
||||
// Skip DMRS
|
||||
if (k % 4 == v) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ssb_grid[3 * SRSRAN_SSB_BW_SUBC + k] = symbols[count++];
|
||||
}
|
||||
|
||||
// if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
|
||||
// PBCH_NR_DEBUG_TX("Symbols: ");
|
||||
// srsran_vec_fprint_c(stdout, symbols, PBCH_NR_M);
|
||||
// }
|
||||
}
|
||||
|
||||
static void
|
||||
pbch_nr_demapping(const srsran_pbch_nr_cfg_t* cfg, const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], cf_t symbols[PBCH_NR_M])
|
||||
{
|
||||
uint32_t count = 0;
|
||||
|
||||
// PBCH DMRS shift
|
||||
uint32_t v = cfg->N_id % 4;
|
||||
|
||||
// Symbol 1
|
||||
for (uint32_t k = 0; k < SRSRAN_SSB_BW_SUBC; k++) {
|
||||
// Skip DMRS
|
||||
if (k % 4 == v) {
|
||||
continue;
|
||||
}
|
||||
|
||||
symbols[count++] = ssb_grid[1 * SRSRAN_SSB_BW_SUBC + k];
|
||||
}
|
||||
|
||||
// Symbol 2
|
||||
for (uint32_t k = 0; k < 48; k++) {
|
||||
// Skip DMRS
|
||||
if (k % 4 == v) {
|
||||
continue;
|
||||
}
|
||||
|
||||
symbols[count++] = ssb_grid[2 * SRSRAN_SSB_BW_SUBC + k];
|
||||
}
|
||||
for (uint32_t k = 192; k < SRSRAN_SSB_BW_SUBC; k++) {
|
||||
// Skip DMRS
|
||||
if (k % 4 == v) {
|
||||
continue;
|
||||
}
|
||||
|
||||
symbols[count++] = ssb_grid[2 * SRSRAN_SSB_BW_SUBC + k];
|
||||
}
|
||||
|
||||
// Symbol 3
|
||||
for (uint32_t k = 0; k < SRSRAN_SSB_BW_SUBC; k++) {
|
||||
// Skip DMRS
|
||||
if (k % 4 == v) {
|
||||
continue;
|
||||
}
|
||||
|
||||
symbols[count++] = ssb_grid[3 * SRSRAN_SSB_BW_SUBC + k];
|
||||
}
|
||||
|
||||
// if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
|
||||
// PBCH_NR_DEBUG_RX("Symbols: ");
|
||||
// srsran_vec_fprint_c(stdout, symbols, PBCH_NR_M);
|
||||
// }
|
||||
}
|
||||
|
||||
int srsran_pbch_nr_encode(srsran_pbch_nr_t* q,
|
||||
const srsran_pbch_nr_cfg_t* cfg,
|
||||
const srsran_pbch_msg_nr_t* msg,
|
||||
cf_t ssb_grid[SRSRAN_SSB_NOF_RE])
|
||||
{
|
||||
if (q == NULL || cfg == NULL || msg == NULL || ssb_grid == NULL) {
|
||||
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
|
||||
// TS 38.212 7.1 Broadcast channel
|
||||
// 7.1.1 PBCH payload generation
|
||||
uint8_t a[PBCH_NR_A];
|
||||
pbch_nr_pbch_msg_pack(cfg, msg, a);
|
||||
|
||||
// 7.1.2 Scrambling
|
||||
uint8_t b[PBCH_NR_K];
|
||||
pbch_nr_scramble(cfg, a, b);
|
||||
|
||||
// 7.1.3 Transport block CRC attachment
|
||||
uint32_t checksum = srsran_crc_attach(&q->crc, b, PBCH_NR_A);
|
||||
PBCH_NR_DEBUG_TX("checksum=%06x", checksum);
|
||||
|
||||
// 7.1.4 Channel coding
|
||||
uint8_t d[PBCH_NR_N];
|
||||
if (pbch_nr_polar_encode(q, b, d) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// 7.1.5 Rate matching
|
||||
uint8_t f[PBCH_NR_E];
|
||||
if (pbch_nr_polar_rm_tx(q, d, f) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// TS 38.211 7.3.3 Physical broadcast channel
|
||||
// 7.3.3.1 Scrambling
|
||||
pbch_nr_scramble_tx(cfg, msg->ssb_idx, f, f);
|
||||
|
||||
// 7.3.3.2 Modulation
|
||||
cf_t symbols[PBCH_NR_M];
|
||||
srsran_mod_modulate(&q->qpsk, f, symbols, PBCH_NR_E);
|
||||
|
||||
// 7.3.3.3 Mapping to physical resources
|
||||
// 7.4.3.1.3 Mapping of PBCH and DM-RS within an SS/PBCH block
|
||||
pbch_nr_mapping(cfg, symbols, ssb_grid);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int srsran_pbch_nr_decode(srsran_pbch_nr_t* q,
|
||||
const srsran_pbch_nr_cfg_t* cfg,
|
||||
uint32_t ssb_idx,
|
||||
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
|
||||
const cf_t ce_grid[SRSRAN_SSB_NOF_RE],
|
||||
srsran_pbch_msg_nr_t* msg)
|
||||
{
|
||||
if (q == NULL || cfg == NULL || msg == NULL || ssb_grid == NULL) {
|
||||
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
|
||||
// 7.3.3.3 Mapping to physical resources
|
||||
// 7.4.3.1.3 Mapping of PBCH and DM-RS within an SS/PBCH block
|
||||
srsran_simd_aligned cf_t symbols[PBCH_NR_M];
|
||||
pbch_nr_demapping(cfg, ssb_grid, symbols);
|
||||
|
||||
srsran_simd_aligned cf_t ce[PBCH_NR_M];
|
||||
pbch_nr_demapping(cfg, ce_grid, ce);
|
||||
|
||||
// Channel equalizer
|
||||
if (srsran_predecoding_single(symbols, ce, symbols, NULL, PBCH_NR_M, 1.0f, 0.0f) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error in predecoder");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// 7.3.3.2 Modulation
|
||||
int8_t llr[PBCH_NR_E];
|
||||
srsran_demod_soft_demodulate_b(SRSRAN_MOD_QPSK, symbols, llr, PBCH_NR_M);
|
||||
|
||||
// TS 38.211 7.3.3 Physical broadcast channel
|
||||
// 7.3.3.1 Scrambling
|
||||
pbch_nr_scramble_rx(cfg, ssb_idx, llr, llr);
|
||||
|
||||
// 7.1.5 Rate matching
|
||||
int8_t d[PBCH_NR_N];
|
||||
if (pbch_nr_polar_rm_rx(q, llr, d) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// TS 38.212 7.1 Broadcast channel
|
||||
// 7.1.4 Channel coding
|
||||
uint8_t b[PBCH_NR_K];
|
||||
if (pbch_nr_polar_decode(q, d, b) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// 7.1.3 Transport block CRC attachment
|
||||
msg->crc = srsran_crc_match(&q->crc, b, PBCH_NR_A);
|
||||
PBCH_NR_DEBUG_RX("crc=%s", msg->crc ? "OK" : "KO");
|
||||
|
||||
// 7.1.2 Scrambling
|
||||
uint8_t a[PBCH_NR_A];
|
||||
pbch_nr_scramble(cfg, b, a);
|
||||
|
||||
// 7.1.1 PBCH payload generation
|
||||
pbch_nr_pbch_msg_unpack(cfg, a, msg);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t srsran_pbch_msg_info(const srsran_pbch_msg_nr_t* msg, char* str, uint32_t str_len)
|
||||
{
|
||||
if (msg == NULL || str == NULL || str_len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t len = 0;
|
||||
|
||||
len = srsran_print_check(str, str_len, len, "payload=");
|
||||
|
||||
len += srsran_vec_sprint_hex(&str[len], str_len - len, (uint8_t*)msg->payload, SRSRAN_PBCH_NR_PAYLOAD_SZ);
|
||||
|
||||
len = srsran_print_check(str,
|
||||
str_len,
|
||||
len,
|
||||
" sfn_lsb=%d ssb_idx=%d k_ssb_msb=%d hrf=%d ",
|
||||
msg->sfn_4lsb,
|
||||
msg->ssb_idx,
|
||||
msg->k_ssb_msb,
|
||||
msg->hrf);
|
||||
|
||||
return len;
|
||||
}
|
@ -0,0 +1,210 @@
|
||||
/**
|
||||
*
|
||||
* \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 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 cf_t wideband_gain = 1.0f + 0.5 * I;
|
||||
static int32_t delay_n = 1;
|
||||
static float cfo_hz = 1000.0f;
|
||||
static float n0_dB = -10.0f;
|
||||
|
||||
// Test context
|
||||
static srsran_random_t random_gen = NULL;
|
||||
static srsran_channel_awgn_t awgn = {};
|
||||
static double srate_hz = 0.0f; // Base-band sampling rate
|
||||
static uint32_t hf_len = 0; // Half-frame length
|
||||
static cf_t* buffer = NULL; // Base-band buffer
|
||||
|
||||
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 < hf_len; i++) {
|
||||
buffer[i] = buffer[(i + delay_n) % hf_len];
|
||||
}
|
||||
|
||||
// CFO
|
||||
srsran_vec_apply_cfo(buffer, -cfo_hz / srate_hz, buffer, hf_len);
|
||||
|
||||
// AWGN
|
||||
srsran_channel_awgn_run_c(&awgn, buffer, buffer, hf_len);
|
||||
|
||||
// Wideband gain
|
||||
srsran_vec_sc_prod_ccc(buffer, wideband_gain, buffer, hf_len);
|
||||
}
|
||||
|
||||
static void gen_pbch_msg(srsran_pbch_msg_nr_t* pbch_msg, uint32_t ssb_idx)
|
||||
{
|
||||
// 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_NR_PAYLOAD_SZ);
|
||||
|
||||
pbch_msg->ssb_idx = ssb_idx;
|
||||
}
|
||||
|
||||
static int test_case_1(srsran_ssb_t* ssb)
|
||||
{
|
||||
// For benchmarking purposes
|
||||
uint64_t t_encode_usec = 0;
|
||||
uint64_t t_decode_usec = 0;
|
||||
|
||||
// SSB configuration
|
||||
srsran_ssb_cfg_t ssb_cfg = {};
|
||||
ssb_cfg.srate_hz = srate_hz;
|
||||
ssb_cfg.center_freq_hz = 3.5e9;
|
||||
ssb_cfg.ssb_freq_hz = 3.5e9 - 960e3;
|
||||
ssb_cfg.scs = ssb_scs;
|
||||
ssb_cfg.pattern = SRSRAN_SSB_PATTERN_C;
|
||||
|
||||
TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS);
|
||||
|
||||
// For each PCI...
|
||||
uint64_t count = 0;
|
||||
for (uint32_t pci = 0; pci < SRSRAN_NOF_NID_NR; pci += 23) {
|
||||
for (uint32_t ssb_idx = 0; ssb_idx < ssb->Lmax; ssb_idx++, count++) {
|
||||
struct timeval t[3] = {};
|
||||
|
||||
// Build PBCH message
|
||||
srsran_pbch_msg_nr_t pbch_msg_tx = {};
|
||||
gen_pbch_msg(&pbch_msg_tx, ssb_idx);
|
||||
|
||||
// Print encoded PBCH message
|
||||
char str[512] = {};
|
||||
srsran_pbch_msg_info(&pbch_msg_tx, str, sizeof(str));
|
||||
INFO("test_case_1 - encoded pci=%d %s", pci, str);
|
||||
|
||||
// Initialise baseband
|
||||
srsran_vec_cf_zero(buffer, hf_len);
|
||||
|
||||
// Add the SSB base-band
|
||||
gettimeofday(&t[1], NULL);
|
||||
TESTASSERT(srsran_ssb_add(ssb, pci, ssb_idx, &pbch_msg_tx, buffer, buffer) == SRSRAN_SUCCESS);
|
||||
gettimeofday(&t[2], NULL);
|
||||
get_time_interval(t);
|
||||
t_encode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
|
||||
|
||||
// Run channel
|
||||
run_channel();
|
||||
|
||||
// Decode
|
||||
gettimeofday(&t[1], NULL);
|
||||
srsran_pbch_msg_nr_t pbch_msg_rx = {};
|
||||
TESTASSERT(srsran_ssb_decode_pbch(ssb, pci, ssb_idx, 0, buffer, &pbch_msg_rx) == SRSRAN_SUCCESS);
|
||||
gettimeofday(&t[2], NULL);
|
||||
get_time_interval(t);
|
||||
t_decode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
|
||||
|
||||
// Print decoded PBCH message
|
||||
srsran_pbch_msg_info(&pbch_msg_rx, str, sizeof(str));
|
||||
INFO("test_case_1 - decoded pci=%d %s crc=%s", pci, str, pbch_msg_rx.crc ? "OK" : "KO");
|
||||
|
||||
// Assert PBCH message CRC
|
||||
TESTASSERT(pbch_msg_rx.crc);
|
||||
}
|
||||
}
|
||||
|
||||
INFO("test_case_1 - %.1f usec/encode; %.1f usec/decode;",
|
||||
(double)t_encode_usec / (double)(count),
|
||||
(double)t_decode_usec / (double)(count));
|
||||
|
||||
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(carrier_nof_prb);
|
||||
hf_len = (uint32_t)ceil(srate_hz * (5.0 / 1000.0));
|
||||
buffer = srsran_vec_cf_malloc(hf_len);
|
||||
|
||||
srsran_ssb_t ssb = {};
|
||||
srsran_ssb_args_t ssb_args = {};
|
||||
ssb_args.enable_encode = true;
|
||||
ssb_args.enable_decode = 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_random_free(random_gen);
|
||||
srsran_ssb_free(&ssb);
|
||||
|
||||
srsran_channel_awgn_free(&awgn);
|
||||
|
||||
if (buffer) {
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue