Merge branch 'next' into agpl_next

master
Codebot 4 years ago committed by Your Name
commit d9cb5d01ff

@ -151,6 +151,7 @@ public:
int distance() const; int distance() const;
int distance_bytes(uint8_t* ref_ptr) const; int distance_bytes(uint8_t* ref_ptr) const;
int distance_bytes() const; int distance_bytes() const;
int distance_bytes_end() const;
template <class T> template <class T>
SRSASN_CODE unpack(T& val, uint32_t n_bits) SRSASN_CODE unpack(T& val, uint32_t n_bits)

@ -77,6 +77,36 @@ public:
*/ */
srsran_duplex_mode_t get_duplex_mode(uint16_t band) const; srsran_duplex_mode_t get_duplex_mode(uint16_t band) const;
class sync_raster_t
{
protected:
sync_raster_t(uint32_t f, uint32_t s, uint32_t l) : first(f), step(s), last(l), gscn(f) {}
uint32_t gscn;
private:
uint32_t first;
uint32_t step;
uint32_t last;
public:
bool valid() const { return step != 0; }
void next()
{
if (gscn <= last) {
gscn += step;
}
}
bool end() const { return (gscn > last or step == 0); }
void reset() { gscn = first; }
double get_frequency() const;
};
sync_raster_t get_sync_raster(uint16_t band, srsran_subcarrier_spacing_t scs) const;
private: private:
// Elements of TS 38.101-1 Table 5.2-1: NR operating bands in FR1 // Elements of TS 38.101-1 Table 5.2-1: NR operating bands in FR1
struct nr_operating_band { struct nr_operating_band {
@ -234,7 +264,7 @@ private:
{261, KHZ_120, 2070833, 2, 2084999, 2070833, 2, 2084999} {261, KHZ_120, 2070833, 2, 2084999, 2070833, 2, 2084999}
}}; }};
// Elements of TS 38.101-1 Table 5.4.3.3-1 : Applicable SS raster entries per operating band // Elements of TS 38.101-1 Table 5.4.3.3-1 : Applicable SS raster entries per operating band
struct nr_band_ss_raster { struct nr_band_ss_raster {
uint16_t band; uint16_t band;
srsran_subcarrier_spacing_t scs; srsran_subcarrier_spacing_t scs;

@ -126,6 +126,7 @@ class rrc_interface_pdcp
{ {
public: public:
virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0;
virtual void notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) = 0;
}; };
} // namespace srsenb } // namespace srsenb

@ -168,6 +168,7 @@ class rrc_interface_pdcp_nr
{ {
public: public:
virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0;
virtual void notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) = 0;
}; };
class phy_interface_stack_nr class phy_interface_stack_nr

@ -93,6 +93,7 @@ public:
virtual void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) = 0; virtual void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) = 0;
virtual void write_pdu_pcch(srsran::unique_byte_buffer_t pdu) = 0; virtual void write_pdu_pcch(srsran::unique_byte_buffer_t pdu) = 0;
virtual void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; virtual void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0;
virtual void notify_pdcp_integrity_error(uint32_t lcid) = 0;
virtual const char* get_rb_name(uint32_t lcid) = 0; virtual const char* get_rb_name(uint32_t lcid) = 0;
}; };

@ -30,7 +30,7 @@
typedef struct SRSRAN_API { typedef struct SRSRAN_API {
uint32_t N_id; ///< Physical cell identifier uint32_t N_id; ///< Physical cell identifier
uint32_t n_hf; ///< Number of half radio frame, 0 or 1 uint32_t n_hf; ///< Number of half radio frame, 0 or 1
uint32_t ssb_idx; ///< SSB candidate index uint32_t ssb_idx; ///< SSB candidate index, up to 3 LSB are significant
uint32_t L_max; ///< Number of SSB opportunities in half radio frame uint32_t L_max; ///< Number of SSB opportunities in half radio frame
float beta; ///< Power allocation specified in TS 38.213 float beta; ///< Power allocation specified in TS 38.213
srsran_subcarrier_spacing_t scs; ///< SSB configured subcarrier spacing srsran_subcarrier_spacing_t scs; ///< SSB configured subcarrier spacing
@ -55,17 +55,26 @@ typedef struct SRSRAN_API {
*/ */
SRSRAN_API int srsran_dmrs_pbch_put(const srsran_dmrs_pbch_cfg_t* cfg, cf_t ssb_grid[SRSRAN_SSB_NOF_RE]); SRSRAN_API int srsran_dmrs_pbch_put(const srsran_dmrs_pbch_cfg_t* cfg, cf_t ssb_grid[SRSRAN_SSB_NOF_RE]);
/**
* @brief Measures NR PBCH DMRS
* @param cfg PBCH DMRS configuration
* @param ssb_grid SSB resource grid
* @param[out] meas Measurement
* @return SRSRAN_SUCCESS if the inputs and configuration are valid, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int srsran_dmrs_pbch_measure(const srsran_dmrs_pbch_cfg_t* cfg,
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
srsran_dmrs_pbch_meas_t* meas);
/** /**
* @brief Estimates NR PBCH DMRS * @brief Estimates NR PBCH DMRS
* @param cfg PBCH DMRS configuration * @param cfg PBCH DMRS configuration
* @param ssb_grid Demodulated SSB resource grid * @param ssb_grid Demodulated SSB resource grid
* @param[out] ce Estimated channel * @param[out] ce Estimated channel
* @param[out] meas Estimated channel measurements
* @return SRSRAN_SUCCESS if the inputs and configuration are valid, SRSRAN_ERROR code otherwise * @return SRSRAN_SUCCESS if the inputs and configuration are valid, SRSRAN_ERROR code otherwise
*/ */
SRSRAN_API int srsran_dmrs_pbch_estimate(const srsran_dmrs_pbch_cfg_t* cfg, SRSRAN_API int srsran_dmrs_pbch_estimate(const srsran_dmrs_pbch_cfg_t* cfg,
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
cf_t ce[SRSRAN_SSB_NOF_RE], cf_t ce[SRSRAN_SSB_NOF_RE]);
srsran_dmrs_pbch_meas_t* meas);
#endif // SRSRAN_DMRS_PBCH_H #endif // SRSRAN_DMRS_PBCH_H

@ -56,7 +56,6 @@ SRSRAN_API uint32_t srsran_crc_attach_byte(srsran_crc_t* h, uint8_t* data, int l
static inline void srsran_crc_checksum_put_byte(srsran_crc_t* h, uint8_t byte) static inline void srsran_crc_checksum_put_byte(srsran_crc_t* h, uint8_t byte)
{ {
uint64_t crc = h->crcinit; uint64_t crc = h->crcinit;
uint32_t idx; uint32_t idx;
@ -83,6 +82,8 @@ SRSRAN_API uint32_t srsran_crc_checksum_byte(srsran_crc_t* h, const uint8_t* dat
SRSRAN_API uint32_t srsran_crc_checksum(srsran_crc_t* h, uint8_t* data, int len); SRSRAN_API uint32_t srsran_crc_checksum(srsran_crc_t* h, uint8_t* data, int len);
SRSRAN_API bool srsran_crc_match_byte(srsran_crc_t* h, uint8_t* data, int len);
SRSRAN_API bool srsran_crc_match(srsran_crc_t* h, uint8_t* data, int len); SRSRAN_API bool srsran_crc_match(srsran_crc_t* h, uint8_t* data, int len);
#endif // SRSRAN_CRC_H #endif // SRSRAN_CRC_H

@ -74,6 +74,15 @@ SRSRAN_API void srsran_softbuffer_rx_reset_cb(srsran_softbuffer_rx_t* q, uint32_
SRSRAN_API void srsran_softbuffer_rx_free(srsran_softbuffer_rx_t* p); SRSRAN_API void srsran_softbuffer_rx_free(srsran_softbuffer_rx_t* p);
/**
* @brief Resets a number of CB CRCs
* @note This function is intended to be used if all CB CRC have matched but the TB CRC failed. In this case, all CB
* should be decoded again
* @param q Rx soft-buffer object
* @param nof_cb Number of CB to reset
*/
SRSRAN_API void srsran_softbuffer_rx_reset_cb_crc(srsran_softbuffer_rx_t* q, uint32_t nof_cb);
SRSRAN_API int srsran_softbuffer_tx_init(srsran_softbuffer_tx_t* q, uint32_t nof_prb); SRSRAN_API int srsran_softbuffer_tx_init(srsran_softbuffer_tx_t* q, uint32_t nof_prb);
/** /**

@ -48,11 +48,11 @@ typedef struct SRSRAN_API {
* @brief Describes the NR PBCH configuration * @brief Describes the NR PBCH configuration
*/ */
typedef struct SRSRAN_API { typedef struct SRSRAN_API {
uint32_t N_id; ///< Physical cell identifier uint32_t N_id; ///< Physical cell identifier
srsran_subcarrier_spacing_t ssb_scs; ///< SSB Subcarrier spacing uint32_t n_hf; ///< Number of half radio frame, 0 or 1
uint32_t Lmax; ///< Number of SSB opportunities, described in TS 38.213 4.1 ... uint32_t ssb_idx; ///< SSB candidate index, up to 4 LSB significant
float beta; ///< Scaling factor for PBCH symbols, set to zero for default uint32_t Lmax; ///< Number of SSB opportunities, described in TS 38.213 4.1 ...
float beta_dmrs; ///< Scaling factor for PBCH DM-RS, set to zero for default float beta; ///< Scaling factor for PBCH symbols, set to zero for default
} srsran_pbch_nr_cfg_t; } srsran_pbch_nr_cfg_t;
/** /**
@ -111,15 +111,12 @@ 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] ce Channel estimates for the SSB resource grid * @param[in] ce Channel estimates for the SSB resource grid
* @param msg NR PBCH message received * @param msg NR PBCH message received
* @return SRSRAN_SUCCESS if decoding is successful, SRSRAN_ERROR code otherwise * @return SRSRAN_SUCCESS if decoding is successful, SRSRAN_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],
const cf_t ce[SRSRAN_SSB_NOF_RE], const cf_t ce[SRSRAN_SSB_NOF_RE],
srsran_pbch_msg_nr_t* msg); srsran_pbch_msg_nr_t* msg);

@ -114,6 +114,16 @@ typedef struct SRSRAN_API {
cf_t* pss_seq[SRSRAN_NOF_NID_2_NR]; ///< Possible frequency domain PSS for find cf_t* pss_seq[SRSRAN_NOF_NID_2_NR]; ///< Possible frequency domain PSS for find
} srsran_ssb_t; } srsran_ssb_t;
/**
* @brief Describes an SSB search result
* @note if pbch.crc is true, SSB transmission is found and decoded. Otherwise, no SSB transmission has been decoded
*/
typedef struct {
uint32_t N_id; ///< Most suitable physical cell identifier
uint32_t t_offset; ///< Time offset in the input samples
srsran_pbch_msg_nr_t pbch_msg; ///< Physical broadcast channel message of the most suitable SSB candidate
} srsran_ssb_search_res_t;
/** /**
* @brief Initialises configures NR SSB with the given arguments * @brief Initialises configures NR SSB with the given arguments
* @param q SSB object * @param q SSB object
@ -140,18 +150,28 @@ SRSRAN_API int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg);
* @note It currently expects an input buffer of half radio frame * @note It currently expects an input buffer of half radio frame
* @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 n_hf Number of hald radio frame, 0 or 1 * @param n_hf Number of hald radio frame, 0 or 1
* @param ssb_idx SSB candidate index
* @param in Input baseband buffer * @param in Input baseband buffer
* @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, SRSRAN_API int srsran_ssb_decode_pbch(srsran_ssb_t* q,
uint32_t N_id, uint32_t N_id,
uint32_t ssb_idx,
uint32_t n_hf, uint32_t n_hf,
uint32_t ssb_idx,
const cf_t* in, const cf_t* in,
srsran_pbch_msg_nr_t* msg); srsran_pbch_msg_nr_t* msg);
/**
* @brief Searches for an SSB transmission and decodes the PBCH message
* @param q SSB object
* @param in Input baseband buffer
* @param nof_samples Number of samples available in the buffer
* @param res SSB Search result
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int srsran_ssb_search(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, srsran_ssb_search_res_t* res);
/** /**
* @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
* @param q SSB object * @param q SSB object
@ -164,16 +184,11 @@ 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_ssb_add(srsran_ssb_t* q, SRSRAN_API int
uint32_t N_id, 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 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

@ -22,6 +22,7 @@
#ifndef SRSRAN_SYS_METRICS_H #ifndef SRSRAN_SYS_METRICS_H
#define SRSRAN_SYS_METRICS_H #define SRSRAN_SYS_METRICS_H
#include <array>
#include <cstdint> #include <cstdint>
namespace srsran { namespace srsran {
@ -30,14 +31,14 @@ constexpr uint32_t metrics_max_supported_cpu = 32u;
/// Metrics of cpu usage, memory consumption and number of thread used by the process. /// Metrics of cpu usage, memory consumption and number of thread used by the process.
struct sys_metrics_t { struct sys_metrics_t {
uint32_t process_realmem_kB = 0; uint32_t process_realmem_kB = 0;
uint32_t process_virtualmem_kB = 0; uint32_t process_virtualmem_kB = 0;
float process_realmem = 0.f; float process_realmem = 0.f;
uint32_t thread_count = 0; uint32_t thread_count = 0;
float process_cpu_usage = 0.f; float process_cpu_usage = 0.f;
float system_mem = 0.f; float system_mem = 0.f;
uint32_t cpu_count = 0; uint32_t cpu_count = 0;
float cpu_load[metrics_max_supported_cpu]; std::array<float, metrics_max_supported_cpu> cpu_load;
}; };
} // namespace srsran } // namespace srsran

@ -138,6 +138,11 @@ int bit_ref_impl<Ptr>::distance_bytes() const
{ {
return ((int)(ptr - start_ptr)) + ((offset) ? 1 : 0); return ((int)(ptr - start_ptr)) + ((offset) ? 1 : 0);
} }
template <typename Ptr>
int bit_ref_impl<Ptr>::distance_bytes_end() const
{
return ((int)(max_ptr - ptr)) - ((offset) ? 1 : 0);
}
SRSASN_CODE bit_ref::pack(uint64_t val, uint32_t n_bits) SRSASN_CODE bit_ref::pack(uint64_t val, uint32_t n_bits)
{ {

@ -21,6 +21,7 @@
#include "srsran/common/band_helper.h" #include "srsran/common/band_helper.h"
#include <algorithm> #include <algorithm>
#include <cmath>
namespace srsran { namespace srsran {
@ -113,6 +114,59 @@ srsran_duplex_mode_t srsran_band_helper::get_duplex_mode(uint16_t band) const
return SRSRAN_DUPLEX_MODE_INVALID; return SRSRAN_DUPLEX_MODE_INVALID;
} }
struct sync_raster_impl : public srsran_band_helper::sync_raster_t {
public:
sync_raster_impl(uint32_t f, uint32_t s, uint32_t l) : sync_raster_t(f, s, l)
{
// Do nothing
}
};
double srsran_band_helper::sync_raster_t::get_frequency() const
{
// Row 1
if (gscn >= 2 and gscn <= 7498) {
double N = std::ceil((gscn - 1) / 3.0);
double M = (gscn - 3 * N) / 2.0 + 3.0;
return N * 1200e3 + M * 50e3;
}
// Row 2
if (gscn >= 7499 and gscn <= 22255) {
double N = gscn - 7499;
return 3000e6 + N * 1.44e6;
}
// Row 3
if (gscn >= 22256 and gscn <= 26639) {
double N = gscn - 22256;
return 2425.08e6 + N * 17.28e6;
}
// Unhandled case
return NAN;
}
srsran_band_helper::sync_raster_t srsran_band_helper::get_sync_raster(uint16_t band,
srsran_subcarrier_spacing_t scs) const
{
// Look for the given band and SCS
for (const nr_band_ss_raster& ss_raster : nr_band_ss_raster_table) {
// Check if band and SCS match!
if (ss_raster.band == band && ss_raster.scs == scs) {
return sync_raster_impl(ss_raster.gscn_first, ss_raster.gscn_step, ss_raster.gscn_last);
}
// As bands are in ascending order, do not waste more time if the current band is bigger
if (ss_raster.band > band) {
return sync_raster_impl(0, 0, 0);
}
}
// Band is out of range, so consider invalid
return sync_raster_impl(0, 0, 0);
}
srsran_band_helper::nr_raster_params srsran_band_helper::get_raster_params(uint32_t nr_arfcn) srsran_band_helper::nr_raster_params srsran_band_helper::get_raster_params(uint32_t nr_arfcn)
{ {
for (auto& fr : nr_fr_params) { for (auto& fr : nr_fr_params) {

@ -146,16 +146,11 @@ int dmrs_pbch_extract_lse(const srsran_dmrs_pbch_cfg_t* cfg,
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
int srsran_dmrs_pbch_estimate(const srsran_dmrs_pbch_cfg_t* cfg, static int dmrs_pbch_meas_estimate(const srsran_dmrs_pbch_cfg_t* cfg,
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
cf_t ce[SRSRAN_SSB_NOF_RE], cf_t ce[SRSRAN_SSB_NOF_RE],
srsran_dmrs_pbch_meas_t* meas) srsran_dmrs_pbch_meas_t* meas)
{ {
// Validate inputs
if (cfg == NULL || ssb_grid == NULL || ce == NULL || meas == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
// Extract least square estimates // Extract least square estimates
cf_t lse[DMRS_PBCH_NOF_RE]; cf_t lse[DMRS_PBCH_NOF_RE];
if (dmrs_pbch_extract_lse(cfg, ssb_grid, lse) < SRSRAN_SUCCESS) { if (dmrs_pbch_extract_lse(cfg, ssb_grid, lse) < SRSRAN_SUCCESS) {
@ -199,29 +194,65 @@ int srsran_dmrs_pbch_estimate(const srsran_dmrs_pbch_cfg_t* cfg,
cfo_hz = cargf(corr1 * conjf(corr3)) / (2.0f * (float)M_PI * distance_s); cfo_hz = cargf(corr1 * conjf(corr3)) / (2.0f * (float)M_PI * distance_s);
} }
// Estimate wideband gain at symbol 0 // Estimate wideband gain at each symbol carrying DMRS
cf_t wideband_gain = (srsran_vec_acc_cc(lse, DMRS_PBCH_NOF_RE) / DMRS_PBCH_NOF_RE) * cf_t wideband_gain_1 =
cexpf(I * 2.0f * M_PI * srsran_symbol_offset_s(2, cfg->scs) * cfo_hz); srsran_vec_acc_cc(&lse[0], 60) * cexpf(I * 2.0f * M_PI * srsran_symbol_offset_s(1, cfg->scs) * cfo_hz);
cf_t wideband_gain_2 =
srsran_vec_acc_cc(&lse[60], 24) * cexpf(I * 2.0f * M_PI * srsran_symbol_offset_s(2, cfg->scs) * cfo_hz);
cf_t wideband_gain_3 =
srsran_vec_acc_cc(&lse[84], 60) * cexpf(I * 2.0f * M_PI * srsran_symbol_offset_s(3, cfg->scs) * cfo_hz);
// Estimate wideband gain equivalent at symbol 0
cf_t wideband_gain = (wideband_gain_1 + wideband_gain_2 + wideband_gain_3) / DMRS_PBCH_NOF_RE;
// Compute RSRP from correlation // Compute RSRP from correlation
float rsrp = SRSRAN_CSQABS((corr1 + corr3) / 2.0f); float rsrp = (SRSRAN_CSQABS(corr1) + SRSRAN_CSQABS(corr3)) / 2.0f;
// Compute EPRE // Compute EPRE
float epre = srsran_vec_avg_power_cf(lse, DMRS_PBCH_NOF_RE); float epre = srsran_vec_avg_power_cf(lse, DMRS_PBCH_NOF_RE);
// Write measurements // Write measurements
meas->corr = rsrp / epre; if (meas != NULL) {
meas->epre = epre; meas->corr = rsrp / epre;
meas->rsrp = rsrp; meas->epre = epre;
meas->cfo_hz = cfo_hz; meas->rsrp = rsrp;
meas->avg_delay_us = avg_delay_us; meas->cfo_hz = cfo_hz;
meas->avg_delay_us = avg_delay_us;
}
// Compute channel estimates // Generate estimated grid
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { if (ce != NULL) {
float t_s = srsran_symbol_offset_s(l, cfg->scs); // Compute channel estimates
cf_t symbol_wideband_gain = cexpf(-I * 2.0f * M_PI * cfo_hz * t_s) * wideband_gain; for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
srsran_vec_gen_sine(symbol_wideband_gain, -avg_delay_norm, &ce[l * SRSRAN_SSB_BW_SUBC], SRSRAN_SSB_BW_SUBC); float t_s = srsran_symbol_offset_s(l, cfg->scs);
cf_t symbol_wideband_gain = cexpf(-I * 2.0f * M_PI * cfo_hz * t_s) * wideband_gain;
srsran_vec_gen_sine(symbol_wideband_gain, -avg_delay_norm, &ce[l * SRSRAN_SSB_BW_SUBC], SRSRAN_SSB_BW_SUBC);
}
} }
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
int srsran_dmrs_pbch_measure(const srsran_dmrs_pbch_cfg_t* cfg,
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
srsran_dmrs_pbch_meas_t* meas)
{
// Validate inputs
if (cfg == NULL || ssb_grid == NULL || meas == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
return dmrs_pbch_meas_estimate(cfg, ssb_grid, NULL, meas);
}
int srsran_dmrs_pbch_estimate(const srsran_dmrs_pbch_cfg_t* cfg,
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
cf_t ce[SRSRAN_SSB_NOF_RE])
{
// Validate inputs
if (cfg == NULL || ssb_grid == NULL || ce == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
return dmrs_pbch_meas_estimate(cfg, ssb_grid, ce, NULL);
}

@ -186,8 +186,10 @@ uint32_t srsran_crc_attach(srsran_crc_t* h, uint8_t* data, int len)
bool srsran_crc_match(srsran_crc_t* h, uint8_t* data, int len) bool srsran_crc_match(srsran_crc_t* h, uint8_t* data, int len)
{ {
uint8_t* ptr = &data[len]; return srsran_crc_checksum(h, data, len + h->order) == 0;
uint32_t checksum1 = srsran_crc_checksum(h, data, len); }
uint32_t checksum2 = srsran_bit_pack(&ptr, h->order);
return (checksum1 == checksum2); bool srsran_crc_match_byte(srsran_crc_t* h, uint8_t* data, int len)
{
return srsran_crc_checksum_byte(h, data, len + h->order) == 0;
} }

@ -168,6 +168,15 @@ void srsran_softbuffer_rx_reset_cb(srsran_softbuffer_rx_t* q, uint32_t nof_cb)
q->tb_crc = false; q->tb_crc = false;
} }
void srsran_softbuffer_rx_reset_cb_crc(srsran_softbuffer_rx_t* q, uint32_t nof_cb)
{
if (q == NULL || nof_cb == 0) {
return;
}
SRSRAN_MEM_ZERO(q->cb_crc, bool, SRSRAN_MIN(q->max_cb, nof_cb));
}
int srsran_softbuffer_tx_init(srsran_softbuffer_tx_t* q, uint32_t nof_prb) int srsran_softbuffer_tx_init(srsran_softbuffer_tx_t* q, uint32_t nof_prb)
{ {
int ret = srsran_ra_tbs_from_idx(SRSRAN_RA_NOF_TBS_IDX - 1, nof_prb); int ret = srsran_ra_tbs_from_idx(SRSRAN_RA_NOF_TBS_IDX - 1, nof_prb);

@ -254,6 +254,7 @@ pbch_nr_pbch_msg_unpack(const srsran_pbch_nr_cfg_t* cfg, const uint8_t a[PBCH_NR
msg->hrf = (a[G[10]] == 1); msg->hrf = (a[G[10]] == 1);
// Put SSB related in a_hat[A_hat + 5] to a_hat[A_hat + 7] // Put SSB related in a_hat[A_hat + 5] to a_hat[A_hat + 7]
msg->ssb_idx = cfg->ssb_idx; // Load 4 LSB
if (cfg->Lmax == 64) { if (cfg->Lmax == 64) {
msg->ssb_idx = msg->ssb_idx & 0b111; 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[11]] << 5U); // 6th bit of SSB index
@ -436,9 +437,9 @@ static void pbch_nr_scramble_rx(const srsran_pbch_nr_cfg_t* cfg,
uint32_t M_bit = PBCH_NR_E; uint32_t M_bit = PBCH_NR_E;
// Select value v // Select value v
uint32_t v = (ssb_idx & 0x7U); uint32_t v = (ssb_idx & 0b111U);
if (cfg->Lmax == 4) { if (cfg->Lmax == 4) {
v = ssb_idx & 0x3U; v = ssb_idx & 0b11U;
} }
// Advance sequence // Advance sequence
@ -603,7 +604,6 @@ int srsran_pbch_nr_encode(srsran_pbch_nr_t* q,
int srsran_pbch_nr_decode(srsran_pbch_nr_t* q, 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],
const cf_t ce_grid[SRSRAN_SSB_NOF_RE], const cf_t ce_grid[SRSRAN_SSB_NOF_RE],
srsran_pbch_msg_nr_t* msg) srsran_pbch_msg_nr_t* msg)
@ -632,7 +632,7 @@ int srsran_pbch_nr_decode(srsran_pbch_nr_t* q,
// TS 38.211 7.3.3 Physical broadcast channel // TS 38.211 7.3.3 Physical broadcast channel
// 7.3.3.1 Scrambling // 7.3.3.1 Scrambling
pbch_nr_scramble_rx(cfg, ssb_idx, llr, llr); pbch_nr_scramble_rx(cfg, cfg->ssb_idx, llr, llr);
// 7.1.5 Rate matching // 7.1.5 Rate matching
int8_t d[PBCH_NR_N]; int8_t d[PBCH_NR_N];

@ -513,72 +513,61 @@ static int decode_tb(srsran_sch_t* q,
int16_t* e_bits, int16_t* e_bits,
uint8_t* data) uint8_t* data)
{ {
if (q != NULL && data != NULL && softbuffer != NULL && e_bits != NULL && cb_segm != NULL && Qm != 0) { // Check inputs
if (cb_segm->tbs == 0 || cb_segm->C == 0) { if (q == NULL || data == NULL || softbuffer == NULL || e_bits == NULL || cb_segm == NULL || Qm == 0) {
return SRSRAN_SUCCESS; ERROR("Missing inputs: data=%d, softbuffer=%d, e_bits=%d, cb_segm=%d Qm=%d",
} data != 0,
softbuffer != 0,
if (cb_segm->F) { e_bits != 0,
fprintf(stderr, "Error filler bits are not supported. Use standard TBS\n"); cb_segm != 0,
return SRSRAN_ERROR_INVALID_INPUTS; Qm);
} return SRSRAN_ERROR_INVALID_INPUTS;
}
if (cb_segm->C > softbuffer->max_cb) { // Check segmentation is valid
fprintf(stderr, if (cb_segm->tbs == 0 || cb_segm->C == 0) {
"Error number of CB to decode (%d) exceeds soft buffer size (%d CBs)\n", return SRSRAN_SUCCESS;
cb_segm->C, }
softbuffer->max_cb);
return SRSRAN_ERROR_INVALID_INPUTS;
}
bool crc_ok = true; if (cb_segm->F) {
fprintf(stderr, "Error filler bits are not supported. Use standard TBS\n");
return SRSRAN_ERROR_INVALID_INPUTS;
}
data[cb_segm->tbs / 8 + 0] = 0; if (cb_segm->C > softbuffer->max_cb) {
data[cb_segm->tbs / 8 + 1] = 0; fprintf(stderr,
data[cb_segm->tbs / 8 + 2] = 0; "Error number of CB to decode (%d) exceeds soft buffer size (%d CBs)\n",
cb_segm->C,
softbuffer->max_cb);
return SRSRAN_ERROR_INVALID_INPUTS;
}
// Process Codeblocks // Process Codeblocks
crc_ok = decode_tb_cb(q, softbuffer, cb_segm, Qm, rv, nof_e_bits, e_bits, data); bool cb_crc_ok = decode_tb_cb(q, softbuffer, cb_segm, Qm, rv, nof_e_bits, e_bits, data);
if (crc_ok) { // If any of the CBs CRC is KO
uint32_t par_rx = 0, par_tx = 0; if (!cb_crc_ok) {
INFO("Error in CB parity");
return SRSRAN_ERROR;
}
// Compute transport block CRC // One CB CRC OK, means TB CRC is OK.
par_rx = srsran_crc_checksum_byte(&q->crc_tb, data, cb_segm->tbs); if (cb_segm->C == 1) {
INFO("TB decoded OK");
return SRSRAN_SUCCESS;
}
// check parity bits // Check TB CRC for whole TB
par_tx = ((uint32_t)data[cb_segm->tbs / 8 + 0]) << 16 | ((uint32_t)data[cb_segm->tbs / 8 + 1]) << 8 | if (srsran_crc_match_byte(&q->crc_tb, data, cb_segm->tbs)) {
((uint32_t)data[cb_segm->tbs / 8 + 2]); INFO("TB decoded OK");
return SRSRAN_SUCCESS;
}
// Check if all the bytes are zeros // TB CRC check failed, as at least one CB had a false alarm, reset all CB CRC flags in the softbuffer
bool all_zeros = true; srsran_softbuffer_rx_reset_cb_crc(softbuffer, cb_segm->C);
for (uint32_t i = 0; i < cb_segm->tbs / 8 && all_zeros; i++) {
all_zeros = (data[i] == 0);
}
if (all_zeros) {
INFO("Error in TB decode: it is all zeros!");
return SRSRAN_ERROR;
}
if (par_rx == par_tx) { INFO("Error in TB parity");
INFO("TB decoded OK"); return SRSRAN_ERROR;
return SRSRAN_SUCCESS;
} else {
INFO("Error in TB parity: par_tx=0x%x, par_rx=0x%x", par_tx, par_rx);
return SRSRAN_ERROR;
}
} else {
return SRSRAN_ERROR;
}
} else {
ERROR("Missing inputs: data=%d, softbuffer=%d, e_bits=%d, cb_segm=%d Qm=%d",
data != 0,
softbuffer != 0,
e_bits != 0,
cb_segm != 0,
Qm);
return SRSRAN_ERROR_INVALID_INPUTS;
}
} }
int srsran_dlsch_decode(srsran_sch_t* q, srsran_pdsch_cfg_t* cfg, int16_t* e_bits, uint8_t* data) int srsran_dlsch_decode(srsran_sch_t* q, srsran_pdsch_cfg_t* cfg, int16_t* e_bits, uint8_t* data)

@ -214,8 +214,14 @@ foreach (nof_prb 6 15 25 50 75 100)
foreach (nof_ports 1 2) foreach (nof_ports 1 2)
foreach (cfi 1 2 3) foreach (cfi 1 2 3)
foreach (snr auto 15 300) foreach (snr auto 15 300)
set(pdcch_test_args "") # Build PDCCH test arguments
set(pdcch_test_args -n ${nof_prb} -p ${nof_ports} -f ${cfi} -S ${snr} -R 1 -F) set(pdcch_test_args -n ${nof_prb} -p ${nof_ports} -f ${cfi} -S ${snr} -R 1)
# The current Viterbi decoder implementation for SSE and NEON does not perform good enough to pass the
# test without false detection. So, enable false alarm check in AVX2 based machines only.
if (HAVE_AVX2)
set(pdcch_test_args ${pdcch_test_args} -F)
endif ()
string(REGEX REPLACE "\ " "" test_name_args ${pdcch_test_args}) string(REGEX REPLACE "\ " "" test_name_args ${pdcch_test_args})

@ -519,12 +519,7 @@ 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, 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)
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) {
@ -567,7 +562,6 @@ int srsran_ssb_add(srsran_ssb_t* q,
// Put PBCH payload // Put PBCH payload
srsran_pbch_nr_cfg_t pbch_cfg = {}; srsran_pbch_nr_cfg_t pbch_cfg = {};
pbch_cfg.N_id = N_id; pbch_cfg.N_id = N_id;
pbch_cfg.ssb_scs = q->cfg.scs;
pbch_cfg.Lmax = q->Lmax; pbch_cfg.Lmax = q->Lmax;
if (srsran_pbch_nr_encode(&q->pbch, &pbch_cfg, msg, ssb_grid) < SRSRAN_SUCCESS) { if (srsran_pbch_nr_encode(&q->pbch, &pbch_cfg, msg, ssb_grid) < SRSRAN_SUCCESS) {
ERROR("Error encoding PBCH"); ERROR("Error encoding PBCH");
@ -575,7 +569,7 @@ int srsran_ssb_add(srsran_ssb_t* q,
} }
// Select start symbol from SSB candidate index // Select start symbol from SSB candidate index
int t_offset = ssb_get_t_offset(q, ssb_idx); int t_offset = ssb_get_t_offset(q, msg->ssb_idx);
if (t_offset < SRSRAN_SUCCESS) { if (t_offset < SRSRAN_SUCCESS) {
ERROR("Invalid SSB candidate index"); ERROR("Invalid SSB candidate index");
return SRSRAN_ERROR; return SRSRAN_ERROR;
@ -782,10 +776,8 @@ ssb_pss_search(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, uint32_t*
continue; continue;
} }
float corr = SRSRAN_CSQABS(q->tmp_time[peak_idx]) / avg_pwr_corr; // Normalise correlation
if (corr < sqrtf(SRSRAN_PSS_NR_LEN)) { float corr = SRSRAN_CSQABS(q->tmp_time[peak_idx]) / avg_pwr_corr / sqrtf(SRSRAN_PSS_NR_LEN);
continue;
}
// Update if the correlation is better than the current best // Update if the correlation is better than the current best
if (best_corr < corr) { if (best_corr < corr) {
@ -822,6 +814,13 @@ int srsran_ssb_csi_search(srsran_ssb_t* q,
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
// Avoid finding a peak in a region that cannot be demodulated
if (nof_samples < (q->symbol_sz + q->cp_sz) * SRSRAN_SSB_DURATION_NSYMB) {
ERROR("Insufficient number of samples (%d/%d)", nof_samples, (q->symbol_sz + q->cp_sz) * SRSRAN_SSB_DURATION_NSYMB);
return SRSRAN_ERROR;
}
nof_samples -= (q->symbol_sz + q->cp_sz) * SRSRAN_SSB_DURATION_NSYMB;
// Search for PSS in time domain // Search for PSS in time domain
uint32_t N_id_2 = 0; uint32_t N_id_2 = 0;
uint32_t t_offset = 0; uint32_t t_offset = 0;
@ -905,10 +904,100 @@ int srsran_ssb_csi_measure(srsran_ssb_t* q,
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
int srsran_ssb_decode_pbch(srsran_ssb_t* q, static int ssb_select_pbch(srsran_ssb_t* q,
uint32_t N_id,
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
uint32_t* found_n_hf,
uint32_t* found_ssb_idx_4lsb)
{
// Prepare PBCH DMRS configuration
srsran_dmrs_pbch_cfg_t pbch_dmrs_cfg = {};
pbch_dmrs_cfg.N_id = N_id;
pbch_dmrs_cfg.n_hf = 0; // Parameter to guess
pbch_dmrs_cfg.ssb_idx = 0; // Parameter to guess
pbch_dmrs_cfg.L_max = q->Lmax;
pbch_dmrs_cfg.beta = 0.0f;
pbch_dmrs_cfg.scs = q->cfg.scs;
// Initialise best values
srsran_dmrs_pbch_meas_t best_meas = {};
uint32_t best_n_hf = 0;
uint32_t best_ssb_idx = 0;
// Iterate over all the parameters to guess and select the most suitable
for (uint32_t n_hf = 0; n_hf < 2; n_hf++) {
for (uint32_t ssb_idx = 0; ssb_idx < SRSRAN_MIN(8, q->Lmax); ssb_idx++) {
// Set parameters
pbch_dmrs_cfg.n_hf = n_hf;
pbch_dmrs_cfg.ssb_idx = ssb_idx;
// Measure
srsran_dmrs_pbch_meas_t meas = {};
if (srsran_dmrs_pbch_measure(&pbch_dmrs_cfg, ssb_grid, &meas) < SRSRAN_SUCCESS) {
ERROR("Error measure for n_hf=%d ssb_idx=%d", n_hf, ssb_idx);
return SRSRAN_ERROR;
}
// Select the result with highest correlation (most suitable)
if (meas.corr > best_meas.corr) {
best_meas = meas;
best_n_hf = n_hf;
best_ssb_idx = ssb_idx;
}
}
}
// Save findings
*found_n_hf = best_n_hf;
*found_ssb_idx_4lsb = best_ssb_idx;
return SRSRAN_SUCCESS;
}
static int ssb_decode_pbch(srsran_ssb_t* q,
uint32_t N_id, uint32_t N_id,
uint32_t n_hf,
uint32_t ssb_idx, uint32_t ssb_idx,
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
srsran_pbch_msg_nr_t* msg)
{
// Prepare PBCH DMRS configuration
srsran_dmrs_pbch_cfg_t pbch_dmrs_cfg = {};
pbch_dmrs_cfg.N_id = N_id;
pbch_dmrs_cfg.n_hf = n_hf;
pbch_dmrs_cfg.ssb_idx = ssb_idx;
pbch_dmrs_cfg.L_max = q->Lmax;
pbch_dmrs_cfg.beta = 0.0f;
pbch_dmrs_cfg.scs = q->cfg.scs;
// Compute PBCH channel estimates
cf_t ce[SRSRAN_SSB_NOF_RE] = {};
if (srsran_dmrs_pbch_estimate(&pbch_dmrs_cfg, ssb_grid, ce) < SRSRAN_SUCCESS) {
ERROR("Error estimating channel");
return SRSRAN_ERROR;
}
// Prepare PBCH configuration
srsran_pbch_nr_cfg_t pbch_cfg = {};
pbch_cfg.N_id = N_id;
pbch_cfg.n_hf = n_hf;
pbch_cfg.ssb_idx = ssb_idx;
pbch_cfg.Lmax = q->Lmax;
pbch_cfg.beta = 0.0f;
// Decode
if (srsran_pbch_nr_decode(&q->pbch, &pbch_cfg, ssb_grid, ce, msg) < SRSRAN_SUCCESS) {
ERROR("Error decoding PBCH");
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
int srsran_ssb_decode_pbch(srsran_ssb_t* q,
uint32_t N_id,
uint32_t n_hf, uint32_t n_hf,
uint32_t ssb_idx,
const cf_t* in, const cf_t* in,
srsran_pbch_msg_nr_t* msg) srsran_pbch_msg_nr_t* msg)
{ {
@ -935,40 +1024,81 @@ int srsran_ssb_decode_pbch(srsran_ssb_t* q,
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
// Prepare PBCH DMRS configuration // Decode PBCH
srsran_dmrs_pbch_cfg_t pbch_dmrs_cfg = {}; if (ssb_decode_pbch(q, N_id, n_hf, ssb_idx, ssb_grid, msg) < SRSRAN_SUCCESS) {
pbch_dmrs_cfg.N_id = N_id; ERROR("Error decoding");
pbch_dmrs_cfg.n_hf = n_hf; return SRSRAN_ERROR;
pbch_dmrs_cfg.ssb_idx = ssb_idx; }
pbch_dmrs_cfg.L_max = q->Lmax;
pbch_dmrs_cfg.beta = 0.0f;
pbch_dmrs_cfg.scs = q->cfg.scs;
// Compute PBCH channel estimates return SRSRAN_SUCCESS;
srsran_dmrs_pbch_meas_t meas = {}; }
cf_t ce[SRSRAN_SSB_NOF_RE] = {};
if (srsran_dmrs_pbch_estimate(&pbch_dmrs_cfg, ssb_grid, ce, &meas) < SRSRAN_SUCCESS) { int srsran_ssb_search(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, srsran_ssb_search_res_t* res)
ERROR("Error estimating channel"); {
// Verify inputs
if (q == NULL || in == NULL || res == NULL || !isnormal(q->scs_hz)) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (!q->args.enable_search || !q->args.enable_decode) {
ERROR("SSB is not configured to search (%c) and decode (%c)",
q->args.enable_search ? 'y' : 'n',
q->args.enable_decode ? 'y' : 'n');
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
// Compare measurement with threshold // Search for PSS in time domain
if (meas.corr < q->args.pbch_dmrs_thr) { uint32_t N_id_2 = 0;
msg->crc = false; uint32_t t_offset = 0;
return SRSRAN_SUCCESS; if (ssb_pss_search(q, in, nof_samples, &N_id_2, &t_offset) < SRSRAN_SUCCESS) {
ERROR("Error searching for N_id_2");
return SRSRAN_ERROR;
} }
// Prepare PBCH configuration // Remove CP offset prior demodulation
srsran_pbch_nr_cfg_t pbch_cfg = {}; if (t_offset >= q->cp_sz) {
pbch_cfg.N_id = N_id; t_offset -= q->cp_sz;
pbch_cfg.ssb_scs = q->cfg.scs; } else {
pbch_cfg.Lmax = q->Lmax; t_offset = 0;
}
// Decode // Demodulate
if (srsran_pbch_nr_decode(&q->pbch, &pbch_cfg, ssb_idx, ssb_grid, ce, msg) < SRSRAN_SUCCESS) { cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
if (ssb_demodulate(q, in, t_offset, ssb_grid) < SRSRAN_SUCCESS) {
ERROR("Error demodulating");
return SRSRAN_ERROR;
}
// Find best N_id_1
uint32_t N_id_1 = 0;
float sss_corr = 0.0f;
if (srsran_sss_nr_find(ssb_grid, N_id_2, &sss_corr, &N_id_1) < SRSRAN_SUCCESS) {
ERROR("Error searching for N_id_2");
return SRSRAN_ERROR;
}
// Select N_id
uint32_t N_id = SRSRAN_NID_NR(N_id_1, N_id_2);
// Select the most suitable SSB candidate
uint32_t n_hf = 0;
uint32_t ssb_idx = 0;
if (ssb_select_pbch(q, N_id, ssb_grid, &n_hf, &ssb_idx) < SRSRAN_SUCCESS) {
ERROR("Error selecting PBCH");
return SRSRAN_ERROR;
}
// Compute PBCH channel estimates
srsran_pbch_msg_nr_t pbch_msg = {};
if (ssb_decode_pbch(q, N_id, n_hf, ssb_idx, ssb_grid, &pbch_msg) < SRSRAN_SUCCESS) {
ERROR("Error decoding PBCH"); ERROR("Error decoding PBCH");
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
// Save result
res->N_id = N_id;
res->t_offset = t_offset;
res->pbch_msg = pbch_msg;
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }

@ -146,3 +146,10 @@ add_nr_test(ssb_measure_test ssb_measure_test)
add_executable(ssb_decode_test ssb_decode_test.c) add_executable(ssb_decode_test ssb_decode_test.c)
target_link_libraries(ssb_decode_test srsran_phy) target_link_libraries(ssb_decode_test srsran_phy)
add_nr_test(ssb_decode_test ssb_decode_test) add_nr_test(ssb_decode_test ssb_decode_test)
add_executable(ssb_file_test ssb_file_test.c)
target_link_libraries(ssb_file_test srsran_phy)
# File test 1
# Captured with command: lib/examples/usrp_capture -a type=x300,clock=external,sampling_rate=46.08e6,rx_subdev_spec=B:0 -g 20 -r 46.08e6 -n 460800 -f 3502.8e6 -o /tmp/n78.fo35028.fs2304M.data
add_nr_test(ssb_file_test ssb_file_test -i ${CMAKE_CURRENT_SOURCE_DIR}/n78.fo35028.fs4608M.data -v -r 46.08e6 -f 3502.8e6 -F 3512.64e6 -n 460800 -A 500 357802 5 0 0 0)

@ -94,6 +94,7 @@ static void gen_pbch_msg(srsran_pbch_msg_nr_t* pbch_msg, uint32_t ssb_idx)
srsran_random_bit_vector(random_gen, pbch_msg->payload, SRSRAN_PBCH_NR_PAYLOAD_SZ); srsran_random_bit_vector(random_gen, pbch_msg->payload, SRSRAN_PBCH_NR_PAYLOAD_SZ);
pbch_msg->ssb_idx = ssb_idx; pbch_msg->ssb_idx = ssb_idx;
pbch_msg->crc = true;
} }
static int test_case_1(srsran_ssb_t* ssb) static int test_case_1(srsran_ssb_t* ssb)
@ -101,6 +102,7 @@ static int test_case_1(srsran_ssb_t* ssb)
// For benchmarking purposes // For benchmarking purposes
uint64_t t_encode_usec = 0; uint64_t t_encode_usec = 0;
uint64_t t_decode_usec = 0; uint64_t t_decode_usec = 0;
uint64_t t_search_usec = 0;
// SSB configuration // SSB configuration
srsran_ssb_cfg_t ssb_cfg = {}; srsran_ssb_cfg_t ssb_cfg = {};
@ -132,7 +134,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, ssb_idx, &pbch_msg_tx, buffer, buffer) == SRSRAN_SUCCESS); TESTASSERT(srsran_ssb_add(ssb, pci, &pbch_msg_tx, buffer, buffer) == SRSRAN_SUCCESS);
gettimeofday(&t[2], NULL); gettimeofday(&t[2], NULL);
get_time_interval(t); get_time_interval(t);
t_encode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; t_encode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
@ -143,7 +145,8 @@ static int test_case_1(srsran_ssb_t* ssb)
// Decode // Decode
gettimeofday(&t[1], NULL); gettimeofday(&t[1], NULL);
srsran_pbch_msg_nr_t pbch_msg_rx = {}; srsran_pbch_msg_nr_t pbch_msg_rx = {};
TESTASSERT(srsran_ssb_decode_pbch(ssb, pci, ssb_idx, 0, buffer, &pbch_msg_rx) == SRSRAN_SUCCESS); TESTASSERT(srsran_ssb_decode_pbch(ssb, pci, pbch_msg_tx.hrf, pbch_msg_tx.ssb_idx, buffer, &pbch_msg_rx) ==
SRSRAN_SUCCESS);
gettimeofday(&t[2], NULL); gettimeofday(&t[2], NULL);
get_time_interval(t); get_time_interval(t);
t_decode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; t_decode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
@ -154,12 +157,30 @@ static int test_case_1(srsran_ssb_t* ssb)
// Assert PBCH message CRC // Assert PBCH message CRC
TESTASSERT(pbch_msg_rx.crc); TESTASSERT(pbch_msg_rx.crc);
TESTASSERT(memcmp(&pbch_msg_rx, &pbch_msg_tx, sizeof(srsran_pbch_msg_nr_t)) == 0);
// Search
srsran_ssb_search_res_t res = {};
gettimeofday(&t[1], NULL);
TESTASSERT(srsran_ssb_search(ssb, buffer, hf_len, &res) == SRSRAN_SUCCESS);
gettimeofday(&t[2], NULL);
get_time_interval(t);
t_search_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
// Print decoded PBCH message
srsran_pbch_msg_info(&res.pbch_msg, str, sizeof(str));
INFO("test_case_1 - found pci=%d %s crc=%s", res.N_id, str, res.pbch_msg.crc ? "OK" : "KO");
// Assert PBCH message CRC
TESTASSERT(res.pbch_msg.crc);
TESTASSERT(memcmp(&res.pbch_msg, &pbch_msg_tx, sizeof(srsran_pbch_msg_nr_t)) == 0);
} }
} }
INFO("test_case_1 - %.1f usec/encode; %.1f usec/decode;", INFO("test_case_1 - %.1f usec/encode; %.1f usec/decode; %.1f usec/decode;",
(double)t_encode_usec / (double)(count), (double)t_encode_usec / (double)(count),
(double)t_decode_usec / (double)(count)); (double)t_decode_usec / (double)(count),
(double)t_search_usec / (double)(count));
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
@ -178,6 +199,7 @@ int main(int argc, char** argv)
srsran_ssb_args_t ssb_args = {}; srsran_ssb_args_t ssb_args = {};
ssb_args.enable_encode = true; ssb_args.enable_encode = true;
ssb_args.enable_decode = true; ssb_args.enable_decode = true;
ssb_args.enable_search = true;
if (buffer == NULL) { if (buffer == NULL) {
ERROR("Malloc"); ERROR("Malloc");
@ -201,6 +223,7 @@ int main(int argc, char** argv)
if (test_case_1(&ssb) != SRSRAN_SUCCESS) { if (test_case_1(&ssb) != SRSRAN_SUCCESS) {
ERROR("test case failed"); ERROR("test case failed");
goto clean_exit;
} }
ret = SRSRAN_SUCCESS; ret = SRSRAN_SUCCESS;

@ -0,0 +1,217 @@
/**
*
* \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/io/filesource.h"
#include "srsran/phy/sync/ssb.h"
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/vector.h"
#include <complex.h>
#include <getopt.h>
#include <stdlib.h>
// NR parameters
static srsran_ssb_patern_t ssb_pattern = SRSRAN_SSB_PATTERN_C;
static srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_30kHz;
static srsran_duplex_mode_t duplex_mode = SRSRAN_DUPLEX_MODE_TDD;
// Test context
static char* filename = NULL;
static double srate_hz = 23.04e6; // Base-band sampling rate in Hz
static double center_freq_hz = NAN; // Center frequency in Hz
static double ssb_freq_hz = NAN; // SSB frequency in Hz
static uint32_t nof_samples = 0; // Number of half-frames
// Assertion
static bool assert = false;
static uint32_t assert_pci = 0;
static uint32_t assert_t_offset = 0;
static uint32_t assert_sfn_lsb = 0;
static uint32_t assert_ssb_idx = 0;
static uint32_t assert_ssb_k = 0;
static uint32_t assert_hrf = 0;
static void usage(char* prog)
{
printf("Usage: %s -i filename [rv]\n", prog);
printf("\t-r sampling rate in Hz [Default %.2f MHz]\n", srate_hz / 1e6);
printf("\t-f absolute baseband center frequency in Hz [Default %.2f MHz]\n", center_freq_hz / 1e3);
printf("\t-F absolute SSB center freuqency in Hz [Default %.2f MHz]\n", ssb_freq_hz / 1e3);
printf("\t-F absolute SSB center freuqency in Hz [Default %.2f MHz]\n", ssb_freq_hz / 1e3);
printf("\t-A Assert: PCI t_offset sfn_lsb ssb_idx ssb_k hrf");
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, "inrfFAv")) != -1) {
switch (opt) {
case 'i':
filename = argv[optind];
break;
case 'n':
nof_samples = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'r':
srate_hz = strtod(argv[optind], NULL);
break;
case 'f':
center_freq_hz = strtod(argv[optind], NULL);
break;
case 'F':
ssb_freq_hz = strtod(argv[optind], NULL);
break;
case 'A':
assert = true;
assert_pci = (uint32_t)strtol(argv[optind++], NULL, 10);
assert_t_offset = (uint32_t)strtol(argv[optind++], NULL, 10);
assert_sfn_lsb = (uint32_t)strtol(argv[optind++], NULL, 10);
assert_ssb_idx = (uint32_t)strtol(argv[optind++], NULL, 10);
assert_ssb_k = (uint32_t)strtol(argv[optind++], NULL, 10);
assert_hrf = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'v':
srsran_verbose++;
break;
default:
usage(argv[0]);
exit(-1);
}
}
}
static int assert_meas(uint32_t N_id, const srsran_csi_trs_measurements_t* res)
{
TESTASSERT(N_id == assert_pci);
return SRSRAN_SUCCESS;
}
static int assert_search(const srsran_ssb_search_res_t* res)
{
TESTASSERT(res->N_id == assert_pci);
TESTASSERT(res->t_offset == assert_t_offset);
TESTASSERT(res->pbch_msg.sfn_4lsb == assert_sfn_lsb);
TESTASSERT(res->pbch_msg.ssb_idx == assert_ssb_idx);
TESTASSERT(res->pbch_msg.k_ssb_msb == assert_ssb_k);
TESTASSERT((res->pbch_msg.hrf ? 1 : 0) == assert_hrf);
return SRSRAN_SUCCESS;
}
int main(int argc, char** argv)
{
srsran_filesource_t filesource = {};
srsran_ssb_t ssb = {};
int ret = SRSRAN_ERROR;
parse_args(argc, argv);
if (nof_samples == 0 || !isnormal(ssb_freq_hz) || !isnormal(center_freq_hz)) {
ERROR("Invalid arguments!");
usage(argv[0]);
return SRSRAN_ERROR;
}
cf_t* buffer = srsran_vec_cf_malloc(nof_samples);
if (buffer == NULL) {
ERROR("Malloc");
goto clean_exit;
}
// Initialise SSB
srsran_ssb_args_t ssb_args = {};
ssb_args.enable_decode = true;
ssb_args.enable_search = true;
if (srsran_ssb_init(&ssb, &ssb_args) < SRSRAN_SUCCESS) {
ERROR("Init");
goto clean_exit;
}
// Configure SSB
srsran_ssb_cfg_t ssb_cfg = {};
ssb_cfg.srate_hz = srate_hz;
ssb_cfg.center_freq_hz = center_freq_hz;
ssb_cfg.ssb_freq_hz = ssb_freq_hz;
ssb_cfg.scs = ssb_scs;
ssb_cfg.pattern = ssb_pattern;
ssb_cfg.duplex_mode = duplex_mode;
if (srsran_ssb_set_cfg(&ssb, &ssb_cfg) < SRSRAN_SUCCESS) {
ERROR("Error setting SSB configuration");
goto clean_exit;
}
// Initialise file source
if (srsran_filesource_init(&filesource, filename, SRSRAN_COMPLEX_FLOAT_BIN) < SRSRAN_SUCCESS) {
ERROR("Error opening file");
goto clean_exit;
}
// Read baseband
if (srsran_filesource_read(&filesource, buffer, (int)nof_samples) < SRSRAN_SUCCESS) {
ERROR("Error reading from file");
goto clean_exit;
}
// Perform SSB-CSI Search
uint32_t N_id = 0;
srsran_csi_trs_measurements_t meas = {};
if (srsran_ssb_csi_search(&ssb, buffer, nof_samples, &N_id, &meas) < SRSRAN_SUCCESS) {
ERROR("Error performing SSB-CSI search");
goto clean_exit;
}
// Print measurement
char str[512] = {};
srsran_csi_meas_info(&meas, str, sizeof(str));
INFO("measure - search pci=%d %s", N_id, str);
// Assert measurement
if (assert) {
if (assert_meas(N_id, &meas)) {
ERROR("Error asserting search");
goto clean_exit;
}
}
// Perform SSB search
srsran_ssb_search_res_t search_res = {};
if (srsran_ssb_search(&ssb, buffer, nof_samples, &search_res) < SRSRAN_SUCCESS) {
ERROR("Error performing SSB search");
goto clean_exit;
}
// Print decoded PBCH message
srsran_pbch_msg_info(&search_res.pbch_msg, str, sizeof(str));
INFO("search - t_offset=%d pci=%d %s crc=%s",
search_res.t_offset,
search_res.N_id,
str,
search_res.pbch_msg.crc ? "OK" : "KO");
// Assert search
if (assert) {
if (assert_search(&search_res)) {
ERROR("Error asserting search");
goto clean_exit;
}
}
ret = SRSRAN_SUCCESS;
clean_exit:
srsran_ssb_free(&ssb);
srsran_filesource_free(&filesource);
if (buffer) {
free(buffer);
}
return ret;
}

@ -125,7 +125,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, 0, &pbch_msg, buffer, buffer) == SRSRAN_SUCCESS); TESTASSERT(srsran_ssb_add(ssb, pci, &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;

@ -1004,7 +1004,9 @@ void radio::handle_rf_msg(srsran_rf_error_t error)
logger.info("Overflow"); logger.info("Overflow");
// inform PHY about overflow // inform PHY about overflow
phy->radio_overflow(); if (phy != nullptr) {
phy->radio_overflow();
}
} else if (error.type == srsran_rf_error_t::SRSRAN_RF_ERROR_UNDERFLOW) { } else if (error.type == srsran_rf_error_t::SRSRAN_RF_ERROR_UNDERFLOW) {
rf_metrics.rf_u++; rf_metrics.rf_u++;
rf_metrics.rf_error = true; rf_metrics.rf_error = true;

@ -58,6 +58,20 @@ sys_metrics_processor::proc_stats_info::proc_stats_info()
arg_end >> env_start >> env_end >> exit_code; arg_end >> env_start >> env_end >> exit_code;
} }
/// Returns a null sys_metrics_t with the cpu count field filled.
static sys_metrics_t create_null_metrics()
{
sys_metrics_t metrics;
if (cpu_count > metrics_max_supported_cpu) {
return metrics;
}
metrics.cpu_count = cpu_count;
metrics.cpu_load.fill(0.f);
return metrics;
}
sys_metrics_t sys_metrics_processor::get_metrics() sys_metrics_t sys_metrics_processor::get_metrics()
{ {
auto current_time = std::chrono::steady_clock::now(); auto current_time = std::chrono::steady_clock::now();
@ -67,7 +81,7 @@ sys_metrics_t sys_metrics_processor::get_metrics()
// The time elapsed between 2 measures must be greater that 100 milliseconds. // The time elapsed between 2 measures must be greater that 100 milliseconds.
if (measure_interval_ms < 100u) { if (measure_interval_ms < 100u) {
logger.warning("Interval less than 100ms, skipping measurement."); logger.warning("Interval less than 100ms, skipping measurement.");
return {}; return create_null_metrics();
} }
sys_metrics_t metrics; sys_metrics_t metrics;

@ -312,6 +312,7 @@ void pdcp_entity_lte::handle_srb_pdu(srsran::unique_byte_buffer_t pdu)
if (integrity_direction == DIRECTION_RX || integrity_direction == DIRECTION_TXRX) { if (integrity_direction == DIRECTION_RX || integrity_direction == DIRECTION_TXRX) {
if (not integrity_verify(pdu->msg, pdu->N_bytes, count, mac)) { if (not integrity_verify(pdu->msg, pdu->N_bytes, count, mac)) {
logger.error(pdu->msg, pdu->N_bytes, "%s Dropping PDU", rrc->get_rb_name(lcid)); logger.error(pdu->msg, pdu->N_bytes, "%s Dropping PDU", rrc->get_rb_name(lcid));
rrc->notify_pdcp_integrity_error(lcid);
return; // Discard return; // Discard
} }
} }
@ -772,7 +773,8 @@ void pdcp_entity_lte::notify_failure(const pdcp_sn_vector_t& pdcp_sns)
***************************************************************************/ ***************************************************************************/
bool pdcp_entity_lte::check_valid_config() bool pdcp_entity_lte::check_valid_config()
{ {
if (cfg.sn_len != PDCP_SN_LEN_5 && cfg.sn_len != PDCP_SN_LEN_7 && cfg.sn_len != PDCP_SN_LEN_12 && cfg.sn_len != PDCP_SN_LEN_18) { if (cfg.sn_len != PDCP_SN_LEN_5 && cfg.sn_len != PDCP_SN_LEN_7 && cfg.sn_len != PDCP_SN_LEN_12 &&
cfg.sn_len != PDCP_SN_LEN_18) {
logger.error("Trying to configure bearer with invalid SN LEN=%d", cfg.sn_len); logger.error("Trying to configure bearer with invalid SN LEN=%d", cfg.sn_len);
return false; return false;
} }

@ -84,6 +84,7 @@ public:
void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) {} void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) {}
void write_pdu_pcch(srsran::unique_byte_buffer_t pdu) {} void write_pdu_pcch(srsran::unique_byte_buffer_t pdu) {}
void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {} void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {}
void notify_pdcp_integrity_error(uint32_t lcid) {}
const char* get_rb_name(uint32_t lcid) { return "None"; } const char* get_rb_name(uint32_t lcid) { return "None"; }

@ -126,6 +126,7 @@ public:
// rrc_interface_pdcp // rrc_interface_pdcp
void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) override; void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) override;
void notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) override;
uint32_t get_nof_users(); uint32_t get_nof_users();

@ -99,8 +99,10 @@ public:
void max_retx_attempted(uint16_t rnti) {} void max_retx_attempted(uint16_t rnti) {}
void protocol_failure(uint16_t rnti) {} void protocol_failure(uint16_t rnti) {}
const char* get_rb_name(uint32_t lcid) { return "invalid"; } const char* get_rb_name(uint32_t lcid) { return "invalid"; }
// PDCP interface // PDCP interface
void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) final; void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) final;
void notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) final;
class ue class ue
{ {

@ -188,7 +188,6 @@ private:
uint32_t rlf_cnt = 0; uint32_t rlf_cnt = 0;
uint8_t transaction_id = 0; uint8_t transaction_id = 0;
rrc_state_t state = RRC_STATE_IDLE; rrc_state_t state = RRC_STATE_IDLE;
uint16_t old_reest_rnti = SRSRAN_INVALID_RNTI;
std::map<uint16_t, srsran::pdcp_lte_state_t> old_reest_pdcp_state = {}; std::map<uint16_t, srsran::pdcp_lte_state_t> old_reest_pdcp_state = {};
bool rlf_info_pending = false; bool rlf_info_pending = false;

@ -108,6 +108,7 @@ private:
void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu); void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu);
void write_pdu_pcch(srsran::unique_byte_buffer_t pdu); void write_pdu_pcch(srsran::unique_byte_buffer_t pdu);
void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {} void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {}
void notify_pdcp_integrity_error(uint32_t lcid);
const char* get_rb_name(uint32_t lcid); const char* get_rb_name(uint32_t lcid);
}; };

@ -96,6 +96,7 @@ private:
void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) final; void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) final;
void write_pdu_pcch(srsran::unique_byte_buffer_t pdu) final; void write_pdu_pcch(srsran::unique_byte_buffer_t pdu) final;
void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final {} void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final {}
void notify_pdcp_integrity_error(uint32_t lcid) final {}
const char* get_rb_name(uint32_t lcid) final; const char* get_rb_name(uint32_t lcid) final;
}; };

@ -163,7 +163,7 @@ void parse_args(all_args_t* args, int argc, char* argv[])
("scheduler.target_bler", bpo::value<float>(&args->stack.mac.sched.target_bler)->default_value(0.05), "Target BLER (in decimal) to achieve via adaptive link") ("scheduler.target_bler", bpo::value<float>(&args->stack.mac.sched.target_bler)->default_value(0.05), "Target BLER (in decimal) to achieve via adaptive link")
("scheduler.max_delta_dl_cqi", bpo::value<float>(&args->stack.mac.sched.max_delta_dl_cqi)->default_value(5.0), "Maximum shift in CQI for adaptive DL link") ("scheduler.max_delta_dl_cqi", bpo::value<float>(&args->stack.mac.sched.max_delta_dl_cqi)->default_value(5.0), "Maximum shift in CQI for adaptive DL link")
("scheduler.max_delta_ul_snr", bpo::value<float>(&args->stack.mac.sched.max_delta_ul_snr)->default_value(5.0), "Maximum shift in UL SNR for adaptive UL link") ("scheduler.max_delta_ul_snr", bpo::value<float>(&args->stack.mac.sched.max_delta_ul_snr)->default_value(5.0), "Maximum shift in UL SNR for adaptive UL link")
("scheduler.adaptive_link_step_size", bpo::value<float>(&args->stack.mac.sched.max_delta_ul_snr)->default_value(0.001), "Step size or learning rate used in adaptive link") ("scheduler.adaptive_link_step_size", bpo::value<float>(&args->stack.mac.sched.adaptive_link_step_size)->default_value(0.001), "Step size or learning rate used in adaptive link")
("scheduler.min_tpc_tti_interval", bpo::value<uint32_t>(&args->stack.mac.sched.min_tpc_tti_interval)->default_value(1), "Minimum TTI interval between positive or negative TPCs") ("scheduler.min_tpc_tti_interval", bpo::value<uint32_t>(&args->stack.mac.sched.min_tpc_tti_interval)->default_value(1), "Minimum TTI interval between positive or negative TPCs")
("scheduler.ul_snr_avg_alpha", bpo::value<float>(&args->stack.mac.sched.ul_snr_avg_alpha)->default_value(0.05), "Exponential Average alpha coefficient used in estimation of UL SNR") ("scheduler.ul_snr_avg_alpha", bpo::value<float>(&args->stack.mac.sched.ul_snr_avg_alpha)->default_value(0.05), "Exponential Average alpha coefficient used in estimation of UL SNR")
("scheduler.init_ul_snr_value", bpo::value<int>(&args->stack.mac.sched.init_ul_snr_value)->default_value(5), "Initial UL SNR value used for computing MCS in the first UL grant") ("scheduler.init_ul_snr_value", bpo::value<int>(&args->stack.mac.sched.init_ul_snr_value)->default_value(5), "Initial UL SNR value used for computing MCS in the first UL grant")

@ -353,7 +353,7 @@ int mac::push_pdu(uint32_t tti_rx,
logger.debug("Discarding PDU rnti=0x%x", rnti); logger.debug("Discarding PDU rnti=0x%x", rnti);
} }
}; };
auto ret = stack_task_queue.try_push(std::bind(process_pdu_task, std::move(pdu))); stack_task_queue.try_push(std::bind(process_pdu_task, std::move(pdu)));
} else { } else {
logger.debug("Discarding PDU rnti=0x%x, tti_rx=%d, nof_bytes=%d", rnti, tti_rx, nof_bytes); logger.debug("Discarding PDU rnti=0x%x, tti_rx=%d, nof_bytes=%d", rnti, tti_rx, nof_bytes);
} }

@ -588,7 +588,7 @@ sf_sched::alloc_ul(sched_ue* user, prb_interval alloc, ul_alloc_t::type_t alloc_
// Check if there is no collision with measGap // Check if there is no collision with measGap
bool needs_pdcch = alloc_type == ul_alloc_t::ADAPT_RETX or (alloc_type == ul_alloc_t::NEWTX and not is_msg3); bool needs_pdcch = alloc_type == ul_alloc_t::ADAPT_RETX or (alloc_type == ul_alloc_t::NEWTX and not is_msg3);
if (not user->pusch_enabled(get_tti_rx(), cc_cfg->enb_cc_idx, needs_pdcch)) { if (not user->pusch_enabled(get_tti_rx(), cc_cfg->enb_cc_idx, needs_pdcch)) {
logger.debug("SCHED: PDCCH would collide with rnti=0x%x Measurement Gap", user->get_rnti()); logger.debug("SCHED: PDCCH/PUSCH would collide with rnti=0x%x Measurement Gap", user->get_rnti());
return alloc_result::no_rnti_opportunity; return alloc_result::no_rnti_opportunity;
} }
@ -654,12 +654,22 @@ alloc_result sf_sched::alloc_phich(sched_ue* user)
if (not user->phich_enabled(get_tti_rx(), cc_cfg->enb_cc_idx)) { if (not user->phich_enabled(get_tti_rx(), cc_cfg->enb_cc_idx)) {
// PHICH falls in measGap. PHICH hi=1 is assumed by UE. In case of NACK, the HARQ is going to be resumed later on. // PHICH falls in measGap. PHICH hi=1 is assumed by UE. In case of NACK, the HARQ is going to be resumed later on.
logger.info( bool ack = h->pop_pending_phich(); // empty pending PHICH
"SCHED: UL skipped retx rnti=0x%x, pid=%d. Cause: PHICH-measGap collision", user->get_rnti(), h->get_id()); if (h->is_empty(0)) {
h->pop_pending_phich(); // empty pending PHICH logger.debug("SCHED: PHICH hi=%d not sent for rnti=0x%x, cc=%d, pid=%d. Cause: PHICH-measGap collision",
// Note: Given that the UE assumes PHICH hi=1, it is not expecting PUSCH grants for tti_tx_ul. Requesting PDCCH (int)ack,
// for the UL Harq has the effect of forbidding PUSCH grants, since phich_tti == pdcch_tti. user->get_rnti(),
h->request_pdcch(); get_enb_cc_idx(),
h->get_id());
} else {
// Note: Given that the UE assumes PHICH hi=1, it is not expecting PUSCH grants for tti_tx_ul. Requesting PDCCH
// for the UL Harq has the effect of forbidding PUSCH grants, since phich_tti == pdcch_tti.
h->request_pdcch();
logger.info("SCHED: UL skipped retx rnti=0x%x, cc=%d, pid=%d. Cause: PHICH-measGap collision",
user->get_rnti(),
get_enb_cc_idx(),
h->get_id());
}
return alloc_result::no_cch_space; return alloc_result::no_cch_space;
} }

@ -239,6 +239,9 @@ void ul_harq_proc::new_tti()
logger->info( logger->info(
"SCHED: discarding UL pid=%d, tti=%d, maximum number of retx exceeded (%d)", get_id(), tti.to_uint(), max_retx); "SCHED: discarding UL pid=%d, tti=%d, maximum number of retx exceeded (%d)", get_id(), tti.to_uint(), max_retx);
active[0] = false; active[0] = false;
if (not pending_phich) {
reset_pending_data();
}
} }
} }
@ -284,6 +287,9 @@ bool ul_harq_proc::set_ack(uint32_t tb_idx, bool ack_)
return false; return false;
} }
set_ack_common(tb_idx, ack_); set_ack_common(tb_idx, ack_);
if (is_empty(0) and not pending_phich) {
reset_pending_data();
}
return true; return true;
} }

@ -23,6 +23,7 @@
#include "srsenb/hdr/stack/rrc/rrc_cell_cfg.h" #include "srsenb/hdr/stack/rrc/rrc_cell_cfg.h"
#include "srsenb/hdr/stack/rrc/rrc_mobility.h" #include "srsenb/hdr/stack/rrc/rrc_mobility.h"
#include "srsenb/hdr/stack/rrc/rrc_paging.h" #include "srsenb/hdr/stack/rrc/rrc_paging.h"
#include "srsenb/hdr/stack/s1ap/s1ap.h"
#include "srsran/asn1/asn1_utils.h" #include "srsran/asn1/asn1_utils.h"
#include "srsran/asn1/rrc_utils.h" #include "srsran/asn1/rrc_utils.h"
#include "srsran/common/bcd_helpers.h" #include "srsran/common/bcd_helpers.h"
@ -299,6 +300,12 @@ void rrc::write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t p
} }
} }
void rrc::notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid)
{
logger.warning("Received integrity protection failure indication, rnti=0x%u, lcid=%u", rnti, lcid);
s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::unspecified);
}
/******************************************************************************* /*******************************************************************************
S1AP interface S1AP interface
*******************************************************************************/ *******************************************************************************/

@ -259,6 +259,7 @@ bool ue_cell_ded_list::alloc_cell_resources(uint32_t ue_cc_idx)
} }
} }
} }
logger.info("Setup measurement gap period=%d offset=%d", cell->meas_gap_period, cell->meas_gap_offset);
} }
} else { } else {
if (ue_cc_idx == 1 and not n_pucch_cs_present) { if (ue_cc_idx == 1 and not n_pucch_cs_present) {

@ -353,6 +353,8 @@ void rrc_nr::write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_
handle_pdu(rnti, lcid, std::move(pdu)); handle_pdu(rnti, lcid, std::move(pdu));
} }
void rrc_nr::notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) {}
/******************************************************************************* /*******************************************************************************
UE class UE class

@ -52,12 +52,7 @@ rrc::ue::ue(rrc* outer_rrc, uint16_t rnti_, const sched_interface::ue_cfg_t& sch
mac_ctrl(rnti, ue_cell_list, bearer_list, parent->cfg, parent->mac, *parent->cell_common_list, sched_ue_cfg) mac_ctrl(rnti, ue_cell_list, bearer_list, parent->cfg, parent->mac, *parent->cell_common_list, sched_ue_cfg)
{} {}
rrc::ue::~ue() rrc::ue::~ue() {}
{
if (old_reest_rnti != SRSRAN_INVALID_RNTI and parent->users.count(old_reest_rnti) > 0) {
parent->rem_user_thread(old_reest_rnti);
}
}
int rrc::ue::init() int rrc::ue::init()
{ {
@ -640,15 +635,18 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg)
parent->logger.debug("rnti=0x%x EUTRA capabilities: %s", rnti, js.to_string().c_str()); parent->logger.debug("rnti=0x%x EUTRA capabilities: %s", rnti, js.to_string().c_str());
} }
// Stop RLF timers to avoid that old RNTI gets removed during RRC Reestablishment // Recover GTP-U tunnels and S1AP context
parent->logger.info("Stopped RLF timers for old rnti=0x%x", old_rnti); parent->gtpu->mod_bearer_rnti(old_rnti, rnti);
old_ue->rlc_rlf_timer.stop(); parent->s1ap->user_mod(old_rnti, rnti);
old_ue->phy_ul_rlf_timer.stop();
old_ue->phy_dl_rlf_timer.stop(); // Reestablish E-RABs of old rnti later, during ConnectionReconfiguration
bearer_list.reestablish_bearers(std::move(old_ue->bearer_list));
// remove old RNTI
old_ue->mac_ctrl.set_drb_activation(false); old_ue->mac_ctrl.set_drb_activation(false);
parent->rem_user_thread(old_rnti);
old_reest_rnti = old_rnti; state = RRC_STATE_WAIT_FOR_CON_REEST_COMPLETE;
state = RRC_STATE_WAIT_FOR_CON_REEST_COMPLETE;
set_activity_timeout(MSG5_RX_TIMEOUT); set_activity_timeout(MSG5_RX_TIMEOUT);
} }
@ -704,36 +702,12 @@ void rrc::ue::handle_rrc_con_reest_complete(rrc_conn_reest_complete_s* msg, srsr
parent->logger.info("RRCConnectionReestablishComplete transaction ID: %d", msg->rrc_transaction_id); parent->logger.info("RRCConnectionReestablishComplete transaction ID: %d", msg->rrc_transaction_id);
auto old_ue_it = parent->users.find(old_reest_rnti);
if (old_ue_it == parent->users.end()) {
parent->logger.error("RRC Reestablishment old rnti=0x%x was erased during the procedure", old_reest_rnti);
parent->release_ue(rnti);
return;
}
auto* old_ue = old_ue_it->second.get();
// TODO: msg->selected_plmn_id - used to select PLMN from SIB1 list // TODO: msg->selected_plmn_id - used to select PLMN from SIB1 list
// TODO: if(msg->registered_mme_present) - the indicated MME should be used from a pool // TODO: if(msg->registered_mme_present) - the indicated MME should be used from a pool
// Modify GTP-U tunnel and S1AP context // signal mac scheduler that configuration was successful
parent->gtpu->mod_bearer_rnti(old_reest_rnti, rnti);
parent->s1ap->user_mod(old_reest_rnti, rnti);
// Signal MAC scheduler that configuration was successful
mac_ctrl.handle_con_reest_complete(); mac_ctrl.handle_con_reest_complete();
// Activate security for SRB1
parent->pdcp->config_security(rnti, srb_to_lcid(lte_srb::srb1), ue_security_cfg.get_as_sec_cfg());
parent->pdcp->enable_integrity(rnti, srb_to_lcid(lte_srb::srb1));
parent->pdcp->enable_encryption(rnti, srb_to_lcid(lte_srb::srb1));
// Reestablish E-RABs of old rnti during ConnectionReconfiguration
bearer_list.reestablish_bearers(std::move(old_ue->bearer_list));
// remove old RNTI
parent->rem_user(old_reest_rnti);
old_reest_rnti = SRSRAN_INVALID_RNTI;
state = RRC_STATE_REESTABLISHMENT_COMPLETE; state = RRC_STATE_REESTABLISHMENT_COMPLETE;
// 2> if the UE has radio link failure or handover failure information available // 2> if the UE has radio link failure or handover failure information available

@ -147,10 +147,14 @@ bool gtpu_tunnel_manager::update_rnti(uint16_t old_rnti, uint16_t new_rnti)
srsran::bounded_vector<uint32_t, MAX_TUNNELS_PER_UE> to_remove; srsran::bounded_vector<uint32_t, MAX_TUNNELS_PER_UE> to_remove;
for (lcid_tunnel& bearer : new_rnti_obj) { for (lcid_tunnel& bearer : new_rnti_obj) {
tunnels[bearer.teid].rnti = new_rnti; tunnels[bearer.teid].rnti = new_rnti;
// Remove forwarding path
if (tunnels[bearer.teid].state == tunnel_state::forward_to) { if (tunnels[bearer.teid].state == tunnel_state::forward_to) {
// Remove forwarding path
tunnels[bearer.teid].state = tunnel_state::pdcp_active; tunnels[bearer.teid].state = tunnel_state::pdcp_active;
tunnels[bearer.teid].fwd_tunnel = nullptr; tunnels[bearer.teid].fwd_tunnel = nullptr;
logger.info("Taking down forwarding tunnel for rnti=0x%x, lcid=%d. New default " TEID_IN_FMT,
new_rnti,
bearer.lcid,
bearer.teid);
} else if (tunnels[bearer.teid].state == tunnel_state::forwarded_from) { } else if (tunnels[bearer.teid].state == tunnel_state::forwarded_from) {
to_remove.push_back(bearer.teid); to_remove.push_back(bearer.teid);
} }
@ -546,10 +550,19 @@ void gtpu::rem_tunnel(uint32_t teidin)
void gtpu::rem_user(uint16_t rnti) void gtpu::rem_user(uint16_t rnti)
{ {
if (tunnels.find_rnti_tunnels(rnti) == nullptr) { const auto* tun_lst = tunnels.find_rnti_tunnels(rnti);
if (tun_lst == nullptr) {
logger.info("Removing user - rnti=0x%x not found.", rnti); logger.info("Removing user - rnti=0x%x not found.", rnti);
return; return;
} }
for (gtpu_tunnel_manager::lcid_tunnel tun_elem : *tun_lst) {
const gtpu_tunnel* tun = tunnels.find_tunnel(tun_elem.teid);
if (tun != nullptr and tun->state == gtpu_tunnel_manager::tunnel_state::forwarded_from) {
// In case of forwarding tunnel tx endpoint, send one extra End Marker on removal
send_end_marker(tun->teid_in);
rem_tunnel(tun->teid_in);
}
}
tunnels.remove_rnti(rnti); tunnels.remove_rnti(rnti);
} }
@ -562,6 +575,7 @@ void gtpu::handle_end_marker(const gtpu_tunnel& rx_tunnel)
// TS 36.300, Sec 10.1.2.2.1 - Path Switch upon handover // TS 36.300, Sec 10.1.2.2.1 - Path Switch upon handover
// END MARKER should be forwarded to TeNB if forwarding is activated // END MARKER should be forwarded to TeNB if forwarding is activated
send_end_marker(rx_tunnel.fwd_tunnel->teid_in); send_end_marker(rx_tunnel.fwd_tunnel->teid_in);
rem_tunnel(rx_tunnel.fwd_tunnel->teid_in);
} }
// Remove tunnel that received End Marker // Remove tunnel that received End Marker

@ -229,6 +229,11 @@ void pdcp::user_interface_rrc::write_pdu(uint32_t lcid, srsran::unique_byte_buff
rrc->write_pdu(rnti, lcid, std::move(pdu)); rrc->write_pdu(rnti, lcid, std::move(pdu));
} }
void pdcp::user_interface_rrc::notify_pdcp_integrity_error(uint32_t lcid)
{
rrc->notify_pdcp_integrity_error(rnti, lcid);
}
void pdcp::user_interface_rrc::write_pdu_bcch_bch(srsran::unique_byte_buffer_t pdu) void pdcp::user_interface_rrc::write_pdu_bcch_bch(srsran::unique_byte_buffer_t pdu)
{ {
ERROR("Error: Received BCCH from ue=%d", rnti); ERROR("Error: Received BCCH from ue=%d", rnti);

@ -214,7 +214,7 @@ void test_gtpu_tunnel_manager()
TESTASSERT(after_tun->state == gtpu_tunnel_manager::tunnel_state::pdcp_active); TESTASSERT(after_tun->state == gtpu_tunnel_manager::tunnel_state::pdcp_active);
} }
enum class tunnel_test_event { success, wait_end_marker_timeout }; enum class tunnel_test_event { success, wait_end_marker_timeout, ue_removal_no_marker, reest_senb };
int test_gtpu_direct_tunneling(tunnel_test_event event) int test_gtpu_direct_tunneling(tunnel_test_event event)
{ {
@ -339,6 +339,25 @@ int test_gtpu_direct_tunneling(tunnel_test_event event)
for (size_t i = 0; i < gtpu_args.indirect_tunnel_timeout_msec + 1; ++i) { for (size_t i = 0; i < gtpu_args.indirect_tunnel_timeout_msec + 1; ++i) {
task_sched.tic(); task_sched.tic();
} }
} else if (event == tunnel_test_event::ue_removal_no_marker) {
// TEST: EndMarker may even reach SeNB, but the SeNB receives in tandem the UEContextReleaseCommand and closes
// the user tunnels before the chance to send an EndMarker
senb_gtpu.rem_user(0x46);
tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_rx_sockets.s1u_fd), senb_sockaddr);
} else if (event == tunnel_test_event::reest_senb) {
// TEST: UE may start a Reestablishment to the SeNB. In such case, the rnti will be updated, the forwarding tunnel
// taken down, and the previous main tunnel reestablished
senb_gtpu.mod_bearer_rnti(0x46, 0x47);
std::iota(data_vec.begin(), data_vec.end(), 0);
std::shuffle(data_vec.begin(), data_vec.end(), g);
pdu = encode_gtpu_packet(data_vec, senb_teid_in, sgw_sockaddr, senb_sockaddr);
encoded_data.assign(pdu->msg + 8u, pdu->msg + pdu->N_bytes);
senb_pdcp.last_sdu = nullptr;
senb_gtpu.handle_gtpu_s1u_rx_packet(std::move(pdu), sgw_sockaddr);
TESTASSERT(senb_pdcp.last_sdu != nullptr);
TESTASSERT(senb_pdcp.last_sdu->N_bytes == encoded_data.size() and
memcmp(senb_pdcp.last_sdu->msg, encoded_data.data(), encoded_data.size()) == 0);
return SRSRAN_SUCCESS;
} else { } else {
// TEST: EndMarker is forwarded via MME->SeNB->TeNB, and TeNB buffered PDUs are flushed // TEST: EndMarker is forwarded via MME->SeNB->TeNB, and TeNB buffered PDUs are flushed
pdu = encode_end_marker(senb_teid_in); pdu = encode_end_marker(senb_teid_in);
@ -366,6 +385,8 @@ int main(int argc, char** argv)
srsenb::test_gtpu_tunnel_manager(); srsenb::test_gtpu_tunnel_manager();
TESTASSERT(srsenb::test_gtpu_direct_tunneling(srsenb::tunnel_test_event::success) == SRSRAN_SUCCESS); TESTASSERT(srsenb::test_gtpu_direct_tunneling(srsenb::tunnel_test_event::success) == SRSRAN_SUCCESS);
TESTASSERT(srsenb::test_gtpu_direct_tunneling(srsenb::tunnel_test_event::wait_end_marker_timeout) == SRSRAN_SUCCESS); TESTASSERT(srsenb::test_gtpu_direct_tunneling(srsenb::tunnel_test_event::wait_end_marker_timeout) == SRSRAN_SUCCESS);
TESTASSERT(srsenb::test_gtpu_direct_tunneling(srsenb::tunnel_test_event::ue_removal_no_marker) == SRSRAN_SUCCESS);
TESTASSERT(srsenb::test_gtpu_direct_tunneling(srsenb::tunnel_test_event::reest_senb) == SRSRAN_SUCCESS);
srslog::flush(); srslog::flush();

@ -80,11 +80,10 @@ public:
/** /**
* @brief Sets the primary cell and selects NR operation mode, configures the cell bandwidth and sampling rate * @brief Sets the primary cell and selects NR operation mode, configures the cell bandwidth and sampling rate
* @param arfcn Frequency the component is receiving base-band from. Used only for reporting the ARFCN to the RRC
* @param cfg Actual configuration * @param cfg Actual configuration
* @return True if configuration is successful, false otherwise * @return True if configuration is successful, false otherwise
*/ */
bool set_config(uint32_t arfcn, const config_t& cfg); bool set_config(const config_t& cfg);
/** /**
* @brief Get current frequency number * @brief Get current frequency number

@ -82,6 +82,13 @@ public:
bool cell_select_start(phy_cell_t cell); bool cell_select_start(phy_cell_t cell);
bool cell_is_camping(); bool cell_is_camping();
/**
* @brief Interface for monitoring UE's synchronization transition to IDLE. In addition to IDLE transitioning, this
* method waits for workers to finish processing and ends the current RF transmission burst.
* @return true if SYNC transitioned to IDLE, false otherwise
*/
bool wait_idle();
// RRC interface for controlling the neighbour cell measurement // RRC interface for controlling the neighbour cell measurement
void set_cells_to_meas(uint32_t earfcn, const std::set<uint32_t>& pci); void set_cells_to_meas(uint32_t earfcn, const std::set<uint32_t>& pci);
void set_inter_frequency_measurement(uint32_t cc_idx, uint32_t earfcn_, srsran_cell_t cell_); void set_inter_frequency_measurement(uint32_t cc_idx, uint32_t earfcn_, srsran_cell_t cell_);
@ -199,7 +206,7 @@ private:
bool set_cell(float cfo); bool set_cell(float cfo);
std::atomic<bool> running = {false}; std::atomic<bool> running = {false};
bool is_overflow = false; bool is_overflow = false;
srsran::rf_timestamp_t last_rx_time; srsran::rf_timestamp_t last_rx_time;
bool forced_rx_time_init = true; // Rx time sync after first receive from radio bool forced_rx_time_init = true; // Rx time sync after first receive from radio
@ -241,7 +248,7 @@ private:
std::atomic<float> sfo = {}; // SFO estimate updated after each sync-cycle std::atomic<float> sfo = {}; // SFO estimate updated after each sync-cycle
std::atomic<float> cfo = {}; // CFO estimate updated after each sync-cycle std::atomic<float> cfo = {}; // CFO estimate updated after each sync-cycle
std::atomic<float> ref_cfo = {}; // provided adjustment value applied before sync std::atomic<float> ref_cfo = {}; // provided adjustment value applied before sync
sync_metrics_t metrics = {}; sync_metrics_t metrics = {};
// in-sync / out-of-sync counters // in-sync / out-of-sync counters
std::atomic<uint32_t> out_of_sync_cnt = {0}; std::atomic<uint32_t> out_of_sync_cnt = {0};
@ -283,7 +290,7 @@ private:
const static int MIN_TTI_JUMP = 1; ///< Time gap reported to stack after receiving subframe const static int MIN_TTI_JUMP = 1; ///< Time gap reported to stack after receiving subframe
const static int MAX_TTI_JUMP = 1000; ///< Maximum time gap tolerance in RF stream metadata const static int MAX_TTI_JUMP = 1000; ///< Maximum time gap tolerance in RF stream metadata
const uint8_t SYNC_CC_IDX = 0; ///< From the sync POV, the CC idx is always the first const uint8_t SYNC_CC_IDX = 0; ///< From the sync POV, the CC idx is always the first
const uint32_t TIMEOUT_TO_IDLE_MS = 2; ///< Timeout in milliseconds for transitioning to IDLE const uint32_t TIMEOUT_TO_IDLE_MS = 2000; ///< Timeout in milliseconds for transitioning to IDLE
}; };
} // namespace srsue } // namespace srsue

@ -156,6 +156,7 @@ public:
void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu); void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu);
void write_pdu_pcch(srsran::unique_byte_buffer_t pdu); void write_pdu_pcch(srsran::unique_byte_buffer_t pdu);
void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu); void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu);
void notify_pdcp_integrity_error(uint32_t lcid);
bool srbs_flushed(); //< Check if data on SRBs still needs to be sent bool srbs_flushed(); //< Check if data on SRBs still needs to be sent

@ -25,12 +25,12 @@
#include "srsran/asn1/rrc_nr.h" #include "srsran/asn1/rrc_nr.h"
#include "srsran/asn1/rrc_nr_utils.h" #include "srsran/asn1/rrc_nr_utils.h"
#include "srsran/common/block_queue.h" #include "srsran/common/block_queue.h"
#include "srsran/common/common_nr.h"
#include "srsran/common/buffer_pool.h" #include "srsran/common/buffer_pool.h"
#include "srsran/common/common_nr.h"
#include "srsran/common/stack_procedure.h" #include "srsran/common/stack_procedure.h"
#include "srsran/common/task_scheduler.h" #include "srsran/common/task_scheduler.h"
#include "srsran/interfaces/ue_rrc_interfaces.h"
#include "srsran/interfaces/ue_nr_interfaces.h" #include "srsran/interfaces/ue_nr_interfaces.h"
#include "srsran/interfaces/ue_rrc_interfaces.h"
#include "srsue/hdr/stack/upper/gw.h" #include "srsue/hdr/stack/upper/gw.h"
namespace srsue { namespace srsue {
@ -124,6 +124,7 @@ public:
void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) final; void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) final;
void write_pdu_pcch(srsran::unique_byte_buffer_t pdu) final; void write_pdu_pcch(srsran::unique_byte_buffer_t pdu) final;
void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final; void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final;
void notify_pdcp_integrity_error(uint32_t lcid) final;
// RRC (LTE) interface // RRC (LTE) interface
void get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps); void get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps);

@ -313,10 +313,15 @@ bool phy::cell_select(phy_cell_t cell)
{ {
sfsync.scell_sync_stop(); sfsync.scell_sync_stop();
if (sfsync.cell_select_init(cell)) { if (sfsync.cell_select_init(cell)) {
reset();
// Update PCI before starting the background command to make sure PRACH gets the updated value // Update PCI before starting the background command to make sure PRACH gets the updated value
selected_cell.id = cell.pci; selected_cell.id = cell.pci;
cmd_worker_cell.add_cmd([this, cell]() { cmd_worker_cell.add_cmd([this, cell]() {
// Wait SYNC transitions to IDLE
sfsync.wait_idle();
// Reset worker once SYNC is IDLE to flush any PHY state including measurements, pending ACKs and pending grants
reset();
bool ret = sfsync.cell_select_start(cell); bool ret = sfsync.cell_select_start(cell);
if (ret) { if (ret) {
srsran_cell_t sync_cell; srsran_cell_t sync_cell;
@ -339,8 +344,13 @@ bool phy::cell_search()
{ {
sfsync.scell_sync_stop(); sfsync.scell_sync_stop();
if (sfsync.cell_search_init()) { if (sfsync.cell_search_init()) {
reset();
cmd_worker_cell.add_cmd([this]() { cmd_worker_cell.add_cmd([this]() {
// Wait SYNC transitions to IDLE
sfsync.wait_idle();
// Reset worker once SYNC is IDLE to flush any PHY state including measurements, pending ACKs and pending grants
reset();
phy_cell_t found_cell = {}; phy_cell_t found_cell = {};
rrc_interface_phy_lte::cell_search_ret_t ret = sfsync.cell_search_start(&found_cell); rrc_interface_phy_lte::cell_search_ret_t ret = sfsync.cell_search_start(&found_cell);
stack->cell_search_complete(ret, found_cell); stack->cell_search_complete(ret, found_cell);

@ -882,12 +882,15 @@ void phy_common::reset()
pcell_report_period = 20; pcell_report_period = 20;
last_ri = 0; last_ri = 0;
ZERO_OBJECT(pathloss); {
ZERO_OBJECT(avg_sinr_db); std::unique_lock<std::mutex> lock(meas_mutex);
ZERO_OBJECT(avg_snr_db); ZERO_OBJECT(pathloss);
ZERO_OBJECT(avg_rsrp); ZERO_OBJECT(avg_sinr_db);
ZERO_OBJECT(avg_rsrp_dbm); ZERO_OBJECT(avg_snr_db);
ZERO_OBJECT(avg_rsrq_db); ZERO_OBJECT(avg_rsrp);
ZERO_OBJECT(avg_rsrp_dbm);
ZERO_OBJECT(avg_rsrq_db);
}
cell_state.reset(); cell_state.reset();
reset_neighbour_cells(); reset_neighbour_cells();

@ -158,7 +158,7 @@ void intra_measure_base::run_tti(uint32_t tti, cf_t* data, uint32_t nsamples)
srsran_ringbuffer_reset(&ring_buffer); srsran_ringbuffer_reset(&ring_buffer);
// Write baseband to ensure measurement starts in the right TTI // Write baseband to ensure measurement starts in the right TTI
Log(info, "Start writing"); Log(debug, "Start writing");
write(data, nsamples); write(data, nsamples);
} }
break; break;

@ -63,10 +63,10 @@ bool intra_measure_nr::init(uint32_t cc_idx_, const args_t& args)
return true; return true;
} }
bool intra_measure_nr::set_config(uint32_t arfcn, const config_t& cfg) bool intra_measure_nr::set_config(const config_t& cfg)
{ {
// Update ARFCN // Update ARFCN
current_arfcn = arfcn; current_arfcn = cfg.arfcn;
serving_cell_pci = cfg.serving_cell_pci; serving_cell_pci = cfg.serving_cell_pci;
// Reset performance measurement // Reset performance measurement

@ -228,18 +228,6 @@ rrc_interface_phy_lte::cell_search_ret_t sync::cell_search_start(phy_cell_t* fou
rrc_proc_state = PROC_SEARCH_RUNNING; rrc_proc_state = PROC_SEARCH_RUNNING;
// Wait for SYNC thread to transition to IDLE (max. 2000ms)
if (not phy_state.wait_idle(TIMEOUT_TO_IDLE_MS)) {
Error("SYNC: Error transitioning to IDLE. Cell search cannot start.");
return ret;
}
// Wait for workers to finish PHY processing
worker_com->semaphore.wait_all();
// Reset worker once SYNC is IDLE to flush any worker states such as ACKs and pending grants
worker_com->reset();
if (srate_mode != SRATE_FIND) { if (srate_mode != SRATE_FIND) {
srate_mode = SRATE_FIND; srate_mode = SRATE_FIND;
radio_h->set_rx_srate(1.92e6); radio_h->set_rx_srate(1.92e6);
@ -401,6 +389,26 @@ bool sync::cell_is_camping()
return phy_state.is_camping(); return phy_state.is_camping();
} }
bool sync::wait_idle()
{
// Wait for SYNC thread to transition to IDLE (max. 2000ms)
if (not phy_state.wait_idle(TIMEOUT_TO_IDLE_MS)) {
Error("SYNC: Failed transitioning to IDLE");
return false;
}
// Reset UE sync. Attention: doing this reset when the FSM is NOT IDLE can cause PSS/SSS out-of-sync
srsran_ue_sync_reset(&ue_sync);
// Wait for workers to finish PHY processing
worker_com->semaphore.wait_all();
// As workers have finished, make sure the Tx burst is ended
radio_h->tx_end();
return true;
}
void sync::run_cell_search_state() void sync::run_cell_search_state()
{ {
cell_search_ret = search_p.run(&cell, mib); cell_search_ret = search_p.run(&cell, mib);
@ -713,9 +721,9 @@ void sync::out_of_sync()
} }
} }
void sync::set_cfo(float cfo) void sync::set_cfo(float cfo_)
{ {
ref_cfo = cfo; ref_cfo = cfo_;
} }
void sync::set_agc_enable(bool enable) void sync::set_agc_enable(bool enable)
@ -768,7 +776,7 @@ float sync::get_tx_cfo()
return ret / 15000; return ret / 15000;
} }
void sync::set_ue_sync_opts(srsran_ue_sync_t* q, float cfo) void sync::set_ue_sync_opts(srsran_ue_sync_t* q, float cfo_)
{ {
if (worker_com->args->cfo_integer_enabled) { if (worker_com->args->cfo_integer_enabled) {
srsran_ue_sync_set_cfo_i_enable(q, true); srsran_ue_sync_set_cfo_i_enable(q, true);
@ -785,9 +793,9 @@ void sync::set_ue_sync_opts(srsran_ue_sync_t* q, float cfo)
worker_com->args->cfo_loop_pss_conv); worker_com->args->cfo_loop_pss_conv);
// Disable CP based CFO estimation during find // Disable CP based CFO estimation during find
if (std::isnormal(cfo)) { if (std::isnormal(cfo_)) {
srsran_ue_sync_cfo_reset(q, cfo); srsran_ue_sync_cfo_reset(q, cfo_);
q->cfo_current_value = cfo / 15000; q->cfo_current_value = cfo_ / 15000;
q->cfo_is_copied = true; q->cfo_is_copied = true;
q->cfo_correct_enable_find = true; q->cfo_correct_enable_find = true;
srsran_sync_set_cfo_cp_enable(&q->sfind, false, 0); srsran_sync_set_cfo_cp_enable(&q->sfind, false, 0);
@ -811,23 +819,8 @@ void sync::set_ue_sync_opts(srsran_ue_sync_t* q, float cfo)
srsran_sync_set_sss_algorithm(&q->sfind, (sss_alg_t)sss_alg); srsran_sync_set_sss_algorithm(&q->sfind, (sss_alg_t)sss_alg);
} }
bool sync::set_cell(float cfo) bool sync::set_cell(float cfo_in)
{ {
// Wait for SYNC thread to transition to IDLE (max. 2000ms)
if (not phy_state.wait_idle(TIMEOUT_TO_IDLE_MS)) {
Error("SYNC: Can not change Cell while not in IDLE");
return false;
}
// Reset UE sync. Attention: doing this reset when the FSM is NOT IDLE can cause PSS/SSS out-of-sync
srsran_ue_sync_reset(&ue_sync);
// Wait for workers to finish PHY processing
worker_com->semaphore.wait_all();
// Reset worker once SYNC is IDLE to flush any worker states such as ACKs and pending grants
worker_com->reset();
if (!srsran_cell_isvalid(&cell)) { if (!srsran_cell_isvalid(&cell)) {
Error("SYNC: Setting cell: invalid cell (nof_prb=%d, pci=%d, ports=%d)", cell.nof_prb, cell.id, cell.nof_ports); Error("SYNC: Setting cell: invalid cell (nof_prb=%d, pci=%d, ports=%d)", cell.nof_prb, cell.id, cell.nof_ports);
return false; return false;
@ -860,7 +853,7 @@ bool sync::set_cell(float cfo)
} }
// Set options defined in expert section // Set options defined in expert section
set_ue_sync_opts(&ue_sync, cfo); set_ue_sync_opts(&ue_sync, cfo_in);
// Reset ue_sync and set CFO/gain from search procedure // Reset ue_sync and set CFO/gain from search procedure
srsran_ue_sync_reset(&ue_sync); srsran_ue_sync_reset(&ue_sync);

@ -1629,6 +1629,11 @@ void rrc::write_pdu(uint32_t lcid, unique_byte_buffer_t pdu)
process_pdu(lcid, std::move(pdu)); process_pdu(lcid, std::move(pdu));
} }
void rrc::notify_pdcp_integrity_error(uint32_t lcid)
{
logger.warning("Received integrity protection failure indication, lcid=%u", lcid);
}
void rrc::process_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) void rrc::process_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu)
{ {
logger.debug("RX PDU, LCID: %d", lcid); logger.debug("RX PDU, LCID: %d", lcid);

@ -215,6 +215,7 @@ void rrc_nr::write_pdu_bcch_bch(srsran::unique_byte_buffer_t pdu) {}
void rrc_nr::write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) {} void rrc_nr::write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) {}
void rrc_nr::write_pdu_pcch(srsran::unique_byte_buffer_t pdu) {} void rrc_nr::write_pdu_pcch(srsran::unique_byte_buffer_t pdu) {}
void rrc_nr::write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {} void rrc_nr::write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {}
void rrc_nr::notify_pdcp_integrity_error(uint32_t lcid) {}
void rrc_nr::get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps_pdu) void rrc_nr::get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps_pdu)
{ {

@ -71,3 +71,15 @@ add_nr_test(nr_cell_search_test nr_cell_search_test --duration=1 --ssb_period=20
# This test checks the search is capable to find a cell with a broad delay # This test checks the search is capable to find a cell with a broad delay
add_nr_test(nr_cell_search_test_delay nr_cell_search_test --duration=1 --ssb_period=20 --meas_period_ms=100 --meas_len_ms=30 --channel.delay_min=0 --channel.delay_max=1000 --simulation_cell_list=500) add_nr_test(nr_cell_search_test_delay nr_cell_search_test --duration=1 --ssb_period=20 --meas_period_ms=100 --meas_len_ms=30 --channel.delay_min=0 --channel.delay_max=1000 --simulation_cell_list=500)
# File test of 10ms captured NR carrier
# Captured using: lib/examples/usrp_capture -a type=b200,master_clock_rate=61.44e6 -g 80 -r 61.44e6 -n 614400 -f 3682.5e6 -o ../srsue/test/phy/n78.fo3675360k.fs6144.data
#add_nr_test(nr_cell_search_test_file nr_cell_search_test --duration=1 --srate=61.44e6 --ssb_arfcn=645024 --carrier_arfcn=645500 --meas_period_ms=10 --meas_len_ms=10 --file.name=${CMAKE_SOURCE_DIR}/n78.fo3675360k.fs6144.data)
add_executable(nr_cell_search_rf nr_cell_search_rf.cc)
target_link_libraries(nr_cell_search_rf
srsue_phy
srsran_common
srsran_phy
srsran_radio
${CMAKE_THREAD_LIBS_INIT}
${Boost_LIBRARIES})

@ -0,0 +1,286 @@
/**
*
* \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/band_helper.h"
#include "srsran/common/string_helpers.h"
#include "srsran/common/test_common.h"
#include "srsran/interfaces/phy_interface_types.h"
#include "srsran/radio/radio.h"
#include "srsran/srslog/srslog.h"
#include "srsue/hdr/phy/scell/intra_measure_nr.h"
#include <boost/program_options.hpp>
#include <boost/program_options/parsers.hpp>
#include <iostream>
#include <map>
#include <memory>
#include <vector>
struct args_t {
// General
std::string log_level = "warning";
double srate_hz = 23.04e6;
// Measurement parameters
uint32_t meas_len_ms = 20;
uint32_t meas_period_ms = 20;
float thr_snr_db = -5.0f;
// Radio parameters
std::string radio_device_name = "auto";
std::string radio_device_args = "auto";
std::string radio_log_level = "info";
float rx_gain = 80.0f;
double freq_offset_hz = 0;
std::string bands = "78";
};
class meas_itf_listener : public srsue::scell::intra_measure_base::meas_itf
{
public:
typedef struct {
float rsrp_avg;
float rsrp_min;
float rsrp_max;
float rsrq_avg;
float rsrq_min;
float rsrq_max;
uint32_t arfcn;
uint32_t count;
} cell_meas_t;
std::map<uint32_t, cell_meas_t> cells;
void cell_meas_reset(uint32_t cc_idx) override {}
void new_cell_meas(uint32_t cc_idx, const std::vector<srsue::phy_meas_t>& meas) override
{
for (const srsue::phy_meas_t& m : meas) {
uint32_t pci = m.pci;
if (!cells.count(pci)) {
cells[pci].rsrp_min = m.rsrp;
cells[pci].rsrp_max = m.rsrp;
cells[pci].rsrp_avg = m.rsrp;
cells[pci].rsrq_min = m.rsrq;
cells[pci].rsrq_max = m.rsrq;
cells[pci].rsrq_avg = m.rsrq;
cells[pci].count = 1;
} else {
cells[pci].rsrp_min = SRSRAN_MIN(cells[pci].rsrp_min, m.rsrp);
cells[pci].rsrp_max = SRSRAN_MAX(cells[pci].rsrp_max, m.rsrp);
cells[pci].rsrp_avg = (m.rsrp + cells[pci].rsrp_avg * cells[pci].count) / (cells[pci].count + 1);
cells[pci].rsrq_min = SRSRAN_MIN(cells[pci].rsrq_min, m.rsrq);
cells[pci].rsrq_max = SRSRAN_MAX(cells[pci].rsrq_max, m.rsrq);
cells[pci].rsrq_avg = (m.rsrq + cells[pci].rsrq_avg * cells[pci].count) / (cells[pci].count + 1);
cells[pci].count++;
}
cells[pci].arfcn = m.earfcn;
}
}
void print_stats()
{
printf("\n-- Statistics:\n");
for (auto& e : cells) {
printf(" pci=%03d; arfcn=%d; count=%3d; rsrp=%+.1f|%+.1f|%+.1fdBfs; rsrq=%+.1f|%+.1f|%+.1fdB;\n",
e.first,
e.second.arfcn,
e.second.count,
e.second.rsrp_min,
e.second.rsrp_avg,
e.second.rsrp_max,
e.second.rsrq_min,
e.second.rsrq_avg,
e.second.rsrq_max);
}
}
};
// shorten boost program options namespace
namespace bpo = boost::program_options;
int parse_args(int argc, char** argv, args_t& args)
{
int ret = SRSRAN_SUCCESS;
bpo::options_description options("General options");
bpo::options_description measure("Measurement options");
bpo::options_description over_the_air("Mode 1: Over the air options (Default)");
// clang-format off
measure.add_options()
("meas_len_ms", bpo::value<uint32_t>(&args.meas_len_ms)->default_value(args.meas_len_ms), "Measurement length")
("meas_period_ms", bpo::value<uint32_t>(&args.meas_period_ms)->default_value(args.meas_period_ms), "Measurement period")
("thr_snr_db", bpo::value<float>(&args.thr_snr_db)->default_value(args.thr_snr_db), "Detection threshold for SNR in dB")
("bands", bpo::value<std::string>(&args.bands)->default_value(args.bands), "band list to measure, comma separated")
;
over_the_air.add_options()
("rf.device_name", bpo::value<std::string>(&args.radio_device_name)->default_value(args.radio_device_name), "RF Device Name")
("rf.device_args", bpo::value<std::string>(&args.radio_device_args)->default_value(args.radio_device_args), "RF Device arguments")
("rf.log_level", bpo::value<std::string>(&args.radio_log_level)->default_value(args.radio_log_level), "RF Log level (none, warning, info, debug)")
("rf.rx_gain", bpo::value<float>(&args.rx_gain)->default_value(args.rx_gain), "RF Receiver gain in dB")
("rf.freq_offset", bpo::value<double>(&args.freq_offset_hz)->default_value(args.freq_offset_hz), "RF frequency offset in Hz")
;
options.add(measure).add(over_the_air).add_options()
("help,h", "Show this message")
("log_level", bpo::value<std::string>(&args.log_level)->default_value(args.log_level), "Intra measurement log level (none, warning, info, debug)")
("srate", bpo::value<double>(&args.srate_hz)->default_value(args.srate_hz), "Sampling Rate in Hz")
;
// clang-format on
bpo::variables_map vm;
try {
bpo::store(bpo::command_line_parser(argc, argv).options(options).run(), vm);
bpo::notify(vm);
} catch (bpo::error& e) {
std::cerr << e.what() << std::endl;
ret = SRSRAN_ERROR;
}
// help option was given or error - print usage and exit
if (vm.count("help") || ret) {
std::cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << std::endl << std::endl;
std::cout << options << std::endl << std::endl;
ret = SRSRAN_ERROR;
}
return ret;
}
int main(int argc, char** argv)
{
// Parse args
args_t args = {};
if (parse_args(argc, argv, args) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
// Initiate logging
srslog::init();
srslog::basic_logger& logger = srslog::fetch_basic_logger("PHY");
logger.set_level(srslog::str_to_basic_level(args.log_level));
// Deduce base-band parameters
uint32_t sf_len = (uint32_t)round(args.srate_hz / 1000.0);
// Allocate buffer
std::vector<cf_t> baseband_buffer(sf_len);
// Create measurement callback
meas_itf_listener rrc;
// Create measurement object
srsue::scell::intra_measure_nr intra_measure(logger, rrc);
// Initialise measurement instance
srsue::scell::intra_measure_nr::args_t meas_args = {};
meas_args.rx_gain_offset_dB = 0.0f;
meas_args.max_len_ms = args.meas_len_ms;
meas_args.max_srate_hz = args.srate_hz;
meas_args.min_scs = srsran_subcarrier_spacing_15kHz;
meas_args.thr_snr_db = args.thr_snr_db;
TESTASSERT(intra_measure.init(0, meas_args));
std::set<srsran_subcarrier_spacing_t> scs_set = {srsran_subcarrier_spacing_15kHz, srsran_subcarrier_spacing_30kHz};
std::set<uint16_t> band_set = {};
srsran::string_parse_list(args.bands, ',', band_set);
// Create Radio
srsran::radio radio;
auto& radio_logger = srslog::fetch_basic_logger("RF", false);
radio_logger.set_level(srslog::str_to_basic_level(args.radio_log_level));
// Init radio
srsran::rf_args_t radio_args = {};
radio_args.device_args = args.radio_device_args;
radio_args.device_name = args.radio_device_name;
radio_args.nof_carriers = 1;
radio_args.nof_antennas = 1;
radio.init(radio_args, nullptr);
// Set sampling rate
radio.set_rx_srate(args.srate_hz);
radio.set_rx_gain(args.rx_gain);
double center_freq_hz = 0.0;
uint32_t tti_count = 0;
// Iterate
for (const uint16_t& band : band_set) {
for (const srsran_subcarrier_spacing_t& scs : scs_set) {
srsran::srsran_band_helper::sync_raster_t sync_raster = srsran::srsran_band_helper().get_sync_raster(band, scs);
// Iterate over all GSCN
for (; not sync_raster.end(); sync_raster.next()) {
double ssb_freq_hz = sync_raster.get_frequency();
// Set frequency if the deviation from the current frequency is too high
if (std::abs(center_freq_hz - ssb_freq_hz) > (args.srate_hz / 2.0)) {
center_freq_hz = ssb_freq_hz + args.srate_hz / 2.0;
// Update Rx frequency
radio.set_rx_freq(0, center_freq_hz + args.freq_offset_hz);
}
logger.info("Measuring SSB frequency %.2f MHz, center %.2f MHz", ssb_freq_hz / 1e6, center_freq_hz / 1e6);
// Setup measurement
srsue::scell::intra_measure_nr::config_t meas_cfg = {};
meas_cfg.arfcn = (uint32_t)(ssb_freq_hz / 1e3);
meas_cfg.srate_hz = args.srate_hz;
meas_cfg.len_ms = args.meas_len_ms;
meas_cfg.period_ms = args.meas_period_ms;
meas_cfg.center_freq_hz = center_freq_hz;
meas_cfg.ssb_freq_hz = ssb_freq_hz;
meas_cfg.scs = scs;
meas_cfg.serving_cell_pci = -1;
TESTASSERT(intra_measure.set_config(meas_cfg));
srsran::rf_buffer_t radio_buffer(baseband_buffer.data(), sf_len);
srsran::rf_timestamp_t ts = {};
// Start measurements
intra_measure.set_cells_to_meas({});
for (uint32_t i = 0; i < args.meas_period_ms * 5; i++) {
radio.rx_now(radio_buffer, ts);
intra_measure.run_tti(tti_count, baseband_buffer.data(), sf_len);
tti_count = TTI_ADD(tti_count, 1);
}
// Stop measurements
intra_measure.meas_stop();
}
}
}
// Stop radio before it overflows
radio.stop();
// make sure last measurement has been received before stopping
intra_measure.wait_meas();
// Stop, it will block until the asynchronous thread quits
intra_measure.stop();
logger.warning("NR intra frequency performance %d Msps\n", intra_measure.get_perf());
srslog::flush();
rrc.print_stats();
return EXIT_SUCCESS;
}

@ -114,7 +114,7 @@ public:
srsran_pbch_msg_nr_t msg = {}; srsran_pbch_msg_nr_t msg = {};
// Add SSB // Add SSB
if (srsran_ssb_add(&ssb, pci, 0, &msg, buffer.data(), buffer.data()) < SRSRAN_SUCCESS) { if (srsran_ssb_add(&ssb, pci, &msg, buffer.data(), buffer.data()) < SRSRAN_SUCCESS) {
logger.error("Error adding SSB"); logger.error("Error adding SSB");
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
@ -165,7 +165,8 @@ struct args_t {
float rx_gain = 60.0f; float rx_gain = 60.0f;
// File parameters // File parameters
std::string filename = ""; std::string filename = "";
double file_freq_offset_hz = 0.0;
}; };
class meas_itf_listener : public srsue::scell::intra_measure_base::meas_itf class meas_itf_listener : public srsue::scell::intra_measure_base::meas_itf
@ -313,6 +314,7 @@ int parse_args(int argc, char** argv, args_t& args)
file.add_options() file.add_options()
("file.name", bpo::value<std::string>(&args.filename)->default_value(args.filename), "File name providing baseband") ("file.name", bpo::value<std::string>(&args.filename)->default_value(args.filename), "File name providing baseband")
("file.freq_offset", bpo::value<double>(&args.file_freq_offset_hz)->default_value(args.file_freq_offset_hz), "File name providing baseband")
; ;
options.add(measure).add(over_the_air).add(simulation).add(file).add_options() options.add(measure).add(over_the_air).add(simulation).add(file).add_options()
@ -413,7 +415,7 @@ int main(int argc, char** argv)
meas_cfg.ssb_freq_hz = ssb_freq_hz; meas_cfg.ssb_freq_hz = ssb_freq_hz;
meas_cfg.scs = srsran_subcarrier_spacing_30kHz; meas_cfg.scs = srsran_subcarrier_spacing_30kHz;
meas_cfg.serving_cell_pci = -1; meas_cfg.serving_cell_pci = -1;
TESTASSERT(intra_measure.set_config(args.carier_arfcn, meas_cfg)); TESTASSERT(intra_measure.set_config(meas_cfg));
// Simulation only // Simulation only
std::vector<std::unique_ptr<test_gnb> > test_gnb_v; std::vector<std::unique_ptr<test_gnb> > test_gnb_v;
@ -492,6 +494,8 @@ int main(int argc, char** argv)
srsran_filesource_free(&filesource); srsran_filesource_free(&filesource);
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
srsran_vec_apply_cfo(baseband_buffer.data(), args.file_freq_offset_hz/args.srate_hz, baseband_buffer.data(), (int)sf_len);
} else if (radio) { } else if (radio) {
// Receive radio // Receive radio
srsran::rf_buffer_t radio_buffer(baseband_buffer.data(), sf_len); srsran::rf_buffer_t radio_buffer(baseband_buffer.data(), sf_len);

@ -159,6 +159,7 @@ public:
// RRC interface for PDCP, PDCP calls RRC to push RRC SDU // RRC interface for PDCP, PDCP calls RRC to push RRC SDU
void write_pdu(uint32_t lcid, unique_byte_buffer_t pdu); void write_pdu(uint32_t lcid, unique_byte_buffer_t pdu);
void notify_pdcp_integrity_error(uint32_t lcid);
// Not supported right now // Not supported right now
void write_pdu_bcch_bch(unique_byte_buffer_t pdu); void write_pdu_bcch_bch(unique_byte_buffer_t pdu);

@ -1148,6 +1148,11 @@ void ttcn3_syssim::protocol_failure()
logger.error("%s not implemented.", __FUNCTION__); logger.error("%s not implemented.", __FUNCTION__);
} }
void ttcn3_syssim::notify_pdcp_integrity_error(uint32_t lcid)
{
logger.error("%s not implemented.", __FUNCTION__);
}
const char* ttcn3_syssim::get_rb_name(uint32_t lcid) const char* ttcn3_syssim::get_rb_name(uint32_t lcid)
{ {
if (lcid < rb_id_vec.size()) { if (lcid < rb_id_vec.size()) {

@ -83,6 +83,7 @@ public:
const char* get_rb_name(uint32_t lcid) { return "lcid"; } const char* get_rb_name(uint32_t lcid) { return "lcid"; }
void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) {} void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) {}
bool is_lcid_enabled(uint32_t lcid) { return false; } bool is_lcid_enabled(uint32_t lcid) { return false; }
void notify_pdcp_integrity_error(uint32_t lcid) {}
}; };
class rrc_dummy : public rrc_interface_nas class rrc_dummy : public rrc_interface_nas

Loading…
Cancel
Save