Implemented initial PBCH decoder and refactored SSB candidate selection

This commit is contained in:
Xavier Arteaga 2021-05-20 13:52:58 +02:00 committed by Andre Puschmann
parent de1b25558f
commit 48e0fc3c99
11 changed files with 677 additions and 151 deletions

View File

@ -30,7 +30,7 @@ struct phy_cfg_nr_t {
*/ */
struct ssb_cfg_t { struct ssb_cfg_t {
uint32_t periodicity_ms; uint32_t periodicity_ms;
std::array<bool, SRSRAN_SSB_NOF_POSITION> position_in_burst; std::array<bool, SRSRAN_SSB_NOF_CANDIDATES> position_in_burst;
srsran_subcarrier_spacing_t scs; srsran_subcarrier_spacing_t scs;
}; };

View File

@ -64,10 +64,10 @@ typedef struct SRSRAN_API {
*/ */
typedef struct SRSRAN_API { typedef struct SRSRAN_API {
uint8_t payload[SRSRAN_PBCH_NR_PAYLOAD_SZ]; ///< Actual PBCH payload provided by higher layers uint8_t payload[SRSRAN_PBCH_NR_PAYLOAD_SZ]; ///< Actual PBCH payload provided by higher layers
uint32_t sfn_4lsb; ///< SFN 4 LSB uint8_t sfn_4lsb; ///< SFN 4 LSB
uint32_t ssb_idx; ///< SS/PBCH blocks index described in TS 38.213 4.1 uint8_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 uint8_t k_ssb_msb; ///< Subcarrier offset MSB described in TS 38.211 7.4.3.1
uint32_t hrf; ///< Half Radio Frame bit bool hrf; ///< Half Radio Frame bit
bool crc; ///< Decoder only, it is true only if the received CRC matches bool crc; ///< Decoder only, it is true only if the received CRC matches
} srsran_pbch_msg_nr_t; } 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 * @brief Decodes an NR PBCH message in the SSB resource grid
* @param q NR PBCH object * @param q NR PBCH object
* @param cfg NR PBCH configuration * @param cfg NR PBCH configuration
* @param ssb_idx SSB candidate index
* @param[in] ssb_grid SSB resource grid * @param[in] ssb_grid SSB resource grid
* @param msg NR PBCH message received * @param msg NR PBCH message received
* @return SRSRAN_SUCCESS if decoding is successful, SRSLTE_ERROR code otherwise * @return SRSRAN_SUCCESS if decoding is successful, SRSLTE_ERROR code otherwise
*/ */
SRSRAN_API int srsran_pbch_nr_decode(srsran_pbch_nr_t* q, SRSRAN_API int srsran_pbch_nr_decode(srsran_pbch_nr_t* q,
const srsran_pbch_nr_cfg_t* cfg, const srsran_pbch_nr_cfg_t* cfg,
uint32_t ssb_idx,
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
srsran_pbch_msg_nr_t* msg); 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 #endif // SRSRAN_PBCH_NR_H

View File

@ -37,7 +37,7 @@
/** /**
* @brief Maximum number of SSB positions in burst. Defined in TS 38.331 ServingCellConfigCommon, ssb-PositionsInBurst * @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 * @brief Describes SSB object initialization arguments
@ -61,7 +61,6 @@ typedef struct SRSRAN_API {
double ssb_freq_hz; ///< SSB center frequency double ssb_freq_hz; ///< SSB center frequency
srsran_subcarrier_spacing_t scs; ///< SSB configured Subcarrier spacing 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 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) srsran_duplex_mode_t duplex_mode; ///< Set to true if the spectrum is paired (FDD)
uint32_t periodicity_ms; ///< SSB periodicity in ms uint32_t periodicity_ms; ///< SSB periodicity in ms
float beta_pss; ////< PSS power allocation float beta_pss; ////< PSS power allocation
@ -85,10 +84,10 @@ typedef struct SRSRAN_API {
uint32_t corr_sz; ///< Correlation size uint32_t corr_sz; ///< Correlation size
uint32_t corr_window; ///< Correlation window length uint32_t corr_window; ///< Correlation window length
int32_t f_offset; ///< Current SSB integer frequency offset (multiple of SCS) 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; ///< CP length for the given symbol size
uint32_t cp_sz[SRSRAN_SSB_DURATION_NSYMB]; ///< CP length for each SSB symbol
/// Other parameters /// Other parameters
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 uint32_t Lmax; ///< Number of SSB candidates
/// Internal Objects /// Internal Objects
@ -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 * @brief Decodes PBCH in the given time domain signal
* @param q SSB object * @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 * @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 * @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 * @brief Adds SSB to a given signal in time domain
* @param q SSB object * @param q SSB object
* @param N_id Physical Cell Identifier * @param N_id Physical Cell Identifier
* @param ssb_idx SSB candidate index
* @param msg NR PBCH message to transmit * @param msg NR PBCH message to transmit
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/ */
SRSRAN_API int SRSRAN_API int srsran_ssb_add(srsran_ssb_t* q,
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); 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 * @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 * @brief Perform Channel State Information (CSI) measurement from the SSB
* @param q NR PSS object * @param q NR PSS object
* @param N_id Physical Cell Identifier * @param N_id Physical Cell Identifier
* @param ssb_idx SSB candidate index
* @param in Base-band signal * @param in Base-band signal
* @param meas SSB-based CSI measurement * @param meas SSB-based CSI measurement
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/ */
SRSRAN_API int SRSRAN_API int srsran_ssb_csi_measure(srsran_ssb_t* q,
srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsran_csi_trs_measurements_t* meas); uint32_t N_id,
uint32_t ssb_idx,
const cf_t* in,
srsran_csi_trs_measurements_t* meas);
#endif // SRSRAN_SSB_H #endif // SRSRAN_SSB_H

View File

@ -1301,9 +1301,9 @@ bool make_phy_carrier_cfg(const freq_info_dl_s& asn1_freq_info_dl, srsran_carrie
template <class bitstring_t> template <class bitstring_t>
static inline void make_ssb_positions_in_burst(const bitstring_t& ans1_position_in_burst, static inline void make_ssb_positions_in_burst(const bitstring_t& ans1_position_in_burst,
std::array<bool, SRSRAN_SSB_NOF_POSITION>& position_in_burst) std::array<bool, SRSRAN_SSB_NOF_CANDIDATES>& 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()) { if (i < ans1_position_in_burst.length()) {
position_in_burst[i] = ans1_position_in_burst.get(ans1_position_in_burst.length() - 1 - i); position_in_burst[i] = ans1_position_in_burst.get(ans1_position_in_burst.length() - 1 - i);
} else { } else {

View File

@ -13,10 +13,15 @@
#include "srsran/phy/phch/pbch_nr.h" #include "srsran/phy/phch/pbch_nr.h"
#include "srsran/phy/common/sequence.h" #include "srsran/phy/common/sequence.h"
#include "srsran/phy/fec/polar/polar_chanalloc.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/modem/mod.h"
#include "srsran/phy/utils/debug.h" #include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/vector.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 * CRC Parameters
*/ */
@ -31,7 +36,7 @@
/* /*
* Polar rate matching I_BIL * 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 * Number of generated payload bits, called A
@ -58,11 +63,6 @@
*/ */
#define PBCH_NR_M (PBCH_NR_E / 2) #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) static int pbch_nr_init_encoder(srsran_pbch_nr_t* q, const srsran_pbch_nr_args_t* args)
{ {
// Skip encoder init if not requested // 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]; 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]) 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 // Allocate channel
uint8_t allocated[PBCH_NR_N]; 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 // Encode bits
if (srsran_polar_encoder_encode(&q->polar_encoder, allocated, d, q->code.n) < SRSRAN_SUCCESS) { if (srsran_polar_encoder_encode(&q->polar_encoder, allocated, d, q->code.n) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR; 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; 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_decode(srsran_pbch_nr_t* q, const int8_t d[PBCH_NR_N], uint8_t c[PBCH_NR_K])
{ {
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) < // 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) { SRSRAN_SUCCESS) {
return SRSRAN_ERROR; 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; 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); 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 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; 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++]; 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, 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 // 7.1.3 Transport block CRC attachment
uint32_t checksum = srsran_crc_attach(&q->crc, b, PBCH_NR_A); 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 // 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) { if (pbch_nr_polar_encode(q, b, d) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
@ -402,3 +589,77 @@ int srsran_pbch_nr_encode(srsran_pbch_nr_t* q,
return SRSRAN_SUCCESS; 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;
}

View File

@ -147,7 +147,7 @@ void srsran_ssb_free(srsran_ssb_t* q)
SRSRAN_MEM_ZERO(q, srsran_ssb_t, 1); 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 // 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 // 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; 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 . // 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 // 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; 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 . // Case C - 30 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes { 2 , 8 } +14 ⋅ n .
// - For paired spectrum operation // - 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; 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 . // 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 . // 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; 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 // 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 , //{ 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; 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; 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; 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 // 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) 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; 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) int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg)
{ {
// Verify input parameters // 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); q->scs_hz = (float)SRSRAN_SUBC_SPACING_NR(cfg->scs);
// Get first symbol // Get first symbol
int l_begin = ssb_first_symbol(cfg, 0, &q->Lmax); q->Lmax = ssb_first_symbol(cfg, q->l_first);
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); // Calculate SSB symbol size and integer frequency offset
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;
}
// Calculate SSB symbol size and integer offset
double freq_offset_hz = cfg->ssb_freq_hz - cfg->center_freq_hz; 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); 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->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++) { // Calculate cyclic prefix
uint32_t l_real = l + (uint32_t)l_begin; q->cp_sz = (144U * symbol_sz) / 2048U;
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 SSB sampling error and check // Calculate SSB sampling error and check
double ssb_srate_error_Hz = ((double)symbol_sz * q->scs_hz) - cfg->srate_hz; 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); 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 // Verify input parameters
if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || msg == NULL || in == NULL || out == NULL) { 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; 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 // Select input/ouput pointers considering the time offset in the slot
const cf_t* in_ptr = &in[q->t_offset]; const cf_t* in_ptr = &in[t_offset];
cf_t* out_ptr = &out[q->t_offset]; cf_t* out_ptr = &out[t_offset];
// For each SSB symbol, modulate // For each SSB symbol, modulate
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { 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 // Map SSB in resource grid and perform IFFT
ssb_modulate_symbol(q, ssb_grid, l); ssb_modulate_symbol(q, ssb_grid, l);
// Add cyclic prefix to input; // Add cyclic prefix to input;
srsran_vec_sum_ccc(in_ptr, &q->tmp_time[q->symbol_sz - 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 += cp_len; in_ptr += q->cp_sz;
out_ptr += cp_len; out_ptr += q->cp_sz;
// Add symbol to the input baseband // Add symbol to the input baseband
srsran_vec_sum_ccc(in_ptr, q->tmp_time, out_ptr, q->symbol_sz); 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]; const cf_t* in_ptr = &in[t_offset];
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { 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 // 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 // Copy FFT window in temporal time domain buffer
srsran_vec_cf_copy(q->tmp_time, in_ptr, q->symbol_sz); 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 // Convert to frequency domain
srsran_dft_run_guru_c(&q->fft); srsran_dft_run_guru_c(&q->fft);
// Compensate half CP delay // 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 // Select symbol in grid
cf_t* ptr = &ssb_grid[l * SRSRAN_SSB_BW_SUBC]; 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 // Remove CP offset prior demodulation
if (t_offset >= q->cp_sz[0]) { if (t_offset >= q->cp_sz) {
t_offset -= q->cp_sz[0]; t_offset -= q->cp_sz;
} else { } else {
t_offset = 0; t_offset = 0;
} }
@ -861,7 +844,11 @@ int srsran_ssb_csi_search(srsran_ssb_t* q,
return SRSRAN_SUCCESS; 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 // Verify inputs
if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || in == NULL || meas == NULL || !isnormal(q->scs_hz)) { 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) { if (!q->args.enable_measure) {
ERROR("SSB is not configured for measure"); ERROR("SSB is not configured to measure");
return SRSRAN_ERROR; 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 // 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"); ERROR("Error demodulating");
return SRSRAN_ERROR; 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; 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;
}

View File

@ -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) add_executable(ssb_measure_test ssb_measure_test.c)
target_link_libraries(ssb_measure_test srsran_phy) 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)

View File

@ -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 <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 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;
}

View File

@ -102,7 +102,6 @@ static int test_case_1(srsran_ssb_t* ssb)
ssb_cfg.ssb_freq_hz = 3.5e9 - 960e3; ssb_cfg.ssb_freq_hz = 3.5e9 - 960e3;
ssb_cfg.scs = ssb_scs; ssb_cfg.scs = ssb_scs;
ssb_cfg.pattern = SRSRAN_SSB_PATTERN_C; 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); 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 // Add the SSB base-band
gettimeofday(&t[1], NULL); 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); gettimeofday(&t[2], NULL);
get_time_interval(t); get_time_interval(t);
t_add_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; 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 // Measure
gettimeofday(&t[1], NULL); gettimeofday(&t[1], NULL);
srsran_csi_trs_measurements_t meas = {}; 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); gettimeofday(&t[2], NULL);
get_time_interval(t); get_time_interval(t);
t_meas_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; t_meas_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;

View File

@ -97,7 +97,6 @@ bool cc_worker::update_cfg()
ssb_cfg.ssb_freq_hz = abs_freq_ssb_freq; ssb_cfg.ssb_freq_hz = abs_freq_ssb_freq;
ssb_cfg.scs = phy->cfg.ssb.scs; ssb_cfg.scs = phy->cfg.ssb.scs;
ssb_cfg.pattern = srsran::srsran_band_helper().get_ssb_pattern(band, 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.duplex_mode = srsran::srsran_band_helper().get_duplex_mode(band);
ssb_cfg.periodicity_ms = phy->cfg.ssb.periodicity_ms; ssb_cfg.periodicity_ms = phy->cfg.ssb.periodicity_ms;
@ -334,7 +333,16 @@ bool cc_worker::measure_csi()
if (srsran_ssb_send(&ssb, dl_slot_cfg.idx)) { if (srsran_ssb_send(&ssb, dl_slot_cfg.idx)) {
srsran_csi_trs_measurements_t meas = {}; srsran_csi_trs_measurements_t meas = {};
if (srsran_ssb_csi_measure(&ssb, phy->cfg.carrier.pci, rx_buffer[0], &meas) < SRSRAN_SUCCESS) { // Iterate all possible candidates
const std::array<bool, SRSRAN_SSB_NOF_CANDIDATES> 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;
}
// 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"); logger.error("Error measuring SSB");
return false; return false;
} }
@ -359,6 +367,10 @@ bool cc_worker::measure_csi()
sync_metrics_t sync_metrics = {}; sync_metrics_t sync_metrics = {};
sync_metrics.cfo = meas.cfo_hz; sync_metrics.cfo = meas.cfo_hz;
phy->set_sync_metrics(sync_metrics); 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 // Iterate all NZP-CSI-RS marked as TRS and perform channel measurements

View File

@ -81,7 +81,6 @@ public:
ssb_cfg.ssb_freq_hz = args.ssb_freq_hz; ssb_cfg.ssb_freq_hz = args.ssb_freq_hz;
ssb_cfg.scs = args.ssb_scs; ssb_cfg.scs = args.ssb_scs;
ssb_cfg.pattern = args.get_ssb_pattern(); ssb_cfg.pattern = args.get_ssb_pattern();
ssb_cfg.position[0] = true;
ssb_cfg.duplex_mode = args.get_duplex_mode(); ssb_cfg.duplex_mode = args.get_duplex_mode();
ssb_cfg.periodicity_ms = args.ssb_period_ms; ssb_cfg.periodicity_ms = args.ssb_period_ms;
if (srsran_ssb_set_cfg(&ssb, &ssb_cfg) < SRSRAN_SUCCESS) { if (srsran_ssb_set_cfg(&ssb, &ssb_cfg) < SRSRAN_SUCCESS) {
@ -106,7 +105,7 @@ public:
srsran_pbch_msg_nr_t msg = {}; srsran_pbch_msg_nr_t msg = {};
// Add SSB // 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"); logger.error("Error adding SSB");
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }