diff --git a/lib/include/srsran/phy/phch/pbch_nr.h b/lib/include/srsran/phy/phch/pbch_nr.h index 6bf9318e3..7a9a41d3d 100644 --- a/lib/include/srsran/phy/phch/pbch_nr.h +++ b/lib/include/srsran/phy/phch/pbch_nr.h @@ -13,13 +13,102 @@ #ifndef SRSRAN_PBCH_NR_H #define SRSRAN_PBCH_NR_H -#include "srsran/config.h" +#include "srsran/phy/common/phy_common_nr.h" +#include "srsran/phy/fec/crc.h" +#include "srsran/phy/fec/polar/polar_code.h" +#include "srsran/phy/fec/polar/polar_decoder.h" +#include "srsran/phy/fec/polar/polar_encoder.h" +#include "srsran/phy/fec/polar/polar_rm.h" +#include "srsran/phy/modem/modem_table.h" /** - * @brief Descibes the NR PBCH message + * @brief NR PBCH payload size generated by higher layers, deduced from TS 38.331 MIB description + */ +#define SRSRAN_PBCH_NR_PAYLOAD_SZ 24 + +/** + * @brief Describes the NR PBCH object initialisation arguments */ typedef struct SRSRAN_API { - void* TBD; + bool enable_encode; ///< Enable encoder + bool enable_decode; ///< Enable decoder + bool disable_simd; ///< Disable SIMD polar encoder/decoder +} srsran_pbch_nr_args_t; + +/** + * @brief Describes the NR PBCH configuration + */ +typedef struct SRSRAN_API { + uint32_t N_id; ///< Physical cell identifier + srsran_subcarrier_spacing_t ssb_scs; ///< SSB Subcarrier spacing + uint32_t Lmax; ///< Number of SSB opportunities, described in TS 38.213 4.1 ... + float beta; ///< Scaling factor for PBCH symbols, set to zero for default + float beta_dmrs; ///< Scaling factor for PBCH DM-RS, set to zero for default +} srsran_pbch_nr_cfg_t; + +/** + * @brief Describes the NR PBCH object initialisation arguments + */ +typedef struct SRSRAN_API { + srsran_polar_code_t code; + srsran_polar_encoder_t polar_encoder; + srsran_polar_decoder_t polar_decoder; + srsran_polar_rm_t polar_rm_tx; + srsran_polar_rm_t polar_rm_rx; + srsran_crc_t crc; + srsran_modem_table_t qpsk; +} srsran_pbch_nr_t; + +/** + * @brief Describes the PBCH message + */ +typedef struct SRSRAN_API { + uint8_t payload[SRSRAN_PBCH_NR_PAYLOAD_SZ]; ///< Actual PBCH payload provided by higher layers + uint32_t sfn_4lsb; ///< SFN 4 LSB + uint32_t ssb_idx; ///< SS/PBCH blocks index described in TS 38.213 4.1 + uint32_t k_ssb_msb; ///< Subcarrier offset MSB described in TS 38.211 7.4.3.1 + uint32_t hrf; ///< Half Radio Frame bit + bool crc; ///< Decoder only, it is true only if the received CRC matches } srsran_pbch_msg_nr_t; +/** + * @brief Initialises an NR PBCH object with the provided arguments + * @param q NR PBCH object + * @param args Arguments providing the desired configuration + * @return SRSRAN_SUCCESS if initialization is successful, SRSLTE_ERROR code otherwise + */ +SRSRAN_API int srsran_pbch_nr_init(srsran_pbch_nr_t* q, const srsran_pbch_nr_args_t* args); + +/** + * @brief Deallocates an NR PBCH object + * @param q NR PBCH object + */ +SRSRAN_API void srsran_pbch_nr_free(srsran_pbch_nr_t* q); + +/** + * @brief Encodes an NR PBCH message into a SSB resource grid + * @param q NR PBCH object + * @param cfg NR PBCH configuration + * @param msg NR PBCH message to transmit + * @param[out] ssb_grid SSB resource grid + * @return SRSRAN_SUCCESS if encoding is successful, SRSLTE_ERROR code otherwise + */ +SRSRAN_API 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]); + +/** + * @brief Decodes an NR PBCH message in the SSB resource grid + * @param q NR PBCH object + * @param cfg NR PBCH configuration + * @param[in] ssb_grid SSB resource grid + * @param msg NR PBCH message received + * @return SRSRAN_SUCCESS if decoding is successful, SRSLTE_ERROR code otherwise + */ +SRSRAN_API int srsran_pbch_nr_decode(srsran_pbch_nr_t* q, + const srsran_pbch_nr_cfg_t* cfg, + const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], + srsran_pbch_msg_nr_t* msg); + #endif // SRSRAN_PBCH_NR_H diff --git a/lib/include/srsran/phy/sync/ssb.h b/lib/include/srsran/phy/sync/ssb.h index 31db87415..3a65fe9d5 100644 --- a/lib/include/srsran/phy/sync/ssb.h +++ b/lib/include/srsran/phy/sync/ssb.h @@ -43,12 +43,13 @@ * @brief Describes SSB object initialization arguments */ typedef struct SRSRAN_API { - double max_srate_hz; ///< Maximum sampling rate in Hz, set to zero to use default - srsran_subcarrier_spacing_t min_scs; ///< Minimum subcarrier spacing - bool enable_search; ///< Enables PSS/SSS blind search - bool enable_measure; ///< Enables PSS/SSS CSI measurements and frequency domain search - bool enable_encode; ///< Enables PBCH Encoder - bool enable_decode; ///< Enables PBCH Decoder + double max_srate_hz; ///< Maximum sampling rate in Hz, set to zero to use default + srsran_subcarrier_spacing_t min_scs; ///< Minimum subcarrier spacing + bool enable_search; ///< Enables PSS/SSS blind search + bool enable_measure; ///< Enables PSS/SSS CSI measurements and frequency domain search + bool enable_encode; ///< Enables PBCH Encoder + bool enable_decode; ///< Enables PBCH Decoder + bool disable_polar_simd; ///< Disables polar encoder/decoder SIMD acceleration } srsran_ssb_args_t; /** @@ -87,11 +88,15 @@ typedef struct SRSRAN_API { uint32_t t_offset; ///< Current SSB integer time offset (number of samples) uint32_t cp_sz[SRSRAN_SSB_DURATION_NSYMB]; ///< CP length for each SSB symbol + /// Other parameters + uint32_t Lmax; ///< Number of SSB candidates + /// Internal Objects srsran_dft_plan_t ifft; ///< IFFT object for modulating the SSB srsran_dft_plan_t fft; ///< FFT object for demodulate the SSB. srsran_dft_plan_t fft_corr; ///< FFT for correlation srsran_dft_plan_t ifft_corr; ///< IFFT for correlation + srsran_pbch_nr_t pbch; ///< PBCH encoder and decoder /// Frequency/Time domain temporal data cf_t* tmp_freq; ///< Temporal frequency domain buffer diff --git a/lib/include/srsran/srsran.h b/lib/include/srsran/srsran.h index 81348d4a0..b3bfd641f 100644 --- a/lib/include/srsran/srsran.h +++ b/lib/include/srsran/srsran.h @@ -86,6 +86,7 @@ extern "C" { #include "srsran/phy/phch/dci.h" #include "srsran/phy/phch/dci_nr.h" #include "srsran/phy/phch/pbch.h" +#include "srsran/phy/phch/pbch_nr.h" #include "srsran/phy/phch/pcfich.h" #include "srsran/phy/phch/pdcch.h" #include "srsran/phy/phch/pdcch_nr.h" diff --git a/lib/src/phy/fec/polar/polar_rm.c b/lib/src/phy/fec/polar/polar_rm.c index b22b64cbf..bffd98dc4 100644 --- a/lib/src/phy/fec/polar/polar_rm.c +++ b/lib/src/phy/fec/polar/polar_rm.c @@ -380,7 +380,6 @@ int srsran_polar_rm_tx_init(srsran_polar_rm_t* p) int srsran_polar_rm_rx_init_f(srsran_polar_rm_t* p) { - if (p == NULL) { return -1; } @@ -405,7 +404,6 @@ int srsran_polar_rm_rx_init_f(srsran_polar_rm_t* p) int srsran_polar_rm_rx_init_s(srsran_polar_rm_t* p) { - if (p == NULL) { return -1; } @@ -430,7 +428,6 @@ int srsran_polar_rm_rx_init_s(srsran_polar_rm_t* p) int srsran_polar_rm_rx_init_c(srsran_polar_rm_t* p) { - if (p == NULL) { return -1; } @@ -455,41 +452,74 @@ int srsran_polar_rm_rx_init_c(srsran_polar_rm_t* p) void srsran_polar_rm_tx_free(srsran_polar_rm_t* q) { - if (q != NULL) { - struct pRM_tx* qq = q->ptr; - free(qq->y_e); - free(qq); + if (q == NULL) { + return; } + + struct pRM_tx* qq = q->ptr; + if (qq == NULL) { + return; + } + + if (qq->y_e) { + free(qq->y_e); + } + + free(qq); } void srsran_polar_rm_rx_free_f(srsran_polar_rm_t* q) { - if (q != NULL) { - struct pRM_rx_f* qq = q->ptr; - free(qq->y_e); - // free(qq->indices); - free(qq); + if (q == NULL) { + return; } + + struct pRM_rx_f* qq = q->ptr; + if (qq == NULL) { + return; + } + + if (qq->y_e) { + free(qq->y_e); + } + + free(qq); } void srsran_polar_rm_rx_free_s(srsran_polar_rm_t* q) { - if (q != NULL) { - struct pRM_rx_s* qq = q->ptr; - free(qq->y_e); - // free(qq->indices); - free(qq); + if (q == NULL) { + return; } + + struct pRM_rx_s* qq = q->ptr; + if (qq == NULL) { + return; + } + + if (qq->y_e) { + free(qq->y_e); + } + + free(qq); } void srsran_polar_rm_rx_free_c(srsran_polar_rm_t* q) { - if (q != NULL) { - struct pRM_rx_c* qq = q->ptr; - free(qq->y_e); - // free(qq->indices); - free(qq); + if (q == NULL) { + return; } + + struct pRM_rx_c* qq = q->ptr; + if (qq == NULL) { + return; + } + + if (qq->y_e) { + free(qq->y_e); + } + + free(qq); } int srsran_polar_rm_tx(srsran_polar_rm_t* q, @@ -529,7 +559,6 @@ int srsran_polar_rm_rx_f(srsran_polar_rm_t* q, const uint32_t K, const uint8_t ibil) { - struct pRM_rx_f* pp = q->ptr; float* y = NULL; float* e = pp->e; // length E @@ -557,7 +586,6 @@ int srsran_polar_rm_rx_s(srsran_polar_rm_t* q, const uint32_t K, const uint8_t ibil) { - struct pRM_rx_s* pp = q->ptr; int16_t* y = NULL; int16_t* e = pp->e; @@ -585,7 +613,6 @@ int srsran_polar_rm_rx_c(srsran_polar_rm_t* q, const uint32_t K, const uint8_t ibil) { - struct pRM_rx_c* pp = q->ptr; int8_t* y = NULL; int8_t* e = pp->e; diff --git a/lib/src/phy/phch/pbch_nr.c b/lib/src/phy/phch/pbch_nr.c new file mode 100644 index 000000000..015963ae1 --- /dev/null +++ b/lib/src/phy/phch/pbch_nr.c @@ -0,0 +1,404 @@ +/** + * + * \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/modem/mod.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" + +/* + * 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_tx_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) + +/* + * Number of DMRS + */ +#define PBCH_NR_NOF_DMRS (143) + +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]; + } + } +} + +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_K]) +{ + // Allocate channel + uint8_t allocated[PBCH_NR_N]; + srsran_polar_chanalloc_tx(c, 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; + } + + return SRSRAN_SUCCESS; +} + +static int pbch_nr_polar_rm_tx(srsran_pbch_nr_t* q, const uint8_t d[PBCH_NR_K], 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_tx_IBIL) < + SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + 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_mapping(const srsran_pbch_nr_cfg_t* cfg, const cf_t symbols[PBCH_NR_E], 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++]; + } +} + +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); + INFO("NR-PBCH: checksum=%06x", checksum); + + // 7.1.4 Channel coding + uint8_t d[PBCH_NR_K]; + 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; +} diff --git a/lib/src/phy/sync/ssb.c b/lib/src/phy/sync/ssb.c index a5a08594e..9a7ecf91a 100644 --- a/lib/src/phy/sync/ssb.c +++ b/lib/src/phy/sync/ssb.c @@ -53,6 +53,25 @@ static int ssb_init_corr(srsran_ssb_t* q) return SRSRAN_SUCCESS; } +static int ssb_init_pbch(srsran_ssb_t* q) +{ + srsran_pbch_nr_args_t args = {}; + args.enable_encode = q->args.enable_encode; + args.enable_decode = q->args.enable_decode; + args.disable_simd = q->args.disable_polar_simd; + + if (!args.enable_encode && !args.enable_decode) { + return SRSRAN_SUCCESS; + } + + if (srsran_pbch_nr_init(&q->pbch, &args) < SRSRAN_SUCCESS) { + ERROR("Error init NR PBCH"); + return SRSRAN_SUCCESS; + } + + return SRSRAN_SUCCESS; +} + int srsran_ssb_init(srsran_ssb_t* q, const srsran_ssb_args_t* args) { // Verify input parameters @@ -86,6 +105,11 @@ int srsran_ssb_init(srsran_ssb_t* q, const srsran_ssb_args_t* args) return SRSRAN_ERROR; } + // PBCH + if (ssb_init_pbch(q) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; } @@ -118,6 +142,7 @@ void srsran_ssb_free(srsran_ssb_t* q) srsran_dft_plan_free(&q->fft); srsran_dft_plan_free(&q->fft_corr); srsran_dft_plan_free(&q->ifft_corr); + srsran_pbch_nr_free(&q->pbch); SRSRAN_MEM_ZERO(q, srsran_ssb_t, 1); } @@ -228,9 +253,8 @@ static uint32_t ssb_first_symbol_caseE(const srsran_ssb_cfg_t* cfg, uint32_t ind return count; } -static int ssb_first_symbol(const srsran_ssb_cfg_t* cfg, uint32_t ssb_i) +static uint32_t ssb_candidates(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION]) { - uint32_t indexes[SRSRAN_SSB_NOF_POSITION]; uint32_t Lmax = 0; switch (cfg->pattern) { @@ -251,12 +275,17 @@ static int ssb_first_symbol(const srsran_ssb_cfg_t* cfg, uint32_t ssb_i) break; case SRSRAN_SSB_PATTERN_INVALID: ERROR("Invalid case"); - return SRSRAN_ERROR; } + return Lmax; +} + +static int ssb_first_symbol(const srsran_ssb_cfg_t* cfg, uint32_t ssb_i, uint32_t* Lmax) +{ + uint32_t indexes[SRSRAN_SSB_NOF_POSITION]; + *Lmax = ssb_candidates(cfg, indexes); uint32_t ssb_count = 0; - - for (uint32_t i = 0; i < Lmax; i++) { + for (uint32_t i = 0; i < *Lmax; i++) { // There is a SSB transmission opportunity if (cfg->position[i]) { // Return the SSB transmission in burst @@ -380,7 +409,7 @@ int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg) q->scs_hz = (float)SRSRAN_SUBC_SPACING_NR(cfg->scs); // Get first symbol - int l_begin = ssb_first_symbol(cfg, 0); + int l_begin = ssb_first_symbol(cfg, 0, &q->Lmax); if (l_begin < SRSRAN_SUCCESS) { // set it to 2 in case it is not selected l_begin = 2; @@ -531,7 +560,14 @@ int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* m // ... // Put PBCH payload - // ... + srsran_pbch_nr_cfg_t pbch_cfg = {}; + pbch_cfg.N_id = N_id; + pbch_cfg.ssb_scs = q->cfg.scs; + pbch_cfg.Lmax = q->Lmax; + if (srsran_pbch_nr_encode(&q->pbch, &pbch_cfg, msg, ssb_grid) < SRSRAN_SUCCESS) { + ERROR("Error encoding PBCH"); + return SRSRAN_ERROR; + } // Select input/ouput pointers considering the time offset in the slot const cf_t* in_ptr = &in[q->t_offset];