Merge branch 'next' into agpl_next

master
Codebot 3 years ago committed by Your Name
commit cf4b0f1739

@ -20,7 +20,7 @@ class BoundedVectorPrinter(object):
self.value_type = self.val.type.template_argument(0)
def children(self):
start = self.val['buffer'].cast(self.value_type.pointer())
start = self.val['buffer']['_M_elems'].cast(self.value_type.pointer())
length = int(self.val['size_'])
for idx in range(length):
yield f'[{idx}]', start[idx]
@ -33,11 +33,10 @@ class BoundedVectorPrinter(object):
def display_hint(self):
return 'array'
@staticmethod
def make(val):
if str(val.type).startswith('srsran::bounded_vector<'):
return BoundedVectorPrinter(val)
def make_bounded_vector(val):
if 'bounded_vector<' in str(val.type):
return BoundedVectorPrinter(val)
gdb.pretty_printers.append(BoundedVectorPrinter.make)
gdb.pretty_printers.append(make_bounded_vector)
end

@ -388,13 +388,16 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
find_package(SSE)
endif (AUTO_DETECT_ISA)
ADD_C_COMPILER_FLAG_IF_AVAILABLE("-march=${GCC_ARCH}" HAVE_MARCH_${GCC_ARCH})
ADD_CXX_COMPILER_FLAG_IF_AVAILABLE("-march=${GCC_ARCH}" HAVE_MARCH_${GCC_ARCH})
if (HAVE_AVX2)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${GCC_ARCH} -mfpmath=sse -mavx2 -DLV_HAVE_AVX2 -DLV_HAVE_AVX -DLV_HAVE_SSE")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -mavx2 -DLV_HAVE_AVX2 -DLV_HAVE_AVX -DLV_HAVE_SSE")
else (HAVE_AVX2)
if(HAVE_AVX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${GCC_ARCH} -mfpmath=sse -mavx -DLV_HAVE_AVX -DLV_HAVE_SSE")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -mavx -DLV_HAVE_AVX -DLV_HAVE_SSE")
elseif(HAVE_SSE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${GCC_ARCH} -mfpmath=sse -msse4.1 -DLV_HAVE_SSE")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -msse4.1 -DLV_HAVE_SSE")
endif(HAVE_AVX)
endif (HAVE_AVX2)
@ -444,12 +447,12 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
find_package(SSE)
endif (AUTO_DETECT_ISA)
if (HAVE_AVX2)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=${GCC_ARCH} -mfpmath=sse -mavx2 -DLV_HAVE_AVX2 -DLV_HAVE_AVX -DLV_HAVE_SSE")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse -mavx2 -DLV_HAVE_AVX2 -DLV_HAVE_AVX -DLV_HAVE_SSE")
else (HAVE_AVX2)
if(HAVE_AVX)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=${GCC_ARCH} -mfpmath=sse -mavx -DLV_HAVE_AVX -DLV_HAVE_SSE")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse -mavx -DLV_HAVE_AVX -DLV_HAVE_SSE")
elseif(HAVE_SSE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=${GCC_ARCH} -mfpmath=sse -msse4.1 -DLV_HAVE_SSE")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse -msse4.1 -DLV_HAVE_SSE")
endif(HAVE_AVX)
endif (HAVE_AVX2)

@ -65,6 +65,7 @@ struct nzp_csi_rs_res_s;
struct pdsch_serving_cell_cfg_s;
struct freq_info_dl_s;
struct serving_cell_cfg_common_s;
struct serving_cell_cfg_s;
} // namespace rrc_nr
} // namespace asn1
@ -82,7 +83,7 @@ void to_asn1(asn1::rrc_nr::plmn_id_s* asn1_type, const plmn_id_t& cfg);
bool make_phy_rach_cfg(const asn1::rrc_nr::rach_cfg_common_s& asn1_type, srsran_prach_cfg_t* prach_cfg);
bool make_phy_tdd_cfg(const asn1::rrc_nr::tdd_ul_dl_cfg_common_s& tdd_ul_dl_cfg_common,
srsran_duplex_config_nr_t* srsran_duplex_config_nr);
srsran_duplex_config_nr_t* srsran_duplex_config_nr);
bool make_phy_harq_ack_cfg(const asn1::rrc_nr::phys_cell_group_cfg_s& phys_cell_group_cfg,
srsran_harq_ack_cfg_hl_t* srsran_ue_dl_nr_harq_ack_cfg);
bool make_phy_coreset_cfg(const asn1::rrc_nr::ctrl_res_set_s& ctrl_res_set, srsran_coreset_t* srsran_coreset);
@ -119,7 +120,10 @@ bool make_phy_zp_csi_rs_resource(const asn1::rrc_nr::zp_csi_rs_res_s& zp_csi_rs_
bool make_phy_nzp_csi_rs_resource(const asn1::rrc_nr::nzp_csi_rs_res_s& nzp_csi_rs_res,
srsran_csi_rs_nzp_resource_t* csi_rs_nzp_resource);
bool make_phy_carrier_cfg(const asn1::rrc_nr::freq_info_dl_s& freq_info_dl, srsran_carrier_nr_t* carrier_nr);
bool make_phy_ssb_cfg(const asn1::rrc_nr::serving_cell_cfg_common_s& serv_cell_cfg, phy_cfg_nr_t::ssb_cfg_t* ssb);
bool make_phy_ssb_cfg(const srsran_carrier_nr_t& carrier,
const asn1::rrc_nr::serving_cell_cfg_common_s& serv_cell_cfg,
phy_cfg_nr_t::ssb_cfg_t* ssb);
bool make_pdsch_cfg_from_serv_cell(asn1::rrc_nr::serving_cell_cfg_s& serv_cell, srsran_sch_hl_cfg_nr_t* sch_hl);
/***************************
* MAC Config

@ -91,6 +91,13 @@ public:
*/
srsran_ssb_patern_t get_ssb_pattern(uint16_t band, srsran_subcarrier_spacing_t scs) const;
/**
* @brief Select the lower SSB subcarrier spacing valid for this band
* @param band NR band number
* @return The SSB subcarrier spacing
*/
srsran_subcarrier_spacing_t get_ssb_scs(uint16_t band) const;
/**
* @brief gets the NR band duplex mode
* @param band Given band
@ -99,65 +106,125 @@ public:
srsran_duplex_mode_t get_duplex_mode(uint16_t band) const;
/**
* @brief Compute the DL center frequency for a NR carrier
* @brief Compute the center frequency for a NR carrier from its bandwidth and the absolute pointA
*
* @param carrier Const Reference to a carrier struct including PRB, abs. frequency point A and carrier offset.
* @param nof_prb Carrier bandwidth in number of RB
* @param freq_point_a_arfcn Absolute Point A frequency ARFCN
* @return double Frequency in Hz
*/
double get_dl_center_freq(const srsran_carrier_nr_t& carrier);
double get_center_freq_from_abs_freq_point_a(uint32_t nof_prb, uint32_t freq_point_a_arfcn);
/**
* @brief Compute the UL center frequency for a NR carrier
* @brief Compute the absolute pointA for a NR carrier from its bandwidth and the center frequency
*
* @param carrier Const Reference to a carrier struct including PRB, abs. frequency point A and carrier offset.
* @return double Frequency in Hz
* @param nof_prb Carrier bandwidth in number of RB
* @param center_freq double Frequency in Hz
* @return Absolute Point A frequency in Hz
*/
double get_ul_center_freq(const srsran_carrier_nr_t& carrier);
double get_abs_freq_point_a_from_center_freq(uint32_t nof_prb, double center_freq);
/**
* @brief Compute the absolute frequency point A for a arfcn
*
* @param band nr frequency band.
* @param nof_prb Number of PRBs.
* @param arfcn Given ARFCN.
* @return frequency point A in arfcn notation.
*/
uint32_t get_abs_freq_point_a_arfcn(uint32_t nof_prb, uint32_t arfcn);
/**
* @brief Compute the absolute frequency of the SSB for a DL ARFCN and a band. This selects an SSB center frequency
* following the band SS/PBCH frequency raster provided by TS38.104 table 5.4.3.1-1 as close as possible to PointA
* without letting any SS/PBCH subcarrier below PointA
*
* @param scs ssb subcarrier spacing.
* @param freq_point_a_arfcn frequency point a in arfcn notation.
* @return absolute frequency of the SSB in arfcn notation.
*/
uint32_t get_abs_freq_ssb_arfcn(uint16_t band, srsran_subcarrier_spacing_t scs, uint32_t freq_point_a_arfcn);
/**
* @brief Compute SSB center frequency for NR carrier
* @param carrier Const Reference to a carrier struct including PRB, abs. frequency point A and carrier offset.
* @return double Frequency in Hz
*/
double get_ssb_center_freq(const srsran_carrier_nr_t& carrier);
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) {}
sync_raster_t(uint32_t gscn_f, uint32_t gscn_s, uint32_t gscn_l) :
gscn_first(gscn_f), gscn_step(gscn_s), gscn_last(gscn_l), gscn(gscn_f)
{
// see TS38.104 Table 5.4.3.1-1
if (gscn_last <= 7498) {
N_first = 1;
N_last = 2499;
} else if (7499 <= gscn_last and gscn_last <= 22255) {
N_last = 14756;
} else if (22256 <= gscn_last and gscn_last <= 26639) {
N_last = 4383;
}
N = N_first;
}
uint32_t gscn;
uint32_t N;
uint32_t M[3] = {1, 3, 5};
uint32_t M_idx = 0;
private:
uint32_t first;
uint32_t step;
uint32_t last;
uint32_t gscn_first;
uint32_t gscn_step;
uint32_t gscn_last;
uint32_t N_first = 0;
uint32_t N_last = 0;
public:
bool valid() const { return step != 0; }
bool valid() const { return gscn_step != 0; }
void next()
{
if (gscn <= last) {
gscn += step;
if (gscn_last <= 7498 and M_idx < 3) {
M_idx += 1;
if (M_idx == 3 and N <= N_last) {
M_idx = 0;
N += 1;
}
} else if (N <= N_last) {
N += 1;
}
}
bool end() const { return (N > N_last or gscn_step == 0); }
void reset()
{
N = N_first;
M_idx = 0;
}
void gscn_next()
{
if (gscn <= gscn_last) {
gscn += gscn_step;
}
}
bool end() const { return (gscn > last or step == 0); }
bool gscn_end() const { return (gscn > gscn_last or gscn_step == 0); }
void reset() { gscn = first; }
void gscn_reset() { gscn = gscn_first; }
double get_frequency() const;
uint32_t get_gscn() const;
};
sync_raster_t get_sync_raster(uint16_t band, srsran_subcarrier_spacing_t scs) const;
private:
// internal helper
double get_center_freq_from_abs_freq_point_a(uint32_t nof_prb, uint32_t freq_point_a_arfcn);
double get_abs_freq_point_a_from_center_freq(uint32_t nof_prb, double center_freq);
// Elements of TS 38.101-1 Table 5.2-1: NR operating bands in FR1
struct nr_operating_band {

@ -42,6 +42,7 @@ struct phy_cfg_nr_t {
uint32_t periodicity_ms = 0;
std::array<bool, SRSRAN_SSB_NOF_CANDIDATES> position_in_burst = {};
srsran_subcarrier_spacing_t scs = srsran_subcarrier_spacing_30kHz;
srsran_ssb_patern_t pattern = SRSRAN_SSB_PATTERN_A;
};
srsran_duplex_config_nr_t duplex = {};
@ -156,6 +157,13 @@ struct phy_cfg_nr_t {
bool get_pusch_uci_cfg(const srsran_slot_cfg_t& slot_cfg,
const srsran_uci_cfg_nr_t& uci_cfg,
srsran_sch_cfg_nr_t& pusch_cfg) const;
/**
* @brief Generate SSB configuration from the overall configuration
* @attention Sampling rate is the only parameter missing
* @return valid SSB configuration
*/
srsran_ssb_cfg_t get_ssb_cfg() const;
};
} // namespace srsran

@ -42,6 +42,8 @@ extern "C" {
static srslog::sink* log_sink = nullptr;
static std::atomic<bool> running = {true};
void srsran_dft_exit();
static void srsran_signal_handler(int signal)
{
switch (signal) {
@ -52,6 +54,7 @@ static void srsran_signal_handler(int signal)
if (log_sink) {
log_sink->flush();
}
srsran_dft_exit();
raise(SIGKILL);
default:
// all other registered signals try to stop the app gracefully

@ -166,7 +166,7 @@ struct formatter<srsran::slot_point> {
template <typename FormatContext>
auto format(srsran::slot_point slot, FormatContext& ctx) -> decltype(std::declval<FormatContext>().out())
{
return format_to(ctx.out(), "{}:{}", slot.sfn(), slot.slot_idx());
return format_to(ctx.out(), "{}", slot.to_uint());
}
};
} // namespace fmt

@ -58,6 +58,7 @@ struct enb_metrics_t {
srsran::rf_metrics_t rf;
std::vector<phy_metrics_t> phy;
stack_metrics_t stack;
stack_metrics_t nr_stack;
srsran::sys_metrics_t sys;
bool running;
};

@ -99,17 +99,9 @@ public:
virtual void set_activity_user(uint16_t eutra_rnti) = 0;
};
class stack_nr_interface_stack_eutra
{
public:
/// Helper method to provide time signal to NR-RRC (PHY only sends TTI ticks to EUTRA stack)
virtual void tti_clock() = 0;
};
// combined interface used by X2 adapter
class x2_interface : public rrc_nr_interface_rrc,
public rrc_eutra_interface_rrc_nr,
public stack_nr_interface_stack_eutra,
public pdcp_interface_gtpu, // allow GTPU to access PDCP in DL direction
public gtpu_interface_pdcp // allow PDCP to access GTPU in UL direction
{};

@ -170,6 +170,7 @@ public:
srsran_carrier_nr_t carrier;
srsran_pdcch_cfg_nr_t pdcch;
srsran_prach_cfg_t prach;
srsran_ssb_cfg_t ssb;
srsran_duplex_mode_t duplex_mode;
};

@ -47,8 +47,12 @@ struct rrc_cell_cfg_nr_t {
uint32_t tac; // Tracking area code
uint32_t dl_arfcn; // DL freq already included in phy_cell
uint32_t ul_arfcn; // UL freq also in phy_cell
uint32_t dl_absolute_freq_point_a; // derived from DL ARFCN
uint32_t ul_absolute_freq_point_a; // derived from UL ARFCN
uint32_t ssb_absolute_freq_point; // derived from DL ARFCN
uint32_t band;
srsran_duplex_mode_t duplex_mode;
srsran_ssb_cfg_t ssb_cfg;
};
typedef std::vector<rrc_cell_cfg_nr_t> rrc_cell_list_nr_t;

@ -38,6 +38,11 @@ private:
bool tx_hold = false; ///< Hold threads until the signal is transmitted
protected:
void reset_last_worker()
{
std::unique_lock<std::mutex> lock(tx_mutex);
tx_hold = true;
}
/**
* @brief Waits for the last worker to call `last_worker()` to prevent that the current SF worker is released and
* overwrites the transmit signal prior transmission
@ -45,7 +50,6 @@ protected:
void wait_last_worker()
{
std::unique_lock<std::mutex> lock(tx_mutex);
tx_hold = true;
while (tx_hold) {
tx_cvar.wait(lock);
}

@ -379,9 +379,9 @@ typedef enum {
*/
typedef struct SRSRAN_API {
uint32_t pci;
uint32_t absolute_frequency_ssb;
uint32_t dl_absolute_frequency_point_a;
uint32_t ul_absolute_frequency_point_a;
double dl_center_frequency_hz; ///< Absolute baseband center frequency in Hz for DL grid
double ul_center_frequency_hz; ///< Absolute baseband center frequency in Hz for UL grid
double ssb_center_freq_hz; ///< SS/PBCH Block center frequency in Hz. Set to 0 if not present
uint32_t offset_to_carrier; ///< Offset between point A and the lowest subcarrier of the lowest RB
srsran_subcarrier_spacing_t scs;
uint32_t nof_prb; ///< @brief See TS 38.101-1 Table 5.3.2-1 for more details
@ -392,6 +392,12 @@ typedef struct SRSRAN_API {
///< TS 38.212 [17], clause 5.4.2.1)
} srsran_carrier_nr_t;
#define SRSRAN_DEFAULT_CARRIER_NR \
{ \
.pci = 500, .dl_center_frequency_hz = 3.5e9, .ul_center_frequency_hz = 3.5e9, .ssb_center_freq_hz = 3.5e9, \
.offset_to_carrier = 0, .scs = srsran_subcarrier_spacing_15kHz, .nof_prb = 52, .start = 0, .max_mimo_layers = 1 \
}
/**
* @brief NR Slot parameters. It contains parameters that change in a slot basis.
*/
@ -599,6 +605,14 @@ SRSRAN_API srsran_mcs_table_t srsran_mcs_table_from_str(const char* str);
*/
SRSRAN_API uint32_t srsran_min_symbol_sz_rb(uint32_t nof_prb);
/**
* @brief Computes the minimum valid symbol size for a given amount of PRB
* @attention The valid FFT sizes are radix 2 and radix 3 between 128 to 4096 points.
* @param nof_prb Number of PRB
* @return The minimum valid FFT size if the number of PRB is in range, 0 otherwise
*/
SRSRAN_API int srsran_symbol_sz_from_srate(double srate_hz, srsran_subcarrier_spacing_t scs);
/**
* @brief Computes the time in seconds between the beginning of the slot and the given symbol
* @remark All symbol size reference and values are taken from TS 38.211 section 5.3 OFDM baseband signal generation

@ -53,12 +53,13 @@ typedef struct SRSRAN_API {
srsran_cp_t cp; ///< Cyclic prefix type
// Optional parameters
srsran_sf_t sf_type; ///< Subframe type, normal or MBSFN
bool normalize; ///< Normalization flag, it divides the output by square root of the symbol size
float freq_shift_f; ///< Frequency shift, normalised by sampling rate (used in UL)
float rx_window_offset; ///< DFT Window offset in CP portion (0-1), RX only
uint32_t symbol_sz; ///< Symbol size, forces a given symbol size for the number of PRB
bool keep_dc; ///< If true, it does not remove the DC
srsran_sf_t sf_type; ///< Subframe type, normal or MBSFN
bool normalize; ///< Normalization flag, it divides the output by square root of the symbol size
float freq_shift_f; ///< Frequency shift, normalised by sampling rate (used in UL)
float rx_window_offset; ///< DFT Window offset in CP portion (0-1), RX only
uint32_t symbol_sz; ///< Symbol size, forces a given symbol size for the number of PRB
bool keep_dc; ///< If true, it does not remove the DC
double phase_compensation_hz; ///< Carrier frequency in Hz for phase compensation, set to 0 to disable
} srsran_ofdm_cfg_t;
/**
@ -83,6 +84,7 @@ typedef struct SRSRAN_API {
uint32_t window_offset_n;
cf_t* shift_buffer;
cf_t* window_offset_buffer;
cf_t phase_compensation[SRSRAN_MAX_NSYMB * SRSRAN_NOF_SLOTS_PER_SF];
} srsran_ofdm_t;
/**
@ -139,6 +141,8 @@ SRSRAN_API int srsran_ofdm_set_freq_shift(srsran_ofdm_t* q, float freq_shift);
SRSRAN_API void srsran_ofdm_set_normalize(srsran_ofdm_t* q, bool normalize_enable);
SRSRAN_API int srsran_ofdm_set_phase_compensation(srsran_ofdm_t* q, double center_freq_hz);
SRSRAN_API void srsran_ofdm_set_non_mbsfn_region(srsran_ofdm_t* q, uint8_t non_mbsfn_region);
#endif // SRSRAN_OFDM_H

@ -28,15 +28,20 @@
#include "srsran/phy/phch/pdcch_cfg_nr.h"
#include "srsran/phy/phch/pdcch_nr.h"
#include "srsran/phy/phch/pdsch_nr.h"
#include "srsran/phy/sync/ssb.h"
typedef struct SRSRAN_API {
srsran_pdsch_nr_args_t pdsch;
srsran_pdcch_nr_args_t pdcch;
uint32_t nof_tx_antennas;
uint32_t nof_max_prb;
srsran_pdsch_nr_args_t pdsch;
srsran_pdcch_nr_args_t pdcch;
uint32_t nof_tx_antennas;
uint32_t nof_max_prb; ///< Maximum number of allocated RB
double srate_hz; ///< Fix sampling rate, set to 0 for minimum to fit nof_max_prb
srsran_subcarrier_spacing_t scs;
} srsran_gnb_dl_args_t;
typedef struct SRSRAN_API {
float srate_hz;
uint32_t symbol_sz;
uint32_t max_prb;
uint32_t nof_tx_antennas;
srsran_carrier_nr_t carrier;
@ -50,12 +55,15 @@ typedef struct SRSRAN_API {
srsran_dci_nr_t dci; ///< Stores DCI configuration
srsran_pdcch_nr_t pdcch;
srsran_ssb_t ssb;
} srsran_gnb_dl_t;
SRSRAN_API int srsran_gnb_dl_init(srsran_gnb_dl_t* q, cf_t* output[SRSRAN_MAX_PORTS], const srsran_gnb_dl_args_t* args);
SRSRAN_API int srsran_gnb_dl_set_carrier(srsran_gnb_dl_t* q, const srsran_carrier_nr_t* carrier);
SRSRAN_API int srsran_gnb_dl_set_ssb_config(srsran_gnb_dl_t* q, const srsran_ssb_cfg_t* ssb);
SRSRAN_API int srsran_gnb_dl_set_pdcch_config(srsran_gnb_dl_t* q,
const srsran_pdcch_cfg_nr_t* cfg,
const srsran_dci_cfg_nr_t* dci_cfg);
@ -66,6 +74,8 @@ SRSRAN_API int srsran_gnb_dl_base_zero(srsran_gnb_dl_t* q);
SRSRAN_API void srsran_gnb_dl_gen_signal(srsran_gnb_dl_t* q);
SRSRAN_API int srsran_gnb_dl_add_ssb(srsran_gnb_dl_t* q, const srsran_pbch_msg_nr_t* pbch_msg, uint32_t sf_idx);
SRSRAN_API int
srsran_gnb_dl_pdcch_put_dl(srsran_gnb_dl_t* q, const srsran_slot_cfg_t* slot_cfg, const srsran_dci_dl_nr_t* dci_dl);

@ -77,6 +77,7 @@ typedef struct SRSRAN_API {
float beta_sss; ///< SSS power allocation
float beta_pbch; ///< PBCH power allocation
float beta_pbch_dmrs; ///< PBCH DMRS power allocation
float scaling; ///< IFFT scaling (used for modulation), set to 0 for default
} srsran_ssb_cfg_t;
/**

@ -21,6 +21,7 @@
#include "srsran/asn1/rrc_nr_utils.h"
#include "srsran/asn1/rrc_nr.h"
#include "srsran/common/band_helper.h"
#include "srsran/config.h"
#include "srsran/interfaces/pdcp_interface_types.h"
#include "srsran/interfaces/rlc_interface_types.h"
@ -1377,13 +1378,14 @@ bool make_phy_carrier_cfg(const freq_info_dl_s& asn1_freq_info_dl, srsran_carrie
}
// As the carrier structure requires parameters from different objects, set fields separately
out_carrier_nr->absolute_frequency_ssb = absolute_frequency_ssb;
out_carrier_nr->dl_absolute_frequency_point_a = asn1_freq_info_dl.absolute_freq_point_a;
out_carrier_nr->ul_absolute_frequency_point_a =
out_carrier_nr->dl_absolute_frequency_point_a; // needs to be updated for FDD
out_carrier_nr->offset_to_carrier = asn1_freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier;
out_carrier_nr->nof_prb = asn1_freq_info_dl.scs_specific_carrier_list[0].carrier_bw;
out_carrier_nr->scs = scs;
srsran::srsran_band_helper bands;
out_carrier_nr->ssb_center_freq_hz = bands.nr_arfcn_to_freq(absolute_frequency_ssb);
out_carrier_nr->dl_center_frequency_hz = bands.get_center_freq_from_abs_freq_point_a(
asn1_freq_info_dl.scs_specific_carrier_list[0].carrier_bw, asn1_freq_info_dl.absolute_freq_point_a);
out_carrier_nr->ul_center_frequency_hz = out_carrier_nr->dl_center_frequency_hz; // needs to be updated for FDD
out_carrier_nr->offset_to_carrier = asn1_freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier;
out_carrier_nr->nof_prb = asn1_freq_info_dl.scs_specific_carrier_list[0].carrier_bw;
out_carrier_nr->scs = scs;
return true;
}
@ -1400,9 +1402,48 @@ static inline void make_ssb_positions_in_burst(const bitstring_t&
}
}
bool make_phy_ssb_cfg(const asn1::rrc_nr::serving_cell_cfg_common_s& serv_cell_cfg, phy_cfg_nr_t::ssb_cfg_t* out_ssb)
bool make_phy_ssb_cfg(const srsran_carrier_nr_t& carrier,
const asn1::rrc_nr::serving_cell_cfg_common_s& serv_cell_cfg,
phy_cfg_nr_t::ssb_cfg_t* out_ssb)
{
srsran::srsran_band_helper bands;
uint16_t band = bands.get_band_from_dl_freq_Hz(carrier.ssb_center_freq_hz);
if (band == UINT16_MAX) {
asn1::log_error("Invalid band for SSB frequency %.3f MHz", carrier.ssb_center_freq_hz);
return false;
}
phy_cfg_nr_t::ssb_cfg_t ssb = {};
// Parse subcarrier spacing
if (serv_cell_cfg.ssb_subcarrier_spacing_present) {
switch (serv_cell_cfg.ssb_subcarrier_spacing) {
case subcarrier_spacing_e::khz15:
ssb.scs = srsran_subcarrier_spacing_15kHz;
break;
case subcarrier_spacing_e::khz30:
ssb.scs = srsran_subcarrier_spacing_30kHz;
break;
default:
asn1::log_error("SSB SCS %s not supported", serv_cell_cfg.ssb_subcarrier_spacing.to_string());
return false;
}
} else {
ssb.scs = bands.get_ssb_scs(band);
if (ssb.scs == srsran_subcarrier_spacing_invalid) {
asn1::log_error("SSB SCS not available for band %d", band);
return false;
}
}
// Get the SSB pattern
ssb.pattern = bands.get_ssb_pattern(band, ssb.scs);
if (ssb.pattern == SRSRAN_SSB_PATTERN_INVALID) {
asn1::log_error(
"Band %d and SS/PBCH block SCS %s results on invalid pattern", band, srsran_subcarrier_spacing_to_str(ssb.scs));
return false;
}
if (serv_cell_cfg.ssb_positions_in_burst_present) {
switch (serv_cell_cfg.ssb_positions_in_burst.type()) {
case serving_cell_cfg_common_s::ssb_positions_in_burst_c_::types_opts::short_bitmap:
@ -1443,6 +1484,46 @@ bool make_phy_ssb_cfg(const asn1::rrc_nr::serving_cell_cfg_common_s& serv_cell_c
return true;
}
bool make_pdsch_cfg_from_serv_cell(asn1::rrc_nr::serving_cell_cfg_s& serv_cell, srsran_sch_hl_cfg_nr_t* sch_hl)
{
if (serv_cell.csi_meas_cfg_present and
serv_cell.csi_meas_cfg.type().value ==
setup_release_c< ::asn1::rrc_nr::csi_meas_cfg_s>::types_opts::options::setup) {
auto& setup = serv_cell.csi_meas_cfg.setup();
if (setup.nzp_csi_rs_res_set_to_add_mod_list_present) {
for (auto& nzp_set : setup.nzp_csi_rs_res_set_to_add_mod_list) {
auto& uecfg_set = sch_hl->nzp_csi_rs_sets[nzp_set.nzp_csi_res_set_id];
uecfg_set.trs_info = nzp_set.trs_info_present;
uecfg_set.count = nzp_set.nzp_csi_rs_res.size();
for (uint8_t nzp_rs_idx : nzp_set.nzp_csi_rs_res) {
auto& res = uecfg_set.data[nzp_rs_idx];
if (not srsran::make_phy_nzp_csi_rs_resource(setup.nzp_csi_rs_res_to_add_mod_list[nzp_rs_idx], &res)) {
return false;
}
}
}
}
}
if (serv_cell.init_dl_bwp.pdsch_cfg_present and
serv_cell.init_dl_bwp.pdsch_cfg.type() == setup_release_c<pdsch_cfg_s>::types_opts::setup) {
const auto& setup = serv_cell.init_dl_bwp.pdsch_cfg.setup();
if (setup.p_zp_csi_rs_res_set_present) {
auto& setup_set = setup.p_zp_csi_rs_res_set.setup();
sch_hl->p_zp_csi_rs_set.count = setup_set.zp_csi_rs_res_id_list.size();
for (uint8_t zp_res_id : setup_set.zp_csi_rs_res_id_list) {
const asn1::rrc_nr::zp_csi_rs_res_s& setup_res = setup.zp_csi_rs_res_to_add_mod_list[zp_res_id];
auto& res = sch_hl->p_zp_csi_rs_set.data[zp_res_id];
if (not srsran::make_phy_zp_csi_rs_resource(setup_res, &res)) {
return false;
}
}
}
}
return true;
}
} // namespace srsran
namespace srsenb {

@ -118,16 +118,6 @@ uint32_t srsran_band_helper::get_ul_arfcn_from_dl_arfcn(uint32_t dl_arfcn) const
return 0;
}
double srsran_band_helper::get_dl_center_freq(const srsran_carrier_nr_t& carrier)
{
return get_center_freq_from_abs_freq_point_a(carrier.nof_prb, carrier.dl_absolute_frequency_point_a);
}
double srsran_band_helper::get_ul_center_freq(const srsran_carrier_nr_t& carrier)
{
return get_center_freq_from_abs_freq_point_a(carrier.nof_prb, carrier.ul_absolute_frequency_point_a);
}
double srsran_band_helper::get_center_freq_from_abs_freq_point_a(uint32_t nof_prb, uint32_t freq_point_a_arfcn)
{
// for FR1 unit of resources blocks for freq calc is always 180kHz regardless for actual SCS of carrier
@ -152,6 +142,31 @@ double srsran_band_helper::get_abs_freq_point_a_from_center_freq(uint32_t nof_pr
SRSRAN_NRE);
}
uint32_t
srsran_band_helper::get_abs_freq_ssb_arfcn(uint16_t band, srsran_subcarrier_spacing_t scs, uint32_t freq_point_a_arfcn)
{
sync_raster_t sync_raster = get_sync_raster(band, scs);
if (!sync_raster.valid()) {
return 0;
}
// double abs_freq_ssb_hz = sync_raster.get_frequency();
double freq_point_a_hz = nr_arfcn_to_freq(freq_point_a_arfcn);
double ssb_bw_hz = SRSRAN_SSB_BW_SUBC * SRSRAN_SUBC_SPACING_NR(scs);
while (!sync_raster.end()) {
double abs_freq_ssb_hz = sync_raster.get_frequency();
if ((abs_freq_ssb_hz > (freq_point_a_hz + ssb_bw_hz / 2)) and
((uint32_t)std::round(abs_freq_ssb_hz - freq_point_a_hz) % SRSRAN_SUBC_SPACING_NR(scs) == 0)) {
return freq_to_nr_arfcn(abs_freq_ssb_hz);
}
sync_raster.next();
}
return 0;
}
srsran_ssb_patern_t srsran_band_helper::get_ssb_pattern(uint16_t band, srsran_subcarrier_spacing_t scs) const
{
// Look for the given band and SCS
@ -171,6 +186,23 @@ srsran_ssb_patern_t srsran_band_helper::get_ssb_pattern(uint16_t band, srsran_su
return SRSRAN_SSB_PATTERN_INVALID;
}
srsran_subcarrier_spacing_t srsran_band_helper::get_ssb_scs(uint16_t band) 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) {
return ss_raster.scs;
}
// As bands are in ascending order, do not waste more time if the current band is bigger
if (ss_raster.band > band) {
return srsran_subcarrier_spacing_invalid;
}
}
return srsran_subcarrier_spacing_invalid;
}
srsran_duplex_mode_t srsran_band_helper::get_duplex_mode(uint16_t band) const
{
// Look for the given band
@ -192,7 +224,7 @@ srsran_duplex_mode_t srsran_band_helper::get_duplex_mode(uint16_t band) const
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)
sync_raster_impl(uint32_t gscn_f, uint32_t gscn_s, uint32_t gscn_l) : sync_raster_t(gscn_f, gscn_s, gscn_l)
{
// Do nothing
}
@ -200,22 +232,20 @@ public:
double srsran_band_helper::sync_raster_t::get_frequency() const
{
// see TS38.104 table 5.4.3.1-1
// 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;
if (gscn_last <= 7498) {
return N * 1200e3 + M[M_idx] * 50e3;
}
// Row 2
if (gscn >= 7499 and gscn <= 22255) {
double N = gscn - 7499;
if (7499 <= gscn_last and gscn_last <= 22255) {
return 3000e6 + N * 1.44e6;
}
// Row 3
if (gscn >= 22256 and gscn <= 26639) {
double N = gscn - 22256;
if (22256 <= gscn_last and gscn_last <= 26639) {
return 2425.08e6 + N * 17.28e6;
}
@ -223,6 +253,19 @@ double srsran_band_helper::sync_raster_t::get_frequency() const
return NAN;
}
uint32_t srsran_band_helper::sync_raster_t::get_gscn() const
{
if (gscn_last <= 7498) {
return 3 * N + (M[M_idx] - 3) / 2;
} else if (7499 <= gscn_last and gscn_last <= 22255) {
return 7499 + N;
} else if (22256 <= gscn_last and gscn_last <= 26639) {
return 22256 + N;
}
return 0;
}
srsran_band_helper::sync_raster_t srsran_band_helper::get_sync_raster(uint16_t band,
srsran_subcarrier_spacing_t scs) const
{

@ -20,6 +20,7 @@
*/
#include "srsran/common/phy_cfg_nr.h"
#include "srsran/common/band_helper.h"
#include "srsran/srsran.h"
namespace srsran {
@ -346,4 +347,30 @@ bool phy_cfg_nr_t::get_pusch_uci_cfg(const srsran_slot_cfg_t& slot_cfg,
return true;
}
srsran_ssb_cfg_t phy_cfg_nr_t::get_ssb_cfg() const
{
// Retrieve band
srsran::srsran_band_helper bh = srsran::srsran_band_helper();
uint16_t band = bh.get_band_from_dl_freq_Hz(carrier.dl_center_frequency_hz);
srsran_assert(band != UINT16_MAX,
"DL frequency %f MHz does not belong to any valid band",
carrier.dl_center_frequency_hz / 1e6);
// Make SSB configuration
srsran_ssb_cfg_t ssb_cfg = {};
ssb_cfg.center_freq_hz = carrier.dl_center_frequency_hz;
ssb_cfg.ssb_freq_hz = carrier.ssb_center_freq_hz;
ssb_cfg.scs = ssb.scs;
ssb_cfg.pattern = bh.get_ssb_pattern(band, ssb.scs);
ssb_cfg.duplex_mode = duplex.mode;
ssb_cfg.periodicity_ms = ssb.periodicity_ms;
srsran_assert(ssb_cfg.pattern != SRSRAN_SSB_PATTERN_INVALID,
"Invalid SSB pattern for band %d and SSB subcarrier spacing %s",
band,
srsran_subcarrier_spacing_to_str(ssb.scs));
return ssb_cfg;
}
} // namespace srsran

@ -75,24 +75,24 @@ phy_cfg_nr_default_t::reference_cfg_t::reference_cfg_t(const std::string& args)
void phy_cfg_nr_default_t::make_carrier_custom_10MHz(srsran_carrier_nr_t& carrier)
{
carrier.nof_prb = 52;
carrier.max_mimo_layers = 1;
carrier.pci = 500;
carrier.dl_absolute_frequency_point_a = 633928;
carrier.absolute_frequency_ssb = 634176;
carrier.offset_to_carrier = 0;
carrier.scs = srsran_subcarrier_spacing_15kHz;
carrier.nof_prb = 52;
carrier.max_mimo_layers = 1;
carrier.pci = 500;
carrier.dl_center_frequency_hz = 2.6e9;
carrier.ssb_center_freq_hz = carrier.dl_center_frequency_hz;
carrier.offset_to_carrier = 0;
carrier.scs = srsran_subcarrier_spacing_15kHz;
}
void phy_cfg_nr_default_t::make_carrier_custom_20MHz(srsran_carrier_nr_t& carrier)
{
carrier.nof_prb = 106;
carrier.max_mimo_layers = 1;
carrier.pci = 500;
carrier.dl_absolute_frequency_point_a = 633928;
carrier.absolute_frequency_ssb = 634176;
carrier.offset_to_carrier = 0;
carrier.scs = srsran_subcarrier_spacing_15kHz;
carrier.nof_prb = 106;
carrier.max_mimo_layers = 1;
carrier.pci = 500;
carrier.dl_center_frequency_hz = 2.6e9;
carrier.ssb_center_freq_hz = carrier.dl_center_frequency_hz;
carrier.offset_to_carrier = 0;
carrier.scs = srsran_subcarrier_spacing_15kHz;
}
void phy_cfg_nr_default_t::make_tdd_custom_6_4(srsran_duplex_config_nr_t& conf)
@ -426,6 +426,17 @@ phy_cfg_nr_default_t::phy_cfg_nr_default_t(const reference_cfg_t& reference_cfg)
srsran_assertion_failure("Invalid TDD reference");
}
if (duplex.mode == SRSRAN_DUPLEX_MODE_TDD) {
carrier.dl_center_frequency_hz = 3513.6e6;
ssb.scs = srsran_subcarrier_spacing_30kHz;
} else {
carrier.dl_center_frequency_hz = 881.5e6;
ssb.scs = srsran_subcarrier_spacing_15kHz;
}
carrier.ssb_center_freq_hz = carrier.dl_center_frequency_hz;
ssb.position_in_burst[0] = true;
ssb.periodicity_ms = 10;
switch (reference_cfg.pdcch) {
case reference_cfg_t::R_PDCCH_CUSTOM_COMMON_SS:
make_pdcch_custom_common_ss(pdcch, carrier);

@ -29,20 +29,20 @@ int bands_test_nr()
TESTASSERT(bands.nr_arfcn_to_freq(632628) == 3489.42e6);
TESTASSERT(bands.nr_arfcn_to_freq(633928) == 3508.92e6); // default refPointA
TESTASSERT(bands.nr_arfcn_to_freq(634240) == 3513.6e6); // default ARFCN with freq divisible by 11.52 MHz
// b28 b67
// n28 n67
TESTASSERT(bands.nr_arfcn_to_freq(140600) == 703.0e6);
TESTASSERT(bands.nr_arfcn_to_freq(145800) == 729.0e6);
TESTASSERT(bands.nr_arfcn_to_freq(153600) == 768.0e6);
TESTASSERT(bands.nr_arfcn_to_freq(157600) == 788.0e6);
// b20
// n20
TESTASSERT(bands.nr_arfcn_to_freq(158200) == 791.0e6);
TESTASSERT(bands.nr_arfcn_to_freq(160200) == 801.0e6);
TESTASSERT(bands.nr_arfcn_to_freq(162200) == 811.0e6);
TESTASSERT(bands.nr_arfcn_to_freq(168400) == 842.0e6);
// b32 b75
// n32 n75
TESTASSERT(bands.nr_arfcn_to_freq(290400) == 1452.0e6);
TESTASSERT(bands.nr_arfcn_to_freq(294400) == 1472.0e6);
// b5
// n5
TESTASSERT(bands.get_duplex_mode(5) == SRSRAN_DUPLEX_MODE_FDD);
TESTASSERT(bands.nr_arfcn_to_freq(176300) == 881.5e6);
TESTASSERT(bands.freq_to_nr_arfcn(881.5e6) == 176300);
@ -50,28 +50,33 @@ int bands_test_nr()
TESTASSERT(bands.nr_arfcn_to_freq(167300) == 836.5e6);
// check actual freqs for FDD carrier (example values are for 52 PRB)
srsran_carrier_nr_t carrier = {};
carrier.dl_absolute_frequency_point_a = 175364;
carrier.ul_absolute_frequency_point_a = 166364;
carrier.nof_prb = 52;
TESTASSERT(bands.get_dl_center_freq(carrier) == 881.5e6);
TESTASSERT(bands.get_ul_center_freq(carrier) == 836.5e6);
TESTASSERT(bands.get_center_freq_from_abs_freq_point_a(52, 175364) == 881.5e6);
TESTASSERT(bands.get_center_freq_from_abs_freq_point_a(52, 166364) == 836.5e6);
// b3
// n3
TESTASSERT(bands.nr_arfcn_to_freq(342000) == 1710.0e6);
TESTASSERT(bands.nr_arfcn_to_freq(348000) == 1740.0e6);
TESTASSERT(bands.nr_arfcn_to_freq(361000) == 1805.0e6);
TESTASSERT(bands.nr_arfcn_to_freq(376000) == 1880.0e6);
// b1
// n1
TESTASSERT(bands.nr_arfcn_to_freq(384000) == 1920.0e6);
TESTASSERT(bands.nr_arfcn_to_freq(388030) == 1940.15e6);
TESTASSERT(bands.nr_arfcn_to_freq(391830) == 1959.15e6);
TESTASSERT(bands.nr_arfcn_to_freq(434000) == 2170.0e6);
// b7 b38
// n7 n38
TESTASSERT(bands.get_duplex_mode(7) == SRSRAN_DUPLEX_MODE_FDD);
TESTASSERT(bands.nr_arfcn_to_freq(500000) == 2500.0e6);
TESTASSERT(bands.nr_arfcn_to_freq(508000) == 2540.0e6);
TESTASSERT(bands.nr_arfcn_to_freq(522000) == 2610.0e6);
TESTASSERT(bands.nr_arfcn_to_freq(538000) == 2690.0e6);
TESTASSERT(bands.get_abs_freq_point_a_arfcn(52, 529920) == 528984);
TESTASSERT(bands.get_abs_freq_ssb_arfcn(7, srsran_subcarrier_spacing_15kHz, 528984) > 529344);
// n78
TESTASSERT(bands.get_duplex_mode(78) == SRSRAN_DUPLEX_MODE_TDD);
TESTASSERT(bands.nr_arfcn_to_freq(634240) == 3513.6e6);
TESTASSERT(bands.get_abs_freq_point_a_arfcn(52, 634240) == 633928);
TESTASSERT(bands.get_abs_freq_ssb_arfcn(78, srsran_subcarrier_spacing_30kHz, 633928) > 634168);
const uint32_t max_valid_nr_arfcn = 3279165;
@ -86,9 +91,6 @@ int bands_test_nr()
TESTASSERT(band_vector.at(0) == 77);
TESTASSERT(band_vector.at(1) == 78);
// n78
TESTASSERT(bands.get_abs_freq_point_a_arfcn(52, 634240) == 633928);
// Invalid configs
// For 30 kHz, 620001 is not a valid ARFCN, only every 2nd
band_vector = bands.get_bands_nr(620001, srsran::srsran_band_helper::KHZ_30);

@ -26,17 +26,7 @@
#include <srsran/srsran.h>
#include <stdlib.h>
static srsran_carrier_nr_t carrier = {
1, // pci
0, // absolute_frequency_ssb
0, // dl_absolute_frequency_point_a
0, // ul_absolute_frequency_point_a
0, // offset_to_carrier
srsran_subcarrier_spacing_15kHz, // scs
50, // nof_prb
0, // start
1 // max_mimo_layers
};
static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR;
static float snr_dB = 20.0;
static float power_control_offset = NAN;

@ -29,17 +29,7 @@
#include <strings.h>
#include <unistd.h>
static srsran_carrier_nr_t carrier = {
1, // pci
0, // absolute_frequency_ssb
0, // dl_absolute_frequency_point_a
0, // ul_absolute_frequency_point_a
0, // offset_to_carrier
srsran_subcarrier_spacing_15kHz, // scs
50, // nof_prb
0, // start
1 // max_mimo_layers
};
static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR;
typedef struct {
srsran_sch_mapping_type_t mapping_type;

@ -253,29 +253,50 @@ uint32_t srsran_min_symbol_sz_rb(uint32_t nof_prb)
return 0;
}
float srsran_symbol_offset_s(uint32_t l, srsran_subcarrier_spacing_t scs)
int srsran_symbol_sz_from_srate(double srate_hz, srsran_subcarrier_spacing_t scs)
{
// Compute at what symbol there is a longer CP
uint32_t cp_boundary = SRSRAN_EXT_CP_SYMBOL(scs);
// Make sure srate is valid
if (!isnormal(srate_hz) || srate_hz < 0.0) {
return SRSRAN_ERROR;
}
// First symbol CP
uint32_t N = 0;
// Convert srate to integer and Hz
uint32_t srate_int_hz = (uint32_t)srate_hz;
// Symbols in between the first and l
N += (2048 + 144) * l;
// Get subcarrier spacing in Hz
uint32_t scs_int_hz = SRSRAN_SUBC_SPACING_NR(scs);
// Add extended CP samples from first OFDM symbol
if (l > 0) {
N += 16;
// Check the symbol size if a integer
if (srate_int_hz % scs_int_hz != 0) {
ERROR("Invalid sampling rate %.2f MHz with subcarrrier spacing %d kHz", srate_hz / 1e6, scs_int_hz / 1000);
return SRSRAN_ERROR;
}
// Add extra samples at the longer CP boundary
if (l >= cp_boundary) {
N += 16;
// Calculate symbol size in samples
uint32_t symbol_sz = srate_int_hz / scs_int_hz;
// Verify the symbol size can have an integer cyclic prefix size
if ((symbol_sz * 144U) % 2048 != 0 && (symbol_sz * (16U << (uint32_t)scs)) % 2048 != 0) {
ERROR("The sampling rate %.2f MHz with subcarrrier spacing %d kHz", srate_hz / 1e6, scs_int_hz / 1000);
return SRSRAN_ERROR;
}
return (int)symbol_sz;
}
float srsran_symbol_offset_s(uint32_t l, srsran_subcarrier_spacing_t scs)
{
// Compute at what symbol there is a longer CP
uint32_t cp_boundary = SRSRAN_EXT_CP_SYMBOL(scs);
// Number of samples (DFT + short CP) in between the first and l symbols
uint32_t N = ((2048 + 144) * l) >> (uint32_t)scs;
// Add extended CP samples from first OFDM symbol
N += 16 * SRSRAN_CEIL(l, cp_boundary);
// Compute time using reference sampling rate
float TS = SRSRAN_LTE_TS / (float)(1U << (uint32_t)scs);
float TS = SRSRAN_LTE_TS;
// Return symbol offset in seconds
return (float)N * TS;

@ -80,7 +80,7 @@ __attribute__((constructor)) static void srsran_dft_load()
}
// This function is called in the ending of any executable where it is linked
__attribute__((destructor)) static void srsran_dft_exit()
__attribute__((destructor)) void srsran_dft_exit()
{
#ifdef FFTW_WISDOM_FILE
char full_path[256];

@ -47,6 +47,11 @@ static int ofdm_init_mbsfn_(srsran_ofdm_t* q, srsran_ofdm_cfg_t* cfg, srsran_dft
cfg->symbol_sz = (uint32_t)symbol_sz_err;
}
// Check if there is nothing to configure
if (memcmp(&q->cfg, cfg, sizeof(srsran_ofdm_cfg_t)) == 0) {
return SRSRAN_SUCCESS;
}
if (q->max_prb > 0) {
// The object was already initialised, update only resizing params
q->cfg.cp = cfg->cp;
@ -55,6 +60,9 @@ static int ofdm_init_mbsfn_(srsran_ofdm_t* q, srsran_ofdm_cfg_t* cfg, srsran_dft
} else {
// Otherwise copy all parameters
q->cfg = *cfg;
// Phase compensation is set when it is calculated
q->cfg.phase_compensation_hz = 0.0;
}
uint32_t symbol_sz = q->cfg.symbol_sz;
@ -146,7 +154,7 @@ static int ofdm_init_mbsfn_(srsran_ofdm_t* q, srsran_ofdm_cfg_t* cfg, srsran_dft
srsran_vec_cf_zero(in_buffer, q->sf_sz);
}
for (int slot = 0; slot < 2; slot++) {
for (int slot = 0; slot < SRSRAN_NOF_SLOTS_PER_SF; slot++) {
// If Guru DFT was allocated, free
if (q->fft_plan_sf[slot].size) {
srsran_dft_plan_free(&q->fft_plan_sf[slot]);
@ -208,6 +216,12 @@ static int ofdm_init_mbsfn_(srsran_ofdm_t* q, srsran_ofdm_cfg_t* cfg, srsran_dft
srsran_dft_plan_set_norm(&q->fft_plan, q->cfg.normalize);
srsran_dft_plan_set_dc(&q->fft_plan, (!cfg->keep_dc) && (!isnormal(q->cfg.freq_shift_f)));
// set phase compensation
if (srsran_ofdm_set_phase_compensation(q, cfg->phase_compensation_hz) < SRSRAN_SUCCESS) {
ERROR("Error setting phase compensation");
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
@ -322,6 +336,61 @@ int srsran_ofdm_tx_set_prb(srsran_ofdm_t* q, srsran_cp_t cp, uint32_t nof_prb)
return ofdm_init_mbsfn_(q, &cfg, SRSRAN_DFT_BACKWARD);
}
int srsran_ofdm_set_phase_compensation(srsran_ofdm_t* q, double center_freq_hz)
{
// Validate pointer
if (q == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
// Check if the center frequency has changed
if (q->cfg.phase_compensation_hz == center_freq_hz) {
return SRSRAN_SUCCESS;
}
// Save the current phase compensation
q->cfg.phase_compensation_hz = center_freq_hz;
// If the center frequency is 0, NAN, INF, then skip
if (!isnormal(center_freq_hz)) {
return SRSRAN_SUCCESS;
}
// Extract modulation required parameters
uint32_t symbol_sz = q->cfg.symbol_sz;
double scs = 15e3; //< Assume 15kHz subcarrier spacing
double srate_hz = symbol_sz * scs;
// Assert parameters
if (!isnormal(srate_hz)) {
return SRSRAN_ERROR;
}
// Otherwise calculate the phase
uint32_t count = 0;
for (uint32_t l = 0; l < q->nof_symbols * SRSRAN_NOF_SLOTS_PER_SF; l++) {
uint32_t cp_len =
SRSRAN_CP_ISNORM(q->cfg.cp) ? SRSRAN_CP_LEN_NORM(l % q->nof_symbols, symbol_sz) : SRSRAN_CP_LEN_EXT(symbol_sz);
// Advance CP
count += cp_len;
// Calculate symbol start time
double t_start = (double)count / srate_hz;
// Calculate phase
double phase_rad = -2.0 * M_PI * center_freq_hz * t_start;
// Calculate compensation phase in double precision and then convert to single
q->phase_compensation[l] = (cf_t)cexp(I * phase_rad);
// Advance symbol
count += symbol_sz;
}
return SRSRAN_SUCCESS;
}
void srsran_ofdm_rx_free(srsran_ofdm_t* q)
{
srsran_ofdm_free_(q);
@ -411,7 +480,18 @@ static void ofdm_rx_slot(srsran_ofdm_t* q, int slot_in_sf)
memcpy(output + nof_re / 2, &tmp[dc], sizeof(cf_t) * nof_re / 2);
// Normalize output
if (q->fft_plan.norm) {
if (isnormal(q->cfg.phase_compensation_hz)) {
// Get phase compensation
cf_t phase_compensation = conjf(q->phase_compensation[slot_in_sf * q->nof_symbols + i]);
// Apply normalization
if (q->fft_plan.norm) {
phase_compensation *= norm;
}
// Apply correction
srsran_vec_sc_prod_ccc(output, phase_compensation, output, nof_re);
} else if (q->fft_plan.norm) {
srsran_vec_sc_prod_cfc(output, norm, output, nof_re);
}
@ -512,8 +592,8 @@ static void ofdm_tx_slot(srsran_ofdm_t* q, int slot_in_sf)
uint32_t dc = (q->fft_plan.dc) ? 1 : 0;
for (int i = 0; i < nof_symbols; i++) {
memcpy(&tmp[dc], &input[nof_re / 2], nof_re / 2 * sizeof(cf_t));
memcpy(&tmp[symbol_sz - nof_re / 2], &input[0], nof_re / 2 * sizeof(cf_t));
srsran_vec_cf_copy(&tmp[dc], &input[nof_re / 2], nof_re / 2);
srsran_vec_cf_copy(&tmp[symbol_sz - nof_re / 2], &input[0], nof_re / 2);
input += nof_re;
tmp += symbol_sz;
@ -524,12 +604,23 @@ static void ofdm_tx_slot(srsran_ofdm_t* q, int slot_in_sf)
for (int i = 0; i < nof_symbols; i++) {
int cp_len = SRSRAN_CP_ISNORM(cp) ? SRSRAN_CP_LEN_NORM(i, symbol_sz) : SRSRAN_CP_LEN_EXT(symbol_sz);
if (q->fft_plan.norm) {
if (isnormal(q->cfg.phase_compensation_hz)) {
// Get phase compensation
cf_t phase_compensation = q->phase_compensation[slot_in_sf * q->nof_symbols + i];
// Apply normalization
if (q->fft_plan.norm) {
phase_compensation *= norm;
}
// Apply correction
srsran_vec_sc_prod_ccc(&output[cp_len], phase_compensation, &output[cp_len], symbol_sz);
} else if (q->fft_plan.norm) {
srsran_vec_sc_prod_cfc(&output[cp_len], norm, &output[cp_len], symbol_sz);
}
/* add CP */
memcpy(output, &output[symbol_sz], cp_len * sizeof(cf_t));
srsran_vec_cf_copy(output, &output[symbol_sz], cp_len);
output += symbol_sz + cp_len;
}
#endif

@ -31,3 +31,5 @@ add_test(ofdm_shifted ofdm_test -s 0.5 -r 1)
add_test(ofdm_offset ofdm_test -o 0.5 -r 1)
add_test(ofdm_force ofdm_test -N 4096 -r 1)
add_test(ofdm_extended_shifted_offset_force ofdm_test -e -o 0.5 -s 0.5 -N 4096 -r 1)
add_test(ofdm_normal_phase_compensation ofdm_test -r 1 -p 2.4e9)
add_test(ofdm_extended_phase_compensation ofdm_test -e -r 1 -p 2.4e9)

@ -29,12 +29,13 @@
#include "srsran/phy/utils/random.h"
#include "srsran/srsran.h"
static int nof_prb = -1;
static srsran_cp_t cp = SRSRAN_CP_NORM;
static int nof_repetitions = 1;
static float rx_window_offset = 0.5f;
static float freq_shift_f = 0.0f;
static uint32_t force_symbol_sz = 0;
static int nof_prb = -1;
static srsran_cp_t cp = SRSRAN_CP_NORM;
static int nof_repetitions = 1;
static float rx_window_offset = 0.5f;
static float freq_shift_f = 0.0f;
static double phase_compensation_hz = 0.0;
static uint32_t force_symbol_sz = 0;
static double elapsed_us(struct timeval* ts_start, struct timeval* ts_end)
{
if (ts_end->tv_usec > ts_start->tv_usec) {
@ -55,12 +56,13 @@ static void usage(char* prog)
printf("\t-r nof_repetitions [Default %d]\n", nof_repetitions);
printf("\t-o rx window offset (portion of CP length) [Default %.1f]\n", rx_window_offset);
printf("\t-s frequency shift (normalised with sampling rate) [Default %.1f]\n", freq_shift_f);
printf("\t-p Phase compensation carrier frequency in Hz [Default %.1f]\n", phase_compensation_hz);
}
static void parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "Nneros")) != -1) {
while ((opt = getopt(argc, argv, "Nnerosp")) != -1) {
switch (opt) {
case 'n':
nof_prb = (int)strtol(argv[optind], NULL, 10);
@ -80,6 +82,9 @@ static void parse_args(int argc, char** argv)
case 's':
freq_shift_f = SRSRAN_MIN(1.0f, SRSRAN_MAX(0.0f, strtof(argv[optind], NULL)));
break;
case 'p':
phase_compensation_hz = strtod(argv[optind], NULL);
break;
default:
usage(argv[0]);
exit(-1);
@ -122,14 +127,15 @@ int main(int argc, char** argv)
}
srsran_vec_cf_zero(outifft, sf_len);
srsran_ofdm_cfg_t ofdm_cfg = {};
ofdm_cfg.cp = cp;
ofdm_cfg.in_buffer = input;
ofdm_cfg.out_buffer = outifft;
ofdm_cfg.nof_prb = n_prb;
ofdm_cfg.symbol_sz = symbol_sz;
ofdm_cfg.freq_shift_f = freq_shift_f;
ofdm_cfg.normalize = true;
srsran_ofdm_cfg_t ofdm_cfg = {};
ofdm_cfg.cp = cp;
ofdm_cfg.in_buffer = input;
ofdm_cfg.out_buffer = outifft;
ofdm_cfg.nof_prb = n_prb;
ofdm_cfg.symbol_sz = symbol_sz;
ofdm_cfg.freq_shift_f = freq_shift_f;
ofdm_cfg.normalize = true;
ofdm_cfg.phase_compensation_hz = phase_compensation_hz;
if (srsran_ofdm_tx_init_cfg(&ifft, &ofdm_cfg)) {
ERROR("Error initializing iFFT");
exit(-1);

@ -70,11 +70,23 @@ int srsran_gnb_dl_init(srsran_gnb_dl_t* q, cf_t* output[SRSRAN_MAX_PORTS], const
return SRSRAN_ERROR;
}
// Check symbol size is vlid
int symbol_sz = srsran_symbol_sz_from_srate(args->srate_hz, args->scs);
if (symbol_sz <= 0) {
ERROR("Error calculating symbol size from sampling rate of %.2f MHz and subcarrier spacing %s",
q->srate_hz / 1e6,
srsran_subcarrier_spacing_to_str(args->scs));
return SRSRAN_ERROR;
}
q->symbol_sz = symbol_sz;
// Create initial OFDM configuration
srsran_ofdm_cfg_t fft_cfg = {};
fft_cfg.nof_prb = args->nof_max_prb;
fft_cfg.symbol_sz = srsran_min_symbol_sz_rb(args->nof_max_prb);
fft_cfg.symbol_sz = (uint32_t)symbol_sz;
fft_cfg.keep_dc = true;
// Initialise a different OFDM modulator per channel
for (uint32_t i = 0; i < q->nof_tx_antennas; i++) {
fft_cfg.in_buffer = q->sf_symbols[i];
fft_cfg.out_buffer = output[i];
@ -91,6 +103,15 @@ int srsran_gnb_dl_init(srsran_gnb_dl_t* q, cf_t* output[SRSRAN_MAX_PORTS], const
return SRSRAN_ERROR;
}
srsran_ssb_args_t ssb_args = {};
ssb_args.enable_encode = true;
ssb_args.max_srate_hz = args->srate_hz;
ssb_args.min_scs = args->scs;
if (srsran_ssb_init(&q->ssb, &ssb_args) < SRSRAN_SUCCESS) {
ERROR("Error SSB");
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
@ -112,6 +133,7 @@ void srsran_gnb_dl_free(srsran_gnb_dl_t* q)
srsran_dmrs_sch_free(&q->dmrs);
srsran_pdcch_nr_free(&q->pdcch);
srsran_ssb_free(&q->ssb);
SRSRAN_MEM_ZERO(q, srsran_gnb_dl_t, 1);
}
@ -133,10 +155,11 @@ int srsran_gnb_dl_set_carrier(srsran_gnb_dl_t* q, const srsran_carrier_nr_t* car
}
if (carrier->nof_prb != q->carrier.nof_prb) {
srsran_ofdm_cfg_t fft_cfg = {};
fft_cfg.nof_prb = carrier->nof_prb;
fft_cfg.symbol_sz = srsran_min_symbol_sz_rb(carrier->nof_prb);
fft_cfg.keep_dc = true;
srsran_ofdm_cfg_t fft_cfg = {};
fft_cfg.nof_prb = carrier->nof_prb;
fft_cfg.symbol_sz = srsran_min_symbol_sz_rb(carrier->nof_prb);
fft_cfg.keep_dc = true;
fft_cfg.phase_compensation_hz = carrier->dl_center_frequency_hz;
for (uint32_t i = 0; i < q->nof_tx_antennas; i++) {
fft_cfg.in_buffer = q->sf_symbols[i];
@ -149,6 +172,38 @@ int srsran_gnb_dl_set_carrier(srsran_gnb_dl_t* q, const srsran_carrier_nr_t* car
return SRSRAN_SUCCESS;
}
int srsran_gnb_dl_set_ssb_config(srsran_gnb_dl_t* q, const srsran_ssb_cfg_t* ssb)
{
if (q == NULL || ssb == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (srsran_ssb_set_cfg(&q->ssb, ssb) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
int srsran_gnb_dl_add_ssb(srsran_gnb_dl_t* q, const srsran_pbch_msg_nr_t* pbch_msg, uint32_t sf_idx)
{
if (q == NULL || pbch_msg == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
// Skip SSB if it is not the time for it
if (!srsran_ssb_send(&q->ssb, sf_idx)) {
return SRSRAN_SUCCESS;
}
if (srsran_ssb_add(&q->ssb, q->carrier.pci, pbch_msg, q->fft[0].cfg.out_buffer, q->fft[0].cfg.out_buffer) <
SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
int srsran_gnb_dl_set_pdcch_config(srsran_gnb_dl_t* q,
const srsran_pdcch_cfg_nr_t* cfg,
const srsran_dci_cfg_nr_t* dci_cfg)

@ -139,11 +139,12 @@ int srsran_gnb_ul_set_carrier(srsran_gnb_ul_t* q, const srsran_carrier_nr_t* car
return SRSRAN_ERROR;
}
srsran_ofdm_cfg_t ofdm_cfg = {};
ofdm_cfg.nof_prb = carrier->nof_prb;
ofdm_cfg.rx_window_offset = GNB_UL_NR_FFT_WINDOW_OFFSET;
ofdm_cfg.symbol_sz = srsran_min_symbol_sz_rb(carrier->nof_prb);
ofdm_cfg.keep_dc = true;
srsran_ofdm_cfg_t ofdm_cfg = {};
ofdm_cfg.nof_prb = carrier->nof_prb;
ofdm_cfg.rx_window_offset = GNB_UL_NR_FFT_WINDOW_OFFSET;
ofdm_cfg.symbol_sz = srsran_min_symbol_sz_rb(carrier->nof_prb);
ofdm_cfg.keep_dc = true;
ofdm_cfg.phase_compensation_hz = carrier->dl_center_frequency_hz;
if (srsran_ofdm_rx_init_cfg(&q->fft, &ofdm_cfg) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;

@ -189,18 +189,33 @@ void srsran_pbch_nr_free(srsran_pbch_nr_t* q)
static const uint32_t G[PBCH_NR_A] = {16, 23, 18, 17, 8, 30, 10, 6, 24, 7, 0, 5, 3, 2, 1, 4,
9, 11, 12, 13, 14, 15, 19, 20, 21, 22, 25, 26, 27, 28, 29, 31};
#define PBCH_SFN_PAYLOAD_BEGIN 1
#define PBCH_SFN_PAYLOAD_LENGTH 6
#define PBCH_SFN_2ND_LSB_G (G[PBCH_SFN_PAYLOAD_LENGTH + 2])
#define PBCH_SFN_3RD_LSB_G (G[PBCH_SFN_PAYLOAD_LENGTH + 1])
static void
pbch_nr_pbch_msg_pack(const srsran_pbch_nr_cfg_t* cfg, const srsran_pbch_msg_nr_t* msg, uint8_t a[PBCH_NR_A])
{
// Extract actual payload size
uint32_t A_hat = SRSRAN_PBCH_MSG_NR_SZ;
// Put actual payload
uint32_t j_sfn = 0;
uint32_t j_other = 14;
for (uint32_t i = 0; i < A_hat; i++) {
if (i >= PBCH_SFN_PAYLOAD_BEGIN && i < (PBCH_SFN_PAYLOAD_BEGIN + PBCH_SFN_PAYLOAD_LENGTH)) {
a[G[j_sfn++]] = msg->payload[i];
} else {
a[G[j_other++]] = msg->payload[i];
}
}
// Put SFN in a_hat[A_hat] to a_hat[A_hat + 3]
uint32_t j_sfn = 0;
a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 3U) & 1U); // 4th LSB of SFN
a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 2U) & 1U); // 3th LSB of SFN
a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 1U) & 1U); // 2th LSB of SFN
a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 0U) & 1U); // 1th LSB of SFN
a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 3U) & 1U); // 4th LSB of SFN
a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 2U) & 1U); // 3th LSB of SFN
a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 1U) & 1U); // 2th LSB of SFN
a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 0U) & 1U); // 1th LSB of SFN
// Put HRF in a_hat[A_hat + 4]
a[G[10]] = (msg->hrf ? 1 : 0);
@ -216,16 +231,6 @@ pbch_nr_pbch_msg_pack(const srsran_pbch_nr_cfg_t* cfg, const srsran_pbch_msg_nr_
a[G[13]] = 0; // Reserved
}
// Put actual payload
uint32_t j_other = 14;
for (uint32_t i = 0; i < A_hat; i++) {
if (i > 0 && i < 7) {
a[G[j_sfn++]] = msg->payload[i];
} else {
a[G[j_other++]] = msg->payload[i];
}
}
if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) {
PBCH_NR_DEBUG_TX("Packed PBCH bits: ");
srsran_vec_fprint_byte(stdout, a, PBCH_NR_A);
@ -242,9 +247,19 @@ pbch_nr_pbch_msg_unpack(const srsran_pbch_nr_cfg_t* cfg, const uint8_t a[PBCH_NR
// Extract actual payload size
uint32_t A_hat = SRSRAN_PBCH_MSG_NR_SZ;
// Get actual payload
uint32_t j_sfn = 0;
uint32_t j_other = 14;
for (uint32_t i = 0; i < A_hat; i++) {
if (i >= PBCH_SFN_PAYLOAD_BEGIN && i < (PBCH_SFN_PAYLOAD_BEGIN + PBCH_SFN_PAYLOAD_LENGTH)) {
msg->payload[i] = a[G[j_sfn++]];
} else {
msg->payload[i] = a[G[j_other++]];
}
}
// Put SFN in a_hat[A_hat] to a_hat[A_hat + 3]
uint32_t j_sfn = 0;
msg->sfn_4lsb = 0;
msg->sfn_4lsb = 0;
msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 3U); // 4th LSB of SFN
msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 2U); // 3th LSB of SFN
msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 1U); // 2th LSB of SFN
@ -263,16 +278,6 @@ pbch_nr_pbch_msg_unpack(const srsran_pbch_nr_cfg_t* cfg, const uint8_t a[PBCH_NR
} else {
msg->k_ssb_msb = a[G[11]];
}
// Put actual payload
uint32_t j_other = 14;
for (uint32_t i = 0; i < A_hat; i++) {
if (i > 0 && i < 7) {
msg->payload[i] = a[G[j_sfn++]];
} else {
msg->payload[i] = a[G[j_other++]];
}
}
}
static void pbch_nr_scramble(const srsran_pbch_nr_cfg_t* cfg, const uint8_t a[PBCH_NR_A], uint8_t a_prime[PBCH_NR_A])
@ -291,7 +296,7 @@ static void pbch_nr_scramble(const srsran_pbch_nr_cfg_t* cfg, const uint8_t a[PB
}
// Select value v
uint32_t v = 2 * a[G[1]] + a[G[2]];
uint32_t v = 2 * a[PBCH_SFN_3RD_LSB_G] + a[PBCH_SFN_2ND_LSB_G];
// Advance sequence
srsran_sequence_state_advance(&sequence_state, M * v);
@ -301,12 +306,14 @@ static void pbch_nr_scramble(const srsran_pbch_nr_cfg_t* cfg, const uint8_t a[PB
srsran_sequence_state_apply_bit(&sequence_state, c, c, PBCH_NR_A);
while (i < PBCH_NR_A) {
// a i corresponds to any one of the bits belonging to the SS/PBCH block index, the half frame index, and 2 nd and 3
// rd least significant bits of the system frame number
uint8_t s_i = c[j];
// else
if (i == G[11] || i == G[12] || i == G[13] || i == G[10] || i == G[1] || i == G[2]) {
// Check if i belongs to a SS/PBCH block index which is only multiplexed when L_max is 64
bool is_ssb_idx = (i == G[11] || i == G[12] || i == G[13]) && cfg->Lmax == 64;
// a i corresponds to any one of the bits belonging to the SS/PBCH block index, the half frame index, and 2 nd and 3
// rd least significant bits of the system frame number
if (is_ssb_idx || i == G[10] || i == PBCH_SFN_2ND_LSB_G || i == PBCH_SFN_3RD_LSB_G) {
s_i = 0;
} else {
j++;

@ -578,7 +578,14 @@ static uint32_t pdsch_nr_grant_info(const srsran_pdsch_nr_t* q,
}
// Append time-domain resource mapping
len = srsran_print_check(str, str_len, len, "prb=%d:%d symb=%d:%d ", first_prb, grant->nof_prb, grant->S, grant->L);
len = srsran_print_check(str,
str_len,
len,
"prb=(%d,%d) symb=(%d,%d) ",
first_prb,
first_prb + grant->nof_prb - 1,
grant->S,
grant->S + grant->L - 1);
// Append TB info
for (uint32_t i = 0; i < SRSRAN_MAX_TB; i++) {

@ -1020,7 +1020,14 @@ static uint32_t pusch_nr_grant_info(const srsran_pusch_nr_t* q,
}
// Append time-domain resource mapping
len = srsran_print_check(str, str_len, len, "prb=%d:%d symb=%d:%d ", first_prb, grant->nof_prb, grant->S, grant->L);
len = srsran_print_check(str,
str_len,
len,
"prb=(%d,%d) symb=(%d,%d) ",
first_prb,
first_prb + grant->nof_prb - 1,
grant->S,
grant->S + grant->L - 1);
// Append TB info
for (uint32_t i = 0; i < SRSRAN_MAX_TB; i++) {

@ -24,17 +24,7 @@
#include "srsran/phy/utils/random.h"
#include <getopt.h>
static srsran_carrier_nr_t carrier = {
1, // pci
0, // absolute_frequency_ssb
0, // dl_absolute_frequency_point_a
0, // ul_absolute_frequency_point_a
0, // offset_to_carrier
srsran_subcarrier_spacing_15kHz, // scs
50, // nof_prb
0, // start
1 // max_mimo_layers
};
static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR;
static uint16_t rnti = 0x1234;
static bool fast_sweep = true;

@ -29,17 +29,7 @@
#include <getopt.h>
#include <math.h>
static srsran_carrier_nr_t carrier = {
1, // pci
0, // absolute_frequency_ssb
0, // dl_absolute_frequency_point_a
0, // ul_absolute_frequency_point_a
0, // offset_to_carrier
srsran_subcarrier_spacing_15kHz, // scs
SRSRAN_MAX_PRB_NR, // nof_prb
0, // start
1 // max_mimo_layers
};
static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR;
static uint32_t n_prb = 0; // Set to 0 for steering
static uint32_t mcs = 30; // Set to 30 for steering

@ -31,17 +31,7 @@
#include <strings.h>
#include <unistd.h>
static srsran_carrier_nr_t carrier = {
1, // pci
0, // absolute_frequency_ssb
0, // dl_absolute_frequency_point_a
0, // ul_absolute_frequency_point_a
0, // offset_to_carrier
srsran_subcarrier_spacing_15kHz, // scs
6, // nof_prb
0, // start
1 // max_mimo_layers
};
static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR;
static uint32_t starting_prb_stride = 4;
static uint32_t starting_symbol_stride = 4;
@ -270,6 +260,9 @@ static void usage(char* prog)
static void parse_args(int argc, char** argv)
{
// Limit default number of RB
carrier.nof_prb = 6;
int opt;
while ((opt = getopt(argc, argv, "cnfsv")) != -1) {
switch (opt) {

@ -28,18 +28,7 @@
#include <complex.h>
#include <getopt.h>
static srsran_carrier_nr_t carrier = {
1, // pci
0, // absolute_frequency_ssb
0, // dl_absolute_frequency_point_a
0, // ul_absolute_frequency_point_a
0, // offset_to_carrier
srsran_subcarrier_spacing_15kHz, // scs
SRSRAN_MAX_PRB_NR, // nof_prb
0, // start
1 // max_mimo_layers
};
static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR;
static uint32_t n_prb = 0; // Set to 0 for steering
static uint32_t mcs = 30; // Set to 30 for steering
static srsran_sch_cfg_nr_t pusch_cfg = {};
@ -312,7 +301,7 @@ int main(int argc, char** argv)
// Check Received SCH LLR match
if (pusch_rx.G_ulsch > 0) {
for (uint32_t i = 0; i < pusch_rx.G_ulsch; i++) {
uint8_t rx_bit = (((int8_t*)pusch_rx.g_ulsch)[i]) < 0 ? 1 : 0;
uint8_t rx_bit = (((int8_t*)pusch_rx.g_ulsch)[i]) < 0 ? 1 : 0;
if (rx_bit == 0) {
pusch_rx.g_ulsch[i] = pusch_tx.g_ulsch[i];
} else {

@ -27,17 +27,7 @@
#include <getopt.h>
#include <srsran/phy/utils/random.h>
static srsran_carrier_nr_t carrier = {
1, // pci
0, // absolute_frequency_ssb
0, // dl_absolute_frequency_point_a
0, // ul_absolute_frequency_point_a
0, // offset_to_carrier
srsran_subcarrier_spacing_15kHz, // scs
SRSRAN_MAX_PRB_NR, // nof_prb
0, // start
1 // max_mimo_layers
};
static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR;
static uint32_t n_prb = 0; // Set to 0 for steering
static uint32_t mcs = 30; // Set to 30 for steering

@ -424,7 +424,8 @@ static inline int ssb_get_t_offset(srsran_ssb_t* q, uint32_t ssb_idx)
int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg)
{
// Verify input parameters
if (q == NULL || cfg == NULL) {
if (q == NULL || cfg == NULL || cfg->pattern == SRSRAN_SSB_PATTERN_INVALID ||
cfg->duplex_mode == SRSRAN_DUPLEX_MODE_INVALID) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
@ -600,6 +601,11 @@ int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* m
// Map SSB in resource grid and perform IFFT
ssb_modulate_symbol(q, ssb_grid, l);
// Phase compensation
cf_t phase_compensation = (cf_t)cexp(-I * 2.0 * M_PI * q->cfg.center_freq_hz * (double)t_offset / q->cfg.srate_hz);
srsran_vec_sc_prod_ccc(q->tmp_time, phase_compensation, q->tmp_time, q->symbol_sz);
t_offset += (int)(q->symbol_sz + q->cp_sz);
// Add cyclic prefix to input;
srsran_vec_sum_ccc(in_ptr, &q->tmp_time[q->symbol_sz - q->cp_sz], out_ptr, q->cp_sz);
in_ptr += q->cp_sz;
@ -625,6 +631,10 @@ static int ssb_demodulate(srsran_ssb_t* q, const cf_t* in, uint32_t t_offset, cf
srsran_vec_cf_copy(q->tmp_time, in_ptr, q->symbol_sz);
in_ptr += q->symbol_sz + SRSRAN_CEIL(q->cp_sz, 2);
// Phase compensation
cf_t phase_compensation = (cf_t)cexp(-I * 2.0 * M_PI * q->cfg.center_freq_hz * (double)t_offset / q->cfg.srate_hz);
t_offset += q->symbol_sz + q->cp_sz;
// Convert to frequency domain
srsran_dft_run_guru_c(&q->fft);
@ -650,7 +660,7 @@ static int ssb_demodulate(srsran_ssb_t* q, const cf_t* in, uint32_t t_offset, cf
// Normalize
float norm = sqrtf((float)q->symbol_sz);
if (isnormal(norm)) {
srsran_vec_sc_prod_cfc(ptr, 1.0f / norm, ptr, SRSRAN_SSB_BW_SUBC);
srsran_vec_sc_prod_ccc(ptr, conjf(phase_compensation) / norm, ptr, SRSRAN_SSB_BW_SUBC);
}
}

@ -162,4 +162,4 @@ 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)
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 2 0 1 0)

@ -25,17 +25,7 @@
#include "srsran/phy/utils/debug.h"
#include <getopt.h>
static srsran_carrier_nr_t carrier = {
501, // pci
0, // absolute_frequency_ssb
0, // dl_absolute_frequency_point_a
0, // ul_absolute_frequency_point_a
0, // offset_to_carrier
srsran_subcarrier_spacing_15kHz, // scs
52, // nof_prb
0, // start
1 // max_mimo_layers
};
static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR;
static char* filename = NULL;
static srsran_pdcch_cfg_nr_t pdcch_cfg = {};

@ -168,12 +168,14 @@ int srsran_ue_dl_nr_set_carrier(srsran_ue_dl_nr_t* q, const srsran_carrier_nr_t*
if (carrier->nof_prb != q->carrier.nof_prb) {
for (uint32_t i = 0; i < q->nof_rx_antennas; i++) {
srsran_ofdm_cfg_t cfg = {};
cfg.nof_prb = carrier->nof_prb;
cfg.symbol_sz = srsran_min_symbol_sz_rb(carrier->nof_prb);
cfg.cp = SRSRAN_CP_NORM;
cfg.keep_dc = true;
cfg.rx_window_offset = UE_DL_NR_FFT_WINDOW_OFFSET;
srsran_ofdm_cfg_t cfg = {};
cfg.nof_prb = carrier->nof_prb;
cfg.symbol_sz = srsran_min_symbol_sz_rb(carrier->nof_prb);
cfg.cp = SRSRAN_CP_NORM;
cfg.keep_dc = true;
cfg.rx_window_offset = UE_DL_NR_FFT_WINDOW_OFFSET;
cfg.phase_compensation_hz = carrier->dl_center_frequency_hz;
srsran_ofdm_rx_init_cfg(&q->fft[i], &cfg);
}
}
@ -382,7 +384,7 @@ static int ue_dl_nr_find_dci_ss(srsran_ue_dl_nr_t* q,
// Calculate possible PDCCH DCI candidates
uint32_t candidates[SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR] = {};
int nof_candidates = srsran_pdcch_nr_locations_coreset(
coreset, search_space, rnti, L, SRSRAN_SLOT_NR_MOD(q->carrier.scs, slot_cfg->idx), candidates);
coreset, search_space, rnti, L, SRSRAN_SLOT_NR_MOD(q->carrier.scs, slot_cfg->idx), candidates);
if (nof_candidates < SRSRAN_SUCCESS) {
ERROR("Error calculating DCI candidate location");
return SRSRAN_ERROR;

@ -77,10 +77,11 @@ int srsran_ue_ul_nr_set_carrier(srsran_ue_ul_nr_t* q, const srsran_carrier_nr_t*
q->carrier = *carrier;
srsran_ofdm_cfg_t fft_cfg = {};
fft_cfg.nof_prb = carrier->nof_prb;
fft_cfg.symbol_sz = srsran_min_symbol_sz_rb(carrier->nof_prb);
fft_cfg.keep_dc = true;
srsran_ofdm_cfg_t fft_cfg = {};
fft_cfg.nof_prb = carrier->nof_prb;
fft_cfg.symbol_sz = srsran_min_symbol_sz_rb(carrier->nof_prb);
fft_cfg.keep_dc = true;
fft_cfg.phase_compensation_hz = carrier->dl_center_frequency_hz;
if (srsran_ofdm_tx_init_cfg(&q->ifft, &fft_cfg) < SRSRAN_SUCCESS) {
ERROR("Initiating OFDM");
return SRSRAN_ERROR;

@ -28,18 +28,7 @@
#include "srsran/phy/utils/vector.h"
#include <getopt.h>
static srsran_carrier_nr_t carrier = {
501, // pci
0, // absolute_frequency_ssb
0, // dl_absolute_frequency_point_a
0, // ul_absolute_frequency_point_a
0, // offset_to_carrier
srsran_subcarrier_spacing_15kHz, // scs
52, // nof_prb
0, // start
1 // max_mimo_layers
};
static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR;
static uint32_t n_prb = 0; // Set to 0 for steering
static uint32_t mcs = 30; // Set to 30 for steering
static srsran_sch_cfg_nr_t pdsch_cfg = {};
@ -272,6 +261,7 @@ int main(int argc, char** argv)
gnb_dl_args.pdsch.sch.disable_simd = false;
gnb_dl_args.pdcch.disable_simd = false;
gnb_dl_args.nof_max_prb = carrier.nof_prb;
gnb_dl_args.srate_hz = SRSRAN_SUBC_SPACING_NR(carrier.scs) * srsran_min_symbol_sz_rb(carrier.nof_prb);
srsran_pdcch_cfg_nr_t pdcch_cfg = {};
@ -415,7 +405,7 @@ int main(int argc, char** argv)
if (data_tx[tb] == NULL) {
continue;
}
srsran_random_byte_vector(rand_gen, data_tx[tb], pdsch_cfg.grant.tb[tb].tbs/8);
srsran_random_byte_vector(rand_gen, data_tx[tb], pdsch_cfg.grant.tb[tb].tbs / 8);
pdsch_cfg.grant.tb[tb].softbuffer.tx = &softbuffer_tx;
}

@ -39,8 +39,8 @@
#include "srsenb/hdr/phy/enb_phy_base.h"
#include "srsenb/hdr/stack/enb_stack_base.h"
#include "srsenb/hdr/stack/rrc/nr/rrc_config_nr.h"
#include "srsenb/hdr/stack/rrc/rrc_config.h"
#include "srsenb/hdr/stack/rrc/rrc_config_nr.h"
#include "srsenb/hdr/stack/mac/sched_interface.h"
#include "srsran/common/bcd_helpers.h"

@ -46,7 +46,7 @@ public:
void stop(){};
private:
void set_metrics_helper(uint32_t num_ue, const mac_metrics_t& mac, const std::vector<phy_metrics_t>& phy);
void set_metrics_helper(uint32_t num_ue, const mac_metrics_t& mac, const std::vector<phy_metrics_t>& phy, bool is_nr);
std::string float_to_string(float f, int digits, int field_width = 6);
std::string float_to_eng_string(float f, int digits);

@ -40,21 +40,46 @@ namespace nr {
class slot_worker final : public srsran::thread_pool::worker
{
public:
/**
* @brief Slot worker synchronization interface
*/
class sync_interface
{
public:
/**
* @brief Wait for the worker to start DL scheduler
* @param w Worker pointer
*/
virtual void wait(slot_worker* w) = 0;
/**
* @brief Releases the current worker
*/
virtual void release() = 0;
};
struct args_t {
uint32_t cell_index = 0;
uint32_t nof_max_prb = SRSRAN_MAX_PRB_NR;
uint32_t nof_tx_ports = 1;
uint32_t nof_rx_ports = 1;
uint32_t rf_port = 0;
uint32_t pusch_max_nof_iter = 10;
uint32_t cell_index = 0;
uint32_t nof_max_prb = SRSRAN_MAX_PRB_NR;
uint32_t nof_tx_ports = 1;
uint32_t nof_rx_ports = 1;
uint32_t rf_port = 0;
srsran_subcarrier_spacing_t scs = srsran_subcarrier_spacing_15kHz;
uint32_t pusch_max_nof_iter = 10;
double srate_hz = 0.0;
};
slot_worker(srsran::phy_common_interface& common_, stack_interface_phy_nr& stack_, srslog::basic_logger& logger);
slot_worker(srsran::phy_common_interface& common_,
stack_interface_phy_nr& stack_,
sync_interface& sync_,
srslog::basic_logger& logger);
~slot_worker();
bool init(const args_t& args);
bool set_common_cfg(const srsran_carrier_nr_t& carrier, const srsran_pdcch_cfg_nr_t& pdcch_cfg_);
bool set_common_cfg(const srsran_carrier_nr_t& carrier,
const srsran_pdcch_cfg_nr_t& pdcch_cfg_,
const srsran_ssb_cfg_t& ssb_cfg_);
/* Functions used by main PHY thread */
cf_t* get_buffer_rx(uint32_t antenna_idx);
@ -83,6 +108,7 @@ private:
srsran::phy_common_interface& common;
stack_interface_phy_nr& stack;
srslog::basic_logger& logger;
sync_interface& sync;
uint32_t sf_len = 0;
uint32_t cell_index = 0;

@ -26,15 +26,20 @@
#include "srsenb/hdr/phy/phy_interfaces.h"
#include "srsenb/hdr/phy/prach_worker.h"
#include "srsran/common/thread_pool.h"
#include "srsran/common/tti_sempahore.h"
#include "srsran/interfaces/enb_mac_interfaces.h"
#include "srsran/interfaces/gnb_interfaces.h"
namespace srsenb {
namespace nr {
class worker_pool
class worker_pool final : private slot_worker::sync_interface
{
private:
srsran::tti_semaphore<slot_worker*> slot_sync; ///< Slot synchronization semaphore
void wait(slot_worker* w) override { slot_sync.wait(w); }
void release() override { slot_sync.release(); }
class prach_stack_adaptor_t : public stack_interface_phy_lte
{
private:
@ -92,14 +97,17 @@ private:
srslog::basic_logger& logger;
prach_stack_adaptor_t prach_stack_adaptor;
uint32_t nof_prach_workers = 0;
double srate_hz = 0.0; ///< Current sampling rate in Hz
// Current configuration
std::mutex common_cfg_mutex;
srsran_carrier_nr_t carrier = {};
srsran_pdcch_cfg_nr_t pdcch_cfg = {};
srsran_ssb_cfg_t ssb_cfg = {};
public:
struct args_t {
double srate_hz = 0.0;
uint32_t nof_phy_threads = 3;
uint32_t nof_prach_workers = 0;
uint32_t prio = 52;

@ -108,7 +108,8 @@ private:
bool initialized = false;
srsran_prach_cfg_t prach_cfg = {};
srsran_prach_cfg_t prach_cfg = {};
common_cfg_t common_cfg = {};
void parse_common_config(const phy_cfg_t& cfg);
};

@ -52,6 +52,7 @@ struct phy_cell_cfg_nr_t {
uint32_t num_ra_preambles;
float gain_db;
srsran_pdcch_cfg_nr_t pdcch = {}; ///< Common CORESET and Search Space configuration
srsran_prach_cfg_t prach = {};
};
typedef std::vector<phy_cell_cfg_t> phy_cell_cfg_list_t;

@ -55,8 +55,8 @@ public:
~enb_stack_lte() final;
// eNB stack base interface
int init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_, phy_interface_stack_lte* phy_, x2_interface* x2_);
void stop() final;
int init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_, phy_interface_stack_lte* phy_, x2_interface* x2_);
void stop() final;
std::string get_type() final;
bool get_metrics(stack_metrics_t* metrics) final;
@ -120,14 +120,21 @@ public:
// rrc_eutra_interface_rrc_nr
void sgnb_addition_ack(uint16_t eutra_rnti, sgnb_addition_ack_params_t params) final
{
rrc.sgnb_addition_ack(eutra_rnti, params);
x2_task_queue.push([this, eutra_rnti, params]() { rrc.sgnb_addition_ack(eutra_rnti, params); });
}
void sgnb_addition_reject(uint16_t eutra_rnti) final
{
x2_task_queue.push([this, eutra_rnti]() { rrc.sgnb_addition_reject(eutra_rnti); });
}
void sgnb_addition_reject(uint16_t eutra_rnti) final { rrc.sgnb_addition_reject(eutra_rnti); }
void sgnb_addition_complete(uint16_t eutra_rnti, uint16_t nr_rnti) final
{
rrc.sgnb_addition_complete(eutra_rnti, nr_rnti);
x2_task_queue.push([this, eutra_rnti, nr_rnti]() { rrc.sgnb_addition_complete(eutra_rnti, nr_rnti); });
}
void set_activity_user(uint16_t eutra_rnti) final
{
// Note: RRC processes activity asynchronously, so there is no need to use x2_task_queue
rrc.set_activity_user(eutra_rnti);
}
void set_activity_user(uint16_t eutra_rnti) final { rrc.set_activity_user(eutra_rnti); }
// gtpu_interface_pdcp
void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu);
@ -160,7 +167,7 @@ private:
// task handling
srsran::task_scheduler task_sched;
srsran::task_queue_handle enb_task_queue, sync_task_queue, metrics_task_queue;
srsran::task_queue_handle enb_task_queue, sync_task_queue, metrics_task_queue, x2_task_queue;
// bearer management
enb_bearer_manager bearers; // helper to manage mapping between EPS and radio bearers
@ -174,7 +181,7 @@ private:
srsenb::s1ap s1ap;
// RAT-specific interfaces
phy_interface_stack_lte* phy = nullptr;
phy_interface_stack_lte* phy = nullptr;
// state
std::atomic<bool> started{false};

@ -96,10 +96,14 @@ public:
// X2 data interface
void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu, int pdcp_sn = -1) final
{
pdcp.write_sdu(rnti, lcid, std::move(sdu), pdcp_sn);
auto task = [this, rnti, lcid, pdcp_sn](srsran::unique_byte_buffer_t& sdu) {
pdcp.write_sdu(rnti, lcid, std::move(sdu), pdcp_sn);
};
gtpu_task_queue.push(std::bind(task, std::move(sdu)));
}
std::map<uint32_t, srsran::unique_byte_buffer_t> get_buffered_pdus(uint16_t rnti, uint32_t lcid) final
{
// TODO: make it thread-safe. For now, this function is unused
return pdcp.get_buffered_pdus(rnti, lcid);
}
@ -120,7 +124,7 @@ private:
// task scheduling
static const int STACK_MAIN_THREAD_PRIO = 4;
srsran::task_scheduler task_sched;
srsran::task_multiqueue::queue_handle sync_task_queue, ue_task_queue, gw_task_queue, mac_task_queue;
srsran::task_multiqueue::queue_handle sync_task_queue, ue_task_queue, gtpu_task_queue, mac_task_queue;
// derived
srsenb::mac_nr mac;

@ -44,8 +44,17 @@ struct mac_ue_metrics_t {
float dl_ri;
float dl_pmi;
float phr;
};
// NR-only UL PHY metrics
float pusch_sinr;
float pucch_sinr;
float ul_rssi;
float fec_iters;
float dl_mcs;
int dl_mcs_samples;
float ul_mcs;
int ul_mcs_samples;
};
/// MAC misc information for each cc.
struct mac_cc_info_t {
/// PCI value.

@ -38,7 +38,7 @@ class ue_buffer_manager
{
protected:
const static uint32_t MAX_LC_ID = isNR ? srsran::MAX_NR_NOF_BEARERS : srsran::MAX_LTE_LCID;
const static uint32_t MAX_LCG_ID = isNR ? 7 : 3;
const static uint32_t MAX_LCG_ID = isNR ? 7 : 3; // Should import from sched_interface and sched_nr_interface
const static uint32_t MAX_SRB_LC_ID = isNR ? srsran::MAX_NR_SRB_ID : srsran::MAX_LTE_SRB_ID;
const static uint32_t MAX_NOF_LCIDS = MAX_LC_ID + 1;
const static uint32_t MAX_NOF_LCGS = MAX_LCG_ID + 1;

@ -112,7 +112,6 @@ private:
std::atomic<bool> started = {false};
const static uint32_t NUMEROLOGY_IDX = 0; /// only 15kHz supported at this stage
srsran::slot_point pdsch_slot, pusch_slot;
srsenb::sched_nr sched;
std::vector<sched_nr_interface::cell_cfg_t> cell_config;

@ -137,6 +137,13 @@ public:
{
return cce_positions_list[ss_id_to_cce_idx[search_id]];
}
uint32_t get_k1(slot_point pdsch_slot) const
{
if (phy().duplex.mode == SRSRAN_DUPLEX_MODE_TDD) {
return phy().harq_ack.dl_data_to_ul_ack[pdsch_slot.to_uint() % phy().duplex.tdd.pattern1.period_ms];
}
return phy().harq_ack.dl_data_to_ul_ack[pdsch_slot.to_uint() % phy().harq_ack.nof_dl_data_to_ul_ack];
}
private:
uint16_t rnti = SRSRAN_INVALID_RNTI;

@ -50,6 +50,8 @@ struct harq_ack_t {
};
using harq_ack_list_t = srsran::bounded_vector<harq_ack_t, MAX_GRANTS>;
/// save data for scheduler to keep track of previous allocations
/// This only contains information about a given slot
struct bwp_slot_grid {
uint32_t slot_idx = 0;
const bwp_params* cfg = nullptr;
@ -88,6 +90,7 @@ struct bwp_res_grid {
const bwp_params* cfg = nullptr;
private:
// TTIMOD_SZ is the longest allocation in the future
srsran::bounded_vector<bwp_slot_grid, TTIMOD_SZ> slots;
};

@ -53,7 +53,7 @@ public:
slot_point harq_slot_tx() const { return slot_tx; }
slot_point harq_slot_ack() const { return slot_ack; }
bool ack_info(uint32_t tb_idx, bool ack);
int ack_info(uint32_t tb_idx, bool ack);
void new_slot(slot_point slot_rx);
void reset();
@ -133,8 +133,8 @@ public:
explicit harq_entity(uint32_t nprb, uint32_t nof_harq_procs = SCHED_NR_MAX_HARQ);
void new_slot(slot_point slot_rx_);
void dl_ack_info(uint32_t pid, uint32_t tb_idx, bool ack) { dl_harqs[pid].ack_info(tb_idx, ack); }
void ul_crc_info(uint32_t pid, bool ack) { ul_harqs[pid].ack_info(0, ack); }
int dl_ack_info(uint32_t pid, uint32_t tb_idx, bool ack) { return dl_harqs[pid].ack_info(tb_idx, ack); }
int ul_crc_info(uint32_t pid, bool ack) { return ul_harqs[pid].ack_info(0, ack); }
uint32_t nof_dl_harqs() const { return dl_harqs.size(); }
uint32_t nof_ul_harqs() const { return ul_harqs.size(); }

@ -40,6 +40,7 @@ const static size_t SCHED_NR_MAX_TB = 1;
const static size_t SCHED_NR_MAX_HARQ = 16;
const static size_t SCHED_NR_MAX_BWP_PER_CELL = 2;
const static size_t SCHED_NR_MAX_LCID = 32;
const static size_t SCHED_NR_MAX_LC_GROUP = 7;
class sched_nr_interface
{

@ -33,6 +33,23 @@ void sched_nzp_csi_rs(srsran::const_span<srsran_csi_rs_nzp_set_t> nzp_csi_rs_set
const srsran_slot_cfg_t& slot_cfg,
nzp_csi_rs_list& csi_rs_list);
/**
* @brief Schedule grant for SSB.
*
* The functions schedules the SSB according to a given periodicity. This function is a simplified version of an
* SSB scheduler and has several hard-coded parameters.
*
* @param[in] sl_point Slot point carrying information about current slot.
* @param[in] ssb_periodicity Periodicity of SSB in ms.
* @param[out] ssb_list List of SSB messages to be sent to PHY.
*
* @remark This function a is basic scheduling function that uses the following simplified assumption:
* 1) Subcarrier spacing: 15kHz
* 2) Frequency below 3GHz
* 3) Position in Burst is 1000, i.e., Only the first SSB of the 4 opportunities gets scheduled
*/
void sched_ssb_basic(const slot_point& sl_point, uint32_t ssb_periodicity, ssb_list& ssb_list);
/// For a given BWP and slot, schedule SSB, NZP CSI RS and SIBs
void sched_dl_signalling(const bwp_params& bwp_params,
slot_point sl_pdcch,

@ -69,6 +69,8 @@ public:
void metrics_dl_ri(uint32_t dl_cqi);
void metrics_dl_pmi(uint32_t dl_cqi);
void metrics_dl_cqi(uint32_t dl_cqi);
void metrics_dl_mcs(uint32_t mcs);
void metrics_ul_mcs(uint32_t mcs);
void metrics_cnt();
uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) final;

@ -0,0 +1,25 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSRAN_CELL_ASN1_CONFIG_H
#define SRSRAN_CELL_ASN1_CONFIG_H
#include "rrc_config_nr.h"
#include "srsran/asn1/rrc_nr.h"
namespace srsenb {
int fill_serv_cell_from_enb_cfg(const rrc_nr_cfg_t& cfg, asn1::rrc_nr::serving_cell_cfg_s& serv_cell);
}
#endif // SRSRAN_CELL_ASN1_CONFIG_H

@ -22,6 +22,7 @@
#ifndef SRSRAN_RRC_CONFIG_NR_H
#define SRSRAN_RRC_CONFIG_NR_H
#include "../rrc_config_common.h"
#include "srsran/asn1/rrc_nr.h"
#include "srsran/interfaces/gnb_rrc_nr_interfaces.h"
#include "srsue/hdr/phy/phy_common.h"

@ -52,6 +52,18 @@ struct srb_cfg_t {
asn1::rrc::srb_to_add_mod_s::rlc_cfg_c_ rlc_cfg;
};
// Parameter required for NR cell measurement handling
struct rrc_endc_cfg_t {
bool act_from_b1_event;
uint32_t nr_dl_arfcn;
uint32_t nr_band;
using ssb_nr_cfg = asn1::rrc::mtc_ssb_nr_r15_s;
using ssb_rs_cfg = asn1::rrc::rs_cfg_ssb_nr_r15_s;
ssb_nr_cfg::periodicity_and_offset_r15_c_ ssb_period_offset;
ssb_nr_cfg::ssb_dur_r15_e_ ssb_duration;
ssb_rs_cfg::subcarrier_spacing_ssb_r15_e_ ssb_ssc;
};
struct rrc_cfg_t {
uint32_t enb_id; ///< Required to pack SIB1
// Per eNB SIBs
@ -79,6 +91,7 @@ struct rrc_cfg_t {
uint32_t rlf_release_timer_ms;
srb_cfg_t srb1_cfg;
srb_cfg_t srb2_cfg;
rrc_endc_cfg_t endc_cfg;
};
constexpr uint32_t UE_PCELL_CC_IDX = 0;

@ -57,15 +57,6 @@ public:
uint16_t nr_rnti; /// RNTI assigned to UE on NR carrier
};
// Parameter of the (NR)-carrier required for NR cell measurement handling
struct rrc_endc_cfg_t {
bool act_from_b1_event = true; // ENDC will only be activated from B1 measurment
uint32_t nr_dl_arfcn = 634176;
uint32_t nr_band = 78;
asn1::rrc::rs_cfg_ssb_nr_r15_s::subcarrier_spacing_ssb_r15_e_ ssb_ssc =
asn1::rrc::rs_cfg_ssb_nr_r15_s::subcarrier_spacing_ssb_r15_opts::khz15;
};
rrc_endc(srsenb::rrc::ue* outer_ue, const rrc_endc_cfg_t& endc_cfg_);
bool fill_conn_recfg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn_recfg);

@ -23,9 +23,9 @@
#define SRSENB_RRC_NR_H
#include "rrc_config_common.h"
#include "rrc_config_nr.h"
#include "rrc_metrics.h"
#include "srsenb/hdr/stack/enb_stack_base.h"
#include "srsenb/hdr/stack/rrc/nr/rrc_config_nr.h"
#include "srsran/asn1/rrc_nr.h"
#include "srsran/common/block_queue.h"
#include "srsran/common/buffer_pool.h"
@ -120,24 +120,73 @@ public:
void crnti_ce_received();
// getters
bool is_connected() { return state == rrc_nr_state_t::RRC_CONNECTED; }
bool is_idle() { return state == rrc_nr_state_t::RRC_IDLE; }
bool is_inactive() { return state == rrc_nr_state_t::RRC_INACTIVE; }
bool is_endc() { return endc; }
bool is_connected() { return state == rrc_nr_state_t::RRC_CONNECTED; }
bool is_idle() { return state == rrc_nr_state_t::RRC_IDLE; }
bool is_inactive() { return state == rrc_nr_state_t::RRC_INACTIVE; }
bool is_endc() { return endc; }
uint16_t get_eutra_rnti() { return eutra_rnti; }
void get_metrics(rrc_ue_metrics_t& ue_metrics) { ue_metrics = {}; /*TODO fill RRC metrics*/ };
// setters
int pack_rrc_reconfiguraiton();
int pack_rrc_reconfiguration();
private:
rrc_nr* parent = nullptr;
uint16_t rnti = SRSRAN_INVALID_RNTI;
int pack_rrc_reconfiguraiton(asn1::dyn_octstring& packed_rrc_reconfig);
int pack_secondary_cell_group_config_common(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_secondary_cell_group_config_fdd(asn1::dyn_octstring& packed_secondary_cell_config);
int pack_secondary_cell_group_config_tdd(asn1::dyn_octstring& packed_secondary_cell_config);
int pack_rrc_reconfiguration(asn1::dyn_octstring& packed_rrc_reconfig);
int pack_secondary_cell_group_cfg(asn1::dyn_octstring& packed_secondary_cell_config);
int pack_secondary_cell_group_rlc_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_secondary_cell_group_mac_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_secondary_cell_group_sp_cell_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_init_dl_bwp(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_init_dl_bwp_pdcch_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_init_dl_bwp_pdsch_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_ul_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_ul_cfg_init_ul_bwp(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_ul_cfg_init_ul_bwp_pucch_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_ul_cfg_init_ul_bwp_pusch_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_pdcch_serving_cell_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_csi_meas_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_csi_meas_cfg_csi_report_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_common(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_common_freq_info_dl(
asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_common_phy_cell_group_cfg(
asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp_pdcch_cfg_common(
asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp_pdsch_cfg_common(
asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common_freq_info_ul(
asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common_init_ul_bwp(
asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common_init_ul_bwp_rach_cfg_common(
asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common_init_ul_bwp_pusch_cfg_common(
asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_ssb_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int
pack_recfg_with_sync_sp_cell_cfg_common_tdd_ul_dl_cfg_common(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_nr_radio_bearer_config(asn1::dyn_octstring& packed_nr_bearer_config);
int add_drb();

@ -37,11 +37,11 @@ namespace srsenb {
class paging_manager
{
public:
paging_manager(uint32_t default_paging_cycle_, uint32_t nb_) :
paging_manager(uint32_t default_paging_cycle_, float nb_) :
T(default_paging_cycle_),
Nb(T * nb_),
Nb(static_cast<uint32_t>((float)T * nb_)),
N(std::min(T, Nb)),
Ns(std::max(nb_, 1u)),
Ns(std::max(1U, Nb)),
logger(srslog::fetch_basic_logger("RRC"))
{
for (subframe_info& sf_obj : sf_pending_pcch) {

@ -102,15 +102,6 @@ public:
eutra_stack->set_activity_user(eutra_rnti);
}
// stack_nr_interface_stack_eutra
void tti_clock()
{
if (nr_stack == nullptr) {
return;
}
nr_stack->tti_clock();
}
// pdcp_interface_gtpu
void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu, int pdcp_sn = -1)
{

@ -211,6 +211,9 @@ bool enb::get_metrics(enb_metrics_t* m)
if (eutra_stack) {
eutra_stack->get_metrics(&m->stack);
}
if (nr_stack) {
nr_stack->get_metrics(&m->nr_stack);
}
m->running = true;
m->sys = sys_proc.get_metrics();
return true;

@ -981,6 +981,7 @@ static int parse_nr_cell_list(all_args_t* args, rrc_nr_cfg_t* rrc_cfg_nr, rrc_cf
cell_cfg.phy_cell.carrier.pci = cell_cfg.phy_cell.carrier.pci % SRSRAN_NOF_NID_NR;
HANDLEPARSERCODE(parse_required_field(cell_cfg.dl_arfcn, cellroot, "dl_arfcn"));
parse_opt_field(cell_cfg.ul_arfcn, cellroot, "ul_arfcn");
HANDLEPARSERCODE(parse_required_field(cell_cfg.band, cellroot, "band"));
// frequencies get derived from ARFCN
// TODO: Add further cell-specific parameters
@ -988,6 +989,7 @@ static int parse_nr_cell_list(all_args_t* args, rrc_nr_cfg_t* rrc_cfg_nr, rrc_cf
rrc_cfg_nr->cell_list.push_back(cell_cfg);
}
srsran::srsran_band_helper band_helper;
// Configuration check
for (auto it = rrc_cfg_nr->cell_list.begin(); it != rrc_cfg_nr->cell_list.end(); ++it) {
// check against NR cells
@ -1019,6 +1021,34 @@ static int parse_nr_cell_list(all_args_t* args, rrc_nr_cfg_t* rrc_cfg_nr, rrc_cf
return SRSRAN_ERROR;
}
}
// Check if dl_arfcn is valid for the given band
bool dl_arfcn_valid = false;
std::vector<uint32_t> bands = band_helper.get_bands_nr(it->dl_arfcn);
for (uint32_t band_idx = 0; band_idx < bands.size(); band_idx++) {
if (bands.at(band_idx) == it->band) {
dl_arfcn_valid = true;
}
}
if (!dl_arfcn_valid) {
ERROR("DL ARFCN (%d) is not valid for the specified band (%d)", it->dl_arfcn, it->band);
return SRSRAN_ERROR;
}
if (it->ul_arfcn != 0) {
// Check if ul_arfcn is valid for the given band
bool ul_arfcn_valid = false;
std::vector<uint32_t> bands = band_helper.get_bands_nr(it->ul_arfcn);
for (uint32_t band_idx = 0; band_idx < bands.size(); band_idx++) {
if (bands.at(band_idx) == it->band) {
ul_arfcn_valid = true;
}
}
if (!ul_arfcn_valid) {
ERROR("UL ARFCN (%d) is not valid for the specified band (%d)", it->ul_arfcn, it->band);
return SRSRAN_ERROR;
}
}
}
return SRSRAN_SUCCESS;
@ -1144,6 +1174,16 @@ int parse_cfg_files(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr
rrc_cfg_->num_nr_cells = rrc_nr_cfg_->cell_list.size();
args_->rf.nof_carriers = rrc_cfg_->cell_list.size() + rrc_nr_cfg_->cell_list.size();
// update EUTRA RRC params for ENDC
if (rrc_nr_cfg_->cell_list.size() == 1) {
rrc_cfg_->endc_cfg.nr_dl_arfcn = rrc_nr_cfg_->cell_list.at(0).dl_arfcn;
rrc_cfg_->endc_cfg.nr_band = rrc_nr_cfg_->cell_list.at(0).band;
rrc_cfg_->endc_cfg.ssb_period_offset.set_sf10_r15();
rrc_cfg_->endc_cfg.ssb_duration = asn1::rrc::mtc_ssb_nr_r15_s::ssb_dur_r15_opts::sf1;
rrc_cfg_->endc_cfg.ssb_ssc = asn1::rrc::rs_cfg_ssb_nr_r15_s::subcarrier_spacing_ssb_r15_opts::khz15;
rrc_cfg_->endc_cfg.act_from_b1_event = true; // ENDC will only be activated from B1 measurment
}
return SRSRAN_SUCCESS;
}
@ -1403,6 +1443,12 @@ int set_derived_args_nr(all_args_t* args_, rrc_nr_cfg_t* rrc_cfg_, phy_cfg_t* ph
// Use helper class to derive NR carrier parameters
srsran::srsran_band_helper band_helper;
// we only support one NR cell
if (rrc_cfg_->cell_list.size() > 1) {
ERROR("Only a single NR cell supported.");
return SRSRAN_ERROR;
}
// Create NR dedicated cell configuration from RRC configuration
for (auto it = rrc_cfg_->cell_list.begin(); it != rrc_cfg_->cell_list.end(); ++it) {
auto& cfg = *it;
@ -1443,12 +1489,78 @@ int set_derived_args_nr(all_args_t* args_, rrc_nr_cfg_t* rrc_cfg_, phy_cfg_t* ph
cfg.phy_cell.ul_freq_hz = band_helper.nr_arfcn_to_freq(cfg.ul_arfcn);
}
// band
cfg.band = band_helper.get_band_from_dl_arfcn(cfg.dl_arfcn);
// duplex mode
cfg.duplex_mode = band_helper.get_duplex_mode(cfg.band);
// PRACH
cfg.phy_cell.prach.is_nr = true;
cfg.phy_cell.prach.config_idx = 0;
cfg.phy_cell.prach.root_seq_idx = 0;
cfg.phy_cell.prach.freq_offset = phy_cfg_->prach_cnfg.prach_cfg_info.prach_freq_offset;
cfg.phy_cell.prach.num_ra_preambles = cfg.phy_cell.num_ra_preambles;
cfg.phy_cell.prach.hs_flag = phy_cfg_->prach_cnfg.prach_cfg_info.high_speed_flag;
cfg.phy_cell.prach.tdd_config.configured = (cfg.duplex_mode == SRSRAN_DUPLEX_MODE_TDD);
// PDCCH
// Configure CORESET ID 1
cfg.phy_cell.pdcch.coreset_present[1] = true;
cfg.phy_cell.pdcch.coreset[1].id = 1;
cfg.phy_cell.pdcch.coreset[1].duration = 1;
cfg.phy_cell.pdcch.coreset[1].mapping_type = srsran_coreset_mapping_type_non_interleaved;
cfg.phy_cell.pdcch.coreset[1].precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle;
// Generate frequency resources for the full BW
for (uint32_t i = 0; i < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; i++) {
cfg.phy_cell.pdcch.coreset[1].freq_resources[i] = i < SRSRAN_FLOOR(cfg.phy_cell.carrier.nof_prb, 6);
}
// Configure Search Space 1 as common
cfg.phy_cell.pdcch.search_space_present[1] = true;
cfg.phy_cell.pdcch.search_space[1].id = 1;
cfg.phy_cell.pdcch.search_space[1].coreset_id = 1;
cfg.phy_cell.pdcch.search_space[1].duration = 1;
cfg.phy_cell.pdcch.search_space[1].formats[0] = srsran_dci_format_nr_0_0; // DCI format for PUSCH
cfg.phy_cell.pdcch.search_space[1].formats[1] = srsran_dci_format_nr_1_0; // DCI format for PDSCH
cfg.phy_cell.pdcch.search_space[1].nof_formats = 2;
cfg.phy_cell.pdcch.search_space[1].type = srsran_search_space_type_common_3;
// Generate 1 candidate for each aggregation level if possible
for (uint32_t L = 0; L < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR; L++) {
cfg.phy_cell.pdcch.search_space[1].nof_candidates[L] =
SRSRAN_MIN(2, srsran_pdcch_nr_max_candidates_coreset(&cfg.phy_cell.pdcch.coreset[1], L));
}
cfg.phy_cell.pdcch.ra_search_space_present = true;
cfg.phy_cell.pdcch.ra_search_space = cfg.phy_cell.pdcch.search_space[1];
cfg.phy_cell.pdcch.ra_search_space.type = srsran_search_space_type_common_1;
// copy center frequencies
cfg.phy_cell.carrier.dl_center_frequency_hz = cfg.phy_cell.dl_freq_hz;
cfg.phy_cell.carrier.ul_center_frequency_hz = cfg.phy_cell.ul_freq_hz;
cfg.dl_absolute_freq_point_a = band_helper.get_abs_freq_point_a_arfcn(cfg.phy_cell.carrier.nof_prb, cfg.dl_arfcn);
cfg.ul_absolute_freq_point_a = band_helper.get_abs_freq_point_a_arfcn(cfg.phy_cell.carrier.nof_prb, cfg.ul_arfcn);
// Calculate SSB absolute frequency (in ARFCN notation)
if (band_helper.get_ssb_pattern(cfg.band, srsran_subcarrier_spacing_15kHz) != SRSRAN_SSB_PATTERN_INVALID) {
cfg.ssb_absolute_freq_point =
band_helper.get_abs_freq_ssb_arfcn(cfg.band, srsran_subcarrier_spacing_15kHz, cfg.dl_absolute_freq_point_a);
} else {
cfg.ssb_absolute_freq_point =
band_helper.get_abs_freq_ssb_arfcn(cfg.band, srsran_subcarrier_spacing_30kHz, cfg.dl_absolute_freq_point_a);
}
if (cfg.ssb_absolute_freq_point == 0) {
ERROR("Can't derive SSB freq point for dl_arfcn %d and band %d", cfg.dl_arfcn, cfg.band);
return SRSRAN_ERROR;
}
// Convert to frequency for PHY
cfg.phy_cell.carrier.ssb_center_freq_hz = band_helper.nr_arfcn_to_freq(cfg.ssb_absolute_freq_point);
// TODO: set SSB config
cfg.ssb_cfg = {};
phy_cfg_->phy_cell_cfg_nr.push_back(cfg.phy_cell);
}

@ -30,8 +30,8 @@
#include <string.h>
#include <string>
#include "srsenb/hdr/stack/rrc/nr/rrc_config_nr.h"
#include "srsenb/hdr/stack/rrc/rrc.h"
#include "srsenb/hdr/stack/rrc/rrc_config_nr.h"
#include "srsran/asn1/asn1_utils.h"
namespace srsenb {

@ -99,7 +99,7 @@ static void fill_ue_metrics(mset_ue_container& ue, const enb_metrics_t& m, unsig
if (!std::isnan(m.phy[i].dl.mcs)) {
ue.write<metric_dl_mcs>(std::max(0.1f, m.phy[i].dl.mcs));
}
if (m.stack.mac.ues[i].tx_brate > 0) {
if (m.stack.mac.ues[i].tx_brate > 0 && m.stack.mac.ues[i].nof_tti > 0) {
ue.write<metric_dl_bitrate>(
std::max(0.1f, (float)m.stack.mac.ues[i].tx_brate / (m.stack.mac.ues[i].nof_tti * 0.001f)));
}
@ -112,7 +112,7 @@ static void fill_ue_metrics(mset_ue_container& ue, const enb_metrics_t& m, unsig
if (!std::isnan(m.phy[i].ul.mcs)) {
ue.write<metric_ul_mcs>(std::max(0.1f, m.phy[i].ul.mcs));
}
if (m.stack.mac.ues[i].rx_brate > 0) {
if (m.stack.mac.ues[i].rx_brate > 0 && m.stack.mac.ues[i].nof_tti > 0) {
ue.write<metric_ul_bitrate>(
std::max(0.1f, (float)m.stack.mac.ues[i].rx_brate / (m.stack.mac.ues[i].nof_tti * 0.001f)));
}

@ -82,11 +82,12 @@ static bool iszero(float x)
void metrics_stdout::set_metrics_helper(uint32_t num_ue,
const mac_metrics_t& mac,
const std::vector<phy_metrics_t>& phy)
const std::vector<phy_metrics_t>& phy,
bool is_nr)
{
for (size_t i = 0; i < num_ue; i++) {
// make sure we have stats for MAC and PHY layer too
if (i >= mac.ues.size() || i >= phy.size()) {
if (i >= mac.ues.size() || ((i >= phy.size()) && !is_nr)) {
break;
}
if (mac.ues[i].tx_errors > mac.ues[i].tx_pkts) {
@ -96,15 +97,17 @@ void metrics_stdout::set_metrics_helper(uint32_t num_ue
fmt::print("rx caution errors {} > {}\n", mac.ues[i].rx_errors, mac.ues[i].rx_pkts);
}
fmt::print("{:>4x}", mac.ues[i].rnti);
fmt::print("{:>3.5}", (is_nr) ? "nr" : "lte");
fmt::print("{:>5x}", mac.ues[i].rnti);
if (not iszero(mac.ues[i].dl_cqi)) {
fmt::print(" {:>3}", int(mac.ues[i].dl_cqi));
} else {
fmt::print(" {:>3.3}", "n/a");
}
fmt::print(" {:>1}", int(mac.ues[i].dl_ri));
if (not isnan(phy[i].dl.mcs)) {
fmt::print(" {:>2}", int(phy[i].dl.mcs));
float dl_mcs = (is_nr) ? mac.ues[i].dl_mcs : phy[i].dl.mcs;
if (not isnan(dl_mcs)) {
fmt::print(" {:>2}", int(dl_mcs));
} else {
fmt::print(" {:>2}", 0);
}
@ -132,22 +135,22 @@ void metrics_stdout::set_metrics_helper(uint32_t num_ue
}
return sinr;
};
if (not isnan(phy[i].ul.pusch_sinr) and not iszero(phy[i].ul.pusch_sinr)) {
fmt::print(" {:>5.1f}", clamp_sinr(phy[i].ul.pusch_sinr));
float pusch_sinr = (is_nr) ? mac.ues[i].pusch_sinr : phy[i].ul.pusch_sinr;
if (not isnan(pusch_sinr) and not iszero(pusch_sinr)) {
fmt::print(" {:>5.1f}", clamp_sinr(pusch_sinr));
} else {
fmt::print(" {:>5.5}", "n/a");
}
if (not isnan(phy[i].ul.pucch_sinr) and not iszero(phy[i].ul.pucch_sinr)) {
fmt::print(" {:>5.1f}", clamp_sinr(phy[i].ul.pucch_sinr));
float pucch_sinr = (is_nr) ? mac.ues[i].pucch_sinr : phy[i].ul.pucch_sinr;
if (not isnan(pucch_sinr) and not iszero(pucch_sinr)) {
fmt::print(" {:>5.1f}", clamp_sinr(pucch_sinr));
} else {
fmt::print(" {:>5.5}", "n/a");
}
fmt::print(" {:>3}", int(mac.ues[i].phr));
if (not isnan(phy[i].ul.mcs)) {
fmt::print(" {:>2}", int(phy[i].ul.mcs));
int phr = (is_nr) ? mac.ues[i].phr : mac.ues[i].phr;
fmt::print(" {:>3}", int(phr));
if (not isnan(phr)) {
fmt::print(" {:>2}", int(phr));
} else {
fmt::print(" {:>2}", 0);
}
@ -186,11 +189,12 @@ void metrics_stdout::set_metrics(const enb_metrics_t& metrics, const uint32_t pe
if (++n_reports > 10) {
n_reports = 0;
fmt::print("\n");
fmt::print(" -----------------DL----------------|-------------------------UL-------------------------\n");
fmt::print("rnti cqi ri mcs brate ok nok (%) | pusch pucch phr mcs brate ok nok (%) bsr\n");
fmt::print(" -----------------DL----------------|-------------------------UL-------------------------\n");
fmt::print("rat rnti cqi ri mcs brate ok nok (%) | pusch pucch phr mcs brate ok nok (%) bsr\n");
}
set_metrics_helper(metrics.stack.rrc.ues.size(), metrics.stack.mac, metrics.phy);
set_metrics_helper(metrics.stack.rrc.ues.size(), metrics.stack.mac, metrics.phy, false);
set_metrics_helper(metrics.nr_stack.mac.ues.size(), metrics.nr_stack.mac, metrics.phy, true);
}
std::string metrics_stdout::float_to_string(float f, int digits, int field_width)

@ -27,8 +27,9 @@ namespace srsenb {
namespace nr {
slot_worker::slot_worker(srsran::phy_common_interface& common_,
stack_interface_phy_nr& stack_,
sync_interface& sync_,
srslog::basic_logger& logger_) :
common(common_), stack(stack_), logger(logger_)
common(common_), stack(stack_), sync(sync_), logger(logger_)
{
// Do nothing
}
@ -38,7 +39,7 @@ bool slot_worker::init(const args_t& args)
std::lock_guard<std::mutex> lock(mutex);
// Calculate subframe length
sf_len = SRSRAN_SF_LEN_PRB_NR(args.nof_max_prb);
sf_len = (uint32_t)(args.srate_hz / 1000.0);
// Copy common configurations
cell_index = args.cell_index;
@ -71,6 +72,7 @@ bool slot_worker::init(const args_t& args)
dl_args.pdsch.max_prb = args.nof_max_prb;
dl_args.nof_tx_antennas = args.nof_tx_ports;
dl_args.nof_max_prb = args.nof_max_prb;
dl_args.srate_hz = args.srate_hz;
// Initialise DL
if (srsran_gnb_dl_init(&gnb_dl, tx_buffer.data(), &dl_args) < SRSRAN_SUCCESS) {
@ -251,9 +253,18 @@ bool slot_worker::work_ul()
bool slot_worker::work_dl()
{
// The Scheduler interface needs to be called synchronously, wait for the sync to be available
sync.wait(this);
// Retrieve Scheduling for the current processing DL slot
stack_interface_phy_nr::dl_sched_t dl_sched = {};
if (stack.get_dl_sched(dl_slot_cfg, dl_sched) < SRSRAN_SUCCESS) {
stack_interface_phy_nr::dl_sched_t dl_sched = {};
bool dl_sched_fail = stack.get_dl_sched(dl_slot_cfg, dl_sched) < SRSRAN_SUCCESS;
// Releases synchronization lock and allow next worker to retrieve scheduling results
sync.release();
// Abort if the scheduling failed
if (dl_sched_fail) {
logger.error("Error retrieving DL scheduling");
return false;
}
@ -351,7 +362,10 @@ bool slot_worker::work_dl()
// Add SSB to the baseband signal
for (const stack_interface_phy_nr::ssb_t& ssb : dl_sched.ssb) {
// ...
if (srsran_gnb_dl_add_ssb(&gnb_dl, &ssb.pbch_msg, dl_slot_cfg.idx) < SRSRAN_SUCCESS) {
logger.error("SSB: Error putting signal");
return false;
}
}
return true;
@ -372,6 +386,9 @@ void slot_worker::work_imp()
// Process uplink
if (not work_ul()) {
// Wait and release synchronization
sync.wait(this);
sync.release();
common.worker_end(context, false, tx_rf_buffer);
return;
}
@ -384,7 +401,9 @@ void slot_worker::work_imp()
common.worker_end(context, true, tx_rf_buffer);
}
bool slot_worker::set_common_cfg(const srsran_carrier_nr_t& carrier, const srsran_pdcch_cfg_nr_t& pdcch_cfg_)
bool slot_worker::set_common_cfg(const srsran_carrier_nr_t& carrier,
const srsran_pdcch_cfg_nr_t& pdcch_cfg_,
const srsran_ssb_cfg_t& ssb_cfg_)
{
// Set gNb DL carrier
if (srsran_gnb_dl_set_carrier(&gnb_dl, &carrier) < SRSRAN_SUCCESS) {
@ -392,6 +411,12 @@ bool slot_worker::set_common_cfg(const srsran_carrier_nr_t& carrier, const srsra
return false;
}
// Configure SSB
if (srsran_gnb_dl_set_ssb_config(&gnb_dl, &ssb_cfg_) < SRSRAN_SUCCESS) {
logger.error("Error setting SSB");
return false;
}
// Set gNb UL carrier
if (srsran_gnb_ul_set_carrier(&gnb_ul, &carrier) < SRSRAN_SUCCESS) {
logger.error("Error setting UL carrier (pci=%d, nof_prb=%d, max_mimo_layers=%d)",

@ -19,6 +19,7 @@
*
*/
#include "srsenb/hdr/phy/nr/worker_pool.h"
#include "srsran/common/band_helper.h"
namespace srsenb {
namespace nr {
@ -41,6 +42,13 @@ bool worker_pool::init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_li
{
nof_prach_workers = args.nof_prach_workers;
// Calculate sampling rate in Hz
if (not std::isnormal(args.srate_hz)) {
srate_hz = SRSRAN_SUBC_SPACING_NR(cell_list[0].carrier.scs) * srsran_min_symbol_sz_rb(cell_list[0].carrier.nof_prb);
} else {
srate_hz = args.srate_hz;
}
// Configure logger
srslog::basic_levels log_level = srslog::str_to_basic_level(args.log.phy_level);
logger.set_level(log_level);
@ -51,7 +59,7 @@ bool worker_pool::init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_li
log.set_level(log_level);
log.set_hex_dump_max_size(args.log.phy_hex_limit);
auto w = new slot_worker(common, stack, log);
auto w = new slot_worker(common, stack, *this, log);
pool.init_worker(i, w, args.prio);
workers.push_back(std::unique_ptr<slot_worker>(w));
@ -62,6 +70,7 @@ bool worker_pool::init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_li
w_args.nof_tx_ports = cell_list[cell_index].carrier.max_mimo_layers;
w_args.nof_rx_ports = cell_list[cell_index].carrier.max_mimo_layers;
w_args.rf_port = cell_list[cell_index].rf_port;
w_args.srate_hz = srate_hz;
w_args.pusch_max_nof_iter = args.pusch_max_nof_iter;
if (not w->init(w_args)) {
@ -74,6 +83,9 @@ bool worker_pool::init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_li
void worker_pool::start_worker(slot_worker* w)
{
// Push worker into synchronization queue
slot_sync.push(w);
// Feed PRACH detection before start processing
prach.new_tti(0, current_tti, w->get_buffer_rx(0));
@ -89,16 +101,18 @@ slot_worker* worker_pool::wait_worker(uint32_t tti)
if (w != nullptr) {
srsran_carrier_nr_t carrier_;
srsran_pdcch_cfg_nr_t pdcch_cfg_;
srsran_ssb_cfg_t ssb_cfg_;
// Copy configuration
{
std::unique_lock<std::mutex> lock(common_cfg_mutex);
carrier_ = carrier;
pdcch_cfg_ = pdcch_cfg;
ssb_cfg_ = ssb_cfg;
}
// Set worker configuration
if (not w->set_common_cfg(carrier_, pdcch_cfg_)) {
if (not w->set_common_cfg(carrier_, pdcch_cfg_, ssb_cfg_)) {
logger.error("Error setting common config");
return nullptr;
}
@ -150,8 +164,12 @@ int worker_pool::set_common_cfg(const phy_interface_rrc_nr::common_cfg_t& common
// Save current configuration
{
std::unique_lock<std::mutex> lock(common_cfg_mutex);
carrier = common_cfg.carrier;
pdcch_cfg = common_cfg.pdcch;
carrier = common_cfg.carrier;
pdcch_cfg = common_cfg.pdcch;
ssb_cfg = common_cfg.ssb;
ssb_cfg.srate_hz = srate_hz;
ssb_cfg.scaling =
srsran_convert_dB_to_amplitude(srsran_gnb_dl_get_maximum_signal_power_dBfs(common_cfg.carrier.nof_prb));
}
return SRSRAN_SUCCESS;

@ -339,6 +339,7 @@ int phy::init_nr(const phy_args_t& args, const phy_cfg_t& cfg, stack_interface_p
nr_workers = std::unique_ptr<nr::worker_pool>(new nr::worker_pool(workers_common, stack, log_sink, MAX_WORKERS));
nr::worker_pool::args_t worker_args = {};
worker_args.nof_phy_threads = args.nof_phy_threads;
worker_args.log.phy_level = args.log.phy_level;
worker_args.log.phy_hex_limit = args.log.phy_hex_limit;
@ -348,17 +349,7 @@ int phy::init_nr(const phy_args_t& args, const phy_cfg_t& cfg, stack_interface_p
tx_rx.set_nr_workers(nr_workers.get());
srsran::srsran_band_helper band_helper;
// perform initial config of PHY (during RRC init PHY isn't running yet)
static const srsran::phy_cfg_nr_t default_phy_cfg =
srsran::phy_cfg_nr_default_t{srsran::phy_cfg_nr_default_t::reference_cfg_t{}};
srsenb::phy_interface_rrc_nr::common_cfg_t common_cfg = {};
common_cfg.carrier = default_phy_cfg.carrier;
common_cfg.pdcch = default_phy_cfg.pdcch;
common_cfg.prach = default_phy_cfg.prach;
common_cfg.duplex_mode = SRSRAN_DUPLEX_MODE_TDD; // TODO: make dynamic
if (set_common_cfg(common_cfg) < SRSRAN_SUCCESS) {
if (nr_workers->set_common_cfg(common_cfg)) {
phy_log.error("Couldn't set common PHY config");
return SRSRAN_ERROR;
}
@ -366,10 +357,12 @@ int phy::init_nr(const phy_args_t& args, const phy_cfg_t& cfg, stack_interface_p
return SRSRAN_SUCCESS;
}
int phy::set_common_cfg(const phy_interface_rrc_nr::common_cfg_t& common_cfg)
int phy::set_common_cfg(const phy_interface_rrc_nr::common_cfg_t& common_cfg_)
{
if (nr_workers.get() == nullptr) {
return SRSRAN_ERROR;
// if nr_workers are not initialized yet, store the configuration in the phy
common_cfg = common_cfg_;
return SRSRAN_SUCCESS;
}
return nr_workers->set_common_cfg(common_cfg);

@ -126,6 +126,10 @@ void phy_common::worker_end(const worker_context_t& w_ctx, const bool& tx_enable
// If the current worker is not the last one, skip transmission
if (not w_ctx.last) {
if (tx_enable) {
reset_last_worker();
}
// Release semaphore and let next worker to get in
semaphore.release();

@ -173,6 +173,11 @@ int enb_stack_lte::init(const stack_args_t& args_,
// add sync queue
sync_task_queue = task_sched.make_task_queue(args.sync_queue_size);
// add x2 queue
if (x2_ != nullptr) {
x2_task_queue = task_sched.make_task_queue();
}
// setup bearer managers
gtpu_adapter.reset(new gtpu_pdcp_adapter(stack_logger, &pdcp, x2_, &gtpu, bearers));
@ -295,7 +300,10 @@ void enb_stack_lte::run_thread()
void enb_stack_lte::write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu)
{
// call GTPU adapter to map to EPS bearer
gtpu_adapter->write_pdu(rnti, lcid, std::move(pdu));
auto task = [this, rnti, lcid](srsran::unique_byte_buffer_t& pdu) {
gtpu_adapter->write_pdu(rnti, lcid, std::move(pdu));
};
x2_task_queue.push(std::bind(task, std::move(pdu)));
}
} // namespace srsenb

@ -41,7 +41,7 @@ gnb_stack_nr::gnb_stack_nr(srslog::sink& log_sink) :
{
ue_task_queue = task_sched.make_task_queue();
sync_task_queue = task_sched.make_task_queue();
gw_task_queue = task_sched.make_task_queue();
gtpu_task_queue = task_sched.make_task_queue();
mac_task_queue = task_sched.make_task_queue();
}

@ -54,10 +54,11 @@ void ue_buffer_manager<isNR>::config_lcid(uint32_t lcid, const mac_lc_ch_cfg_t&
channels[lcid].bucket_size = channels[lcid].cfg.bsd * channels[lcid].cfg.pbr;
channels[lcid].Bj = 0;
}
logger.info("SCHED: bearer configured: lcid=%d, mode=%s, prio=%d",
logger.info("SCHED: bearer configured: lcid=%d, mode=%s, prio=%d, lcg=%d",
lcid,
to_string(channels[lcid].cfg.direction),
channels[lcid].cfg.priority);
channels[lcid].cfg.priority,
channels[lcid].cfg.group);
}
}

@ -284,12 +284,7 @@ int mac_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched
{
logger.set_context(slot_cfg.idx);
if (not pdsch_slot.valid()) {
pdsch_slot = srsran::slot_point{NUMEROLOGY_IDX, slot_cfg.idx};
} else {
pdsch_slot++;
}
slot_point pdsch_slot = srsran::slot_point{NUMEROLOGY_IDX, slot_cfg.idx};
sched_nr_interface::dl_sched_res_t dl_res;
int ret = sched.get_dl_sched(pdsch_slot, 0, dl_res);
if (ret != SRSRAN_SUCCESS) {
@ -314,6 +309,7 @@ int mac_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched
uint32_t pid = 0; // TODO: get PID from PDCCH struct?
pcap->write_dl_crnti_nr(tb_data->msg, tb_data->N_bytes, rnti, pid, slot_cfg.idx);
}
ue_db[rnti]->metrics_dl_mcs(pdsch.sch.grant.tb->mcs);
}
}
} else if (pdsch.sch.grant.rnti_type == srsran_rnti_type_ra) {
@ -322,18 +318,24 @@ int mac_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched
pdsch.data[0] = assemble_rar(rar.grants);
}
}
for (auto& u : ue_db) {
u.second->metrics_cnt();
}
return SRSRAN_SUCCESS;
}
int mac_nr::get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched)
{
if (not pusch_slot.valid()) {
pusch_slot = srsran::slot_point{NUMEROLOGY_IDX, slot_cfg.idx};
} else {
pusch_slot++;
}
int ret = 0;
return sched.get_ul_sched(pusch_slot, 0, ul_sched);
slot_point pusch_slot = srsran::slot_point{NUMEROLOGY_IDX, slot_cfg.idx};
ret = sched.get_ul_sched(pusch_slot, 0, ul_sched);
for (auto& pusch : ul_sched.pusch) {
if (ue_db.contains(pusch.sch.grant.rnti)) {
ue_db[pusch.sch.grant.rnti]->metrics_ul_mcs(pusch.sch.grant.tb->mcs);
}
}
return ret;
}
int mac_nr::pucch_info(const srsran_slot_cfg_t& slot_cfg, const mac_interface_phy_nr::pucch_info_t& pucch_info)
@ -352,6 +354,9 @@ bool mac_nr::handle_uci_data(const uint16_t rnti, const srsran_uci_cfg_nr_t& cfg
const srsran_harq_ack_bit_t* ack_bit = &cfg_.ack.bits[i];
bool is_ok = (value.ack[i] == 1) and value.valid;
sched.dl_ack_info(rnti, 0, ack_bit->pid, 0, is_ok);
if (ue_db.contains(rnti)) {
ue_db[rnti]->metrics_tx(is_ok, 0 /*TODO get size of packet from scheduler somehow*/);
}
}
// Process SR
@ -363,8 +368,8 @@ bool mac_nr::handle_uci_data(const uint16_t rnti, const srsran_uci_cfg_nr_t& cfg
int mac_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, mac_interface_phy_nr::pusch_info_t& pusch_info)
{
uint16_t rnti = pusch_info.rnti;
uint16_t rnti = pusch_info.rnti;
uint32_t nof_bytes = pusch_info.pdu->N_bytes;
// Handle UCI data
if (not handle_uci_data(rnti, pusch_info.uci_cfg, pusch_info.pusch_data.uci)) {
logger.error("Error handling UCI data from PUCCH reception");
@ -390,7 +395,10 @@ int mac_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, mac_interface_phy_nr::
};
stack_task_queue.try_push(std::bind(process_pdu_task, std::move(pusch_info.pdu)));
}
if (ue_db.contains(rnti)) {
ue_db[rnti]->metrics_rx(pusch_info.pusch_data.tb[0].crc, nof_bytes);
ue_db[rnti]->metrics_dl_cqi(15); // TODO extract correct CQI measurments
}
return SRSRAN_SUCCESS;
}

@ -164,13 +164,20 @@ int sched_nr::dl_rach_info(uint32_t cc, const dl_sched_rar_info_t& rar_info)
void sched_nr::dl_ack_info(uint16_t rnti, uint32_t cc, uint32_t pid, uint32_t tb_idx, bool ack)
{
sched_workers->enqueue_cc_feedback(
rnti, cc, [pid, tb_idx, ack](ue_carrier& ue_cc) { ue_cc.harq_ent.dl_ack_info(pid, tb_idx, ack); });
sched_workers->enqueue_cc_feedback(rnti, cc, [this, pid, tb_idx, ack](ue_carrier& ue_cc) {
if (ue_cc.harq_ent.dl_ack_info(pid, tb_idx, ack) != SRSRAN_SUCCESS) {
logger->warning("SCHED: rnti=0x%x, received DL HARQ-ACK for empty pid=%d", ue_cc.rnti, pid);
}
});
}
void sched_nr::ul_crc_info(uint16_t rnti, uint32_t cc, uint32_t pid, bool crc)
{
sched_workers->enqueue_cc_feedback(rnti, cc, [pid, crc](ue_carrier& ue_cc) { ue_cc.harq_ent.ul_crc_info(pid, crc); });
sched_workers->enqueue_cc_feedback(rnti, cc, [this, pid, crc](ue_carrier& ue_cc) {
if (ue_cc.harq_ent.ul_crc_info(pid, crc) != SRSRAN_SUCCESS) {
logger->warning("SCHED: rnti=0x%x, received CRC for empty pid=%d", ue_cc.rnti, pid);
}
});
}
void sched_nr::ul_sr_info(uint16_t rnti)

@ -153,7 +153,7 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t
bwp_pdcch_slot.pdschs.emplace_back();
pdsch_t& pdsch = bwp_pdcch_slot.pdschs.back();
srsran_slot_cfg_t slot_cfg;
slot_cfg.idx = pdcch_slot.slot_idx();
slot_cfg.idx = pdcch_slot.to_uint();
bool success = phy_cfg.get_pdsch_cfg(slot_cfg, pdcch.dci, pdsch.sch);
srsran_assert(success, "Error converting DCI to grant");
pdsch.sch.grant.tb[0].softbuffer.tx = bwp_pdcch_slot.rar_softbuffer->get();
@ -161,7 +161,7 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t
// Generate Msg3 grants in PUSCH
uint32_t last_msg3 = msg3_rbs.start();
const int mcs = 0, max_harq_msg3_retx = 4;
slot_cfg.idx = msg3_slot.slot_idx();
slot_cfg.idx = msg3_slot.to_uint();
bwp_pdcch_slot.rar.emplace_back();
sched_nr_interface::sched_rar_t& rar_out = bwp_pdcch_slot.rar.back();
for (const dl_sched_rar_info_t& grant : pending_rars) {
@ -191,6 +191,8 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t
return alloc_result::success;
}
// ue is the UE (1 only) that will be allocated
// func computes the grant allocation for this UE
alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, const prb_grant& dl_grant)
{
if (ue.cfg->active_bwp().bwp_id != bwp_grid.cfg->bwp_id) {
@ -204,14 +206,20 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, const prb_grant& dl_gr
}
bwp_slot_grid& bwp_pdcch_slot = bwp_grid[ue.pdcch_slot];
bwp_slot_grid& bwp_pdsch_slot = bwp_grid[ue.pdsch_slot];
bwp_slot_grid& bwp_uci_slot = bwp_grid[ue.uci_slot];
bwp_slot_grid& bwp_uci_slot = bwp_grid[ue.uci_slot]; // UCI : UL control info
alloc_result result = verify_pdsch_space(bwp_pdsch_slot, bwp_pdcch_slot);
if (result != alloc_result::success) {
return result;
}
if (bwp_pdcch_slot.dl_prbs.collides(dl_grant)) {
if (bwp_pdsch_slot.dl_prbs.collides(dl_grant)) {
return alloc_result::sch_collision;
}
if (not bwp_pdcch_slot.ssb.empty()) {
// TODO: support concurrent PDSCH and SSB
logger.info("SCHED: skipping rnti=0x%x PDSCH allocation. Cause: concurrent PDSCH and SSB not yet supported",
ue.rnti);
return alloc_result::no_sch_space;
}
// Find space in PUCCH
// TODO
@ -259,7 +267,7 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, const prb_grant& dl_gr
bwp_pdsch_slot.pdschs.emplace_back();
pdsch_t& pdsch = bwp_pdsch_slot.pdschs.back();
srsran_slot_cfg_t slot_cfg;
slot_cfg.idx = ue.pdsch_slot.slot_idx();
slot_cfg.idx = ue.pdsch_slot.to_uint();
bool ret = ue.cfg->phy().get_pdsch_cfg(slot_cfg, pdcch.dci, pdsch.sch);
srsran_assert(ret, "Error converting DCI to grant");
pdsch.sch.grant.tb[0].softbuffer.tx = ue.h_dl->get_softbuffer().get();
@ -319,6 +327,7 @@ alloc_result bwp_slot_allocator::alloc_pusch(slot_ue& ue, const prb_grant& ul_pr
pusch_t& pusch = bwp_pusch_slot.puschs.back();
srsran_slot_cfg_t slot_cfg;
slot_cfg.idx = ue.pusch_slot.to_uint();
pusch.pid = ue.h_ul->pid;
bool success = ue.cfg->phy().get_pusch_cfg(slot_cfg, pdcch.dci, pusch.sch);
srsran_assert(success, "Error converting DCI to PUSCH grant");
pusch.sch.grant.tb[0].softbuffer.rx = ue.h_ul->get_softbuffer().get();

@ -25,16 +25,16 @@
namespace srsenb {
namespace sched_nr_impl {
bool harq_proc::ack_info(uint32_t tb_idx, bool ack)
int harq_proc::ack_info(uint32_t tb_idx, bool ack)
{
if (empty(tb_idx)) {
return false;
return SRSRAN_ERROR;
}
tb[tb_idx].ack_state = ack;
if (ack) {
tb[tb_idx].active = false;
}
return true;
return SRSRAN_SUCCESS;
}
void harq_proc::new_slot(slot_point slot_rx)

@ -103,7 +103,7 @@ void fill_dl_dci_ue_fields(const slot_ue& ue,
fill_dci_common(ue, bwp_cfg, dci);
if (dci.ctx.format == srsran_dci_format_nr_1_0) {
dci.harq_feedback = ue.cfg->phy().harq_ack.dl_data_to_ul_ack[ue.pdsch_slot.slot_idx()] - 1;
dci.harq_feedback = (ue.uci_slot - ue.pdsch_slot) - 1;
} else {
dci.harq_feedback = ue.pdsch_slot.slot_idx();
}

@ -21,6 +21,13 @@
#include "srsenb/hdr/stack/mac/nr/sched_nr_signalling.h"
#define POS_IN_BURST_FIRST_BIT_IDX 0
#define POS_IN_BURST_SECOND_BIT_IDX 1
#define POS_IN_BURST_THIRD_BIT_IDX 2
#define POS_IN_BURST_FOURTH_BIT_IDX 3
#define DEFAULT_SSB_PERIODICITY 5
namespace srsenb {
namespace sched_nr_impl {
@ -42,6 +49,41 @@ void sched_nzp_csi_rs(srsran::const_span<srsran_csi_rs_nzp_set_t> nzp_csi_rs_set
}
}
void sched_ssb_basic(const slot_point& sl_point, uint32_t ssb_periodicity, ssb_list& ssb_list)
{
// If the periodicity is 0, it means that the parameter was not passed by the upper layers.
// In that case, we use default value of 5ms (see Clause 4.1, TS 38.213)
if (ssb_periodicity == 0) {
ssb_periodicity = DEFAULT_SSB_PERIODICITY;
}
uint32_t sl_cnt = sl_point.to_uint();
// Perform mod operation of slot index by ssb_periodicity;
// "ssb_periodicity * nof_slots_per_subframe" gives the number of slots in 1 ssb_periodicity time interval
uint32_t sl_point_mod = sl_cnt % (ssb_periodicity * (uint32_t)sl_point.nof_slots_per_subframe());
// code below is simplified, it assumes 15kHz subcarrier spacing and sub 3GHz carrier
if (sl_point_mod == 0) {
ssb_t ssb_msg = {};
srsran_mib_nr_t mib_msg = {};
mib_msg.sfn = sl_point.sfn();
mib_msg.hrf = (sl_point.slot_idx() % SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz) >=
SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz) / 2);
// This corresponds to "Position in Burst" = 1000
mib_msg.ssb_idx = 0;
// Setting the following 4 parameters is redundant, but it makes it explicit what values are passed to PHY
mib_msg.dmrs_typeA_pos = srsran_dmrs_sch_typeA_pos_2;
mib_msg.scs_common = srsran_subcarrier_spacing_15kHz;
mib_msg.coreset0_idx = 0;
mib_msg.ss0_idx = 0;
// Pack mib message to be sent to PHY
int packing_ret_code = srsran_pbch_msg_nr_mib_pack(&mib_msg, &ssb_msg.pbch_msg);
srsran_assert(packing_ret_code == SRSRAN_SUCCESS, "SSB packing returned en error");
ssb_list.push_back(ssb_msg);
}
}
void sched_dl_signalling(const bwp_params& bwp_params,
slot_point sl_pdcch,
ssb_list& ssb_list,
@ -51,7 +93,7 @@ void sched_dl_signalling(const bwp_params& bwp_params,
cfg.idx = sl_pdcch.to_uint();
// Schedule SSB
// TODO
sched_ssb_basic(sl_pdcch, bwp_params.cell_cfg.ssb.periodicity_ms, ssb_list);
// Schedule NZP-CSI-RS
sched_nzp_csi_rs(bwp_params.cfg.pdsch.nzp_csi_rs_sets, cfg, nzp_csi_rs);

@ -57,14 +57,12 @@ slot_ue ue_carrier::try_reserve(slot_point pdcch_slot,
sfu.harq_ent = &harq_ent;
const uint32_t k0 = 0;
sfu.pdsch_slot = sfu.pdcch_slot + k0;
uint32_t k1 =
sfu.cfg->phy()
.harq_ack.dl_data_to_ul_ack[sfu.pdsch_slot.slot_idx() % sfu.cfg->phy().harq_ack.nof_dl_data_to_ul_ack];
sfu.uci_slot = sfu.pdsch_slot + k1;
uint32_t k2 = bwp_cfg.active_bwp().pusch_ra_list[0].K;
sfu.pusch_slot = sfu.pdcch_slot + k2;
sfu.dl_cqi = dl_cqi;
sfu.ul_cqi = ul_cqi;
uint32_t k1 = sfu.cfg->get_k1(sfu.pdsch_slot);
sfu.uci_slot = sfu.pdsch_slot + k1;
uint32_t k2 = bwp_cfg.active_bwp().pusch_ra_list[0].K;
sfu.pusch_slot = sfu.pdcch_slot + k2;
sfu.dl_cqi = dl_cqi;
sfu.ul_cqi = ul_cqi;
// set UE-common parameters
sfu.dl_pending_bytes = dl_pending_bytes;

@ -77,10 +77,10 @@ void slot_cc_worker::run(slot_point pdcch_slot, ue_map_t& ue_db)
srsran_assert(not running(), "scheduler worker::start() called for active worker");
slot_rx = pdcch_slot - TX_ENB_DELAY;
// Run pending cell feedback
// Run pending cell feedback (process feedback)
run_feedback(ue_db);
// Reserve UEs for this worker slot
// Reserve UEs for this worker slot (select candidate UEs)
for (auto& ue_pair : ue_db) {
uint16_t rnti = ue_pair.first;
ue& u = *ue_pair.second;
@ -88,6 +88,7 @@ void slot_cc_worker::run(slot_point pdcch_slot, ue_map_t& ue_db)
continue;
}
// info for a given UE on a slot to be process
slot_ues.insert(rnti, u.try_reserve(pdcch_slot, cfg.cc));
if (slot_ues[rnti].empty()) {
// Failed to generate slot UE because UE has no conditions for DL/UL tx

@ -108,7 +108,14 @@ int ue_nr::process_pdu(srsran::unique_byte_buffer_t pdu)
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::SHORT_TRUNC_BSR: {
srsran::mac_sch_subpdu_nr::lcg_bsr_t sbsr = subpdu.get_sbsr();
uint32_t buffer_size_bytes = buff_size_field_to_bytes(sbsr.buffer_size, srsran::SHORT_BSR);
sched->ul_bsr(rnti, sbsr.lcg_id, buffer_size_bytes);
// Assume all LCGs are 0 if reported SBSR is 0
if (buffer_size_bytes == 0) {
for (uint32_t j = 0; j < SCHED_NR_MAX_LC_GROUP; j++) {
sched->ul_bsr(rnti, j, 0);
}
} else {
sched->ul_bsr(rnti, sbsr.lcg_id, buffer_size_bytes);
}
} break;
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::LONG_BSR:
logger.info("LONG_BSR CE not implemented.");
@ -118,6 +125,7 @@ int ue_nr::process_pdu(srsran::unique_byte_buffer_t pdu)
break;
default:
if (subpdu.is_sdu()) {
rrc->set_activity_user(rnti);
rlc->write_pdu(rnti, subpdu.get_lcid(), subpdu.get_sdu(), subpdu.get_sdu_length());
}
}
@ -186,8 +194,7 @@ void ue_nr::metrics_read(mac_ue_metrics_t* metrics_)
auto it = std::find(cc_list.begin(), cc_list.end(), 0);
ue_metrics.cc_idx = std::distance(cc_list.begin(), it);
*metrics_ = ue_metrics;
*metrics_ = ue_metrics;
phr_counter = 0;
dl_cqi_counter = 0;
ue_metrics = {};
@ -222,6 +229,18 @@ void ue_nr::metrics_tx(bool crc, uint32_t tbs)
ue_metrics.tx_pkts++;
}
void ue_nr::metrics_dl_mcs(uint32_t mcs)
{
ue_metrics.dl_mcs = SRSRAN_VEC_CMA((float)mcs, ue_metrics.dl_mcs, ue_metrics.dl_mcs_samples);
ue_metrics.dl_mcs_samples++;
}
void ue_nr::metrics_ul_mcs(uint32_t mcs)
{
ue_metrics.ul_mcs = SRSRAN_VEC_CMA((float)mcs, ue_metrics.ul_mcs, ue_metrics.ul_mcs_samples);
ue_metrics.ul_mcs_samples++;
}
void ue_nr::metrics_cnt()
{
std::lock_guard<std::mutex> lock(metrics_mutex);

@ -21,5 +21,5 @@
set(SOURCES rrc.cc rrc_ue.cc rrc_mobility.cc rrc_cell_cfg.cc rrc_bearer_cfg.cc mac_controller.cc ue_rr_cfg.cc ue_meas_cfg.cc rrc_endc.cc)
add_library(srsenb_rrc STATIC ${SOURCES})
set(SOURCES rrc_nr.cc)
set(SOURCES rrc_nr.cc nr/cell_asn1_config.cc)
add_library(srsgnb_rrc STATIC ${SOURCES})

@ -0,0 +1,176 @@
/**
*
* \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 "srsenb/hdr/stack/rrc/nr/cell_asn1_config.h"
using namespace asn1::rrc_nr;
namespace srsenb {
int fill_csi_meas_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_meas_cfg)
{
// Fill NZP-CSI Resources
csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list_present = true;
if (cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) {
csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list.resize(5);
auto& nzp_csi_res = csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list;
// item 0
nzp_csi_res[0].nzp_csi_rs_res_id = 0;
nzp_csi_res[0].res_map.freq_domain_alloc.set_row2();
nzp_csi_res[0].res_map.freq_domain_alloc.row2().from_number(0b100000000000);
nzp_csi_res[0].res_map.nrof_ports = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1;
nzp_csi_res[0].res_map.first_ofdm_symbol_in_time_domain = 4;
nzp_csi_res[0].res_map.cdm_type = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm;
nzp_csi_res[0].res_map.density.set_one();
nzp_csi_res[0].res_map.freq_band.start_rb = 0;
nzp_csi_res[0].res_map.freq_band.nrof_rbs = 52;
nzp_csi_res[0].pwr_ctrl_offset = 0;
// Skip pwr_ctrl_offset_ss_present
nzp_csi_res[0].scrambling_id = cfg.cell_list[0].phy_cell.cell_id;
nzp_csi_res[0].periodicity_and_offset_present = true;
nzp_csi_res[0].periodicity_and_offset.set_slots80();
nzp_csi_res[0].periodicity_and_offset.slots80() = 1;
// optional
nzp_csi_res[0].qcl_info_periodic_csi_rs_present = true;
nzp_csi_res[0].qcl_info_periodic_csi_rs = 0;
// item 1
nzp_csi_res[1].nzp_csi_rs_res_id = 1;
nzp_csi_res[1].res_map.freq_domain_alloc.set_row1();
nzp_csi_res[1].res_map.freq_domain_alloc.row1().from_number(0b0001);
nzp_csi_res[1].res_map.nrof_ports = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1;
nzp_csi_res[1].res_map.first_ofdm_symbol_in_time_domain = 4;
nzp_csi_res[1].res_map.cdm_type = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm;
nzp_csi_res[1].res_map.density.set_three();
nzp_csi_res[1].res_map.freq_band.start_rb = 0;
nzp_csi_res[1].res_map.freq_band.nrof_rbs = 52;
nzp_csi_res[1].pwr_ctrl_offset = 0;
// Skip pwr_ctrl_offset_ss_present
nzp_csi_res[1].scrambling_id = cfg.cell_list[0].phy_cell.cell_id;
nzp_csi_res[1].periodicity_and_offset_present = true;
nzp_csi_res[1].periodicity_and_offset.set_slots40();
nzp_csi_res[1].periodicity_and_offset.slots40() = 11;
// optional
nzp_csi_res[1].qcl_info_periodic_csi_rs_present = true;
nzp_csi_res[1].qcl_info_periodic_csi_rs = 0;
// item 2
nzp_csi_res[2].nzp_csi_rs_res_id = 2;
nzp_csi_res[2].res_map.freq_domain_alloc.set_row1();
nzp_csi_res[2].res_map.freq_domain_alloc.row1().from_number(0b0001);
nzp_csi_res[2].res_map.nrof_ports = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1;
nzp_csi_res[2].res_map.first_ofdm_symbol_in_time_domain = 8;
nzp_csi_res[2].res_map.cdm_type = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm;
nzp_csi_res[2].res_map.density.set_three();
nzp_csi_res[2].res_map.freq_band.start_rb = 0;
nzp_csi_res[2].res_map.freq_band.nrof_rbs = 52;
nzp_csi_res[2].pwr_ctrl_offset = 0;
// Skip pwr_ctrl_offset_ss_present
nzp_csi_res[2].scrambling_id = cfg.cell_list[0].phy_cell.cell_id;
nzp_csi_res[2].periodicity_and_offset_present = true;
nzp_csi_res[2].periodicity_and_offset.set_slots40();
nzp_csi_res[2].periodicity_and_offset.slots40() = 11;
// optional
nzp_csi_res[2].qcl_info_periodic_csi_rs_present = true;
nzp_csi_res[2].qcl_info_periodic_csi_rs = 0;
// item 3
nzp_csi_res[3].nzp_csi_rs_res_id = 3;
nzp_csi_res[3].res_map.freq_domain_alloc.set_row1();
nzp_csi_res[3].res_map.freq_domain_alloc.row1().from_number(0b0001);
nzp_csi_res[3].res_map.nrof_ports = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1;
nzp_csi_res[3].res_map.first_ofdm_symbol_in_time_domain = 4;
nzp_csi_res[3].res_map.cdm_type = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm;
nzp_csi_res[3].res_map.density.set_three();
nzp_csi_res[3].res_map.freq_band.start_rb = 0;
nzp_csi_res[3].res_map.freq_band.nrof_rbs = 52;
nzp_csi_res[3].pwr_ctrl_offset = 0;
// Skip pwr_ctrl_offset_ss_present
nzp_csi_res[3].scrambling_id = cfg.cell_list[0].phy_cell.cell_id;
nzp_csi_res[3].periodicity_and_offset_present = true;
nzp_csi_res[3].periodicity_and_offset.set_slots40();
nzp_csi_res[3].periodicity_and_offset.slots40() = 12;
// optional
nzp_csi_res[3].qcl_info_periodic_csi_rs_present = true;
nzp_csi_res[3].qcl_info_periodic_csi_rs = 0;
// item 4
nzp_csi_res[4].nzp_csi_rs_res_id = 4;
nzp_csi_res[4].res_map.freq_domain_alloc.set_row1();
nzp_csi_res[4].res_map.freq_domain_alloc.row1().from_number(0b0001);
nzp_csi_res[4].res_map.nrof_ports = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1;
nzp_csi_res[4].res_map.first_ofdm_symbol_in_time_domain = 8;
nzp_csi_res[4].res_map.cdm_type = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm;
nzp_csi_res[4].res_map.density.set_three();
nzp_csi_res[4].res_map.freq_band.start_rb = 0;
nzp_csi_res[4].res_map.freq_band.nrof_rbs = 52;
nzp_csi_res[4].pwr_ctrl_offset = 0;
// Skip pwr_ctrl_offset_ss_present
nzp_csi_res[4].scrambling_id = cfg.cell_list[0].phy_cell.cell_id;
nzp_csi_res[4].periodicity_and_offset_present = true;
nzp_csi_res[4].periodicity_and_offset.set_slots40();
nzp_csi_res[4].periodicity_and_offset.slots40() = 12;
// optional
nzp_csi_res[4].qcl_info_periodic_csi_rs_present = true;
nzp_csi_res[4].qcl_info_periodic_csi_rs = 0;
} else {
csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list.resize(1);
auto& nzp_csi_res = csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list;
nzp_csi_res[0].nzp_csi_rs_res_id = 0;
nzp_csi_res[0].res_map.freq_domain_alloc.set_row2();
nzp_csi_res[0].res_map.freq_domain_alloc.row2().from_number(0b100000000000);
nzp_csi_res[0].res_map.nrof_ports = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1;
nzp_csi_res[0].res_map.first_ofdm_symbol_in_time_domain = 4;
nzp_csi_res[0].res_map.cdm_type = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm;
nzp_csi_res[0].res_map.density.set_one();
nzp_csi_res[0].res_map.freq_band.start_rb = 0;
nzp_csi_res[0].res_map.freq_band.nrof_rbs = 52;
nzp_csi_res[0].pwr_ctrl_offset = 0;
// Skip pwr_ctrl_offset_ss_present
nzp_csi_res[0].periodicity_and_offset_present = true;
nzp_csi_res[0].periodicity_and_offset.set_slots80() = 0;
// optional
nzp_csi_res[0].qcl_info_periodic_csi_rs_present = true;
nzp_csi_res[0].qcl_info_periodic_csi_rs = 0;
}
// Fill NZP-CSI Resource Sets
csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list_present = true;
if (cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) {
csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list.resize(2);
auto& nzp_csi_res_set = csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list;
// item 0
nzp_csi_res_set[0].nzp_csi_res_set_id = 0;
nzp_csi_res_set[0].nzp_csi_rs_res.resize(1);
nzp_csi_res_set[0].nzp_csi_rs_res[0] = 0;
// item 1
nzp_csi_res_set[1].nzp_csi_res_set_id = 1;
nzp_csi_res_set[1].nzp_csi_rs_res.resize(4);
nzp_csi_res_set[1].nzp_csi_rs_res[0] = 1;
nzp_csi_res_set[1].nzp_csi_rs_res[1] = 2;
nzp_csi_res_set[1].nzp_csi_rs_res[2] = 3;
nzp_csi_res_set[1].nzp_csi_rs_res[3] = 4;
// Skip TRS info
} else {
csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list.resize(1);
auto& nzp_csi_res_set = csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list;
nzp_csi_res_set[0].nzp_csi_res_set_id = 0;
nzp_csi_res_set[0].nzp_csi_rs_res.resize(1);
nzp_csi_res_set[0].nzp_csi_rs_res[0] = 0;
// Skip TRS info
}
return SRSRAN_SUCCESS;
}
int fill_serv_cell_from_enb_cfg(const rrc_nr_cfg_t& cfg, serving_cell_cfg_s& serv_cell)
{
serv_cell.csi_meas_cfg_present = true;
return fill_csi_meas_from_enb_cfg(cfg, serv_cell.csi_meas_cfg.set_setup());
}
} // namespace srsenb

@ -60,7 +60,7 @@ bool rrc::ue::rrc_endc::fill_conn_recfg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn
}
if (not is_endc_activation_running() && endc_cfg.act_from_b1_event) {
// add hard-coded measConfig
// add measConfig
conn_recfg->meas_cfg_present = true;
meas_cfg_s& meas_cfg = conn_recfg->meas_cfg;
@ -69,14 +69,14 @@ bool rrc::ue::rrc_endc::fill_conn_recfg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn
meas_obj_to_add_mod_s meas_obj = {};
meas_obj.meas_obj_id = meas_cfg.meas_obj_to_add_mod_list.size() + 1;
meas_obj.meas_obj.set_meas_obj_nr_r15();
meas_obj.meas_obj.meas_obj_nr_r15().carrier_freq_r15 = endc_cfg.nr_dl_arfcn;
meas_obj.meas_obj.meas_obj_nr_r15().rs_cfg_ssb_r15.meas_timing_cfg_r15.periodicity_and_offset_r15.set_sf20_r15();
meas_obj.meas_obj.meas_obj_nr_r15().rs_cfg_ssb_r15.meas_timing_cfg_r15.ssb_dur_r15 =
asn1::rrc::mtc_ssb_nr_r15_s::ssb_dur_r15_opts::sf1;
meas_obj.meas_obj.meas_obj_nr_r15().rs_cfg_ssb_r15.subcarrier_spacing_ssb_r15 = endc_cfg.ssb_ssc;
meas_obj.meas_obj.meas_obj_nr_r15().ext = true;
meas_obj.meas_obj.meas_obj_nr_r15().band_nr_r15.set_present(true);
meas_obj.meas_obj.meas_obj_nr_r15().band_nr_r15.get()->set_setup() = endc_cfg.nr_band;
auto& meas_obj_nr = meas_obj.meas_obj.meas_obj_nr_r15();
meas_obj_nr.carrier_freq_r15 = endc_cfg.nr_dl_arfcn;
meas_obj_nr.rs_cfg_ssb_r15.meas_timing_cfg_r15.periodicity_and_offset_r15 = endc_cfg.ssb_period_offset;
meas_obj_nr.rs_cfg_ssb_r15.meas_timing_cfg_r15.ssb_dur_r15 = endc_cfg.ssb_duration;
meas_obj_nr.rs_cfg_ssb_r15.subcarrier_spacing_ssb_r15 = endc_cfg.ssb_ssc;
meas_obj_nr.ext = true;
meas_obj_nr.band_nr_r15.set_present(true);
meas_obj_nr.band_nr_r15.get()->set_setup() = endc_cfg.nr_band;
meas_cfg.meas_obj_to_add_mod_list.push_back(meas_obj);
// report config

File diff suppressed because it is too large Load Diff

@ -96,8 +96,7 @@ int rrc::ue::init()
mobility_handler = make_rnti_obj<rrc_mobility>(rnti, this);
if (parent->rrc_nr != nullptr) {
rrc::ue::rrc_endc::rrc_endc_cfg_t endc_cfg = {}; // TODO: set or derive parameter in eNB config
endc_handler = make_rnti_obj<rrc_endc>(rnti, this, endc_cfg);
endc_handler = make_rnti_obj<rrc_endc>(rnti, this, parent->cfg.endc_cfg);
}
return SRSRAN_SUCCESS;
@ -852,18 +851,6 @@ void rrc::ue::send_connection_reconf(srsran::unique_byte_buffer_t pdu,
return;
}
/* Apply updates present in RRCConnectionReconfiguration to lower layers */
// apply PHY config
apply_reconf_phy_config(recfg_r8, true);
// setup SRB2/DRBs in PDCP and RLC
apply_rlc_rb_updates(recfg_r8.rr_cfg_ded);
apply_pdcp_srb_updates(recfg_r8.rr_cfg_ded);
apply_pdcp_drb_updates(recfg_r8.rr_cfg_ded);
// UE MAC scheduler updates
mac_ctrl.handle_con_reconf(recfg_r8, ue_capabilities);
// Fill in NAS PDU - Only for RRC Connection Reconfiguration during E-RAB Release Command
if (nas_pdu.size() > 0 and !recfg_r8.ded_info_nas_list_present) {
recfg_r8.ded_info_nas_list_present = true;
@ -879,6 +866,18 @@ void rrc::ue::send_connection_reconf(srsran::unique_byte_buffer_t pdu,
endc_handler->fill_conn_recfg(&recfg_r8);
}
/* Apply updates present in RRCConnectionReconfiguration to lower layers */
// apply PHY config
apply_reconf_phy_config(recfg_r8, true);
// setup SRB2/DRBs in PDCP and RLC
apply_rlc_rb_updates(recfg_r8.rr_cfg_ded);
apply_pdcp_srb_updates(recfg_r8.rr_cfg_ded);
apply_pdcp_drb_updates(recfg_r8.rr_cfg_ded);
// UE MAC scheduler updates
mac_ctrl.handle_con_reconf(recfg_r8, ue_capabilities);
// Reuse same PDU
if (pdu != nullptr) {
pdu->clear();

@ -58,6 +58,12 @@ public:
srsenb::sched_interface::cell_cfg_t cellcfgobj;
};
class phy_nr_dummy : public phy_interface_stack_nr
{
public:
int set_common_cfg(const phy_interface_rrc_nr::common_cfg_t& common_cfg_) override { return SRSRAN_SUCCESS; }
};
} // namespace srsenb
#endif // SRSRAN_DUMMY_NR_CLASSES_H

@ -46,7 +46,7 @@ public:
{
// first entry
metrics[0].rf.rf_o = 10;
metrics[0].stack.rrc.ues.resize(1);
metrics[0].stack.rrc.ues.resize(2);
metrics[0].stack.mac.ues.resize(metrics[0].stack.rrc.ues.size());
metrics[0].stack.mac.ues[0].rnti = 0x46;
metrics[0].stack.mac.ues[0].tx_pkts = 1000;
@ -61,12 +61,32 @@ public:
metrics[0].stack.mac.ues[0].dl_ri = 1.5;
metrics[0].stack.mac.ues[0].dl_pmi = 1.0;
metrics[0].stack.mac.ues[0].phr = 12.0;
metrics[0].phy.resize(1);
metrics[0].phy.resize(2);
metrics[0].phy[0].dl.mcs = 28.0;
metrics[0].phy[0].ul.mcs = 20.2;
metrics[0].phy[0].ul.pucch_sinr = 14.2;
metrics[0].phy[0].ul.pusch_sinr = 14.2;
metrics[0].rf.rf_o = 10;
metrics[0].nr_stack.mac.ues.resize(1);
metrics[0].nr_stack.mac.ues[0].rnti = 0x4601;
metrics[0].nr_stack.mac.ues[0].tx_pkts = 2000;
metrics[0].nr_stack.mac.ues[0].tx_errors = 2000;
metrics[0].nr_stack.mac.ues[0].tx_brate = 0;
metrics[0].nr_stack.mac.ues[0].rx_pkts = 50;
metrics[0].nr_stack.mac.ues[0].rx_errors = 49;
metrics[0].nr_stack.mac.ues[0].rx_brate = 2;
metrics[0].nr_stack.mac.ues[0].ul_buffer = 100;
metrics[0].nr_stack.mac.ues[0].dl_buffer = 200;
metrics[0].nr_stack.mac.ues[0].dl_cqi = 15.9;
metrics[0].nr_stack.mac.ues[0].dl_ri = 1.5;
metrics[0].nr_stack.mac.ues[0].dl_pmi = 1.0;
metrics[0].nr_stack.mac.ues[0].phr = 12.0;
metrics[0].nr_stack.mac.ues[0].dl_mcs = 28;
metrics[0].nr_stack.mac.ues[0].ul_mcs = 22;
metrics[0].nr_stack.mac.ues[0].pusch_sinr = 14;
metrics[0].nr_stack.mac.ues[0].pucch_sinr = 14.7;
// second
metrics[1].rf.rf_o = 10;
metrics[1].stack.rrc.ues.resize(1);
@ -115,7 +135,7 @@ public:
// fourth entry with incomple PHY and MAC stats
metrics[3].rf.rf_o = 10;
metrics[3].stack.rrc.ues.resize(2);
metrics[3].stack.rrc.ues.resize(1);
metrics[3].stack.mac.ues.resize(metrics[3].stack.rrc.ues.size());
metrics[3].stack.mac.ues[0].rnti = 0x1;
metrics[3].stack.mac.ues[0].tx_pkts = 9999;

@ -46,6 +46,7 @@ inline sched_nr_interface::cell_cfg_t get_default_cell_cfg(
cell_cfg.carrier = phy_cfg.carrier;
cell_cfg.duplex = phy_cfg.duplex;
cell_cfg.ssb = phy_cfg.ssb;
cell_cfg.bwps.resize(1);
cell_cfg.bwps[0].pdcch = phy_cfg.pdcch;
@ -101,7 +102,7 @@ inline sched_nr_interface::ue_cfg_t get_default_ue_cfg(
// Note: dynamic MCS not yet supported
uecfg.fixed_dl_mcs = 28;
uecfg.fixed_ul_mcs = 10;
uecfg.fixed_ul_mcs = 28;
return uecfg;
}

@ -20,6 +20,7 @@
*/
#include "sched_nr_common_test.h"
#include "srsenb/hdr/stack/mac/nr/sched_nr_cfg.h"
#include "srsran/support/srsran_test.h"
namespace srsenb {
@ -49,4 +50,30 @@ void test_pdsch_consistency(srsran::const_span<mac_interface_phy_nr::pdsch_t> pd
}
}
void test_ssb_scheduled_grant(
const srsran::slot_point& sl_point,
const sched_nr_interface::cell_cfg_t& cell_cfg,
const srsran::bounded_vector<mac_interface_phy_nr::ssb_t, mac_interface_phy_nr::MAX_SSB>& ssb_list)
{
/*
* Verify that, with correct SSB periodicity, dl_res has:
* 1) SSB grant
* 2) 4 LSBs of SFN in packed MIB message are correct
* 3) SSB index is 0
*/
if (sl_point.to_uint() % (cell_cfg.ssb.periodicity_ms * (uint32_t)sl_point.nof_slots_per_subframe()) == 0) {
TESTASSERT(ssb_list.size() == 1);
auto& ssb_item = ssb_list.back();
TESTASSERT(ssb_item.pbch_msg.sfn_4lsb == ((uint8_t)sl_point.sfn() & 0b1111));
bool expected_hrf = sl_point.slot_idx() % SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz) >=
SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz) / 2;
TESTASSERT(ssb_item.pbch_msg.hrf == expected_hrf);
TESTASSERT(ssb_item.pbch_msg.ssb_idx == 0);
}
// Verify that, outside SSB periodicity, there is NO SSB grant
else {
TESTASSERT(ssb_list.size() == 0);
}
}
} // namespace srsenb

@ -29,6 +29,11 @@ namespace srsenb {
void test_dl_pdcch_consistency(srsran::const_span<sched_nr_impl::pdcch_dl_t> dl_pdcch);
void test_pdsch_consistency(srsran::const_span<mac_interface_phy_nr::pdsch_t> dl_pdcch);
/// @brief Test whether the SSB grant gets scheduled with the correct periodicity.
void test_ssb_scheduled_grant(
const srsran::slot_point& sl_point,
const sched_nr_interface::cell_cfg_t& cell_cfg,
const srsran::bounded_vector<mac_interface_phy_nr::ssb_t, mac_interface_phy_nr::MAX_SSB>& ssb_list);
} // namespace srsenb

@ -171,6 +171,7 @@ void sched_nr_sim_base::update(sched_nr_cc_output_res_t& cc_out)
// Run common tests
test_dl_pdcch_consistency(cc_out.dl_cc_result->dl_sched.pdcch_dl);
test_pdsch_consistency(cc_out.dl_cc_result->dl_sched.pdsch);
test_ssb_scheduled_grant(cc_out.slot, ctxt.cell_params[cc_out.cc].cell_cfg, cc_out.dl_cc_result->dl_sched.ssb);
// Run UE-dedicated tests
test_dl_sched_result(ctxt, cc_out);

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save