diff --git a/lib/include/srsran/interfaces/rrc_nr_interface_types.h b/lib/include/srsran/interfaces/rrc_nr_interface_types.h index e7eee83df..a1725e734 100644 --- a/lib/include/srsran/interfaces/rrc_nr_interface_types.h +++ b/lib/include/srsran/interfaces/rrc_nr_interface_types.h @@ -29,9 +29,9 @@ struct phy_cfg_nr_t { * SSB configuration */ struct ssb_cfg_t { - uint32_t periodicity_ms; - std::array position_in_burst; - srsran_subcarrier_spacing_t scs; + uint32_t periodicity_ms; + std::array position_in_burst; + srsran_subcarrier_spacing_t scs; }; srsran_tdd_config_nr_t tdd = {}; diff --git a/lib/include/srsran/phy/phch/pbch_nr.h b/lib/include/srsran/phy/phch/pbch_nr.h index 7a9a41d3d..f849f4285 100644 --- a/lib/include/srsran/phy/phch/pbch_nr.h +++ b/lib/include/srsran/phy/phch/pbch_nr.h @@ -63,12 +63,12 @@ typedef struct SRSRAN_API { * @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 + uint8_t payload[SRSRAN_PBCH_NR_PAYLOAD_SZ]; ///< Actual PBCH payload provided by higher layers + uint8_t sfn_4lsb; ///< SFN 4 LSB + uint8_t ssb_idx; ///< SS/PBCH blocks index described in TS 38.213 4.1 + uint8_t k_ssb_msb; ///< Subcarrier offset MSB described in TS 38.211 7.4.3.1 + bool hrf; ///< Half Radio Frame bit + bool crc; ///< Decoder only, it is true only if the received CRC matches } srsran_pbch_msg_nr_t; /** @@ -102,13 +102,17 @@ SRSRAN_API int srsran_pbch_nr_encode(srsran_pbch_nr_t* q, * @brief Decodes an NR PBCH message in the SSB resource grid * @param q NR PBCH object * @param cfg NR PBCH configuration + * @param ssb_idx SSB candidate index * @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, + uint32_t ssb_idx, const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], srsran_pbch_msg_nr_t* msg); +SRSRAN_API uint32_t srsran_pbch_msg_info(const srsran_pbch_msg_nr_t* msg, char* str, uint32_t str_len); + #endif // SRSRAN_PBCH_NR_H diff --git a/lib/include/srsran/phy/sync/ssb.h b/lib/include/srsran/phy/sync/ssb.h index 3a65fe9d5..033d670a6 100644 --- a/lib/include/srsran/phy/sync/ssb.h +++ b/lib/include/srsran/phy/sync/ssb.h @@ -37,7 +37,7 @@ /** * @brief Maximum number of SSB positions in burst. Defined in TS 38.331 ServingCellConfigCommon, ssb-PositionsInBurst */ -#define SRSRAN_SSB_NOF_POSITION 64 +#define SRSRAN_SSB_NOF_CANDIDATES 64 /** * @brief Describes SSB object initialization arguments @@ -61,13 +61,12 @@ typedef struct SRSRAN_API { double ssb_freq_hz; ///< SSB center frequency srsran_subcarrier_spacing_t scs; ///< SSB configured Subcarrier spacing srsran_ssb_patern_t pattern; ///< SSB pattern as defined in TS 38.313 section 4.1 Cell search - bool position[SRSRAN_SSB_NOF_POSITION]; ///< Indicates the time domain positions of the transmitted SS-blocks - srsran_duplex_mode_t duplex_mode; ///< Set to true if the spectrum is paired (FDD) - uint32_t periodicity_ms; ///< SSB periodicity in ms - float beta_pss; ////< PSS power allocation - float beta_sss; ////< SSS power allocation - float beta_pbch; ////< PBCH power allocation - float beta_pbch_dmrs; ////< PBCH DMRS power allocation + srsran_duplex_mode_t duplex_mode; ///< Set to true if the spectrum is paired (FDD) + uint32_t periodicity_ms; ///< SSB periodicity in ms + float beta_pss; ////< PSS power allocation + float beta_sss; ////< SSS power allocation + float beta_pbch; ////< PBCH power allocation + float beta_pbch_dmrs; ////< PBCH DMRS power allocation } srsran_ssb_cfg_t; /** @@ -78,18 +77,18 @@ typedef struct SRSRAN_API { srsran_ssb_cfg_t cfg; ///< Stores last configuration /// Sampling rate dependent parameters - float scs_hz; ///< Subcarrier spacing in Hz - uint32_t max_symbol_sz; ///< Maximum symbol size given the minimum supported SCS and sampling rate - uint32_t max_corr_sz; ///< Maximum correlation size - uint32_t symbol_sz; ///< Current SSB symbol size (for the given base-band sampling rate) - uint32_t corr_sz; ///< Correlation size - uint32_t corr_window; ///< Correlation window length - int32_t f_offset; ///< Current SSB integer frequency offset (multiple of SCS) - 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 + float scs_hz; ///< Subcarrier spacing in Hz + uint32_t max_symbol_sz; ///< Maximum symbol size given the minimum supported SCS and sampling rate + uint32_t max_corr_sz; ///< Maximum correlation size + uint32_t symbol_sz; ///< Current SSB symbol size (for the given base-band sampling rate) + uint32_t corr_sz; ///< Correlation size + uint32_t corr_window; ///< Correlation window length + int32_t f_offset; ///< Current SSB integer frequency offset (multiple of SCS) + uint32_t cp_sz; ///< CP length for the given symbol size /// Other parameters - uint32_t Lmax; ///< Number of SSB candidates + uint32_t l_first[SRSRAN_SSB_NOF_CANDIDATES]; ///< Start symbol for each SSB candidate in half radio frame + uint32_t Lmax; ///< Number of SSB candidates /// Internal Objects srsran_dft_plan_t ifft; ///< IFFT object for modulating the SSB @@ -129,9 +128,12 @@ 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 * @param q SSB object + * @param N_id Physical Cell Identifier + * @param ssb_idx SSB candidate index * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise */ -SRSRAN_API int srsran_ssb_decode_pbch(srsran_ssb_t* q, const cf_t* in, srsran_pbch_msg_nr_t* msg); +SRSRAN_API int +srsran_ssb_decode_pbch(srsran_ssb_t* q, uint32_t N_id, uint32_t ssb_idx, const cf_t* in, srsran_pbch_msg_nr_t* msg); /** * @brief Decides if the SSB object is configured and a given subframe is configured for SSB transmission @@ -145,11 +147,16 @@ SRSRAN_API bool srsran_ssb_send(srsran_ssb_t* q, uint32_t sf_idx); * @brief Adds SSB to a given signal in time domain * @param q SSB object * @param N_id Physical Cell Identifier + * @param ssb_idx SSB candidate index * @param msg NR PBCH message to transmit * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise */ -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); +SRSRAN_API int srsran_ssb_add(srsran_ssb_t* q, + uint32_t N_id, + uint32_t ssb_idx, + const srsran_pbch_msg_nr_t* msg, + const cf_t* in, + cf_t* out); /** * @brief Perform cell search and measurement @@ -170,11 +177,15 @@ SRSRAN_API int srsran_ssb_csi_search(srsran_ssb_t* q, * @brief Perform Channel State Information (CSI) measurement from the SSB * @param q NR PSS object * @param N_id Physical Cell Identifier + * @param ssb_idx SSB candidate index * @param in Base-band signal * @param meas SSB-based CSI measurement * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise */ -SRSRAN_API int -srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsran_csi_trs_measurements_t* meas); +SRSRAN_API int srsran_ssb_csi_measure(srsran_ssb_t* q, + uint32_t N_id, + uint32_t ssb_idx, + const cf_t* in, + srsran_csi_trs_measurements_t* meas); #endif // SRSRAN_SSB_H diff --git a/lib/src/asn1/rrc_nr_utils.cc b/lib/src/asn1/rrc_nr_utils.cc index 022c4fc08..71746d106 100644 --- a/lib/src/asn1/rrc_nr_utils.cc +++ b/lib/src/asn1/rrc_nr_utils.cc @@ -1300,10 +1300,10 @@ bool make_phy_carrier_cfg(const freq_info_dl_s& asn1_freq_info_dl, srsran_carrie } template -static inline void make_ssb_positions_in_burst(const bitstring_t& ans1_position_in_burst, - std::array& position_in_burst) +static inline void make_ssb_positions_in_burst(const bitstring_t& ans1_position_in_burst, + std::array& position_in_burst) { - for (uint32_t i = 0; i < SRSRAN_SSB_NOF_POSITION; i++) { + for (uint32_t i = 0; i < SRSRAN_SSB_NOF_CANDIDATES; i++) { if (i < ans1_position_in_burst.length()) { position_in_burst[i] = ans1_position_in_burst.get(ans1_position_in_burst.length() - 1 - i); } else { diff --git a/lib/src/phy/phch/pbch_nr.c b/lib/src/phy/phch/pbch_nr.c index 015963ae1..d6ab4a288 100644 --- a/lib/src/phy/phch/pbch_nr.c +++ b/lib/src/phy/phch/pbch_nr.c @@ -13,10 +13,15 @@ #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/modem/demod_soft.h" #include "srsran/phy/modem/mod.h" #include "srsran/phy/utils/debug.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 */ @@ -31,7 +36,7 @@ /* * Polar rate matching I_BIL */ -#define pbch_nr_polar_rm_tx_IBIL 0 +#define PBCH_NR_POLAR_RM_IBIL 0 /* * Number of generated payload bits, called A @@ -58,11 +63,6 @@ */ #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 @@ -214,6 +214,53 @@ pbch_nr_pbch_msg_pack(const srsran_pbch_nr_cfg_t* cfg, const srsran_pbch_msg_nr_ 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]) @@ -258,27 +305,85 @@ static void pbch_nr_scramble(const srsran_pbch_nr_cfg_t* cfg, const uint8_t a[PB } } -static int pbch_nr_polar_encode(srsran_pbch_nr_t* q, const uint8_t c[PBCH_NR_K], uint8_t d[PBCH_NR_K]) +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, allocated, q->code.N, q->code.K, q->code.nPC, q->code.K_set, q->code.PC_set); + 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_tx(srsran_pbch_nr_t* q, const uint8_t d[PBCH_NR_K], uint8_t o[PBCH_NR_E]) +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_tx(&q->polar_rm_tx, d, o, q->code.n, PBCH_NR_E, PBCH_NR_K, pbch_nr_polar_rm_tx_IBIL) < + 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; } @@ -307,8 +412,33 @@ static void pbch_nr_scramble_tx(const srsran_pbch_nr_cfg_t* cfg, 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_E], cf_t ssb_grid[SRSRAN_SSB_NOF_RE]) +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; @@ -352,6 +482,63 @@ pbch_nr_mapping(const srsran_pbch_nr_cfg_t* cfg, const cf_t symbols[PBCH_NR_E], 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, @@ -374,10 +561,10 @@ int srsran_pbch_nr_encode(srsran_pbch_nr_t* q, // 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); + PBCH_NR_DEBUG_TX("checksum=%06x", checksum); // 7.1.4 Channel coding - uint8_t d[PBCH_NR_K]; + uint8_t d[PBCH_NR_N]; if (pbch_nr_polar_encode(q, b, d) < SRSRAN_SUCCESS) { return SRSRAN_ERROR; } @@ -402,3 +589,77 @@ int srsran_pbch_nr_encode(srsran_pbch_nr_t* q, 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], + 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 + cf_t symbols[PBCH_NR_M]; + pbch_nr_demapping(cfg, ssb_grid, symbols); + + // 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; +} diff --git a/lib/src/phy/sync/ssb.c b/lib/src/phy/sync/ssb.c index 9a7ecf91a..106f0f85b 100644 --- a/lib/src/phy/sync/ssb.c +++ b/lib/src/phy/sync/ssb.c @@ -147,7 +147,7 @@ void srsran_ssb_free(srsran_ssb_t* q) SRSRAN_MEM_ZERO(q, srsran_ssb_t, 1); } -static uint32_t ssb_first_symbol_caseA(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION]) +static uint32_t ssb_first_symbol_caseA(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES]) { // Case A - 15 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes of { 2 , 8 } + 14 ⋅ n . For // carrier frequencies smaller than or equal to 3 GHz, n = 0 , 1 . For carrier frequencies within FR1 larger than 3 @@ -169,7 +169,7 @@ static uint32_t ssb_first_symbol_caseA(const srsran_ssb_cfg_t* cfg, uint32_t ind return count; } -static uint32_t ssb_first_symbol_caseB(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION]) +static uint32_t ssb_first_symbol_caseB(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES]) { // Case B - 30 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes { 4 , 8 , 16 , 20 } + 28 ⋅ n . // For carrier frequencies smaller than or equal to 3 GHz, n = 0 . For carrier frequencies within FR1 larger than 3 @@ -191,7 +191,7 @@ static uint32_t ssb_first_symbol_caseB(const srsran_ssb_cfg_t* cfg, uint32_t ind return count; } -static uint32_t ssb_first_symbol_caseC(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION]) +static uint32_t ssb_first_symbol_caseC(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES]) { // Case C - 30 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes { 2 , 8 } +14 ⋅ n . // - For paired spectrum operation @@ -218,7 +218,7 @@ static uint32_t ssb_first_symbol_caseC(const srsran_ssb_cfg_t* cfg, uint32_t ind return count; } -static uint32_t ssb_first_symbol_caseD(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION]) +static uint32_t ssb_first_symbol_caseD(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES]) { // Case D - 120 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes { 4 , 8 , 16 , 20 } + 28 ⋅ n . // For carrier frequencies within FR2, n = 0 , 1 , 2 , 3 , 5 , 6 , 7 , 8 , 10 , 11 , 12 , 13 , 15 , 16 , 17 , 18 . @@ -235,7 +235,7 @@ static uint32_t ssb_first_symbol_caseD(const srsran_ssb_cfg_t* cfg, uint32_t ind return count; } -static uint32_t ssb_first_symbol_caseE(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION]) +static uint32_t ssb_first_symbol_caseE(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES]) { // Case E - 240 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes //{ 8 , 12 , 16 , 20 , 32 , 36 , 40 , 44 } + 56 ⋅ n . For carrier frequencies within FR2, n = 0 , 1 , 2 , 3 , 5 , 6 , @@ -253,7 +253,7 @@ static uint32_t ssb_first_symbol_caseE(const srsran_ssb_cfg_t* cfg, uint32_t ind return count; } -static uint32_t ssb_candidates(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION]) +static uint32_t ssb_first_symbol(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES]) { uint32_t Lmax = 0; @@ -279,27 +279,6 @@ static uint32_t ssb_candidates(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRS 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++) { - // There is a SSB transmission opportunity - if (cfg->position[i]) { - // Return the SSB transmission in burst - if (ssb_i == ssb_count) { - return (int)indexes[i]; - } - - ssb_count++; - } - } - - return SRSRAN_ERROR; -} - // Modulates a given symbol l and stores the time domain signal in q->tmp_time static void ssb_modulate_symbol(srsran_ssb_t* q, cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t l) { @@ -398,6 +377,23 @@ static int ssb_setup_corr(srsran_ssb_t* q) return SRSRAN_SUCCESS; } +static inline int ssb_get_t_offset(srsran_ssb_t* q, uint32_t ssb_idx) +{ + // Get baseband time offset from the begining of the half radio frame to the first symbol + if (ssb_idx >= SRSRAN_SSB_NOF_CANDIDATES) { + ERROR("Invalid SSB candidate index (%d)", ssb_idx); + return SRSRAN_ERROR; + } + + float t_offset_s = srsran_symbol_offset_s(q->l_first[ssb_idx], q->cfg.scs); + if (isnan(t_offset_s) || isinf(t_offset_s) || t_offset_s < 0.0f) { + ERROR("Invalid first symbol (l_first=%d)", q->l_first[ssb_idx]); + return SRSRAN_ERROR; + } + + return (int)round(t_offset_s * q->cfg.srate_hz); +} + int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg) { // Verify input parameters @@ -409,34 +405,15 @@ 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, &q->Lmax); - if (l_begin < SRSRAN_SUCCESS) { - // set it to 2 in case it is not selected - l_begin = 2; - } - - float t_offset_s = srsran_symbol_offset_s((uint32_t)l_begin, cfg->scs); - if (isnan(t_offset_s) || isinf(t_offset_s) || t_offset_s < 0.0f) { - ERROR("Invalid first symbol (l_first=%d)", l_begin); - return SRSRAN_ERROR; - } + q->Lmax = ssb_first_symbol(cfg, q->l_first); - // Calculate SSB symbol size and integer offset + // Calculate SSB symbol size and integer frequency offset double freq_offset_hz = cfg->ssb_freq_hz - cfg->center_freq_hz; uint32_t symbol_sz = (uint32_t)round(cfg->srate_hz / q->scs_hz); q->f_offset = (int32_t)round(freq_offset_hz / q->scs_hz); - q->t_offset = (uint32_t)round(t_offset_s * cfg->srate_hz); - - for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { - uint32_t l_real = l + (uint32_t)l_begin; - uint32_t ref_cp_sz = 144U; - if (l_real == 0 || l_real == SRSRAN_EXT_CP_SYMBOL(cfg->scs)) { - ref_cp_sz = 160U; - } - - q->cp_sz[l] = (ref_cp_sz * symbol_sz) / 2048U; - } + // Calculate cyclic prefix + q->cp_sz = (144U * symbol_sz) / 2048U; // Calculate SSB sampling error and check double ssb_srate_error_Hz = ((double)symbol_sz * q->scs_hz) - cfg->srate_hz; @@ -528,7 +505,12 @@ 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) +int srsran_ssb_add(srsran_ssb_t* q, + uint32_t N_id, + uint32_t ssb_idx, + 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) { @@ -569,22 +551,26 @@ int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* m return SRSRAN_ERROR; } + // Select start symbol from SSB candidate index + int t_offset = ssb_get_t_offset(q, ssb_idx); + if (t_offset < SRSRAN_SUCCESS) { + ERROR("Invalid SSB candidate index"); + return SRSRAN_ERROR; + } + // Select input/ouput pointers considering the time offset in the slot - const cf_t* in_ptr = &in[q->t_offset]; - cf_t* out_ptr = &out[q->t_offset]; + const cf_t* in_ptr = &in[t_offset]; + cf_t* out_ptr = &out[t_offset]; // For each SSB symbol, modulate for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { - // Get CP length - uint32_t cp_len = q->cp_sz[l]; - // Map SSB in resource grid and perform IFFT ssb_modulate_symbol(q, ssb_grid, l); // Add cyclic prefix to input; - srsran_vec_sum_ccc(in_ptr, &q->tmp_time[q->symbol_sz - cp_len], out_ptr, cp_len); - in_ptr += cp_len; - out_ptr += cp_len; + srsran_vec_sum_ccc(in_ptr, &q->tmp_time[q->symbol_sz - q->cp_sz], out_ptr, q->cp_sz); + in_ptr += q->cp_sz; + out_ptr += q->cp_sz; // Add symbol to the input baseband srsran_vec_sum_ccc(in_ptr, q->tmp_time, out_ptr, q->symbol_sz); @@ -599,21 +585,18 @@ static int ssb_demodulate(srsran_ssb_t* q, const cf_t* in, uint32_t t_offset, cf { const cf_t* in_ptr = &in[t_offset]; for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { - // Get CP length - uint32_t cp_len = q->cp_sz[l]; - // Advance half CP, to avoid inter symbol interference - in_ptr += SRSRAN_FLOOR(cp_len, 2); + in_ptr += SRSRAN_FLOOR(q->cp_sz, 2); // Copy FFT window in temporal time domain buffer srsran_vec_cf_copy(q->tmp_time, in_ptr, q->symbol_sz); - in_ptr += q->symbol_sz + SRSRAN_CEIL(cp_len, 2); + in_ptr += q->symbol_sz + SRSRAN_CEIL(q->cp_sz, 2); // Convert to frequency domain srsran_dft_run_guru_c(&q->fft); // Compensate half CP delay - srsran_vec_apply_cfo(q->tmp_freq, SRSRAN_CEIL(cp_len, 2) / (float)(q->symbol_sz), q->tmp_freq, q->symbol_sz); + srsran_vec_apply_cfo(q->tmp_freq, SRSRAN_CEIL(q->cp_sz, 2) / (float)(q->symbol_sz), q->tmp_freq, q->symbol_sz); // Select symbol in grid cf_t* ptr = &ssb_grid[l * SRSRAN_SSB_BW_SUBC]; @@ -825,8 +808,8 @@ int srsran_ssb_csi_search(srsran_ssb_t* q, } // Remove CP offset prior demodulation - if (t_offset >= q->cp_sz[0]) { - t_offset -= q->cp_sz[0]; + if (t_offset >= q->cp_sz) { + t_offset -= q->cp_sz; } else { t_offset = 0; } @@ -861,7 +844,11 @@ int srsran_ssb_csi_search(srsran_ssb_t* q, return SRSRAN_SUCCESS; } -int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsran_csi_trs_measurements_t* meas) +int srsran_ssb_csi_measure(srsran_ssb_t* q, + uint32_t N_id, + uint32_t ssb_idx, + const cf_t* in, + srsran_csi_trs_measurements_t* meas) { // Verify inputs if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || in == NULL || meas == NULL || !isnormal(q->scs_hz)) { @@ -869,14 +856,19 @@ int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsra } if (!q->args.enable_measure) { - ERROR("SSB is not configured for measure"); + ERROR("SSB is not configured to measure"); return SRSRAN_ERROR; } - cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; + // Select start symbol from SSB candidate index + int t_offset = ssb_get_t_offset(q, ssb_idx); + if (t_offset < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } // Demodulate - if (ssb_demodulate(q, in, q->t_offset, ssb_grid) < SRSRAN_SUCCESS) { + cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; + if (ssb_demodulate(q, in, (uint32_t)t_offset, ssb_grid) < SRSRAN_SUCCESS) { ERROR("Error demodulating"); return SRSRAN_ERROR; } @@ -889,3 +881,43 @@ int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsra return SRSRAN_SUCCESS; } + +int srsran_ssb_decode_pbch(srsran_ssb_t* q, uint32_t N_id, uint32_t ssb_idx, const cf_t* in, srsran_pbch_msg_nr_t* msg) +{ + // Verify inputs + if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || in == NULL || msg == NULL || !isnormal(q->scs_hz)) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + if (!q->args.enable_decode) { + ERROR("SSB is not configured to decode"); + return SRSRAN_ERROR; + } + + // Select start symbol from SSB candidate index + int t_offset = ssb_get_t_offset(q, ssb_idx); + if (t_offset < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // Demodulate + cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; + if (ssb_demodulate(q, in, (uint32_t)t_offset, ssb_grid) < SRSRAN_SUCCESS) { + ERROR("Error demodulating"); + return SRSRAN_ERROR; + } + + // Prepare configuration + 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; + + // Decode + if (srsran_pbch_nr_decode(&q->pbch, &pbch_cfg, ssb_idx, ssb_grid, msg) < SRSRAN_SUCCESS) { + ERROR("Error decoding PBCH"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} diff --git a/lib/src/phy/sync/test/CMakeLists.txt b/lib/src/phy/sync/test/CMakeLists.txt index bfec15b99..0d2352563 100644 --- a/lib/src/phy/sync/test/CMakeLists.txt +++ b/lib/src/phy/sync/test/CMakeLists.txt @@ -124,10 +124,13 @@ add_test(cfo_test_2 cfo_test -f 0.99849 -n 1000) ######################################################################## -# NE TEST +# NR TEST ######################################################################## add_executable(ssb_measure_test ssb_measure_test.c) target_link_libraries(ssb_measure_test srsran_phy) +add_nr_test(ssb_measure_test ssb_measure_test) -add_test(ssb_measure_test ssb_measure_test) +add_executable(ssb_decode_test ssb_decode_test.c) +target_link_libraries(ssb_decode_test srsran_phy) +add_nr_test(ssb_decode_test ssb_decode_test) diff --git a/lib/src/phy/sync/test/ssb_decode_test.c b/lib/src/phy/sync/test/ssb_decode_test.c new file mode 100644 index 000000000..e85898b9a --- /dev/null +++ b/lib/src/phy/sync/test/ssb_decode_test.c @@ -0,0 +1,205 @@ +/** + * + * \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 +#include +#include + +// NR parameters +static uint32_t carrier_nof_prb = 52; +static srsran_subcarrier_spacing_t carrier_scs = srsran_subcarrier_spacing_15kHz; +static srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_30kHz; + +// Channel parameters +static int32_t delay_n = 0; +static float cfo_hz = 0.0f; +static float n0_dB = -300.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); +} + +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, 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; +} \ No newline at end of file diff --git a/lib/src/phy/sync/test/ssb_measure_test.c b/lib/src/phy/sync/test/ssb_measure_test.c index 9f87da361..b13815415 100644 --- a/lib/src/phy/sync/test/ssb_measure_test.c +++ b/lib/src/phy/sync/test/ssb_measure_test.c @@ -102,7 +102,6 @@ static int test_case_1(srsran_ssb_t* ssb) ssb_cfg.ssb_freq_hz = 3.5e9 - 960e3; ssb_cfg.scs = ssb_scs; ssb_cfg.pattern = SRSRAN_SSB_PATTERN_C; - ssb_cfg.position[0] = true; // Rest to false TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS); @@ -117,7 +116,7 @@ static int test_case_1(srsran_ssb_t* ssb) // Add the SSB base-band gettimeofday(&t[1], NULL); - TESTASSERT(srsran_ssb_add(ssb, pci, &pbch_msg, buffer, buffer) == SRSRAN_SUCCESS); + TESTASSERT(srsran_ssb_add(ssb, pci, 0, &pbch_msg, buffer, buffer) == SRSRAN_SUCCESS); gettimeofday(&t[2], NULL); get_time_interval(t); t_add_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; @@ -145,7 +144,7 @@ static int test_case_1(srsran_ssb_t* ssb) // Measure gettimeofday(&t[1], NULL); srsran_csi_trs_measurements_t meas = {}; - TESTASSERT(srsran_ssb_csi_measure(ssb, pci, buffer, &meas) == SRSRAN_SUCCESS); + TESTASSERT(srsran_ssb_csi_measure(ssb, pci, 0, buffer, &meas) == SRSRAN_SUCCESS); gettimeofday(&t[2], NULL); get_time_interval(t); t_meas_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index 1798b4dd0..ba818bdfe 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -97,7 +97,6 @@ bool cc_worker::update_cfg() ssb_cfg.ssb_freq_hz = abs_freq_ssb_freq; ssb_cfg.scs = phy->cfg.ssb.scs; ssb_cfg.pattern = srsran::srsran_band_helper().get_ssb_pattern(band, phy->cfg.ssb.scs); - memcpy(ssb_cfg.position, phy->cfg.ssb.position_in_burst.data(), sizeof(bool) * SRSRAN_SSB_NOF_POSITION); ssb_cfg.duplex_mode = srsran::srsran_band_helper().get_duplex_mode(band); ssb_cfg.periodicity_ms = phy->cfg.ssb.periodicity_ms; @@ -334,31 +333,44 @@ bool cc_worker::measure_csi() if (srsran_ssb_send(&ssb, dl_slot_cfg.idx)) { srsran_csi_trs_measurements_t meas = {}; - if (srsran_ssb_csi_measure(&ssb, phy->cfg.carrier.pci, rx_buffer[0], &meas) < SRSRAN_SUCCESS) { - logger.error("Error measuring SSB"); - return false; - } + // Iterate all possible candidates + const std::array position_in_burst = phy->cfg.ssb.position_in_burst; + for (uint32_t ssb_idx = 0; ssb_idx < SRSRAN_SSB_NOF_CANDIDATES; ssb_idx++) { + // Skip SSB candidate if not enabled + if (not position_in_burst[ssb_idx]) { + continue; + } - if (logger.debug.enabled()) { - std::array str = {}; - srsran_csi_meas_info(&meas, str.data(), (uint32_t)str.size()); - logger.debug("SSB-CSI: %s", str.data()); - } + // Measure SSB candidate + if (srsran_ssb_csi_measure(&ssb, phy->cfg.carrier.pci, ssb_idx, rx_buffer[0], &meas) < SRSRAN_SUCCESS) { + logger.error("Error measuring SSB"); + return false; + } - // Compute channel metrics and push it - ch_metrics_t ch_metrics = {}; - ch_metrics.sinr = meas.snr_dB; - ch_metrics.rsrp = meas.rsrp_dB; - ch_metrics.rsrq = 0.0f; // Not supported - ch_metrics.rssi = 0.0f; // Not supported - ch_metrics.sync_err = - meas.delay_us / (float)(ue_dl.fft->fft_plan.size * SRSRAN_SUBC_SPACING_NR(phy->cfg.carrier.scs)); - phy->set_channel_metrics(ch_metrics); + if (logger.debug.enabled()) { + std::array str = {}; + srsran_csi_meas_info(&meas, str.data(), (uint32_t)str.size()); + logger.debug("SSB-CSI: %s", str.data()); + } - // Compute synch metrics and report it to the PHY state - sync_metrics_t sync_metrics = {}; - sync_metrics.cfo = meas.cfo_hz; - phy->set_sync_metrics(sync_metrics); + // Compute channel metrics and push it + ch_metrics_t ch_metrics = {}; + ch_metrics.sinr = meas.snr_dB; + ch_metrics.rsrp = meas.rsrp_dB; + ch_metrics.rsrq = 0.0f; // Not supported + ch_metrics.rssi = 0.0f; // Not supported + ch_metrics.sync_err = + meas.delay_us / (float)(ue_dl.fft->fft_plan.size * SRSRAN_SUBC_SPACING_NR(phy->cfg.carrier.scs)); + phy->set_channel_metrics(ch_metrics); + + // Compute synch metrics and report it to the PHY state + sync_metrics_t sync_metrics = {}; + sync_metrics.cfo = meas.cfo_hz; + phy->set_sync_metrics(sync_metrics); + + // Report SSB candidate channel measurement to the PHY state + // ... + } } // Iterate all NZP-CSI-RS marked as TRS and perform channel measurements diff --git a/srsue/test/phy/nr_cell_search_test.cc b/srsue/test/phy/nr_cell_search_test.cc index 162d94aaa..3c9c7bf85 100644 --- a/srsue/test/phy/nr_cell_search_test.cc +++ b/srsue/test/phy/nr_cell_search_test.cc @@ -81,7 +81,6 @@ public: ssb_cfg.ssb_freq_hz = args.ssb_freq_hz; ssb_cfg.scs = args.ssb_scs; ssb_cfg.pattern = args.get_ssb_pattern(); - ssb_cfg.position[0] = true; ssb_cfg.duplex_mode = args.get_duplex_mode(); ssb_cfg.periodicity_ms = args.ssb_period_ms; if (srsran_ssb_set_cfg(&ssb, &ssb_cfg) < SRSRAN_SUCCESS) { @@ -106,7 +105,7 @@ public: srsran_pbch_msg_nr_t msg = {}; // Add SSB - if (srsran_ssb_add(&ssb, pci, &msg, buffer.data(), buffer.data()) < SRSRAN_SUCCESS) { + if (srsran_ssb_add(&ssb, pci, 0, &msg, buffer.data(), buffer.data()) < SRSRAN_SUCCESS) { logger.error("Error adding SSB"); return SRSRAN_ERROR; }