Merge branch 'next' into agpl_next

# Conflicts:
#	srsue/hdr/phy/scell/intra_measure.h
#	srsue/src/phy/scell/intra_measure.cc
master
Codebot 4 years ago committed by Your Name
commit e77c197633

@ -212,6 +212,9 @@ static void parse_args(int argc, char** argv)
case 'Q': case 'Q':
use_standard_lte_rate ^= true; use_standard_lte_rate ^= true;
break; break;
case 'E':
cell.cp = SRSRAN_CP_EXT;
break;
default: default:
usage(argv[0]); usage(argv[0]);
exit(-1); exit(-1);
@ -316,7 +319,7 @@ static void base_init()
/* create ifft object */ /* create ifft object */
for (i = 0; i < cell.nof_ports; i++) { for (i = 0; i < cell.nof_ports; i++) {
if (srsran_ofdm_tx_init(&ifft[i], SRSRAN_CP_NORM, sf_buffer[i], output_buffer[i], cell.nof_prb)) { if (srsran_ofdm_tx_init(&ifft[i], cell.cp, sf_buffer[i], output_buffer[i], cell.nof_prb)) {
ERROR("Error creating iFFT object"); ERROR("Error creating iFFT object");
exit(-1); exit(-1);
} }
@ -734,7 +737,7 @@ int main(int argc, char** argv)
generate_mcch_table(mch_table, mbsfn_sf_mask); generate_mcch_table(mch_table, mbsfn_sf_mask);
} }
N_id_2 = cell.id % 3; N_id_2 = cell.id % 3;
sf_n_re = 2 * SRSRAN_CP_NORM_NSYMB * cell.nof_prb * SRSRAN_NRE; sf_n_re = SRSRAN_SF_LEN_RE(cell.nof_prb, cell.cp);
sf_n_samples = 2 * SRSRAN_SLOT_LEN(srsran_symbol_sz(cell.nof_prb)); sf_n_samples = 2 * SRSRAN_SLOT_LEN(srsran_symbol_sz(cell.nof_prb));
cell.phich_length = SRSRAN_PHICH_NORM; cell.phich_length = SRSRAN_PHICH_NORM;
@ -850,8 +853,8 @@ int main(int argc, char** argv)
srsran_vec_cf_zero(sf_symbols[0], sf_n_re); srsran_vec_cf_zero(sf_symbols[0], sf_n_re);
if (sf_idx == 0 || sf_idx == 5) { if (sf_idx == 0 || sf_idx == 5) {
srsran_pss_put_slot(pss_signal, sf_symbols[0], cell.nof_prb, SRSRAN_CP_NORM); srsran_pss_put_slot(pss_signal, sf_symbols[0], cell.nof_prb, cell.cp);
srsran_sss_put_slot(sf_idx ? sss_signal5 : sss_signal0, sf_symbols[0], cell.nof_prb, SRSRAN_CP_NORM); srsran_sss_put_slot(sf_idx ? sss_signal5 : sss_signal0, sf_symbols[0], cell.nof_prb, cell.cp);
} }
/* Copy zeros, SSS, PSS into the rest of antenna ports */ /* Copy zeros, SSS, PSS into the rest of antenna ports */

@ -372,6 +372,13 @@ public:
return get_word_(0); return get_word_(0);
} }
void from_uint64(uint64_t v)
{
srsran_assert(nof_words_() == 1, "ERROR: cannot convert bitset of size=%zd to uint64_t", size());
srsran_assert(v < (1U << size()), "ERROR: Provided uint64=%ld does not fit in bitset of size=%zd", v, size());
buffer[0] = v;
}
template <typename OutputIt> template <typename OutputIt>
OutputIt to_hex(OutputIt&& mem_buffer) const noexcept OutputIt to_hex(OutputIt&& mem_buffer) const noexcept
{ {

@ -25,7 +25,7 @@
#include "srsran/interfaces/mac_interface_types.h" #include "srsran/interfaces/mac_interface_types.h"
#include "srsran/interfaces/pdcp_interface_types.h" #include "srsran/interfaces/pdcp_interface_types.h"
#include "srsran/interfaces/rlc_interface_types.h" #include "srsran/interfaces/rlc_interface_types.h"
#include "srsran/interfaces/rrc_interface_types.h" #include "srsran/interfaces/rrc_nr_interface_types.h"
#include "srsran/interfaces/sched_interface.h" #include "srsran/interfaces/sched_interface.h"
/************************ /************************
@ -64,6 +64,7 @@ struct zp_csi_rs_res_s;
struct nzp_csi_rs_res_s; struct nzp_csi_rs_res_s;
struct pdsch_serving_cell_cfg_s; struct pdsch_serving_cell_cfg_s;
struct freq_info_dl_s; struct freq_info_dl_s;
struct serving_cell_cfg_common_s;
} // namespace rrc_nr } // namespace rrc_nr
} // namespace asn1 } // namespace asn1
@ -115,6 +116,8 @@ 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, 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); 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_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);
/*************************** /***************************
* MAC Config * MAC Config
**************************/ **************************/

@ -22,6 +22,7 @@
#ifndef SRSRAN_BAND_HELPER_H #ifndef SRSRAN_BAND_HELPER_H
#define SRSRAN_BAND_HELPER_H #define SRSRAN_BAND_HELPER_H
#include "srsran/phy/common/phy_common_nr.h"
#include <array> #include <array>
#include <stdint.h> #include <stdint.h>
#include <vector> #include <vector>
@ -53,8 +54,77 @@ public:
// For bands with 2 possible raster offsets, delta_f_raster needs to be specified // For bands with 2 possible raster offsets, delta_f_raster needs to be specified
std::vector<uint32_t> get_bands_nr(uint32_t nr_arfcn, delta_f_raster_t delta_f_raster = DEFAULT); std::vector<uint32_t> get_bands_nr(uint32_t nr_arfcn, delta_f_raster_t delta_f_raster = DEFAULT);
/**
* @brief Get the lowest band that includes a given Downlink frequency in Hz
* @param dl_freq_Hz Given frequency in Hz
* @return The band number if the frequency is bounded in a band, UINT16_MAX otherwise
*/
uint16_t get_band_from_dl_freq_Hz(double dl_freq_Hz) const;
/**
* @brief Selects the SSB pattern case according to the band number and subcarrier spacing
* @remark Described by TS 38.101-1 Table 5.4.3.3-1: Applicable SS raster entries per operating band
* @param band NR Band number
* @param scs SSB Subcarrier spacing
* @return The SSB pattern case if band and subcarrier spacing match, SRSRAN_SSB_PATTERN_INVALID otherwise
*/
srsran_ssb_patern_t get_ssb_pattern(uint16_t band, srsran_subcarrier_spacing_t scs) const;
/**
* @brief gets the NR band duplex mode
* @param band Given band
* @return A valid SRSRAN_DUPLEX_MODE if the band is valid, SRSRAN_DUPLEX_MODE_INVALID otherwise
*/
srsran_duplex_mode_t get_duplex_mode(uint16_t band) const;
private: private:
// Table 5.4.2.1-1 // Elements of TS 38.101-1 Table 5.2-1: NR operating bands in FR1
struct nr_operating_band {
uint16_t band;
uint32_t F_UL_low; // in MHz
uint32_t F_UL_high; // in MHz
uint32_t F_DL_low; // in MHz
uint32_t F_DL_high; // in MHz
srsran_duplex_mode_t duplex_mode;
};
static const uint32_t nof_nr_operating_band_fr1 = 32;
static constexpr std::array<nr_operating_band, nof_nr_operating_band_fr1> nr_operating_bands_fr1 = {{
// clang-format off
{1, 1920, 1080, 2110, 2170, SRSRAN_DUPLEX_MODE_FDD},
{2, 1850, 1810, 1930, 1990, SRSRAN_DUPLEX_MODE_FDD},
{3, 1710, 1785, 1805, 1880, SRSRAN_DUPLEX_MODE_FDD},
{5, 824, 849, 869, 894, SRSRAN_DUPLEX_MODE_FDD},
{7, 2500, 2570, 2620, 2690, SRSRAN_DUPLEX_MODE_FDD},
{8, 880, 915, 925, 960, SRSRAN_DUPLEX_MODE_FDD},
{12, 699, 716, 729, 746, SRSRAN_DUPLEX_MODE_FDD},
{20, 832, 862, 791, 821, SRSRAN_DUPLEX_MODE_FDD},
{25, 1850, 1915, 1930, 1995, SRSRAN_DUPLEX_MODE_FDD},
{28, 703, 748, 758, 803, SRSRAN_DUPLEX_MODE_FDD},
{34, 2010, 2025, 2010, 2025, SRSRAN_DUPLEX_MODE_TDD},
{38, 2570, 2620, 2570, 2620, SRSRAN_DUPLEX_MODE_TDD},
{39, 1880, 1920, 1880, 1920, SRSRAN_DUPLEX_MODE_TDD},
{40, 2300, 2400, 2300, 2400, SRSRAN_DUPLEX_MODE_TDD},
{41, 2496, 2690, 2496, 2690, SRSRAN_DUPLEX_MODE_TDD},
{50, 1432, 1517, 1432, 1517, SRSRAN_DUPLEX_MODE_TDD},
{51, 1427, 1432, 1427, 1432, SRSRAN_DUPLEX_MODE_TDD},
{66, 1710, 1780, 2110, 2200, SRSRAN_DUPLEX_MODE_FDD},
{70, 1695, 1710, 1995, 2020, SRSRAN_DUPLEX_MODE_FDD},
{71, 663, 698, 617, 652, SRSRAN_DUPLEX_MODE_FDD},
{74, 1427, 1470, 1475, 1518, SRSRAN_DUPLEX_MODE_FDD},
{75, 0, 0, 1432, 1517, SRSRAN_DUPLEX_MODE_SDL},
{76, 0, 0, 1427, 1432, SRSRAN_DUPLEX_MODE_SDL},
{77, 3300, 4200, 3300, 4200, SRSRAN_DUPLEX_MODE_TDD},
{78, 3300, 3800, 3300, 3800, SRSRAN_DUPLEX_MODE_TDD},
{79, 4400, 5000, 4400, 5000, SRSRAN_DUPLEX_MODE_TDD},
{80, 1710, 1785, 0, 0, SRSRAN_DUPLEX_MODE_SUL},
{81, 880, 915, 0, 0, SRSRAN_DUPLEX_MODE_SUL},
{82, 832, 862, 0, 0, SRSRAN_DUPLEX_MODE_SUL},
{83, 703, 748, 0, 0, SRSRAN_DUPLEX_MODE_SUL},
{84, 1920, 1980, 0, 0, SRSRAN_DUPLEX_MODE_SUL},
{86, 1710, 1780, 0, 0, SRSRAN_DUPLEX_MODE_SUL}
// clang-format on
}};
struct nr_raster_params { struct nr_raster_params {
double delta_F_global_kHz; double delta_F_global_kHz;
double F_REF_Offs_MHz; double F_REF_Offs_MHz;
@ -149,7 +219,7 @@ private:
// clang-format on // clang-format on
}}; }};
static const uint32_t nof_nr_bands_fr2 = 36; static const uint32_t nof_nr_bands_fr2 = 8;
static constexpr std::array<nr_band, nof_nr_bands_fr2> nr_band_table_fr2 = {{ static constexpr std::array<nr_band, nof_nr_bands_fr2> nr_band_table_fr2 = {{
{257, KHZ_60, 2054166, 1, 2104165, 2054166, 1, 2104165}, {257, KHZ_60, 2054166, 1, 2104165, 2054166, 1, 2104165},
{257, KHZ_120, 2054167, 2, 2104165, 2054167, 20, 2104165}, {257, KHZ_120, 2054167, 2, 2104165, 2054167, 20, 2104165},
@ -163,6 +233,48 @@ private:
{261, KHZ_60, 2070833, 1, 2084999, 2070833, 1, 2084999}, {261, KHZ_60, 2070833, 1, 2084999, 2070833, 1, 2084999},
{261, KHZ_120, 2070833, 2, 2084999, 2070833, 2, 2084999} {261, KHZ_120, 2070833, 2, 2084999, 2070833, 2, 2084999}
}}; }};
// Elements of TS 38.101-1 Table 5.4.3.3-1 : Applicable SS raster entries per operating band
struct nr_band_ss_raster {
uint16_t band;
srsran_subcarrier_spacing_t scs;
srsran_ssb_patern_t pattern;
uint32_t gscn_first;
uint32_t gscn_step;
uint32_t gscn_last;
};
static const uint32_t nof_nr_band_ss_raster = 29;
static constexpr std::array<nr_band_ss_raster, nof_nr_band_ss_raster> nr_band_ss_raster_table = {{
{1, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 5279, 1, 5419},
{2, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 4829, 1, 4969},
{3, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 4517, 1, 4693},
{5, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 2177, 1, 2230},
{5, srsran_subcarrier_spacing_30kHz, SRSRAN_SSB_PATTERN_B, 2183, 1, 2224},
{7, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 6554, 1, 6718},
{8, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 2318, 1, 2395},
{12, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 1828, 1, 1858},
{20, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 1982, 1, 2047},
{25, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 4829, 1, 4981},
{28, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 1901, 1, 2002},
{34, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 5030, 1, 5056},
{38, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 6431, 1, 6544},
{39, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 4706, 1, 4795},
{40, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 5756, 1, 5995},
{41, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 6246, 3, 6717},
{41, srsran_subcarrier_spacing_30kHz, SRSRAN_SSB_PATTERN_C, 6252, 3, 6714},
{50, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 3584, 1, 3787},
{51, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 3572, 1, 3574},
{66, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 5279, 1, 5494},
{66, srsran_subcarrier_spacing_30kHz, SRSRAN_SSB_PATTERN_B, 5285, 1, 5488},
{70, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 4993, 1, 5044},
{71, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 1547, 1, 1624},
{74, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 3692, 1, 3790},
{75, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 3584, 1, 3787},
{76, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 3572, 1, 3574},
{77, srsran_subcarrier_spacing_30kHz, SRSRAN_SSB_PATTERN_C, 7711, 1, 8329},
{78, srsran_subcarrier_spacing_30kHz, SRSRAN_SSB_PATTERN_C, 7711, 1, 8051},
{79, srsran_subcarrier_spacing_30kHz, SRSRAN_SSB_PATTERN_C, 8480, 16, 8880},
}};
}; };
} // namespace srsran } // namespace srsran

@ -59,11 +59,12 @@ enum class lte_srb { srb0, srb1, srb2, count };
const uint32_t MAX_LTE_SRB_ID = 2; const uint32_t MAX_LTE_SRB_ID = 2;
enum class lte_drb { drb1 = 1, drb2, drb3, drb4, drb5, drb6, drb7, drb8, drb9, drb10, drb11, invalid }; enum class lte_drb { drb1 = 1, drb2, drb3, drb4, drb5, drb6, drb7, drb8, drb9, drb10, drb11, invalid };
const uint32_t MAX_LTE_DRB_ID = 11; const uint32_t MAX_LTE_DRB_ID = 11;
const uint32_t MAX_NOF_BEARERS = 14; const uint32_t MAX_LTE_LCID = 10; // logicalChannelIdentity 3..10 in TS 36.331 v15.3
const uint32_t INVALID_LCID = 99; // random invalid LCID
constexpr bool is_lte_rb(uint32_t lcid) constexpr bool is_lte_rb(uint32_t lcid)
{ {
return lcid < MAX_NOF_BEARERS; return lcid <= MAX_LTE_LCID;
} }
constexpr bool is_lte_srb(uint32_t lcid) constexpr bool is_lte_srb(uint32_t lcid)

@ -166,7 +166,6 @@ class multiqueue_handler
// To ensure that the consumer noticed that the queue was empty before a push, we store the last // To ensure that the consumer noticed that the queue was empty before a push, we store the last
// try_pop() return in a member variable. // try_pop() return in a member variable.
// Doing this reduces the contention of multiple producers for the same condition variable // Doing this reduces the contention of multiple producers for the same condition variable
consumer_notify_needed = false;
lock.unlock(); lock.unlock();
parent->signal_pushed_data(); parent->signal_pushed_data();
} }

@ -139,21 +139,22 @@ struct formatter<srsran::tti_point> {
namespace srsenb { namespace srsenb {
using tti_point = srsran::tti_point; using tti_point = srsran::tti_point;
using tti_interval = srsran::tti_interval;
inline srsran::tti_point to_tx_dl(srsran::tti_point t) inline tti_point to_tx_dl(tti_point t)
{ {
return t + TX_ENB_DELAY; return t + TX_ENB_DELAY;
} }
inline srsran::tti_point to_tx_ul(srsran::tti_point t) inline tti_point to_tx_ul(tti_point t)
{ {
return t + (TX_ENB_DELAY + FDD_HARQ_DELAY_DL_MS); return t + (TX_ENB_DELAY + FDD_HARQ_DELAY_DL_MS);
} }
inline srsran::tti_point to_tx_dl_ack(srsran::tti_point t) inline tti_point to_tx_dl_ack(tti_point t)
{ {
return to_tx_ul(t); return to_tx_ul(t);
} }
inline srsran::tti_point to_tx_ul_ack(srsran::tti_point t) inline tti_point to_tx_ul_ack(tti_point t)
{ {
return to_tx_ul(t) + TX_ENB_DELAY; return to_tx_ul(t) + TX_ENB_DELAY;
} }

@ -34,6 +34,8 @@ public:
* @param gain Relative gain * @param gain Relative gain
*/ */
virtual void cmd_cell_gain(uint32_t cell_id, float gain) = 0; virtual void cmd_cell_gain(uint32_t cell_id, float gain) = 0;
virtual void toggle_padding() = 0;
}; };
} // namespace srsenb } // namespace srsenb

@ -30,6 +30,7 @@ namespace srsenb {
struct mac_args_t { struct mac_args_t {
uint32_t nof_prb; ///< Needed to dimension MAC softbuffers for all cells uint32_t nof_prb; ///< Needed to dimension MAC softbuffers for all cells
sched_interface::sched_args_t sched; sched_interface::sched_args_t sched;
int lcid_padding;
int nr_tb_size = -1; int nr_tb_size = -1;
uint32_t nof_prealloc_ues; ///< Number of UE resources to pre-allocate at eNB startup uint32_t nof_prealloc_ues; ///< Number of UE resources to pre-allocate at eNB startup
uint32_t max_nof_kos; uint32_t max_nof_kos;

@ -28,7 +28,7 @@
#include "srsenb/hdr/phy/phy_metrics.h" #include "srsenb/hdr/phy/phy_metrics.h"
#include "srsenb/hdr/stack/mac/mac_metrics.h" #include "srsenb/hdr/stack/mac/mac_metrics.h"
#include "srsenb/hdr/stack/rrc/rrc_metrics.h" #include "srsenb/hdr/stack/rrc/rrc_metrics.h"
#include "srsenb/hdr/stack/upper/s1ap_metrics.h" #include "srsenb/hdr/stack/s1ap/s1ap_metrics.h"
#include "srsran/common/metrics_hub.h" #include "srsran/common/metrics_hub.h"
#include "srsran/radio/radio_metrics.h" #include "srsran/radio/radio_metrics.h"
#include "srsran/system/sys_metrics.h" #include "srsran/system/sys_metrics.h"

@ -22,6 +22,7 @@
#ifndef SRSRAN_PHY_INTERFACE_TYPES_H #ifndef SRSRAN_PHY_INTERFACE_TYPES_H
#define SRSRAN_PHY_INTERFACE_TYPES_H #define SRSRAN_PHY_INTERFACE_TYPES_H
#include "srsran/common/common.h"
#include "srsran/srsran.h" #include "srsran/srsran.h"
/// Common types defined by the PHY layer. /// Common types defined by the PHY layer.
@ -61,11 +62,12 @@ struct phy_meas_nr_t {
}; };
struct phy_meas_t { struct phy_meas_t {
float rsrp; srsran::srsran_rat_t rat; ///< LTE or NR
float rsrq; float rsrp;
float cfo_hz; float rsrq;
uint32_t earfcn; float cfo_hz;
uint32_t pci; uint32_t earfcn;
uint32_t pci;
}; };
struct phy_cell_t { struct phy_cell_t {

@ -24,6 +24,7 @@
#include "srsran/config.h" #include "srsran/config.h"
#include "srsran/srsran.h" #include "srsran/srsran.h"
#include <array>
#include <string> #include <string>
namespace srsran { namespace srsran {
@ -33,6 +34,15 @@ namespace srsran {
**************************/ **************************/
struct phy_cfg_nr_t { struct phy_cfg_nr_t {
/**
* SSB configuration
*/
struct ssb_cfg_t {
uint32_t periodicity_ms;
std::array<bool, SRSRAN_SSB_NOF_POSITION> position_in_burst;
srsran_subcarrier_spacing_t scs;
};
srsran_tdd_config_nr_t tdd = {}; srsran_tdd_config_nr_t tdd = {};
srsran_sch_hl_cfg_nr_t pdsch = {}; srsran_sch_hl_cfg_nr_t pdsch = {};
srsran_sch_hl_cfg_nr_t pusch = {}; srsran_sch_hl_cfg_nr_t pusch = {};
@ -42,6 +52,7 @@ struct phy_cfg_nr_t {
srsran_ue_dl_nr_harq_ack_cfg_t harq_ack = {}; srsran_ue_dl_nr_harq_ack_cfg_t harq_ack = {};
srsran_csi_hl_cfg_t csi = {}; srsran_csi_hl_cfg_t csi = {};
srsran_carrier_nr_t carrier = {}; srsran_carrier_nr_t carrier = {};
ssb_cfg_t ssb;
phy_cfg_nr_t() {} phy_cfg_nr_t() {}

@ -93,6 +93,7 @@ struct phy_args_t {
uint32_t intra_freq_meas_len_ms = 20; uint32_t intra_freq_meas_len_ms = 20;
uint32_t intra_freq_meas_period_ms = 200; uint32_t intra_freq_meas_period_ms = 200;
float force_ul_amplitude = 0.0f; float force_ul_amplitude = 0.0f;
bool detect_cp = false;
float in_sync_rsrp_dbm_th = -130.0f; float in_sync_rsrp_dbm_th = -130.0f;
float in_sync_snr_db_th = 1.0f; float in_sync_snr_db_th = 1.0f;

@ -81,7 +81,7 @@ SRSRAN_API int srsran_csi_rs_append_resource_to_pattern(const srsran_carrier_nr_
* @param slot_cfg Provides current slot configuration * @param slot_cfg Provides current slot configuration
* @param resource Provides a NZP-CSI-RS resource * @param resource Provides a NZP-CSI-RS resource
* @param[out] grid Resource grid * @param[out] grid Resource grid
* @return SRSLTE_SUCCESS if the arguments and the resource are valid. SRSLTE_ERROR code otherwise. * @return SRSRAN_SUCCESS if the arguments and the resource are valid. SRSRAN_ERROR code otherwise.
*/ */
SRSRAN_API int srsran_csi_rs_nzp_put_resource(const srsran_carrier_nr_t* carrier, SRSRAN_API int srsran_csi_rs_nzp_put_resource(const srsran_carrier_nr_t* carrier,
const srsran_slot_cfg_t* slot_cfg, const srsran_slot_cfg_t* slot_cfg,
@ -96,7 +96,7 @@ SRSRAN_API int srsran_csi_rs_nzp_put_resource(const srsran_carrier_nr_t*
* @param set Provides a NZP-CSI-RS resource set * @param set Provides a NZP-CSI-RS resource set
* @param[out] grid Resource grid * @param[out] grid Resource grid
* @return The number of NZP-CSI-RS resources that have been scheduled for this slot if the arguments and the resource * @return The number of NZP-CSI-RS resources that have been scheduled for this slot if the arguments and the resource
* are valid. SRSLTE_ERROR code otherwise. * are valid. SRSRAN_ERROR code otherwise.
*/ */
SRSRAN_API int srsran_csi_rs_nzp_put_set(const srsran_carrier_nr_t* carrier, SRSRAN_API int srsran_csi_rs_nzp_put_set(const srsran_carrier_nr_t* carrier,
const srsran_slot_cfg_t* slot_cfg, const srsran_slot_cfg_t* slot_cfg,
@ -132,7 +132,7 @@ SRSRAN_API int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* car
* @param set Provides NZP-CSI-RS resource * @param set Provides NZP-CSI-RS resource
* @param grid Resource grid * @param grid Resource grid
* @param measure Provides measurement * @param measure Provides measurement
* @return The number of NZP-CSI-RS resources scheduled for this TTI if the configuration is right, SRSLTE_ERROR code if * @return The number of NZP-CSI-RS resources scheduled for this TTI if the configuration is right, SRSRAN_ERROR code if
* the configuration is invalid * the configuration is invalid
*/ */
SRSRAN_API int srsran_csi_rs_nzp_measure_trs(const srsran_carrier_nr_t* carrier, SRSRAN_API int srsran_csi_rs_nzp_measure_trs(const srsran_carrier_nr_t* carrier,
@ -160,7 +160,7 @@ SRSRAN_API uint32_t srsran_csi_rs_measure_info(const srsran_csi_trs_measurements
* @param set Provides NZP-CSI-RS resource * @param set Provides NZP-CSI-RS resource
* @param grid Resource grid * @param grid Resource grid
* @param measure Provides CSI measurement * @param measure Provides CSI measurement
* @return The number of NZP-CSI-RS resources scheduled for this slot if the configuration is right, SRSLTE_ERROR code * @return The number of NZP-CSI-RS resources scheduled for this slot if the configuration is right, SRSRAN_ERROR code
* if the configuration is invalid * if the configuration is invalid
*/ */
SRSRAN_API int srsran_csi_rs_nzp_measure_channel(const srsran_carrier_nr_t* carrier, SRSRAN_API int srsran_csi_rs_nzp_measure_channel(const srsran_carrier_nr_t* carrier,
@ -182,7 +182,7 @@ SRSRAN_API int srsran_csi_rs_nzp_measure_channel(const srsran_carrier_nr_t*
* @param set Provides ZP-CSI-RS resource * @param set Provides ZP-CSI-RS resource
* @param grid Resource grid * @param grid Resource grid
* @param measure Provides CSI measurement * @param measure Provides CSI measurement
* @return The number of ZP-CSI-RS resources scheduled for this slot if the configuration is right, SRSLTE_ERROR code if * @return The number of ZP-CSI-RS resources scheduled for this slot if the configuration is right, SRSRAN_ERROR code if
* the configuration is invalid * the configuration is invalid
*/ */
SRSRAN_API int srsran_csi_rs_zp_measure_channel(const srsran_carrier_nr_t* carrier, SRSRAN_API int srsran_csi_rs_zp_measure_channel(const srsran_carrier_nr_t* carrier,

@ -190,12 +190,22 @@ extern "C" {
*/ */
#define SRSRAN_NID_2_NR(N_ID) ((N_ID) % SRSRAN_NOF_NID_2_NR) #define SRSRAN_NID_2_NR(N_ID) ((N_ID) % SRSRAN_NOF_NID_2_NR)
/**
* @brief Compute Physical Cell Identifier (PCI) N_id from N_id_1 and N_id_2
*/
#define SRSRAN_NID_NR(NID_1, NID_2) (SRSRAN_NOF_NID_2_NR * (NID_1) + (NID_2))
/** /**
* @brief SSB number of resource elements, described in TS 38.211 section 7.4.3.1 Time-frequency structure of an SS/PBCH * @brief SSB number of resource elements, described in TS 38.211 section 7.4.3.1 Time-frequency structure of an SS/PBCH
* block * block
*/ */
#define SRSRAN_SSB_NOF_RE (SRSRAN_SSB_BW_SUBC * SRSRAN_SSB_DURATION_NSYMB) #define SRSRAN_SSB_NOF_RE (SRSRAN_SSB_BW_SUBC * SRSRAN_SSB_DURATION_NSYMB)
/**
* @brief Symbol index with extended CP
*/
#define SRSRAN_EXT_CP_SYMBOL(SCS) (7U << (uint32_t)(SCS))
typedef enum SRSRAN_API { typedef enum SRSRAN_API {
srsran_coreset_mapping_type_non_interleaved = 0, srsran_coreset_mapping_type_non_interleaved = 0,
srsran_coreset_mapping_type_interleaved, srsran_coreset_mapping_type_interleaved,
@ -332,8 +342,26 @@ typedef enum SRSRAN_API {
srsran_subcarrier_spacing_60kHz, srsran_subcarrier_spacing_60kHz,
srsran_subcarrier_spacing_120kHz, srsran_subcarrier_spacing_120kHz,
srsran_subcarrier_spacing_240kHz, srsran_subcarrier_spacing_240kHz,
srsran_subcarrier_spacing_invalid
} srsran_subcarrier_spacing_t; } srsran_subcarrier_spacing_t;
typedef enum SRSRAN_API {
SRSRAN_SSB_PATTERN_A = 0, // FR1, 15 kHz SCS
SRSRAN_SSB_PATTERN_B, // FR1, 30 kHz SCS
SRSRAN_SSB_PATTERN_C, // FR1, 30 kHz SCS
SRSRAN_SSB_PATTERN_D, // FR2, 120 kHz SCS
SRSRAN_SSB_PATTERN_E, // FR2, 240 kHz SCS
SRSRAN_SSB_PATTERN_INVALID,
} srsran_ssb_patern_t;
typedef enum SRSRAN_API {
SRSRAN_DUPLEX_MODE_FDD = 0, // Paired
SRSRAN_DUPLEX_MODE_TDD, // Unpaired
SRSRAN_DUPLEX_MODE_SDL, // Supplementary DownLink
SRSRAN_DUPLEX_MODE_SUL, // Supplementary UpLink
SRSRAN_DUPLEX_MODE_INVALID
} srsran_duplex_mode_t;
/** /**
* @brief NR carrier parameters. It is a combination of fixed cell and bandwidth-part (BWP) * @brief NR carrier parameters. It is a combination of fixed cell and bandwidth-part (BWP)
*/ */
@ -459,6 +487,13 @@ typedef struct SRSRAN_API {
*/ */
SRSRAN_API const char* srsran_rnti_type_str(srsran_rnti_type_t rnti_type); SRSRAN_API const char* srsran_rnti_type_str(srsran_rnti_type_t rnti_type);
/**
* @brief Get the short RNTI type name for NR
* @param rnti_type RNTI type name
* @return Constant string with the short RNTI type name
*/
SRSRAN_API const char* srsran_rnti_type_str_short(srsran_rnti_type_t rnti_type);
/** /**
* @brief Get the RNTI type name for NR * @brief Get the RNTI type name for NR
* @param rnti_type RNTI type name * @param rnti_type RNTI type name
@ -519,6 +554,15 @@ 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); SRSRAN_API uint32_t srsran_min_symbol_sz_rb(uint32_t nof_prb);
/**
* @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
* @param l Given symbol index
* @param scs Subcarrier spacing
* @return Returns the symbol time offset in seconds
*/
SRSRAN_API float srsran_symbol_offset_s(uint32_t l, srsran_subcarrier_spacing_t scs);
/** /**
* @brief Computes the time in seconds between two symbols in a slot * @brief Computes the time in seconds between two symbols in a slot
* @note l0 is expected to be smaller than l1 * @note l0 is expected to be smaller than l1
@ -559,6 +603,13 @@ SRSRAN_API int srsran_carrier_to_cell(const srsran_carrier_nr_t* carrier, srsran
*/ */
SRSRAN_API uint32_t srsran_csi_meas_info(const srsran_csi_trs_measurements_t* meas, char* str, uint32_t str_len); SRSRAN_API uint32_t srsran_csi_meas_info(const srsran_csi_trs_measurements_t* meas, char* str, uint32_t str_len);
/**
* @brief Converts a given string into a subcarrier spacing
* @param str Provides the string
* @return A valid subcarrier if the string is valid, srsran_subcarrier_spacing_invalid otherwise
*/
SRSRAN_API srsran_subcarrier_spacing_t srsran_subcarrier_spacing_from_str(const char* str);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

@ -65,7 +65,7 @@ typedef struct SRSRAN_API {
srsran_dl_sf_cfg_t dl_sf; srsran_dl_sf_cfg_t dl_sf;
cf_t* sf_symbols[SRSRAN_MAX_PORTS]; cf_t* sf_symbols[SRSRAN_MAX_PORTS];
cf_t* out_buffer[SRSRAN_MAX_PORTS];
srsran_ofdm_t ifft[SRSRAN_MAX_PORTS]; srsran_ofdm_t ifft[SRSRAN_MAX_PORTS];
srsran_ofdm_t ifft_mbsfn; srsran_ofdm_t ifft_mbsfn;

@ -53,6 +53,7 @@ typedef struct SRSRAN_API {
srsran_cell_t cell; srsran_cell_t cell;
cf_t* sf_symbols; cf_t* sf_symbols;
cf_t* in_buffer;
srsran_chest_ul_res_t chest_res; srsran_chest_ul_res_t chest_res;
srsran_ofdm_t fft; srsran_ofdm_t fft;

@ -44,7 +44,7 @@ typedef struct SRSRAN_API {
srsran_datatype_t type; srsran_datatype_t type;
} srsran_filesource_t; } srsran_filesource_t;
SRSRAN_API int srsran_filesource_init(srsran_filesource_t* q, char* filename, srsran_datatype_t type); SRSRAN_API int srsran_filesource_init(srsran_filesource_t* q, const char* filename, srsran_datatype_t type);
SRSRAN_API void srsran_filesource_free(srsran_filesource_t* q); SRSRAN_API void srsran_filesource_free(srsran_filesource_t* q);

@ -59,6 +59,7 @@ typedef struct {
uint32_t ri_idx; uint32_t ri_idx;
bool ri_idx_present; bool ri_idx_present;
bool format_is_subband; bool format_is_subband;
uint8_t subband_wideband_ratio; ///< K value in TS 36.331. 0 for wideband reporting, (1..4) otherwise
uint32_t subband_size; uint32_t subband_size;
srsran_cqi_report_mode_t periodic_mode; srsran_cqi_report_mode_t periodic_mode;
srsran_cqi_report_mode_t aperiodic_mode; srsran_cqi_report_mode_t aperiodic_mode;

@ -31,7 +31,7 @@
* @param measurements Current CSI measurements * @param measurements Current CSI measurements
* @param new_measure New NZP-CSI-RS channel measurement * @param new_measure New NZP-CSI-RS channel measurement
* @param nzp_csi_rs_id NZP-CSI-RS resource set identifier * @param nzp_csi_rs_id NZP-CSI-RS resource set identifier
* @return SRSLTE_SUCCESS if the provided information is valid, SRSLTE_ERROR code otherwise * @return SRSRAN_SUCCESS if the provided information is valid, SRSRAN_ERROR code otherwise
*/ */
SRSRAN_API int SRSRAN_API int
srsran_csi_new_nzp_csi_rs_measurement(const srsran_csi_hl_resource_cfg_t csi_resources[SRSRAN_CSI_MAX_NOF_RESOURCES], srsran_csi_new_nzp_csi_rs_measurement(const srsran_csi_hl_resource_cfg_t csi_resources[SRSRAN_CSI_MAX_NOF_RESOURCES],

@ -28,7 +28,7 @@
* @brief Descibes the NR PBCH message * @brief Descibes the NR PBCH message
*/ */
typedef struct SRSRAN_API { typedef struct SRSRAN_API {
// TBD void* TBD;
} srsran_pbch_msg_nr_t; } srsran_pbch_msg_nr_t;
#endif // SRSRAN_PBCH_NR_H #endif // SRSRAN_PBCH_NR_H

@ -42,7 +42,7 @@
* @param ssb_grid SSB resource grid * @param ssb_grid SSB resource grid
* @param N_id_2 Physical cell ID 2 * @param N_id_2 Physical cell ID 2
* @param beta PSS power allocation * @param beta PSS power allocation
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/ */
SRSRAN_API int srsran_pss_nr_put(cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id_2, float beta); SRSRAN_API int srsran_pss_nr_put(cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id_2, float beta);
@ -51,8 +51,18 @@ SRSRAN_API int srsran_pss_nr_put(cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id
* @param ssb_grid received SSB resource grid * @param ssb_grid received SSB resource grid
* @param N_id_2 Physical cell ID 2 * @param N_id_2 Physical cell ID 2
* @param lse Provides LSE pointer * @param lse Provides LSE pointer
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/ */
SRSRAN_API int srsran_pss_nr_extract_lse(const cf_t* ssb_grid, uint32_t N_id_2, cf_t lse[SRSRAN_PSS_NR_LEN]); SRSRAN_API int srsran_pss_nr_extract_lse(const cf_t* ssb_grid, uint32_t N_id_2, cf_t lse[SRSRAN_PSS_NR_LEN]);
/**
* @brief Find the best PSS sequence given the SSB resource grid
* @attention Assumes the SSB is synchronized and the average delay is pre-compensated
* @param ssb_grid The SSB resource grid to search
* @param norm_corr Normalised correlation of the best found sequence
* @param found_N_id_2 The N_id_2 of the best sequence
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int srsran_pss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], float* norm_corr, uint32_t* found_N_id_2);
#endif // SRSRAN_PSS_NR_H #endif // SRSRAN_PSS_NR_H

@ -44,13 +44,13 @@ typedef struct {
uint32_t peak_index; uint32_t peak_index;
} srsran_refsignal_dl_sync_t; } srsran_refsignal_dl_sync_t;
SRSRAN_API int srsran_refsignal_dl_sync_init(srsran_refsignal_dl_sync_t* q); SRSRAN_API int srsran_refsignal_dl_sync_init(srsran_refsignal_dl_sync_t* q, srsran_cp_t cp);
SRSRAN_API int srsran_refsignal_dl_sync_set_cell(srsran_refsignal_dl_sync_t* q, srsran_cell_t cell); SRSRAN_API int srsran_refsignal_dl_sync_set_cell(srsran_refsignal_dl_sync_t* q, srsran_cell_t cell);
SRSRAN_API void srsran_refsignal_dl_sync_free(srsran_refsignal_dl_sync_t* q); SRSRAN_API void srsran_refsignal_dl_sync_free(srsran_refsignal_dl_sync_t* q);
SRSRAN_API void srsran_refsignal_dl_sync_run(srsran_refsignal_dl_sync_t* q, cf_t* buffer, uint32_t nsamples); SRSRAN_API int srsran_refsignal_dl_sync_run(srsran_refsignal_dl_sync_t* q, cf_t* buffer, uint32_t nsamples);
SRSRAN_API void srsran_refsignal_dl_sync_measure_sf(srsran_refsignal_dl_sync_t* q, SRSRAN_API void srsran_refsignal_dl_sync_measure_sf(srsran_refsignal_dl_sync_t* q,
cf_t* buffer, cf_t* buffer,

@ -43,16 +43,21 @@
*/ */
#define SRSRAN_SSB_DEFAULT_BETA 1.0f #define SRSRAN_SSB_DEFAULT_BETA 1.0f
/**
* @brief Maximum number of SSB positions in burst. Defined in TS 38.331 ServingCellConfigCommon, ssb-PositionsInBurst
*/
#define SRSRAN_SSB_NOF_POSITION 64
/** /**
* @brief Describes SSB object initialization arguments * @brief Describes SSB object initialization arguments
*/ */
typedef struct SRSRAN_API { typedef struct SRSRAN_API {
double max_srate_hz; ///< Maximum sampling rate in Hz (common for gNb and UE), set to zero to use default double max_srate_hz; ///< Maximum sampling rate in Hz, set to zero to use default
srsran_subcarrier_spacing_t min_scs; ///< Minimum subcarrier spacing srsran_subcarrier_spacing_t min_scs; ///< Minimum subcarrier spacing
bool enable_correlate; ///< Enables PSS/SSS correlation and peak search (UE cell search) bool enable_search; ///< Enables PSS/SSS blind search
bool enable_encode; ///< Enables PBCH Encoder (intended for gNb) bool enable_measure; ///< Enables PSS/SSS CSI measurements and frequency domain search
bool enable_decode; ///< Enables PBCH Decoder (intented for UE) bool enable_encode; ///< Enables PBCH Encoder
bool enable_measure; ///< Enables PSS/SSS CSI measurements bool enable_decode; ///< Enables PBCH Decoder
} srsran_ssb_args_t; } srsran_ssb_args_t;
/** /**
@ -60,12 +65,17 @@ typedef struct SRSRAN_API {
*/ */
typedef struct SRSRAN_API { typedef struct SRSRAN_API {
double srate_hz; ///< Current sampling rate in Hz double srate_hz; ///< Current sampling rate in Hz
double freq_offset_hz; ///< SSB base-band frequency offset double center_freq_hz; ///< Base-band center frequency in Hz
double ssb_freq_hz; ///< SSB center frequency
srsran_subcarrier_spacing_t scs; ///< SSB configured Subcarrier spacing srsran_subcarrier_spacing_t scs; ///< SSB configured Subcarrier spacing
float beta_pss; ////< PSS power allocation srsran_ssb_patern_t pattern; ///< SSB pattern as defined in TS 38.313 section 4.1 Cell search
float beta_sss; ////< SSS power allocation bool position[SRSRAN_SSB_NOF_POSITION]; ///< Indicates the time domain positions of the transmitted SS-blocks
float beta_pbch; ////< PBCH power allocation srsran_duplex_mode_t duplex_mode; ///< Set to true if the spectrum is paired (FDD)
float beta_pbch_dmrs; ////< PBCH DMRS power allocation uint32_t periodicity_ms; ///< SSB periodicity in ms
float beta_pss; ////< PSS power allocation
float beta_sss; ////< SSS power allocation
float beta_pbch; ////< PBCH power allocation
float beta_pbch_dmrs; ////< PBCH DMRS power allocation
} srsran_ssb_cfg_t; } srsran_ssb_cfg_t;
/** /**
@ -76,27 +86,34 @@ typedef struct SRSRAN_API {
srsran_ssb_cfg_t cfg; ///< Stores last configuration srsran_ssb_cfg_t cfg; ///< Stores last configuration
/// Sampling rate dependent parameters /// Sampling rate dependent parameters
float scs_hz; ///< Subcarrier spacing in Hz float scs_hz; ///< Subcarrier spacing in Hz
uint32_t max_symbol_sz; ///< Maximum symbol size given the minimum supported SCS and sampling rate uint32_t max_symbol_sz; ///< Maximum symbol size given the minimum supported SCS and sampling rate
uint32_t symbol_sz; ///< Current SSB symbol size (for the given base-band sampling rate) uint32_t max_corr_sz; ///< Maximum correlation size
int32_t offset; ///< Current SSB integer offset (multiple of SCS) uint32_t symbol_sz; ///< Current SSB symbol size (for the given base-band sampling rate)
uint32_t cp0_sz; ///< First symbol cyclic prefix size uint32_t corr_sz; ///< Correlation size
uint32_t cp_sz; ///< Other symbol cyclic prefix size uint32_t corr_window; ///< Correlation window length
int32_t f_offset; ///< Current SSB integer frequency offset (multiple of SCS)
uint32_t t_offset; ///< Current SSB integer time offset (number of samples)
uint32_t cp_sz[SRSRAN_SSB_DURATION_NSYMB]; ///< CP length for each SSB symbol
/// Internal Objects /// Internal Objects
srsran_dft_plan_t ifft; ///< IFFT object for modulating the SSB srsran_dft_plan_t ifft; ///< IFFT object for modulating the SSB
srsran_dft_plan_t fft; ///< FFT object for demodulate the SSB. srsran_dft_plan_t fft; ///< FFT object for demodulate the SSB.
srsran_dft_plan_t fft_corr; ///< FFT for correlation
srsran_dft_plan_t ifft_corr; ///< IFFT for correlation
/// Frequency/Time domain temporal data /// Frequency/Time domain temporal data
cf_t* tmp_freq; cf_t* tmp_freq; ///< Temporal frequency domain buffer
cf_t* tmp_time; cf_t* tmp_time; ///< Temporal time domain buffer
cf_t* tmp_corr; ///< Temporal correlation frequency domain buffer
cf_t* pss_seq[SRSRAN_NOF_NID_2_NR]; ///< Possible frequency domain PSS for find
} srsran_ssb_t; } srsran_ssb_t;
/** /**
* @brief Initialises configures NR SSB with the given arguments * @brief Initialises configures NR SSB with the given arguments
* @param q SSB object * @param q SSB object
* @param args NR PSS initialization arguments * @param args NR PSS initialization arguments
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/ */
SRSRAN_API int srsran_ssb_init(srsran_ssb_t* q, const srsran_ssb_args_t* args); SRSRAN_API int srsran_ssb_init(srsran_ssb_t* q, const srsran_ssb_args_t* args);
@ -110,33 +127,56 @@ SRSRAN_API void srsran_ssb_free(srsran_ssb_t* q);
* @brief Sets SSB configuration with the current SSB configuration * @brief Sets SSB configuration with the current SSB configuration
* @param q SSB object * @param q SSB object
* @param cfg Current SSB configuration * @param cfg Current SSB configuration
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/ */
SRSRAN_API int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg); SRSRAN_API int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg);
/** /**
* @brief Decodes PBCH in the given time domain signal * @brief Decodes PBCH in the given time domain signal
* @param q SSB object * @param q SSB object
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/ */
SRSRAN_API int srsran_ssb_decode_pbch(srsran_ssb_t* q, const cf_t* in, srsran_pbch_msg_nr_t* msg); SRSRAN_API int srsran_ssb_decode_pbch(srsran_ssb_t* q, const cf_t* in, srsran_pbch_msg_nr_t* msg);
/**
* @brief Decides if the SSB object is configured and a given subframe is configured for SSB transmission
* @param q SSB object
* @param sf_idx Subframe index within the radio frame
* @return true if the SSB object is configured and SSB is transmitted, false otherwise
*/
SRSRAN_API bool srsran_ssb_send(srsran_ssb_t* q, uint32_t sf_idx);
/** /**
* @brief Adds SSB to a given signal in time domain * @brief Adds SSB to a given signal in time domain
* @param q SSB object * @param q SSB object
* @param N_id Physical Cell Identifier * @param N_id Physical Cell Identifier
* @param msg NR PBCH message to transmit * @param msg NR PBCH message to transmit
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/ */
SRSRAN_API int SRSRAN_API int
srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, const cf_t* in, cf_t* out); srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, const cf_t* in, cf_t* out);
/**
* @brief Perform cell search and measurement
* @note This function assumes the SSB transmission is aligned with the input base-band signal
* @param q NR PSS object
* @param in Base-band signal buffer
* @param N_id Physical Cell Identifier of the most suitable cell identifier
* @param meas SSB-based CSI measurement of the most suitable cell identifier
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int srsran_ssb_csi_search(srsran_ssb_t* q,
const cf_t* in,
uint32_t nof_samples,
uint32_t* N_id,
srsran_csi_trs_measurements_t* meas);
/** /**
* @brief Perform Channel State Information (CSI) measurement from the SSB * @brief Perform Channel State Information (CSI) measurement from the SSB
* @param q NR PSS object * @param q NR PSS object
* @param N_id Physical Cell Identifier * @param N_id Physical Cell Identifier
* @param in Base-band signal * @param in Base-band signal
* @param meas SSB-based CSI measurement * @param meas SSB-based CSI measurement
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/ */
SRSRAN_API int SRSRAN_API int
srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsran_csi_trs_measurements_t* meas); srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsran_csi_trs_measurements_t* meas);

@ -43,7 +43,7 @@
* @param N_id_1 Physical cell ID 1 * @param N_id_1 Physical cell ID 1
* @param N_id_2 Physical cell ID 2 * @param N_id_2 Physical cell ID 2
* @param beta SSS power allocation * @param beta SSS power allocation
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/ */
SRSRAN_API int srsran_sss_nr_put(cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id_1, uint32_t N_id_2, float beta); SRSRAN_API int srsran_sss_nr_put(cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id_1, uint32_t N_id_2, float beta);
@ -53,7 +53,7 @@ SRSRAN_API int srsran_sss_nr_put(cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id
* @param N_id_1 Physical cell ID 1 * @param N_id_1 Physical cell ID 1
* @param N_id_2 Physical cell ID 2 * @param N_id_2 Physical cell ID 2
* @param lse Provides LSE pointer * @param lse Provides LSE pointer
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/ */
SRSRAN_API int SRSRAN_API int
srsran_sss_nr_extract_lse(const cf_t* ssb_grid, uint32_t N_id_1, uint32_t N_id_2, cf_t lse[SRSRAN_SSS_NR_LEN]); srsran_sss_nr_extract_lse(const cf_t* ssb_grid, uint32_t N_id_1, uint32_t N_id_2, cf_t lse[SRSRAN_SSS_NR_LEN]);
@ -65,7 +65,7 @@ srsran_sss_nr_extract_lse(const cf_t* ssb_grid, uint32_t N_id_1, uint32_t N_id_2
* @param N_id_2 Fix N_id_2 to search, it reduces the search space 1/3 * @param N_id_2 Fix N_id_2 to search, it reduces the search space 1/3
* @param norm_corr Normalised correlation of the best found sequence * @param norm_corr Normalised correlation of the best found sequence
* @param found_N_id_1 The N_id_1 of the best sequence * @param found_N_id_1 The N_id_1 of the best sequence
* @return SRSLTE_SUCCESS if the parameters are valid, SRSLTE_ERROR code otherwise * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/ */
SRSRAN_API int SRSRAN_API int
srsran_sss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id_2, float* norm_corr, uint32_t* found_N_id_1); srsran_sss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id_2, float* norm_corr, uint32_t* found_N_id_1);

@ -99,5 +99,7 @@ SRSRAN_API int srsran_ue_cellsearch_scan(srsran_ue_cellsearch_t* q,
SRSRAN_API int srsran_ue_cellsearch_set_nof_valid_frames(srsran_ue_cellsearch_t* q, uint32_t nof_frames); SRSRAN_API int srsran_ue_cellsearch_set_nof_valid_frames(srsran_ue_cellsearch_t* q, uint32_t nof_frames);
SRSRAN_API void srsran_set_detect_cp(srsran_ue_cellsearch_t* q, bool enable);
#endif // SRSRAN_UE_CELL_SEARCH_H #endif // SRSRAN_UE_CELL_SEARCH_H

@ -247,6 +247,8 @@ SRSRAN_API uint32_t srsran_ue_sync_get_sfidx(srsran_ue_sync_t* q);
SRSRAN_API float srsran_ue_sync_get_cfo(srsran_ue_sync_t* q); SRSRAN_API float srsran_ue_sync_get_cfo(srsran_ue_sync_t* q);
SRSRAN_API void srsran_ue_sync_cp_en(srsran_ue_sync_t* q, bool enabled);
SRSRAN_API float srsran_ue_sync_get_sfo(srsran_ue_sync_t* q); SRSRAN_API float srsran_ue_sync_get_sfo(srsran_ue_sync_t* q);
SRSRAN_API int srsran_ue_sync_get_last_sample_offset(srsran_ue_sync_t* q); SRSRAN_API int srsran_ue_sync_get_last_sample_offset(srsran_ue_sync_t* q);

@ -27,9 +27,6 @@
namespace srslog { namespace srslog {
/// This type is used to store small strings without doing any memory allocation.
using small_str_buffer = fmt::basic_memory_buffer<char, 64>;
namespace detail { namespace detail {
/// This structure gives the user a way to log generic information as a context. /// This structure gives the user a way to log generic information as a context.
@ -49,7 +46,6 @@ struct log_entry_metadata {
fmt::dynamic_format_arg_store<fmt::printf_context>* store; fmt::dynamic_format_arg_store<fmt::printf_context>* store;
std::string log_name; std::string log_name;
char log_tag; char log_tag;
small_str_buffer small_str;
std::vector<uint8_t> hex_dump; std::vector<uint8_t> hex_dump;
}; };

@ -69,8 +69,8 @@ public:
return; return;
} }
scoped_lock lock(m);
p->clear(); p->clear();
scoped_lock lock(m);
free_list.push_back(p); free_list.push_back(p);
} }

@ -37,7 +37,14 @@ public:
mutex(const mutex&) = delete; mutex(const mutex&) = delete;
mutex& operator=(const mutex&) = delete; mutex& operator=(const mutex&) = delete;
mutex() { ::pthread_mutex_init(&m, nullptr); } mutex()
{
::pthread_mutexattr_t mutex_attr;
::pthread_mutexattr_init(&mutex_attr);
::pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_INHERIT);
::pthread_mutex_init(&m, &mutex_attr);
}
~mutex() { ::pthread_mutex_destroy(&m); } ~mutex() { ::pthread_mutex_destroy(&m); }
/// Mutex lock. /// Mutex lock.

@ -25,6 +25,7 @@
#include "srsran/srslog/detail/log_backend.h" #include "srsran/srslog/detail/log_backend.h"
#include "srsran/srslog/detail/log_entry.h" #include "srsran/srslog/detail/log_entry.h"
#include "srsran/srslog/sink.h" #include "srsran/srslog/sink.h"
#include <atomic>
namespace srslog { namespace srslog {
@ -74,11 +75,11 @@ public:
log_channel& operator=(const log_channel& other) = delete; log_channel& operator=(const log_channel& other) = delete;
/// Controls when the channel accepts incoming log entries. /// Controls when the channel accepts incoming log entries.
void set_enabled(bool enabled) { is_enabled = enabled; } void set_enabled(bool enabled) { is_enabled.store(enabled, std::memory_order_relaxed); }
/// Returns true if the channel is accepting incoming log entries, otherwise /// Returns true if the channel is accepting incoming log entries, otherwise
/// false. /// false.
bool enabled() const { return is_enabled; } bool enabled() const { return is_enabled.load(std::memory_order_relaxed); }
/// Returns the id string of the channel. /// Returns the id string of the channel.
const std::string& id() const { return log_id; } const std::string& id() const { return log_id; }
@ -117,32 +118,7 @@ public:
fmtstr, fmtstr,
store, store,
log_name, log_name,
log_tag, log_tag}};
small_str_buffer()}};
backend.push(std::move(entry));
}
/// Builds the provided log entry and passes it to the backend. When the
/// channel is disabled the log entry will be discarded.
void operator()(small_str_buffer&& str)
{
if (!enabled()) {
return;
}
// Send the log entry to the backend.
log_formatter& formatter = log_sink.get_formatter();
detail::log_entry entry = {&log_sink,
[&formatter](detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer) {
formatter.format(std::move(metadata), buffer);
},
{std::chrono::high_resolution_clock::now(),
{ctx_value, should_print_context},
nullptr,
nullptr,
log_name,
log_tag,
std::move(str)}};
backend.push(std::move(entry)); backend.push(std::move(entry));
} }
@ -179,7 +155,6 @@ public:
store, store,
log_name, log_name,
log_tag, log_tag,
small_str_buffer(),
std::vector<uint8_t>(buffer, buffer + len)}}; std::vector<uint8_t>(buffer, buffer + len)}};
backend.push(std::move(entry)); backend.push(std::move(entry));
} }
@ -204,8 +179,7 @@ public:
nullptr, nullptr,
nullptr, nullptr,
log_name, log_name,
log_tag, log_tag}};
small_str_buffer()}};
backend.push(std::move(entry)); backend.push(std::move(entry));
} }
@ -236,21 +210,20 @@ public:
fmtstr, fmtstr,
store, store,
log_name, log_name,
log_tag, log_tag}};
small_str_buffer()}};
backend.push(std::move(entry)); backend.push(std::move(entry));
} }
private: private:
const std::string log_id; const std::string log_id;
sink& log_sink; sink& log_sink;
detail::log_backend& backend; detail::log_backend& backend;
const std::string log_name; const std::string log_name;
const char log_tag; const char log_tag;
const bool should_print_context; const bool should_print_context;
detail::shared_variable<uint32_t> ctx_value; std::atomic<uint32_t> ctx_value;
detail::shared_variable<int> hex_max_size; std::atomic<int> hex_max_size;
detail::shared_variable<bool> is_enabled; std::atomic<bool> is_enabled;
}; };
} // namespace srslog } // namespace srslog

@ -134,6 +134,7 @@ extern "C" {
#include "srsran/phy/sync/pss.h" #include "srsran/phy/sync/pss.h"
#include "srsran/phy/sync/refsignal_dl_sync.h" #include "srsran/phy/sync/refsignal_dl_sync.h"
#include "srsran/phy/sync/sfo.h" #include "srsran/phy/sync/sfo.h"
#include "srsran/phy/sync/ssb.h"
#include "srsran/phy/sync/sss.h" #include "srsran/phy/sync/sss.h"
#include "srsran/phy/sync/sync.h" #include "srsran/phy/sync/sync.h"

@ -402,7 +402,7 @@ private:
void debug_state(); void debug_state();
int required_buffer_size(rlc_amd_retx_t retx); int required_buffer_size(rlc_amd_retx_t retx);
void retransmit_pdu(); void retransmit_pdu(uint32_t sn);
// Helpers // Helpers
bool poll_required(); bool poll_required();

@ -1254,11 +1254,36 @@ bool make_phy_nzp_csi_rs_resource(const asn1::rrc_nr::nzp_csi_rs_res_s& asn1_nzp
return true; return true;
} }
static inline srsran_subcarrier_spacing_t make_subcarrier_spacing(const subcarrier_spacing_e& asn1_scs)
{
switch (asn1_scs) {
case subcarrier_spacing_opts::options::khz15:
return srsran_subcarrier_spacing_15kHz;
case subcarrier_spacing_opts::options::khz30:
return srsran_subcarrier_spacing_30kHz;
case subcarrier_spacing_opts::options::khz60:
return srsran_subcarrier_spacing_60kHz;
case subcarrier_spacing_opts::options::khz120:
return srsran_subcarrier_spacing_120kHz;
case subcarrier_spacing_opts::options::khz240:
return srsran_subcarrier_spacing_240kHz;
case subcarrier_spacing_opts::spare3:
case subcarrier_spacing_opts::spare2:
case subcarrier_spacing_opts::spare1:
case subcarrier_spacing_opts::nulltype:
default:
asn1::log_warning("Not supported subcarrier spacing ");
break;
}
return srsran_subcarrier_spacing_invalid;
}
bool make_phy_carrier_cfg(const freq_info_dl_s& asn1_freq_info_dl, srsran_carrier_nr_t* out_carrier_nr) bool make_phy_carrier_cfg(const freq_info_dl_s& asn1_freq_info_dl, srsran_carrier_nr_t* out_carrier_nr)
{ {
uint32_t absolute_frequency_ssb = 0; uint32_t absolute_frequency_ssb = 0;
if (asn1_freq_info_dl.absolute_freq_ssb_present) { if (asn1_freq_info_dl.absolute_freq_ssb_present) {
absolute_frequency_ssb = asn1_freq_info_dl.absolute_freq_ssb_present; absolute_frequency_ssb = asn1_freq_info_dl.absolute_freq_ssb;
} else { } else {
asn1::log_warning("Option absolute_freq_ssb not present"); asn1::log_warning("Option absolute_freq_ssb not present");
return false; return false;
@ -1268,26 +1293,12 @@ bool make_phy_carrier_cfg(const freq_info_dl_s& asn1_freq_info_dl, srsran_carrie
return false; return false;
} }
srsran_subcarrier_spacing_t scs = srsran_subcarrier_spacing_15kHz; srsran_subcarrier_spacing_t scs =
switch (asn1_freq_info_dl.scs_specific_carrier_list[0].subcarrier_spacing) { make_subcarrier_spacing(asn1_freq_info_dl.scs_specific_carrier_list[0].subcarrier_spacing);
case subcarrier_spacing_opts::options::khz15: if (scs == srsran_subcarrier_spacing_invalid) {
scs = srsran_subcarrier_spacing_15kHz; return false;
break;
case subcarrier_spacing_opts::options::khz30:
scs = srsran_subcarrier_spacing_30kHz;
break;
case subcarrier_spacing_opts::options::khz60:
scs = srsran_subcarrier_spacing_60kHz;
break;
case subcarrier_spacing_opts::options::khz120:
scs = srsran_subcarrier_spacing_120kHz;
break;
case subcarrier_spacing_opts::options::khz240:
scs = srsran_subcarrier_spacing_240kHz;
break;
default:
asn1::log_warning("Not supported subcarrier spacing ");
} }
// As the carrier structure requires parameters from different objects, set fields separately // As the carrier structure requires parameters from different objects, set fields separately
out_carrier_nr->absolute_frequency_ssb = absolute_frequency_ssb; out_carrier_nr->absolute_frequency_ssb = absolute_frequency_ssb;
out_carrier_nr->absolute_frequency_point_a = asn1_freq_info_dl.absolute_freq_point_a; out_carrier_nr->absolute_frequency_point_a = asn1_freq_info_dl.absolute_freq_point_a;
@ -1296,6 +1307,63 @@ bool make_phy_carrier_cfg(const freq_info_dl_s& asn1_freq_info_dl, srsran_carrie
out_carrier_nr->scs = scs; out_carrier_nr->scs = scs;
return true; return true;
} }
template <class bitstring_t>
static inline void make_ssb_positions_in_burst(const bitstring_t& ans1_position_in_burst,
std::array<bool, SRSRAN_SSB_NOF_POSITION>& position_in_burst)
{
for (uint32_t i = 0; i < SRSRAN_SSB_NOF_POSITION; i++) {
if (i < ans1_position_in_burst.length()) {
position_in_burst[i] = ans1_position_in_burst.get(ans1_position_in_burst.length() - 1 - i);
} else {
position_in_burst[i] = false;
}
}
}
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)
{
phy_cfg_nr_t::ssb_cfg_t ssb = {};
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:
make_ssb_positions_in_burst(serv_cell_cfg.ssb_positions_in_burst.short_bitmap(), ssb.position_in_burst);
break;
case serving_cell_cfg_common_s::ssb_positions_in_burst_c_::types_opts::medium_bitmap:
make_ssb_positions_in_burst(serv_cell_cfg.ssb_positions_in_burst.medium_bitmap(), ssb.position_in_burst);
break;
case serving_cell_cfg_common_s::ssb_positions_in_burst_c_::types_opts::long_bitmap:
make_ssb_positions_in_burst(serv_cell_cfg.ssb_positions_in_burst.long_bitmap(), ssb.position_in_burst);
break;
case serving_cell_cfg_common_s::ssb_positions_in_burst_c_::types_opts::nulltype:
asn1::log_warning("SSB position in burst nulltype");
return false;
}
} else {
asn1::log_warning("SSB position in burst not present");
return false;
}
if (serv_cell_cfg.ssb_periodicity_serving_cell_present) {
ssb.periodicity_ms = (uint32_t)serv_cell_cfg.ssb_periodicity_serving_cell.to_number();
} else {
asn1::log_warning("SSB periodicity not present");
return false;
}
if (serv_cell_cfg.ssb_subcarrier_spacing_present) {
ssb.scs = make_subcarrier_spacing(serv_cell_cfg.ssb_subcarrier_spacing);
if (ssb.scs == srsran_subcarrier_spacing_invalid) {
return false;
}
} else {
asn1::log_warning("SSB subcarrier spacing not present");
return false;
}
if (out_ssb != nullptr) {
*out_ssb = ssb;
}
return true;
}
} // namespace srsran } // namespace srsran
namespace srsenb { namespace srsenb {

@ -28,6 +28,10 @@ namespace srsran {
constexpr std::array<srsran_band_helper::nr_band, srsran_band_helper::nof_nr_bands_fr1> constexpr std::array<srsran_band_helper::nr_band, srsran_band_helper::nof_nr_bands_fr1>
srsran_band_helper::nr_band_table_fr1; srsran_band_helper::nr_band_table_fr1;
constexpr std::array<srsran_band_helper::nr_raster_params, 3> srsran_band_helper::nr_fr_params; constexpr std::array<srsran_band_helper::nr_raster_params, 3> srsran_band_helper::nr_fr_params;
constexpr std::array<srsran_band_helper::nr_operating_band, srsran_band_helper::nof_nr_operating_band_fr1>
srsran_band_helper::nr_operating_bands_fr1;
constexpr std::array<srsran_band_helper::nr_band_ss_raster, srsran_band_helper::nof_nr_band_ss_raster>
srsran_band_helper::nr_band_ss_raster_table;
// Formula in 5.4.2.1 // Formula in 5.4.2.1
double srsran_band_helper::nr_arfcn_to_freq(uint32_t nr_arfcn) double srsran_band_helper::nr_arfcn_to_freq(uint32_t nr_arfcn)
@ -60,6 +64,55 @@ std::vector<uint32_t> srsran_band_helper::get_bands_nr(uint32_t
return bands; return bands;
} }
uint16_t srsran_band_helper::get_band_from_dl_freq_Hz(double freq) const
{
uint32_t freq_MHz = (uint32_t)round(freq / 1e6);
for (const nr_operating_band& band : nr_operating_bands_fr1) {
if (freq_MHz >= band.F_DL_low and freq_MHz <= band.F_DL_high) {
return band.band;
}
}
return UINT16_MAX;
}
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
for (const nr_band_ss_raster& ss_raster : nr_band_ss_raster_table) {
// Check if band and SCS match!
if (ss_raster.band == band && ss_raster.scs == scs) {
return ss_raster.pattern;
}
// As bands are in ascending order, do not waste more time if the current band is bigger
if (ss_raster.band > band) {
return SRSRAN_SSB_PATTERN_INVALID;
}
}
// Band is out of range, so consider invalid
return SRSRAN_SSB_PATTERN_INVALID;
}
srsran_duplex_mode_t srsran_band_helper::get_duplex_mode(uint16_t band) const
{
// Look for the given band
for (const nr_operating_band& b : nr_operating_bands_fr1) {
// Check if band and SCS match!
if (b.band == band) {
return b.duplex_mode;
}
// As bands are in ascending order, do not waste more time if the current band is bigger
if (b.band > band) {
return SRSRAN_DUPLEX_MODE_INVALID;
}
}
// Band is out of range, so consider invalid
return SRSRAN_DUPLEX_MODE_INVALID;
}
srsran_band_helper::nr_raster_params srsran_band_helper::get_raster_params(uint32_t nr_arfcn) srsran_band_helper::nr_raster_params srsran_band_helper::get_raster_params(uint32_t nr_arfcn)
{ {
for (auto& fr : nr_fr_params) { for (auto& fr : nr_fr_params) {

@ -975,7 +975,8 @@ uint32_t srsran_csi_rs_measure_info(const srsran_csi_trs_measurements_t* measure
measure->rsrp_dB, measure->rsrp_dB,
measure->epre_dB, measure->epre_dB,
measure->n0_dB, measure->n0_dB,
measure->snr_dB); measure->snr_dB,
measure->delay_us);
// Append measured CFO and the maximum CFO that can be measured // Append measured CFO and the maximum CFO that can be measured
if (isnormal(measure->cfo_hz_max)) { if (isnormal(measure->cfo_hz_max)) {

@ -221,7 +221,7 @@ void channel::run(cf_t* in[SRSRAN_MAX_CHANNELS],
// Logging // Logging
std::stringstream str; std::stringstream str;
str << "t=" << t.full_secs + t.frac_secs << "s; "; str << "Channel: t=" << t.full_secs + t.frac_secs << "s; ";
if (delay[0]) { if (delay[0]) {
str << "delay=" << delay[0]->delay_us << "us; "; str << "delay=" << delay[0]->delay_us << "us; ";
} }

@ -21,6 +21,7 @@
#include "srsran/phy/common/phy_common_nr.h" #include "srsran/phy/common/phy_common_nr.h"
#include "srsran/phy/utils/vector.h" #include "srsran/phy/utils/vector.h"
#include <stdlib.h>
#include <string.h> #include <string.h>
const char* srsran_rnti_type_str(srsran_rnti_type_t rnti_type) const char* srsran_rnti_type_str(srsran_rnti_type_t rnti_type)
@ -46,6 +47,29 @@ const char* srsran_rnti_type_str(srsran_rnti_type_t rnti_type)
} }
return "unknown"; return "unknown";
} }
const char* srsran_rnti_type_str_short(srsran_rnti_type_t rnti_type)
{
switch (rnti_type) {
case srsran_rnti_type_c:
return "c";
case srsran_rnti_type_p:
return "p";
case srsran_rnti_type_si:
return "si";
case srsran_rnti_type_ra:
return "ra";
case srsran_rnti_type_tc:
return "tc";
case srsran_rnti_type_cs:
return "cs";
case srsran_rnti_type_sp_csi:
return "sp-csi";
case srsran_rnti_type_mcs_c:
return "mcs-c";
default:; // Do nothing
}
return "unknown";
}
const char* srsran_dci_format_nr_string(srsran_dci_format_nr_t format) const char* srsran_dci_format_nr_string(srsran_dci_format_nr_t format)
{ {
@ -161,34 +185,45 @@ uint32_t srsran_min_symbol_sz_rb(uint32_t nof_prb)
return 0; return 0;
} }
float srsran_symbol_distance_s(uint32_t l0, uint32_t l1, srsran_subcarrier_spacing_t scs) float srsran_symbol_offset_s(uint32_t l, srsran_subcarrier_spacing_t scs)
{ {
// l0 must be smaller than l1 // Compute at what symbol there is a longer CP
if (l0 >= l1) { uint32_t cp_boundary = SRSRAN_EXT_CP_SYMBOL(scs);
return 0.0f;
}
// Count number of symbols in between // First symbol CP
uint32_t count = l1 - l0; uint32_t N = 0;
// Compute at what symbol there is a longer CP // Symbols in between the first and l
uint32_t cp_boundary = 7U << (uint32_t)scs; N += (2048 + 144) * l;
// Select whether extra CP shall be added // Add extended CP samples from first OFDM symbol
uint32_t extra_cp = 0; if (l > 0) {
if (l0 < cp_boundary && l1 >= cp_boundary) { N += 16;
extra_cp = 16;
} }
// Compute reference FFT size // Add extra samples at the longer CP boundary
uint32_t N = (2048 + 144) * count + extra_cp; if (l >= cp_boundary) {
N += 16;
}
// Compute time using reference sampling rate
float TS = SRSRAN_LTE_TS / (float)(1U << (uint32_t)scs); float TS = SRSRAN_LTE_TS / (float)(1U << (uint32_t)scs);
// Return symbol distance in microseconds // Return symbol offset in seconds
return (float)N * TS; return (float)N * TS;
} }
float srsran_symbol_distance_s(uint32_t l0, uint32_t l1, srsran_subcarrier_spacing_t scs)
{
// l0 must be smaller than l1
if (l0 >= l1) {
return 0.0f;
}
// Return symbol distance in seconds
return srsran_symbol_offset_s(l1, scs) - srsran_symbol_offset_s(l0, scs);
}
bool srsran_tdd_nr_is_dl(const srsran_tdd_config_nr_t* cfg, uint32_t numerology, uint32_t slot_idx) bool srsran_tdd_nr_is_dl(const srsran_tdd_config_nr_t* cfg, uint32_t numerology, uint32_t slot_idx)
{ {
// Protect NULL pointer access // Protect NULL pointer access
@ -296,3 +331,32 @@ uint32_t srsran_csi_meas_info(const srsran_csi_trs_measurements_t* meas, char* s
meas->cfo_hz, meas->cfo_hz,
meas->delay_us); meas->delay_us);
} }
srsran_subcarrier_spacing_t srsran_subcarrier_spacing_from_str(const char* str)
{
if (str == NULL) {
return srsran_subcarrier_spacing_invalid;
}
uint32_t scs = (uint32_t)roundf(strtof(str, NULL));
switch (scs) {
case 15:
case 15000:
return srsran_subcarrier_spacing_15kHz;
case 30:
case 30000:
return srsran_subcarrier_spacing_30kHz;
case 60:
case 60000:
return srsran_subcarrier_spacing_60kHz;
case 120:
case 120000:
return srsran_subcarrier_spacing_120kHz;
case 240:
case 240000:
return srsran_subcarrier_spacing_240kHz;
default:; // Do nothing
}
return srsran_subcarrier_spacing_invalid;
}

@ -50,21 +50,14 @@ int srsran_enb_dl_init(srsran_enb_dl_t* q, cf_t* out_buffer[SRSRAN_MAX_PORTS], u
goto clean_exit; goto clean_exit;
} }
} }
for (int i = 0; i < SRSRAN_MAX_PORTS; i++) {
q->out_buffer[i] = out_buffer[i];
}
srsran_ofdm_cfg_t ofdm_cfg = {}; srsran_ofdm_cfg_t ofdm_cfg = {};
ofdm_cfg.nof_prb = max_prb; ofdm_cfg.nof_prb = max_prb;
ofdm_cfg.cp = SRSRAN_CP_NORM; ofdm_cfg.cp = SRSRAN_CP_EXT;
ofdm_cfg.normalize = false; ofdm_cfg.normalize = false;
for (int i = 0; i < SRSRAN_MAX_PORTS; i++) {
ofdm_cfg.in_buffer = q->sf_symbols[i];
ofdm_cfg.out_buffer = out_buffer[i];
ofdm_cfg.sf_type = SRSRAN_SF_NORM;
if (srsran_ofdm_tx_init_cfg(&q->ifft[i], &ofdm_cfg)) {
ERROR("Error initiating FFT (%d)", i);
goto clean_exit;
}
}
ofdm_cfg.in_buffer = q->sf_symbols[0]; ofdm_cfg.in_buffer = q->sf_symbols[0];
ofdm_cfg.out_buffer = out_buffer[0]; ofdm_cfg.out_buffer = out_buffer[0];
ofdm_cfg.sf_type = SRSRAN_SF_MBSFN; ofdm_cfg.sf_type = SRSRAN_SF_MBSFN;
@ -159,6 +152,19 @@ int srsran_enb_dl_set_cell(srsran_enb_dl_t* q, srsran_cell_t cell)
srsran_regs_free(&q->regs); srsran_regs_free(&q->regs);
} }
q->cell = cell; q->cell = cell;
srsran_ofdm_cfg_t ofdm_cfg = {};
ofdm_cfg.nof_prb = q->cell.nof_prb;
ofdm_cfg.cp = cell.cp;
ofdm_cfg.normalize = false;
for (int i = 0; i < SRSRAN_MAX_PORTS; i++) {
ofdm_cfg.in_buffer = q->sf_symbols[i];
ofdm_cfg.out_buffer = q->out_buffer[i];
ofdm_cfg.sf_type = SRSRAN_SF_NORM;
if (srsran_ofdm_tx_init_cfg(&q->ifft[i], &ofdm_cfg)) {
ERROR("Error initiating FFT (%d)", i);
return SRSRAN_ERROR;
}
}
if (srsran_regs_init(&q->regs, q->cell)) { if (srsran_regs_init(&q->regs, q->cell)) {
ERROR("Error resizing REGs"); ERROR("Error resizing REGs");
return SRSRAN_ERROR; return SRSRAN_ERROR;

@ -46,19 +46,7 @@ int srsran_enb_ul_init(srsran_enb_ul_t* q, cf_t* in_buffer, uint32_t max_prb)
perror("malloc"); perror("malloc");
goto clean_exit; goto clean_exit;
} }
q->in_buffer = in_buffer;
srsran_ofdm_cfg_t ofdm_cfg = {};
ofdm_cfg.nof_prb = max_prb;
ofdm_cfg.in_buffer = in_buffer;
ofdm_cfg.out_buffer = q->sf_symbols;
ofdm_cfg.cp = SRSRAN_CP_NORM;
ofdm_cfg.freq_shift_f = -0.5f;
ofdm_cfg.normalize = false;
ofdm_cfg.rx_window_offset = 0.5f;
if (srsran_ofdm_rx_init_cfg(&q->fft, &ofdm_cfg)) {
ERROR("Error initiating FFT");
goto clean_exit;
}
if (srsran_pucch_init_enb(&q->pucch)) { if (srsran_pucch_init_enb(&q->pucch)) {
ERROR("Error creating PUCCH object"); ERROR("Error creating PUCCH object");
@ -117,6 +105,18 @@ int srsran_enb_ul_set_cell(srsran_enb_ul_t* q,
if (cell.id != q->cell.id || q->cell.nof_prb == 0) { if (cell.id != q->cell.id || q->cell.nof_prb == 0) {
q->cell = cell; q->cell = cell;
srsran_ofdm_cfg_t ofdm_cfg = {};
ofdm_cfg.nof_prb = q->cell.nof_prb;
ofdm_cfg.in_buffer = q->in_buffer;
ofdm_cfg.out_buffer = q->sf_symbols;
ofdm_cfg.cp = q->cell.cp;
ofdm_cfg.freq_shift_f = -0.5f;
ofdm_cfg.normalize = false;
ofdm_cfg.rx_window_offset = 0.5f;
if (srsran_ofdm_rx_init_cfg(&q->fft, &ofdm_cfg)) {
ERROR("Error initiating FFT");
return SRSRAN_ERROR;
}
if (srsran_ofdm_rx_set_prb(&q->fft, q->cell.cp, q->cell.nof_prb)) { if (srsran_ofdm_rx_set_prb(&q->fft, q->cell.cp, q->cell.nof_prb)) {
ERROR("Error initiating FFT"); ERROR("Error initiating FFT");
return SRSRAN_ERROR; return SRSRAN_ERROR;

@ -26,7 +26,7 @@
#include "srsran/phy/io/filesource.h" #include "srsran/phy/io/filesource.h"
#include "srsran/phy/utils/debug.h" #include "srsran/phy/utils/debug.h"
int srsran_filesource_init(srsran_filesource_t* q, char* filename, srsran_datatype_t type) int srsran_filesource_init(srsran_filesource_t* q, const char* filename, srsran_datatype_t type)
{ {
bzero(q, sizeof(srsran_filesource_t)); bzero(q, sizeof(srsran_filesource_t));
q->f = fopen(filename, "r"); q->f = fopen(filename, "r");

@ -1997,7 +1997,13 @@ int srsran_dci_ctx_to_str(const srsran_dci_ctx_t* ctx, char* str, uint32_t str_l
uint32_t len = 0; uint32_t len = 0;
// Print base // Print base
len = srsran_print_check(str, str_len, len, "rnti=%04x dci=%s ", ctx->rnti, srsran_dci_format_nr_string(ctx->format)); len = srsran_print_check(str,
str_len,
len,
"%s-rnti=%04x dci=%s ",
srsran_rnti_type_str_short(ctx->rnti_type),
ctx->rnti,
srsran_dci_format_nr_string(ctx->format));
if (ctx->format != srsran_dci_format_nr_rar) { if (ctx->format != srsran_dci_format_nr_rar) {
len = srsran_print_check(str, str_len, len, "L=%d cce=%d ", ctx->location.L, ctx->location.ncce); len = srsran_print_check(str, str_len, len, "L=%d cce=%d ", ctx->location.L, ctx->location.ncce);

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

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

@ -198,16 +198,12 @@ static ra_nr_table_t ra_nr_select_table_pdsch(srsran_mcs_table_t mcs_tab
srsran_search_space_type_t search_space_type, srsran_search_space_type_t search_space_type,
srsran_rnti_type_t rnti_type) srsran_rnti_type_t rnti_type)
{ {
// Non-implemented parameters
bool sps_config_mcs_table_present = false;
bool is_pdcch_sps = false;
// - the higher layer parameter mcs-Table given by PDSCH-Config is set to 'qam256', and // - the higher layer parameter mcs-Table given by PDSCH-Config is set to 'qam256', and
// - the PDSCH is scheduled by a PDCCH with DCI format 1_1 with // - the PDSCH is scheduled by a PDCCH with DCI format 1_1 with
// - CRC scrambled by C-RNTI // - CRC scrambled by C-RNTI
if (mcs_table == srsran_mcs_table_256qam && dci_format == srsran_dci_format_nr_1_1 && if (mcs_table == srsran_mcs_table_256qam && dci_format == srsran_dci_format_nr_1_1 &&
rnti_type == srsran_rnti_type_c) { rnti_type == srsran_rnti_type_c) {
return ra_nr_table_1; return ra_nr_table_2;
} }
// the UE is not configured with MCS-C-RNTI, // the UE is not configured with MCS-C-RNTI,
@ -223,10 +219,10 @@ static ra_nr_table_t ra_nr_select_table_pdsch(srsran_mcs_table_t mcs_tab
// - the higher layer parameter mcs-Table given by PDSCH-Config is set to 'qam256', // - the higher layer parameter mcs-Table given by PDSCH-Config is set to 'qam256',
// - if the PDSCH is scheduled by a PDCCH with DCI format 1_1 with CRC scrambled by CS-RNTI or // - if the PDSCH is scheduled by a PDCCH with DCI format 1_1 with CRC scrambled by CS-RNTI or
// - if the PDSCH is scheduled without corresponding PDCCH transmission using SPS-Config, // - if the PDSCH is scheduled without corresponding PDCCH transmission using SPS-Config,
if (!sps_config_mcs_table_present && mcs_table == srsran_mcs_table_256qam && // if (!sps_config_mcs_table_present && mcs_table == srsran_mcs_table_256qam &&
((dci_format == srsran_dci_format_nr_1_1 && rnti_type == srsran_rnti_type_c) || (!is_pdcch_sps))) { // ((dci_format == srsran_dci_format_nr_1_1 && rnti_type == srsran_rnti_type_cs) || (!is_pdcch_sps))) {
return ra_nr_table_2; // return ra_nr_table_2;
} // }
// - the UE is configured with the higher layer parameter mcs-Table given by SPS-Config set to 'qam64LowSE' // - the UE is configured with the higher layer parameter mcs-Table given by SPS-Config set to 'qam64LowSE'
// - if the PDSCH is scheduled by a PDCCH with CRC scrambled by CS-RNTI or // - if the PDSCH is scheduled by a PDCCH with CRC scrambled by CS-RNTI or

@ -74,6 +74,43 @@ uint32_t sch_nr_n_prb_lbrm(uint32_t nof_prb)
return 273; return 273;
} }
static int sch_nr_cbsegm(srsran_basegraph_t bg, uint32_t tbs, srsran_cbsegm_t* cbsegm)
{
if (bg == BG1) {
if (srsran_cbsegm_ldpc_bg1(cbsegm, tbs) != SRSRAN_SUCCESS) {
ERROR("Error: calculating LDPC BG1 code block segmentation for tbs=%d", tbs);
return SRSRAN_ERROR;
}
} else {
if (srsran_cbsegm_ldpc_bg2(cbsegm, tbs) != SRSRAN_SUCCESS) {
ERROR("Error: calculating LDPC BG1 code block segmentation for tbs=%d", tbs);
return SRSRAN_ERROR;
}
}
return SRSRAN_SUCCESS;
}
static int sch_nr_Nref(uint32_t N_rb, srsran_mcs_table_t mcs_table, uint32_t max_mimo_layers)
{
uint32_t N_re_lbrm = SRSRAN_MAX_NRE_NR * sch_nr_n_prb_lbrm(N_rb);
double TCR_lbrm = 948.0 / 1024.0;
uint32_t Qm_lbrm = (mcs_table == srsran_mcs_table_256qam) ? 8 : 6;
uint32_t TBS_LRBM = srsran_ra_nr_tbs(N_re_lbrm, 1.0, TCR_lbrm, Qm_lbrm, max_mimo_layers);
double R = 2.0 / 3.0;
srsran_basegraph_t bg = srsran_sch_nr_select_basegraph(TBS_LRBM, R);
// Compute segmentation
srsran_cbsegm_t cbsegm = {};
int r = sch_nr_cbsegm(bg, TBS_LRBM, &cbsegm);
if (r < SRSRAN_SUCCESS) {
ERROR("Error computing TB segmentation");
return SRSRAN_ERROR;
}
return (int)ceil((double)TBS_LRBM / (double)(cbsegm.C * R));
}
int srsran_sch_nr_fill_tb_info(const srsran_carrier_nr_t* carrier, int srsran_sch_nr_fill_tb_info(const srsran_carrier_nr_t* carrier,
const srsran_sch_cfg_t* sch_cfg, const srsran_sch_cfg_t* sch_cfg,
const srsran_sch_tb_t* tb, const srsran_sch_tb_t* tb,
@ -88,16 +125,9 @@ int srsran_sch_nr_fill_tb_info(const srsran_carrier_nr_t* carrier,
// Compute code block segmentation // Compute code block segmentation
srsran_cbsegm_t cbsegm = {}; srsran_cbsegm_t cbsegm = {};
if (bg == BG1) { if (sch_nr_cbsegm(bg, tb->tbs, &cbsegm) < SRSRAN_SUCCESS) {
if (srsran_cbsegm_ldpc_bg1(&cbsegm, tb->tbs) != SRSRAN_SUCCESS) { ERROR("Error calculation TB segmentation");
ERROR("Error: calculating LDPC BG1 code block segmentation for tbs=%d", tb->tbs); return SRSRAN_ERROR;
return SRSRAN_ERROR;
}
} else {
if (srsran_cbsegm_ldpc_bg2(&cbsegm, tb->tbs) != SRSRAN_SUCCESS) {
ERROR("Error: calculating LDPC BG1 code block segmentation for tbs=%d", tb->tbs);
return SRSRAN_ERROR;
}
} }
if (cbsegm.Z > MAX_LIFTSIZE) { if (cbsegm.Z > MAX_LIFTSIZE) {
@ -120,13 +150,11 @@ int srsran_sch_nr_fill_tb_info(const srsran_carrier_nr_t* carrier,
cfg->Nl = tb->N_L; cfg->Nl = tb->N_L;
// Calculate Nref // Calculate Nref
uint32_t N_re_lbrm = SRSRAN_MAX_NRE_NR * sch_nr_n_prb_lbrm(carrier->nof_prb); int Nref = sch_nr_Nref(carrier->nof_prb, sch_cfg->mcs_table, carrier->max_mimo_layers);
double TCR_lbrm = 948.0 / 1024.0; if (Nref < SRSRAN_SUCCESS) {
uint32_t Qm_lbrm = (sch_cfg->mcs_table == srsran_mcs_table_256qam) ? 8 : 6; ERROR("Error computing N_ref");
uint32_t max_mimo_layers = SRSRAN_MAX(carrier->max_mimo_layers, 4); }
uint32_t TBS_LRBM = srsran_ra_nr_tbs(N_re_lbrm, 1.0, TCR_lbrm, Qm_lbrm, max_mimo_layers); cfg->Nref = (uint32_t)Nref;
double R = 2.0 / 3.0;
cfg->Nref = (uint32_t)ceil((double)TBS_LRBM / (double)(cbsegm.C * R));
// Calculate number of code blocks after applying CBGTI... not implemented, activate all CB // Calculate number of code blocks after applying CBGTI... not implemented, activate all CB
for (uint32_t r = 0; r < cbsegm.C; r++) { for (uint32_t r = 0; r < cbsegm.C; r++) {
@ -665,7 +693,6 @@ static int sch_nr_decode(srsran_sch_nr_t* q,
res->avg_iter = NAN; res->avg_iter = NAN;
} }
// Not all CB are decoded, skip TB union and CRC check // Not all CB are decoded, skip TB union and CRC check
if (cb_ok != cfg.C) { if (cb_ok != cfg.C) {
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;

@ -201,7 +201,7 @@ public:
// Set receiver subdev spec if specified // Set receiver subdev spec if specified
if (not rx_subdev.empty()) { if (not rx_subdev.empty()) {
err = set_rx_subdev(tx_subdev); err = set_rx_subdev(rx_subdev);
if (err != UHD_ERROR_NONE) { if (err != UHD_ERROR_NONE) {
return err; return err;
} }

@ -103,3 +103,52 @@ int srsran_pss_nr_extract_lse(const cf_t* ssb_grid, uint32_t N_id_2, cf_t lse[SR
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
int srsran_pss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], float* norm_corr, uint32_t* found_N_id_2)
{
// Verify inputs
if (ssb_grid == NULL || norm_corr == NULL || found_N_id_2 == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
const cf_t* pss_ptr = &ssb_grid[SRSRAN_PSS_NR_SYMBOL_IDX * SRSRAN_SSB_BW_SUBC + PSS_NR_SUBC_BEGIN];
// Measure PSS region average power
float avg_power = srsran_vec_avg_power_cf(pss_ptr, SRSRAN_PSS_NR_LEN);
// If no energy detected or invalid, consider zero correlation
if (!isnormal(avg_power)) {
*norm_corr = 0.0f;
*found_N_id_2 = 0;
return SRSRAN_SUCCESS;
}
// Search state
float max_corr = -INFINITY; //< Stores best correlation
uint32_t N_id_2 = 0; //< Best N_id_2
// Iterate over all possible N_id_2
for (uint32_t N_id_2_candidate = 0; N_id_2_candidate < SRSRAN_NOF_NID_2_NR; N_id_2_candidate++) {
uint32_t m = PSS_NR_SEQUENCE_M(N_id_2_candidate);
cf_t acc = 0.0f;
// Correlate d sequence fist part
acc += srsran_vec_dot_prod_ccc(&pss_nr_d[m], &pss_ptr[0], SRSRAN_PSS_NR_LEN - m);
// Correlate d sequence second part
acc += srsran_vec_dot_prod_ccc(&pss_nr_d[0], &pss_ptr[SRSRAN_PSS_NR_LEN - m], m);
// Correlate
float corr = SRSRAN_CSQABS(acc);
if (corr > max_corr) {
N_id_2 = N_id_2_candidate;
max_corr = corr;
}
}
// Copy found result
*norm_corr = max_corr / avg_power / SRSRAN_PSS_NR_LEN;
*found_N_id_2 = N_id_2;
return SRSRAN_SUCCESS;
}

@ -77,8 +77,14 @@ static inline void refsignal_sf_prepare_correlation(srsran_refsignal_dl_sync_t*
{ {
uint32_t sf_len = q->ifft.sf_sz; uint32_t sf_len = q->ifft.sf_sz;
cf_t* ptr_filt = q->conv_fft_cc.filter_fft; cf_t* ptr_filt = q->conv_fft_cc.filter_fft;
memcpy(ptr_filt, q->sequences[0], sizeof(cf_t) * sf_len);
srsran_vec_cf_zero(&ptr_filt[sf_len], sf_len); // Put first subframe in buffer
srsran_vec_cf_copy(ptr_filt, q->sequences[0], sf_len);
// Zero the rest of the buffer
srsran_vec_cf_zero(&ptr_filt[sf_len], q->conv_fft_cc.output_len - sf_len);
// Make correlation sequence in frequency domain
srsran_dft_run_c(&q->conv_fft_cc.filter_plan, ptr_filt, ptr_filt); srsran_dft_run_c(&q->conv_fft_cc.filter_plan, ptr_filt, ptr_filt);
} }
@ -142,7 +148,7 @@ static inline void refsignal_dl_pss_sss_strength(srsran_refsignal_dl_sync_t* q,
} }
} }
int srsran_refsignal_dl_sync_init(srsran_refsignal_dl_sync_t* q) int srsran_refsignal_dl_sync_init(srsran_refsignal_dl_sync_t* q, srsran_cp_t cp)
{ {
int ret = SRSRAN_ERROR_INVALID_INPUTS; int ret = SRSRAN_ERROR_INVALID_INPUTS;
@ -180,12 +186,12 @@ int srsran_refsignal_dl_sync_init(srsran_refsignal_dl_sync_t* q)
// Initiate OFDM modulator // Initiate OFDM modulator
if (!ret) { if (!ret) {
ret = srsran_ofdm_tx_init(&q->ifft, SRSRAN_CP_NORM, q->ifft_buffer_in, q->ifft_buffer_out, SRSRAN_MAX_PRB); ret = srsran_ofdm_tx_init(&q->ifft, cp, q->ifft_buffer_in, q->ifft_buffer_out, SRSRAN_MAX_PRB);
} }
// Set PRB // Set PRB
if (!ret) { if (!ret) {
ret = srsran_ofdm_tx_set_prb(&q->ifft, SRSRAN_CP_NORM, SRSRAN_MAX_PRB); ret = srsran_ofdm_tx_set_prb(&q->ifft, cp, SRSRAN_MAX_PRB);
} }
// Initiate FFT Convolution // Initiate FFT Convolution
@ -258,7 +264,12 @@ int srsran_refsignal_dl_sync_set_cell(srsran_refsignal_dl_sync_t* q, srsran_cell
srsran_ofdm_tx_sf(&q->ifft); srsran_ofdm_tx_sf(&q->ifft);
// Undo scaling and normalize overall power to 1 // Undo scaling and normalize overall power to 1
float scale = 1.0f / nof_re; float scale = 1.0f;
// Avoid zero division
if (nof_re != 0) {
scale /= (float)nof_re;
}
// Copy time domain signal, normalized by number of RE // Copy time domain signal, normalized by number of RE
srsran_vec_sc_prod_cfc(q->ifft_buffer_out, scale, q->sequences[i], q->ifft.sf_sz); srsran_vec_sc_prod_cfc(q->ifft_buffer_out, scale, q->sequences[i], q->ifft.sf_sz);
@ -298,22 +309,23 @@ void srsran_refsignal_dl_sync_free(srsran_refsignal_dl_sync_t* q)
} }
} }
int srsran_refsignal_dl_sync_find_peak(srsran_refsignal_dl_sync_t* q, cf_t* buffer, uint32_t nsamples) int refsignal_dl_sync_find_peak(srsran_refsignal_dl_sync_t* q, cf_t* buffer, uint32_t nsamples)
{ {
int ret = SRSRAN_ERROR; int ret = SRSRAN_ERROR;
float peak_value = 0.0f; float peak_value = 0.0f;
int peak_idx = 0; int peak_idx = 0;
float rms_avg = 0; float rms_avg = 0;
uint32_t sf_len = q->ifft.sf_sz;
uint32_t sf_len = q->ifft.sf_sz;
if (sf_len == 0) {
return SRSRAN_ERROR;
}
// Load correlation sequence and convert to frequency domain // Load correlation sequence and convert to frequency domain
refsignal_sf_prepare_correlation(q); refsignal_sf_prepare_correlation(q);
// Limit correlate for a frame or less
nsamples = SRSRAN_MIN(nsamples - sf_len, SRSRAN_NOF_SF_X_FRAME * sf_len);
// Correlation // Correlation
for (int n = 0; n < nsamples; n += sf_len) { for (uint32_t n = 0; n + q->conv_fft_cc.filter_len < nsamples; n += q->conv_fft_cc.input_len) {
// Correlate, find maximum, calculate RMS and peak // Correlate, find maximum, calculate RMS and peak
uint32_t imax = 0; uint32_t imax = 0;
float peak = 0.0f; float peak = 0.0f;
@ -336,7 +348,7 @@ int srsran_refsignal_dl_sync_find_peak(srsran_refsignal_dl_sync_t* q, cf_t* buff
} }
// Double check sub-frame selection failure due to high PSS // Double check sub-frame selection failure due to high PSS
if (ret > 0) { if (ret >= 0) {
float sss_strength = 0.0f; float sss_strength = 0.0f;
float sss_strength_false = 0.0f; float sss_strength_false = 0.0f;
refsignal_dl_pss_sss_strength(q, &buffer[peak_idx], 0, NULL, &sss_strength, &sss_strength_false); refsignal_dl_pss_sss_strength(q, &buffer[peak_idx], 0, NULL, &sss_strength, &sss_strength_false);
@ -364,151 +376,154 @@ int srsran_refsignal_dl_sync_find_peak(srsran_refsignal_dl_sync_t* q, cf_t* buff
return ret; return ret;
} }
void srsran_refsignal_dl_sync_run(srsran_refsignal_dl_sync_t* q, cf_t* buffer, uint32_t nsamples) int srsran_refsignal_dl_sync_run(srsran_refsignal_dl_sync_t* q, cf_t* buffer, uint32_t nsamples)
{ {
if (q) { if (q == NULL || buffer == NULL) {
uint32_t sf_len = q->ifft.sf_sz; return SRSRAN_ERROR_INVALID_INPUTS;
uint32_t sf_count = 0; }
float rsrp_lin = 0.0f; uint32_t sf_len = q->ifft.sf_sz;
float rsrp_lin_min = +INFINITY; uint32_t sf_count = 0;
float rsrp_lin_max = -INFINITY; float rsrp_lin = 0.0f;
float rssi_lin = 0.0f; float rsrp_lin_min = +INFINITY;
float cfo_acc = 0.0f; float rsrp_lin_max = -INFINITY;
float cfo_min = +INFINITY; float rssi_lin = 0.0f;
float cfo_max = -INFINITY; float cfo_acc = 0.0f;
float sss_strength_avg = 0.0f; float cfo_min = +INFINITY;
float sss_strength_false_avg = 0.0f; float cfo_max = -INFINITY;
float rsrp_false_avg = 0.0f; float sss_strength_avg = 0.0f;
bool false_alarm = false; float sss_strength_false_avg = 0.0f;
float rsrp_false_avg = 0.0f;
// Stage 1: find peak bool false_alarm = false;
int peak_idx = srsran_refsignal_dl_sync_find_peak(q, buffer, nsamples);
// Stage 1: find peak
// Stage 2: Proccess subframes int peak_idx = refsignal_dl_sync_find_peak(q, buffer, nsamples);
if (peak_idx >= 0) {
// Calculate initial subframe index and sample // Stage 2: Proccess subframes
uint32_t sf_idx_init = (2 * SRSRAN_NOF_SF_X_FRAME - peak_idx / sf_len) % SRSRAN_NOF_SF_X_FRAME; if (peak_idx >= 0) {
uint32_t n_init = peak_idx % sf_len; // Calculate initial subframe index and sample
uint32_t sf_idx_init = SRSRAN_NOF_SF_X_FRAME - (peak_idx / sf_len) % SRSRAN_NOF_SF_X_FRAME;
for (int sf_idx = sf_idx_init, n = n_init; n < (nsamples - sf_len + 1); uint32_t n_init = peak_idx % sf_len;
sf_idx = (sf_idx + 1) % SRSRAN_NOF_SF_X_FRAME, n += sf_len) {
cf_t* buf = &buffer[n]; for (uint32_t sf_idx = sf_idx_init, n = n_init; n < (nsamples - sf_len + 1);
sf_idx = (sf_idx + 1) % SRSRAN_NOF_SF_X_FRAME, n += sf_len) {
// Measure subframe rsrp, rssi and accumulate cf_t* buf = &buffer[n];
float rsrp = 0.0f, rssi = 0.0f, cfo = 0.0f;
srsran_refsignal_dl_sync_measure_sf(q, buf, sf_idx, &rsrp, &rssi, &cfo); // Measure subframe rsrp, rssi and accumulate
float rsrp = 0.0f, rssi = 0.0f, cfo = 0.0f;
// Update measurements srsran_refsignal_dl_sync_measure_sf(q, buf, sf_idx, &rsrp, &rssi, &cfo);
rsrp_lin += rsrp;
rsrp_lin_min = SRSRAN_MIN(rsrp_lin_min, rsrp); // Update measurements
rsrp_lin_max = SRSRAN_MAX(rsrp_lin_max, rsrp); rsrp_lin += rsrp;
rsrp_lin_min = SRSRAN_MIN(rsrp_lin_min, rsrp);
rssi_lin += rssi; rsrp_lin_max = SRSRAN_MAX(rsrp_lin_max, rsrp);
cfo_acc += cfo; rssi_lin += rssi;
cfo_min = SRSRAN_MIN(cfo_min, cfo);
cfo_max = SRSRAN_MAX(cfo_max, cfo); cfo_acc += cfo;
cfo_min = SRSRAN_MIN(cfo_min, cfo);
// Compute PSS/SSS strength cfo_max = SRSRAN_MAX(cfo_max, cfo);
if (sf_idx % (SRSRAN_NOF_SF_X_FRAME / 2) == 0) {
float sss_strength = 0.0f; // Compute PSS/SSS strength
float sss_strength_false = 0.0f; if (sf_idx % (SRSRAN_NOF_SF_X_FRAME / 2) == 0) {
refsignal_dl_pss_sss_strength(q, buf, sf_idx, NULL, &sss_strength, &sss_strength_false); float sss_strength = 0.0f;
float sss_strength_false = 0.0f;
float rsrp_false = 0.0f; refsignal_dl_pss_sss_strength(q, buf, sf_idx, NULL, &sss_strength, &sss_strength_false);
srsran_refsignal_dl_sync_measure_sf(q, buf, sf_idx + 1, &rsrp_false, NULL, NULL);
float rsrp_false = 0.0f;
sss_strength_avg += sss_strength; srsran_refsignal_dl_sync_measure_sf(q, buf, sf_idx + 1, &rsrp_false, NULL, NULL);
sss_strength_false_avg += sss_strength_false;
rsrp_false_avg += rsrp_false; sss_strength_avg += sss_strength;
} sss_strength_false_avg += sss_strength_false;
rsrp_false_avg += rsrp_false;
// Increment counter
sf_count++;
} }
// Average measurements // Increment counter
if (sf_count) { sf_count++;
rsrp_lin /= sf_count; }
rssi_lin /= sf_count;
cfo_acc /= sf_count;
sss_strength_avg /= (2.0f * sf_count / SRSRAN_NOF_SF_X_FRAME);
sss_strength_false_avg /= (2.0f * sf_count / SRSRAN_NOF_SF_X_FRAME);
rsrp_false_avg /= (2.0f * sf_count / SRSRAN_NOF_SF_X_FRAME);
}
// RSRP conversion to dB // Average measurements
float rsrp_dB_min = srsran_convert_power_to_dBm(rsrp_lin_min); if (sf_count) {
float rsrp_dB_max = srsran_convert_power_to_dBm(rsrp_lin_max); rsrp_lin /= sf_count;
float rsrp_dB = srsran_convert_power_to_dBm(rsrp_lin); rssi_lin /= sf_count;
float rsrp_false_dB = srsran_convert_power_to_dBm(rsrp_false_avg); cfo_acc /= sf_count;
sss_strength_avg /= (2.0f * sf_count / SRSRAN_NOF_SF_X_FRAME);
// Stage 3: Final false alarm decision sss_strength_false_avg /= (2.0f * sf_count / SRSRAN_NOF_SF_X_FRAME);
uint32_t false_count = 0; rsrp_false_avg /= (2.0f * sf_count / SRSRAN_NOF_SF_X_FRAME);
if (sss_strength_avg < sss_strength_false_avg * REFSIGNAL_DL_SSS_FALSE_RATIO_SEVERE) { }
false_alarm = true;
} else if (sss_strength_avg < sss_strength_false_avg * REFSIGNAL_DL_SSS_FALSE_RATIO_MILD) {
false_count++;
}
if (cfo_max - cfo_min > REFSIGNAL_DL_CFO_MIN_MAX_SEVERE) { // RSRP conversion to dB
false_alarm = true; float rsrp_dB_min = srsran_convert_power_to_dBm(rsrp_lin_min);
} else if (cfo_max - cfo_min > REFSIGNAL_DL_CFO_MIN_MAX_MILD) { float rsrp_dB_max = srsran_convert_power_to_dBm(rsrp_lin_max);
false_count++; float rsrp_dB = srsran_convert_power_to_dBm(rsrp_lin);
} float rsrp_false_dB = srsran_convert_power_to_dBm(rsrp_false_avg);
// Stage 3: Final false alarm decision
uint32_t false_count = 0;
if (sss_strength_avg < sss_strength_false_avg * REFSIGNAL_DL_SSS_FALSE_RATIO_SEVERE) {
false_alarm = true;
} else if (sss_strength_avg < sss_strength_false_avg * REFSIGNAL_DL_SSS_FALSE_RATIO_MILD) {
false_count++;
}
if (rsrp_dB_max - rsrp_dB_min > REFSIGNAL_DL_RSRP_MIN_MAX_SEVERE) { if (cfo_max - cfo_min > REFSIGNAL_DL_CFO_MIN_MAX_SEVERE) {
false_alarm = true; false_alarm = true;
} else if (rsrp_dB_max - rsrp_dB_min > REFSIGNAL_DL_RSRP_MIN_MAX_MILD) { } else if (cfo_max - cfo_min > REFSIGNAL_DL_CFO_MIN_MAX_MILD) {
false_count++; false_count++;
} }
if (rsrp_dB - rsrp_false_dB < REFSIGNAL_DL_RSRP_FALSE_RATIO_SEVERE) { if (rsrp_dB_max - rsrp_dB_min > REFSIGNAL_DL_RSRP_MIN_MAX_SEVERE) {
false_alarm = true; false_alarm = true;
} else if (rsrp_dB - rsrp_false_dB < REFSIGNAL_DL_RSRP_FALSE_RATIO_MILD) { } else if (rsrp_dB_max - rsrp_dB_min > REFSIGNAL_DL_RSRP_MIN_MAX_MILD) {
false_count++; false_count++;
} }
// Allow only one check fail if (rsrp_dB - rsrp_false_dB < REFSIGNAL_DL_RSRP_FALSE_RATIO_SEVERE) {
if (false_count > REFSIGNAL_DL_MAX_FAULT_CHECK) { false_alarm = true;
false_alarm = true; } else if (rsrp_dB - rsrp_false_dB < REFSIGNAL_DL_RSRP_FALSE_RATIO_MILD) {
} false_count++;
}
INFO("-- pci=%03d; rsrp_dB=(%+.1f|%+.1f|%+.1f); rsrp_max-min=%.1f; rsrp_false_ratio=%.1f; " // Allow only one check fail
"cfo=(%.1f|%.1f|%.1f); cfo_max-min=%.1f; sss_ratio=%f; false_count=%d;", if (false_count > REFSIGNAL_DL_MAX_FAULT_CHECK) {
q->refsignal.cell.id, false_alarm = true;
rsrp_dB_min, }
rsrp_dB,
rsrp_dB_max, INFO("-- pci=%03d; rsrp_dB=(%+.1f|%+.1f|%+.1f); rsrp_max-min=%.1f; rsrp_false_ratio=%.1f; "
rsrp_dB_max - rsrp_dB_min, "cfo=(%.1f|%.1f|%.1f); cfo_max-min=%.1f; sss_ratio=%f; false_count=%d;",
rsrp_dB - rsrp_false_dB, q->refsignal.cell.id,
cfo_min, rsrp_dB_min,
cfo_acc, rsrp_dB,
cfo_max, rsrp_dB_max,
cfo_max - cfo_min, rsrp_dB_max - rsrp_dB_min,
sss_strength_avg / sss_strength_false_avg, rsrp_dB - rsrp_false_dB,
false_count); cfo_min,
cfo_acc,
if (!false_alarm) { cfo_max,
// Calculate in dBm cfo_max - cfo_min,
q->rsrp_dBfs = rsrp_dB; sss_strength_avg / sss_strength_false_avg,
false_count);
// Calculate RSSI in dBm
q->rssi_dBfs = srsran_convert_power_to_dBm(rssi_lin); if (!false_alarm) {
// Calculate in dBm
// Calculate RSRQ q->rsrp_dBfs = rsrp_dB;
q->rsrq_dB = srsran_convert_power_to_dB(q->refsignal.cell.nof_prb) + q->rsrp_dBfs - q->rssi_dBfs;
// Calculate RSSI in dBm
q->found = true; q->rssi_dBfs = srsran_convert_power_to_dBm(rssi_lin);
q->cfo_Hz = cfo_acc;
q->peak_index = peak_idx; // Calculate RSRQ
} else { q->rsrq_dB = srsran_convert_power_to_dB(q->refsignal.cell.nof_prb) + q->rsrp_dBfs - q->rssi_dBfs;
refsignal_set_results_not_found(q);
} q->found = true;
q->cfo_Hz = cfo_acc;
q->peak_index = peak_idx;
} else { } else {
refsignal_set_results_not_found(q); refsignal_set_results_not_found(q);
} }
} else {
refsignal_set_results_not_found(q);
} }
return SRSRAN_SUCCESS;
} }
void srsran_refsignal_dl_sync_measure_sf(srsran_refsignal_dl_sync_t* q, void srsran_refsignal_dl_sync_measure_sf(srsran_refsignal_dl_sync_t* q,

@ -36,6 +36,32 @@
*/ */
#define SSB_FREQ_OFFSET_MAX_ERROR_HZ 0.01 #define SSB_FREQ_OFFSET_MAX_ERROR_HZ 0.01
/*
* Correlation size in function of the symbol size. It selects a power of two number at least 8 times bigger than the
* given symbol size but not bigger than 2^13 points.
*/
#define SSB_CORR_SZ(SYMB_SZ) SRSRAN_MIN(1U << (uint32_t)ceil(log2((double)(SYMB_SZ)) + 3.0), 1U << 13U)
static int ssb_init_corr(srsran_ssb_t* q)
{
// Initialise correlation only if it is enabled
if (!q->args.enable_search) {
return SRSRAN_SUCCESS;
}
// For each PSS sequence allocate
for (uint32_t N_id_2 = 0; N_id_2 < SRSRAN_NOF_NID_2_NR; N_id_2++) {
// Allocate sequences
q->pss_seq[N_id_2] = srsran_vec_cf_malloc(q->max_corr_sz);
if (q->pss_seq[N_id_2] == NULL) {
ERROR("Malloc");
return SRSRAN_ERROR;
}
}
return SRSRAN_SUCCESS;
}
int srsran_ssb_init(srsran_ssb_t* q, const srsran_ssb_args_t* args) int srsran_ssb_init(srsran_ssb_t* q, const srsran_ssb_args_t* args)
{ {
// Verify input parameters // Verify input parameters
@ -53,15 +79,22 @@ int srsran_ssb_init(srsran_ssb_t* q, const srsran_ssb_args_t* args)
q->scs_hz = (float)SRSRAN_SUBC_SPACING_NR(q->args.min_scs); q->scs_hz = (float)SRSRAN_SUBC_SPACING_NR(q->args.min_scs);
q->max_symbol_sz = (uint32_t)round(q->args.max_srate_hz / q->scs_hz); q->max_symbol_sz = (uint32_t)round(q->args.max_srate_hz / q->scs_hz);
q->max_corr_sz = SSB_CORR_SZ(q->max_symbol_sz);
// Allocate temporal data // Allocate temporal data
q->tmp_time = srsran_vec_cf_malloc(q->max_symbol_sz); q->tmp_time = srsran_vec_cf_malloc(q->max_corr_sz);
q->tmp_freq = srsran_vec_cf_malloc(q->max_symbol_sz); q->tmp_freq = srsran_vec_cf_malloc(q->max_corr_sz);
if (q->tmp_time == NULL || q->tmp_time == NULL) { q->tmp_corr = srsran_vec_cf_malloc(q->max_corr_sz);
if (q->tmp_time == NULL || q->tmp_freq == NULL || q->tmp_corr == NULL) {
ERROR("Malloc"); ERROR("Malloc");
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
// Allocate correlation buffers
if (ssb_init_corr(q) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
@ -79,12 +112,272 @@ void srsran_ssb_free(srsran_ssb_t* q)
free(q->tmp_freq); free(q->tmp_freq);
} }
if (q->tmp_corr != NULL) {
free(q->tmp_corr);
}
// For each PSS sequence allocate
for (uint32_t N_id_2 = 0; N_id_2 < SRSRAN_NOF_NID_2_NR; N_id_2++) {
if (q->pss_seq[N_id_2] != NULL) {
free(q->pss_seq[N_id_2]);
}
}
srsran_dft_plan_free(&q->ifft); srsran_dft_plan_free(&q->ifft);
srsran_dft_plan_free(&q->fft); srsran_dft_plan_free(&q->fft);
srsran_dft_plan_free(&q->fft_corr);
srsran_dft_plan_free(&q->ifft_corr);
SRSRAN_MEM_ZERO(q, srsran_ssb_t, 1); SRSRAN_MEM_ZERO(q, srsran_ssb_t, 1);
} }
static uint32_t ssb_first_symbol_caseA(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION])
{
// Case A - 15 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes of { 2 , 8 } + 14 ⋅ n . For
// carrier frequencies smaller than or equal to 3 GHz, n = 0 , 1 . For carrier frequencies within FR1 larger than 3
// GHz, n = 0 , 1 , 2 , 3 .
uint32_t count = 0;
uint32_t base_indexes[2] = {2, 8};
uint32_t N = 2;
if (cfg->center_freq_hz > 3e9) {
N = 4;
}
for (uint32_t n = 0; n < N; n++) {
for (uint32_t i = 0; i < 2; i++) {
indexes[count++] = base_indexes[i] + 14 * n;
}
}
return count;
}
static uint32_t ssb_first_symbol_caseB(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION])
{
// Case B - 30 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes { 4 , 8 , 16 , 20 } + 28 ⋅ n .
// For carrier frequencies smaller than or equal to 3 GHz, n = 0 . For carrier frequencies within FR1 larger than 3
// GHz, n = 0 , 1 .
uint32_t count = 0;
uint32_t base_indexes[4] = {4, 8, 16, 20};
uint32_t N = 1;
if (cfg->center_freq_hz > 3e9) {
N = 2;
}
for (uint32_t n = 0; n < N; n++) {
for (uint32_t i = 0; i < 4; i++) {
indexes[count++] = base_indexes[i] + 28 * n;
}
}
return count;
}
static uint32_t ssb_first_symbol_caseC(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION])
{
// Case C - 30 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes { 2 , 8 } +14 ⋅ n .
// - For paired spectrum operation
// - For carrier frequencies smaller than or equal to 3 GHz, n = 0 , 1 . For carrier frequencies within FR1 larger
// than 3 GHz, n = 0 , 1 , 2 , 3 .
// - For unpaired spectrum operation
// - For carrier frequencies smaller than or equal to 2.3 GHz, n = 0 , 1 . For carrier frequencies within FR1
// larger than 2.3 GHz, n = 0 , 1 , 2 , 3 .
uint32_t count = 0;
uint32_t base_indexes[2] = {2, 8};
uint32_t N = 4;
if ((cfg->duplex_mode == SRSRAN_DUPLEX_MODE_FDD && cfg->center_freq_hz <= 3e9) ||
(cfg->duplex_mode == SRSRAN_DUPLEX_MODE_TDD && cfg->center_freq_hz <= 2.3e9)) {
N = 2;
}
for (uint32_t n = 0; n < N; n++) {
for (uint32_t i = 0; i < 2; i++) {
indexes[count++] = base_indexes[i] + 14 * n;
}
}
return count;
}
static uint32_t ssb_first_symbol_caseD(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION])
{
// Case D - 120 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes { 4 , 8 , 16 , 20 } + 28 ⋅ n .
// For carrier frequencies within FR2, n = 0 , 1 , 2 , 3 , 5 , 6 , 7 , 8 , 10 , 11 , 12 , 13 , 15 , 16 , 17 , 18 .
uint32_t count = 0;
uint32_t base_indexes[4] = {4, 8, 16, 20};
uint32_t n_indexes[16] = {0, 1, 2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 15, 16, 17, 18};
for (uint32_t j = 0; j < 16; j++) {
for (uint32_t i = 0; i < 4; i++) {
indexes[count++] = base_indexes[i] + 28 * n_indexes[j];
}
}
return count;
}
static uint32_t ssb_first_symbol_caseE(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_POSITION])
{
// Case E - 240 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes
//{ 8 , 12 , 16 , 20 , 32 , 36 , 40 , 44 } + 56 ⋅ n . For carrier frequencies within FR2, n = 0 , 1 , 2 , 3 , 5 , 6 ,
// 7 , 8 .
uint32_t count = 0;
uint32_t base_indexes[8] = {8, 12, 16, 20, 32, 38, 40, 44};
uint32_t n_indexes[8] = {0, 1, 2, 3, 5, 6, 7, 8};
for (uint32_t j = 0; j < 8; j++) {
for (uint32_t i = 0; i < 8; i++) {
indexes[count++] = base_indexes[i] + 56 * n_indexes[j];
}
}
return count;
}
static int ssb_first_symbol(const srsran_ssb_cfg_t* cfg, uint32_t ssb_i)
{
uint32_t indexes[SRSRAN_SSB_NOF_POSITION];
uint32_t Lmax = 0;
switch (cfg->pattern) {
case SRSRAN_SSB_PATTERN_A:
Lmax = ssb_first_symbol_caseA(cfg, indexes);
break;
case SRSRAN_SSB_PATTERN_B:
Lmax = ssb_first_symbol_caseB(cfg, indexes);
break;
case SRSRAN_SSB_PATTERN_C:
Lmax = ssb_first_symbol_caseC(cfg, indexes);
break;
case SRSRAN_SSB_PATTERN_D:
Lmax = ssb_first_symbol_caseD(cfg, indexes);
break;
case SRSRAN_SSB_PATTERN_E:
Lmax = ssb_first_symbol_caseE(cfg, indexes);
break;
case SRSRAN_SSB_PATTERN_INVALID:
ERROR("Invalid case");
return SRSRAN_ERROR;
}
uint32_t ssb_count = 0;
for (uint32_t i = 0; i < Lmax; i++) {
// There is a SSB transmission opportunity
if (cfg->position[i]) {
// Return the SSB transmission in burst
if (ssb_i == ssb_count) {
return (int)indexes[i];
}
ssb_count++;
}
}
return SRSRAN_ERROR;
}
// Modulates a given symbol l and stores the time domain signal in q->tmp_time
static void ssb_modulate_symbol(srsran_ssb_t* q, cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t l)
{
// Select symbol in grid
cf_t* ptr = &ssb_grid[l * SRSRAN_SSB_BW_SUBC];
// Initialise frequency domain
srsran_vec_cf_zero(q->tmp_freq, q->symbol_sz);
// Map grid into frequency domain symbol
if (q->f_offset >= SRSRAN_SSB_BW_SUBC / 2) {
srsran_vec_cf_copy(&q->tmp_freq[q->f_offset - SRSRAN_SSB_BW_SUBC / 2], ptr, SRSRAN_SSB_BW_SUBC);
} else if (q->f_offset <= -SRSRAN_SSB_BW_SUBC / 2) {
srsran_vec_cf_copy(&q->tmp_freq[q->symbol_sz + q->f_offset - SRSRAN_SSB_BW_SUBC / 2], ptr, SRSRAN_SSB_BW_SUBC);
} else {
srsran_vec_cf_copy(
&q->tmp_freq[0], &ptr[SRSRAN_SSB_BW_SUBC / 2 - q->f_offset], SRSRAN_SSB_BW_SUBC / 2 + q->f_offset);
srsran_vec_cf_copy(&q->tmp_freq[q->symbol_sz - SRSRAN_SSB_BW_SUBC / 2 + q->f_offset],
&ptr[0],
SRSRAN_SSB_BW_SUBC / 2 - q->f_offset);
}
// Convert to time domain
srsran_dft_run_guru_c(&q->ifft);
// Normalise output
float norm = sqrtf((float)q->symbol_sz);
if (isnormal(norm)) {
srsran_vec_sc_prod_cfc(q->tmp_time, 1.0f / norm, q->tmp_time, q->symbol_sz);
}
}
static int ssb_setup_corr(srsran_ssb_t* q)
{
// Skip if disabled
if (!q->args.enable_search) {
return SRSRAN_SUCCESS;
}
// Compute new correlation size
uint32_t corr_sz = SSB_CORR_SZ(q->symbol_sz);
// Skip if the symbol size is unchanged
if (q->corr_sz == corr_sz) {
return SRSRAN_SUCCESS;
}
q->corr_sz = corr_sz;
// Select correlation window, return error if the correlation window is smaller than a symbol
if (corr_sz < 2 * q->symbol_sz) {
ERROR("Correlation size (%d) is not sufficient (min. %d)", corr_sz, q->symbol_sz * 2);
return SRSRAN_ERROR;
}
q->corr_window = corr_sz - q->symbol_sz;
// Free correlation
srsran_dft_plan_free(&q->fft_corr);
srsran_dft_plan_free(&q->ifft_corr);
// Prepare correlation FFT
if (srsran_dft_plan_guru_c(&q->fft_corr, (int)corr_sz, SRSRAN_DFT_FORWARD, q->tmp_time, q->tmp_freq, 1, 1, 1, 1, 1) <
SRSRAN_SUCCESS) {
ERROR("Error planning correlation DFT");
return SRSRAN_ERROR;
}
if (srsran_dft_plan_guru_c(
&q->ifft_corr, (int)corr_sz, SRSRAN_DFT_BACKWARD, q->tmp_corr, q->tmp_time, 1, 1, 1, 1, 1) < SRSRAN_SUCCESS) {
ERROR("Error planning correlation DFT");
return SRSRAN_ERROR;
}
// Zero the time domain signal last samples
srsran_vec_cf_zero(&q->tmp_time[q->symbol_sz], q->corr_window);
// Temporal grid
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
// Initialise correlation sequence
for (uint32_t N_id_2 = 0; N_id_2 < SRSRAN_NOF_NID_2_NR; N_id_2++) {
// Put the PSS in SSB grid
if (srsran_pss_nr_put(ssb_grid, N_id_2, 1.0f) < SRSRAN_SUCCESS) {
ERROR("Error putting PDD N_id_2=%d", N_id_2);
return SRSRAN_ERROR;
}
// Modulate symbol with PSS
ssb_modulate_symbol(q, ssb_grid, SRSRAN_PSS_NR_SYMBOL_IDX);
// Convert to frequency domain
srsran_dft_run_guru_c(&q->fft_corr);
// Copy frequency domain sequence
srsran_vec_cf_copy(q->pss_seq[N_id_2], q->tmp_freq, q->corr_sz);
}
return SRSRAN_SUCCESS;
}
int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg) int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg)
{ {
// Verify input parameters // Verify input parameters
@ -93,13 +386,37 @@ int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg)
} }
// Calculate subcarrier spacing in Hz // Calculate subcarrier spacing in Hz
q->scs_hz = (double)SRSRAN_SUBC_SPACING_NR(cfg->scs); q->scs_hz = (float)SRSRAN_SUBC_SPACING_NR(cfg->scs);
// Get first symbol
int l_begin = ssb_first_symbol(cfg, 0);
if (l_begin < SRSRAN_SUCCESS) {
// set it to 2 in case it is not selected
l_begin = 2;
}
float t_offset_s = srsran_symbol_offset_s((uint32_t)l_begin, cfg->scs);
if (isnan(t_offset_s) || isinf(t_offset_s) || t_offset_s < 0.0f) {
ERROR("Invalid first symbol (l_first=%d)", l_begin);
return SRSRAN_ERROR;
}
// Calculate SSB symbol size and integer offset // Calculate SSB symbol size and integer offset
uint32_t symbol_sz = (uint32_t)round(cfg->srate_hz / q->scs_hz); double freq_offset_hz = cfg->ssb_freq_hz - cfg->center_freq_hz;
q->offset = (uint32_t)(cfg->freq_offset_hz / q->scs_hz); uint32_t symbol_sz = (uint32_t)round(cfg->srate_hz / q->scs_hz);
q->cp0_sz = (160U * symbol_sz) / 2048U; q->f_offset = (int32_t)round(freq_offset_hz / q->scs_hz);
q->cp_sz = (144U * symbol_sz) / 2048U; q->t_offset = (uint32_t)round(t_offset_s * cfg->srate_hz);
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
uint32_t l_real = l + (uint32_t)l_begin;
uint32_t ref_cp_sz = 144U;
if (l_real == 0 || l_real == SRSRAN_EXT_CP_SYMBOL(cfg->scs)) {
ref_cp_sz = 160U;
}
q->cp_sz[l] = (ref_cp_sz * symbol_sz) / 2048U;
}
// Calculate SSB sampling error and check // Calculate SSB sampling error and check
double ssb_srate_error_Hz = ((double)symbol_sz * q->scs_hz) - cfg->srate_hz; double ssb_srate_error_Hz = ((double)symbol_sz * q->scs_hz) - cfg->srate_hz;
@ -109,9 +426,9 @@ int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg)
} }
// Calculate SSB offset error and check // Calculate SSB offset error and check
double ssb_offset_error_Hz = ((double)q->offset * q->scs_hz) - cfg->freq_offset_hz; double ssb_offset_error_Hz = ((double)q->f_offset * q->scs_hz) - freq_offset_hz;
if (fabs(ssb_offset_error_Hz) > SSB_FREQ_OFFSET_MAX_ERROR_HZ) { if (fabs(ssb_offset_error_Hz) > SSB_FREQ_OFFSET_MAX_ERROR_HZ) {
ERROR("SSB Offset error exceeds maximum allowed"); ERROR("SSB Offset (%.1f kHz) error exceeds maximum allowed", freq_offset_hz / 1e3);
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
@ -121,7 +438,7 @@ int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg)
} }
// Replan iFFT // Replan iFFT
if ((q->args.enable_encode) && q->symbol_sz != symbol_sz) { if ((q->args.enable_encode || q->args.enable_search) && q->symbol_sz != symbol_sz) {
// free the current IFFT, it internally checks if the plan was created // free the current IFFT, it internally checks if the plan was created
srsran_dft_plan_free(&q->ifft); srsran_dft_plan_free(&q->ifft);
@ -134,7 +451,7 @@ int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg)
} }
// Replan FFT // Replan FFT
if ((q->args.enable_measure || q->args.enable_decode) && q->symbol_sz != symbol_sz) { if ((q->args.enable_measure || q->args.enable_decode || q->args.enable_search) && q->symbol_sz != symbol_sz) {
// free the current FFT, it internally checks if the plan was created // free the current FFT, it internally checks if the plan was created
srsran_dft_plan_free(&q->fft); srsran_dft_plan_free(&q->fft);
@ -150,6 +467,12 @@ int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg)
q->cfg = *cfg; q->cfg = *cfg;
q->symbol_sz = symbol_sz; q->symbol_sz = symbol_sz;
// Initialise correlation
if (ssb_setup_corr(q) < SRSRAN_SUCCESS) {
ERROR("Error initialising correlation");
return SRSRAN_ERROR;
}
if (!isnormal(q->cfg.beta_pss)) { if (!isnormal(q->cfg.beta_pss)) {
q->cfg.beta_pss = SRSRAN_SSB_DEFAULT_BETA; q->cfg.beta_pss = SRSRAN_SSB_DEFAULT_BETA;
} }
@ -169,6 +492,22 @@ int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg)
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
bool srsran_ssb_send(srsran_ssb_t* q, uint32_t sf_idx)
{
// Verify input
if (q == NULL) {
return false;
}
// Verify periodicity
if (q->cfg.periodicity_ms == 0) {
return false;
}
// Check periodicity
return (sf_idx % q->cfg.periodicity_ms == 0);
}
int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, const cf_t* in, cf_t* out) int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, const cf_t* in, cf_t* out)
{ {
// Verify input parameters // Verify input parameters
@ -203,38 +542,17 @@ int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* m
// Put PBCH payload // Put PBCH payload
// ... // ...
// Initialise frequency domain // Select input/ouput pointers considering the time offset in the slot
srsran_vec_cf_zero(q->tmp_freq, q->symbol_sz); const cf_t* in_ptr = &in[q->t_offset];
cf_t* out_ptr = &out[q->t_offset];
// Modulate // For each SSB symbol, modulate
const cf_t* in_ptr = in;
cf_t* out_ptr = out;
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
// Get CP length // Get CP length
uint32_t cp_len = (l == 0) ? q->cp0_sz : q->cp_sz; uint32_t cp_len = q->cp_sz[l];
// Select symbol in grid
cf_t* ptr = &ssb_grid[l * SRSRAN_SSB_BW_SUBC];
// Map grid into frequency domain symbol // Map SSB in resource grid and perform IFFT
if (q->offset >= SRSRAN_SSB_BW_SUBC / 2) { ssb_modulate_symbol(q, ssb_grid, l);
srsran_vec_cf_copy(&q->tmp_freq[q->offset - SRSRAN_SSB_BW_SUBC / 2], ptr, SRSRAN_SSB_BW_SUBC);
} else if (q->offset <= -SRSRAN_SSB_BW_SUBC / 2) {
srsran_vec_cf_copy(&q->tmp_freq[q->symbol_sz + q->offset - SRSRAN_SSB_BW_SUBC / 2], ptr, SRSRAN_SSB_BW_SUBC);
} else {
srsran_vec_cf_copy(&q->tmp_freq[0], &ptr[SRSRAN_SSB_BW_SUBC / 2 - q->offset], SRSRAN_SSB_BW_SUBC / 2 + q->offset);
srsran_vec_cf_copy(
&q->tmp_freq[q->symbol_sz - SRSRAN_SSB_BW_SUBC / 2 + q->offset], &ptr[0], SRSRAN_SSB_BW_SUBC / 2 - q->offset);
}
// Convert to time domain
srsran_dft_run_guru_c(&q->ifft);
// Normalise output
float norm = sqrtf((float)q->symbol_sz);
if (isnormal(norm)) {
srsran_vec_sc_prod_cfc(q->tmp_time, 1.0f / norm, q->tmp_time, q->symbol_sz);
}
// Add cyclic prefix to input; // Add cyclic prefix to input;
srsran_vec_sum_ccc(in_ptr, &q->tmp_time[q->symbol_sz - cp_len], out_ptr, cp_len); srsran_vec_sum_ccc(in_ptr, &q->tmp_time[q->symbol_sz - cp_len], out_ptr, cp_len);
@ -250,27 +568,12 @@ int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* m
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsran_csi_trs_measurements_t* meas) static int ssb_demodulate(srsran_ssb_t* q, const cf_t* in, uint32_t t_offset, cf_t ssb_grid[SRSRAN_SSB_NOF_RE])
{ {
// Verify inputs const cf_t* in_ptr = &in[t_offset];
if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || in == NULL || meas == NULL || !isnormal(q->scs_hz)) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (!q->args.enable_measure) {
ERROR("SSB is not configured for measure");
return SRSRAN_ERROR;
}
uint32_t N_id_1 = SRSRAN_NID_1_NR(N_id);
uint32_t N_id_2 = SRSRAN_NID_2_NR(N_id);
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
// Demodulate
const cf_t* in_ptr = in;
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
// Get CP length // Get CP length
uint32_t cp_len = (l == 0) ? q->cp0_sz : q->cp_sz; uint32_t cp_len = q->cp_sz[l];
// Advance half CP, to avoid inter symbol interference // Advance half CP, to avoid inter symbol interference
in_ptr += SRSRAN_FLOOR(cp_len, 2); in_ptr += SRSRAN_FLOOR(cp_len, 2);
@ -289,14 +592,16 @@ int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsra
cf_t* ptr = &ssb_grid[l * SRSRAN_SSB_BW_SUBC]; cf_t* ptr = &ssb_grid[l * SRSRAN_SSB_BW_SUBC];
// Map frequency domain symbol into the SSB grid // Map frequency domain symbol into the SSB grid
if (q->offset >= SRSRAN_SSB_BW_SUBC / 2) { if (q->f_offset >= SRSRAN_SSB_BW_SUBC / 2) {
srsran_vec_cf_copy(ptr, &q->tmp_freq[q->offset - SRSRAN_SSB_BW_SUBC / 2], SRSRAN_SSB_BW_SUBC); srsran_vec_cf_copy(ptr, &q->tmp_freq[q->f_offset - SRSRAN_SSB_BW_SUBC / 2], SRSRAN_SSB_BW_SUBC);
} else if (q->offset <= -SRSRAN_SSB_BW_SUBC / 2) { } else if (q->f_offset <= -SRSRAN_SSB_BW_SUBC / 2) {
srsran_vec_cf_copy(ptr, &q->tmp_freq[q->symbol_sz + q->offset - SRSRAN_SSB_BW_SUBC / 2], SRSRAN_SSB_BW_SUBC); srsran_vec_cf_copy(ptr, &q->tmp_freq[q->symbol_sz + q->f_offset - SRSRAN_SSB_BW_SUBC / 2], SRSRAN_SSB_BW_SUBC);
} else { } else {
srsran_vec_cf_copy(&ptr[SRSRAN_SSB_BW_SUBC / 2 - q->offset], &q->tmp_freq[0], SRSRAN_SSB_BW_SUBC / 2 + q->offset);
srsran_vec_cf_copy( srsran_vec_cf_copy(
&ptr[0], &q->tmp_freq[q->symbol_sz - SRSRAN_SSB_BW_SUBC / 2 + q->offset], SRSRAN_SSB_BW_SUBC / 2 - q->offset); &ptr[SRSRAN_SSB_BW_SUBC / 2 - q->f_offset], &q->tmp_freq[0], SRSRAN_SSB_BW_SUBC / 2 + q->f_offset);
srsran_vec_cf_copy(&ptr[0],
&q->tmp_freq[q->symbol_sz - SRSRAN_SSB_BW_SUBC / 2 + q->f_offset],
SRSRAN_SSB_BW_SUBC / 2 - q->f_offset);
} }
// Normalize // Normalize
@ -306,6 +611,15 @@ int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsra
} }
} }
return SRSRAN_SUCCESS;
}
static int
ssb_measure(srsran_ssb_t* q, const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id, srsran_csi_trs_measurements_t* meas)
{
uint32_t N_id_1 = SRSRAN_NID_1_NR(N_id);
uint32_t N_id_2 = SRSRAN_NID_2_NR(N_id);
// Extract PSS LSE // Extract PSS LSE
cf_t pss_lse[SRSRAN_PSS_NR_LEN]; cf_t pss_lse[SRSRAN_PSS_NR_LEN];
cf_t sss_lse[SRSRAN_SSS_NR_LEN]; cf_t sss_lse[SRSRAN_SSS_NR_LEN];
@ -322,14 +636,17 @@ int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsra
float delay_avg_us = 1e6f * delay_avg_norm / q->scs_hz; float delay_avg_us = 1e6f * delay_avg_norm / q->scs_hz;
// Pre-compensate delay // Pre-compensate delay
cf_t ssb_grid_corrected[SRSRAN_SSB_NOF_RE];
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
srsran_vec_apply_cfo( srsran_vec_apply_cfo(&ssb_grid[SRSRAN_SSB_BW_SUBC * l],
&ssb_grid[SRSRAN_SSB_BW_SUBC * l], delay_avg_norm, &ssb_grid[SRSRAN_SSB_BW_SUBC * l], SRSRAN_SSB_BW_SUBC); delay_avg_norm,
&ssb_grid_corrected[SRSRAN_SSB_BW_SUBC * l],
SRSRAN_SSB_BW_SUBC);
} }
// Extract LSE again // Extract LSE again
if (srsran_pss_nr_extract_lse(ssb_grid, N_id_2, pss_lse) < SRSRAN_SUCCESS || if (srsran_pss_nr_extract_lse(ssb_grid_corrected, N_id_2, pss_lse) < SRSRAN_SUCCESS ||
srsran_sss_nr_extract_lse(ssb_grid, N_id_1, N_id_2, sss_lse) < SRSRAN_SUCCESS) { srsran_sss_nr_extract_lse(ssb_grid_corrected, N_id_1, N_id_2, sss_lse) < SRSRAN_SUCCESS) {
ERROR("Error extracting LSE"); ERROR("Error extracting LSE");
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
@ -349,19 +666,26 @@ int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsra
float cfo_hz = cargf(corr_pss * conjf(corr_sss)) / (2.0f * M_PI) * cfo_hz_max; float cfo_hz = cargf(corr_pss * conjf(corr_sss)) / (2.0f * M_PI) * cfo_hz_max;
// Compute average RSRP // Compute average RSRP
float rsrp = (SRSRAN_CSQABS(corr_pss) + SRSRAN_CSQABS(corr_sss)) / 2.0f; float rsrp_pss = SRSRAN_CSQABS(corr_pss);
float rsrp_sss = SRSRAN_CSQABS(corr_sss);
float rsrp = (rsrp_pss + rsrp_sss) / 2.0f;
// Compute Noise // Compute Noise
float n0 = 1e-9; // Almost 0 float n0_pss = 1e-9; // Almost 0
if (epre > rsrp) { float n0_sss = 1e-9; // Almost 0
n0 = epre - rsrp; if (epre_pss > rsrp_pss) {
n0_pss = epre - rsrp_pss;
}
if (epre_pss > rsrp_pss) {
n0_sss = epre - rsrp_sss;
} }
float n0 = (n0_pss + n0_sss) / 2.0f;
// Put measurements together // Put measurements together
meas->epre = epre; meas->epre = epre;
meas->epre_dB = srsran_convert_power_to_dB(epre); meas->epre_dB = srsran_convert_power_to_dB(epre);
meas->rsrp = rsrp; meas->rsrp = rsrp;
meas->epre_dB = srsran_convert_power_to_dB(rsrp); meas->rsrp_dB = srsran_convert_power_to_dB(rsrp);
meas->n0 = n0; meas->n0 = n0;
meas->n0_dB = srsran_convert_power_to_dB(n0); meas->n0_dB = srsran_convert_power_to_dB(n0);
meas->snr_dB = meas->rsrp_dB - meas->n0_dB; meas->snr_dB = meas->rsrp_dB - meas->n0_dB;
@ -371,4 +695,170 @@ int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsra
meas->nof_re = SRSRAN_PSS_NR_LEN + SRSRAN_SSS_NR_LEN; meas->nof_re = SRSRAN_PSS_NR_LEN + SRSRAN_SSS_NR_LEN;
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
static int
ssb_pss_search(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, uint32_t* found_N_id_2, uint32_t* found_delay)
{
// verify it is initialised
if (q->corr_sz == 0) {
return SRSRAN_ERROR;
}
// Correlation best sequence
float best_corr = 0;
uint32_t best_delay = 0;
uint32_t best_N_id_2 = 0;
// Delay in correlation window
uint32_t t_offset = 0;
while ((t_offset + q->symbol_sz) < nof_samples) {
// Number of samples taken in this iteration
uint32_t n = q->corr_sz;
// Detect if the correlation input exceeds the input length, take the maximum amount of samples
if (t_offset + q->corr_sz > nof_samples) {
n = nof_samples - t_offset;
}
// Copy the amount of samples
srsran_vec_cf_copy(q->tmp_time, &in[t_offset], n);
// Append zeros if there is space left
if (n < q->corr_sz) {
srsran_vec_cf_zero(&q->tmp_time[n], q->corr_sz - n);
}
// Convert to frequency domain
srsran_dft_run_guru_c(&q->fft_corr);
// Try each N_id_2 sequence
for (uint32_t N_id_2 = 0; N_id_2 < SRSRAN_NOF_NID_2_NR; N_id_2++) {
// Actual correlation in frequency domain
srsran_vec_prod_conj_ccc(q->tmp_freq, q->pss_seq[N_id_2], q->tmp_corr, q->corr_sz);
// Convert to time domain
srsran_dft_run_guru_c(&q->ifft_corr);
// Find maximum
uint32_t peak_idx = srsran_vec_max_abs_ci(q->tmp_time, q->corr_window);
// Average power, skip window if value is invalid (0.0, nan or inf)
float avg_pwr_corr = srsran_vec_avg_power_cf(&q->tmp_time[peak_idx], q->symbol_sz);
if (!isnormal(avg_pwr_corr)) {
continue;
}
float corr = SRSRAN_CSQABS(q->tmp_time[peak_idx]) / avg_pwr_corr;
if (corr < sqrtf(SRSRAN_PSS_NR_LEN)) {
continue;
}
// Update if the correlation is better than the current best
if (best_corr < corr) {
best_corr = corr;
best_delay = peak_idx + t_offset;
best_N_id_2 = N_id_2;
}
}
// Advance time
t_offset += q->corr_window;
}
// Save findings
*found_delay = best_delay;
*found_N_id_2 = best_N_id_2;
return SRSRAN_SUCCESS;
}
int srsran_ssb_csi_search(srsran_ssb_t* q,
const cf_t* in,
uint32_t nof_samples,
uint32_t* N_id,
srsran_csi_trs_measurements_t* meas)
{
// Verify inputs
if (q == NULL || in == NULL || N_id == NULL || meas == NULL || !isnormal(q->scs_hz)) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (!q->args.enable_search) {
ERROR("SSB is not configured for search");
return SRSRAN_ERROR;
}
// Search for PSS in time domain
uint32_t N_id_2 = 0;
uint32_t t_offset = 0;
if (ssb_pss_search(q, in, nof_samples, &N_id_2, &t_offset) < SRSRAN_SUCCESS) {
ERROR("Error searching for N_id_2");
return SRSRAN_ERROR;
}
// Remove CP offset prior demodulation
if (t_offset >= q->cp_sz[0]) {
t_offset -= q->cp_sz[0];
} else {
t_offset = 0;
}
// Demodulate
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
if (ssb_demodulate(q, in, t_offset, ssb_grid) < SRSRAN_SUCCESS) {
ERROR("Error demodulating");
return SRSRAN_ERROR;
}
// Find best N_id_1
uint32_t N_id_1 = 0;
float sss_corr = 0.0f;
if (srsran_sss_nr_find(ssb_grid, N_id_2, &sss_corr, &N_id_1) < SRSRAN_SUCCESS) {
ERROR("Error searching for N_id_2");
return SRSRAN_ERROR;
}
// Select N_id
*N_id = SRSRAN_NID_NR(N_id_1, N_id_2);
// Measure selected N_id
if (ssb_measure(q, ssb_grid, *N_id, meas)) {
ERROR("Error measuring");
return SRSRAN_ERROR;
}
// Add delay to measure
meas->delay_us += (float)(1e6 * t_offset / q->cfg.srate_hz);
return SRSRAN_SUCCESS;
}
int srsran_ssb_csi_measure(srsran_ssb_t* q, uint32_t N_id, const cf_t* in, srsran_csi_trs_measurements_t* meas)
{
// Verify inputs
if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || in == NULL || meas == NULL || !isnormal(q->scs_hz)) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (!q->args.enable_measure) {
ERROR("SSB is not configured for measure");
return SRSRAN_ERROR;
}
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
// Demodulate
if (ssb_demodulate(q, in, q->t_offset, ssb_grid) < SRSRAN_SUCCESS) {
ERROR("Error demodulating");
return SRSRAN_ERROR;
}
// Actual measurement
if (ssb_measure(q, ssb_grid, N_id, meas)) {
ERROR("Error measuring");
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}

@ -151,7 +151,7 @@ int srsran_sss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
uint32_t* found_N_id_1) uint32_t* found_N_id_1)
{ {
// Verify inputs // Verify inputs
if (ssb_grid == NULL || N_id_2 >= SRSRAN_NOF_NID_2_NR) { if (ssb_grid == NULL || N_id_2 >= SRSRAN_NOF_NID_2_NR || norm_corr == NULL || found_N_id_1 == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS; return SRSRAN_ERROR_INVALID_INPUTS;
} }
@ -163,9 +163,8 @@ int srsran_sss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
// If the measured power is invalid or zero, consider no SSS signal // If the measured power is invalid or zero, consider no SSS signal
if (!isnormal(avg_power)) { if (!isnormal(avg_power)) {
if (norm_corr) { *norm_corr = 0.0f;
*norm_corr = 0.0f; *found_N_id_1 = 0;
}
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
@ -185,19 +184,18 @@ int srsran_sss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
srsran_vec_prod_ccc(&sss_ptr[SRSRAN_SSS_NR_LEN - m1], &sss_nr_d1[0], &sss_seq_m1[SRSRAN_SSS_NR_LEN - m1], m1); srsran_vec_prod_ccc(&sss_ptr[SRSRAN_SSS_NR_LEN - m1], &sss_nr_d1[0], &sss_seq_m1[SRSRAN_SSS_NR_LEN - m1], m1);
// Iterate over all N_id_1 with the given m1 sequence // Iterate over all N_id_1 with the given m1 sequence
for (uint32_t N_id_1_blind = m1; N_id_1_blind < SRSRAN_NOF_NID_1; N_id_1_blind += SSS_NR_NOF_M1) { for (uint32_t N_id_1_blind = m1; N_id_1_blind < SRSRAN_NOF_NID_1_NR; N_id_1_blind += SSS_NR_NOF_M1) {
uint32_t m0 = SSS_NR_SEQUENCE_M0(N_id_1_blind, N_id_2); uint32_t m0 = SSS_NR_SEQUENCE_M0(N_id_1_blind, N_id_2);
cf_t acc = 0.0f;
cf_t sss_seq_m0[SRSRAN_SSS_NR_LEN];
// Apply d0 sequence fist part // Correlate d0 sequence fist part
srsran_vec_prod_ccc(&sss_seq_m1[0], &sss_nr_d0[m0], &sss_seq_m0[0], SRSRAN_SSS_NR_LEN - m0); acc += srsran_vec_dot_prod_ccc(&sss_seq_m1[0], &sss_nr_d0[m0], SRSRAN_SSS_NR_LEN - m0);
// Apply d0 sequence second part // Correlate d0 sequence second part
srsran_vec_prod_ccc(&sss_seq_m1[SRSRAN_SSS_NR_LEN - m0], &sss_nr_d0[0], &sss_seq_m0[SRSRAN_SSS_NR_LEN - m0], m0); acc += srsran_vec_dot_prod_ccc(&sss_seq_m1[SRSRAN_SSS_NR_LEN - m0], &sss_nr_d0[0], m0);
// Correlate // Normalise
float corr = SRSRAN_CSQABS(srsran_vec_acc_cc(sss_seq_m0, SRSRAN_SSS_NR_LEN)) / avg_power; float corr = SRSRAN_CSQABS(acc);
if (corr > max_corr) { if (corr > max_corr) {
N_id_1 = N_id_1_blind; N_id_1 = N_id_1_blind;
max_corr = corr; max_corr = corr;
@ -205,13 +203,9 @@ int srsran_sss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
} }
} }
if (norm_corr) { // Copy found result
*norm_corr = max_corr; *norm_corr = max_corr / avg_power / SRSRAN_SSS_NR_LEN;
} *found_N_id_1 = N_id_1;
if (found_N_id_1) {
*found_N_id_1 = N_id_1;
}
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }

@ -46,8 +46,8 @@ static cf_t* buffer = NULL; // Base-band buffer
#define RSRP_MAX_ERROR 1.0f #define RSRP_MAX_ERROR 1.0f
#define EPRE_MAX_ERROR 1.0f #define EPRE_MAX_ERROR 1.0f
#define N0_MAX_ERROR 2.0f #define N0_MAX_ERROR 2.5f
#define SNR_MAX_ERROR 2.0f #define SNR_MAX_ERROR 2.5f
#define CFO_MAX_ERROR (cfo_hz * 0.3f) #define CFO_MAX_ERROR (cfo_hz * 0.3f)
#define DELAY_MAX_ERROR (delay_us * 0.1f) #define DELAY_MAX_ERROR (delay_us * 0.1f)
@ -86,13 +86,31 @@ static void run_channel()
srsran_channel_awgn_run_c(&awgn, buffer, buffer, sf_len); srsran_channel_awgn_run_c(&awgn, buffer, buffer, sf_len);
} }
static int assert_measure(const srsran_csi_trs_measurements_t* meas)
{
TESTASSERT(fabsf(meas->rsrp_dB - 0.0f) < RSRP_MAX_ERROR);
TESTASSERT(fabsf(meas->epre_dB - 0.0f) < EPRE_MAX_ERROR);
TESTASSERT(fabsf(meas->n0_dB - n0_dB) < N0_MAX_ERROR);
TESTASSERT(fabsf(meas->snr_dB + n0_dB) < SNR_MAX_ERROR);
TESTASSERT(fabsf(meas->cfo_hz - cfo_hz) < CFO_MAX_ERROR);
TESTASSERT(fabsf(meas->delay_us + delay_us) < DELAY_MAX_ERROR);
return SRSRAN_SUCCESS;
}
static int test_case_1(srsran_ssb_t* ssb) static int test_case_1(srsran_ssb_t* ssb)
{ {
uint64_t t_usec = 0; // For benchmarking purposes
uint64_t t_find_usec = 0;
uint64_t t_meas_usec = 0;
// SSB configuration
srsran_ssb_cfg_t ssb_cfg = {}; srsran_ssb_cfg_t ssb_cfg = {};
ssb_cfg.srate_hz = srate_hz; ssb_cfg.srate_hz = srate_hz;
ssb_cfg.freq_offset_hz = 0.0; ssb_cfg.center_freq_hz = 3.5e9;
ssb_cfg.ssb_freq_hz = 3.5e9 - 960e3;
ssb_cfg.scs = ssb_scs; ssb_cfg.scs = ssb_scs;
ssb_cfg.pattern = SRSRAN_SSB_PATTERN_C;
ssb_cfg.position[0] = true; // Rest to false
TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS); TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS);
@ -111,29 +129,42 @@ static int test_case_1(srsran_ssb_t* ssb)
// Run channel // Run channel
run_channel(); run_channel();
// Find
gettimeofday(&t[1], NULL);
uint32_t N_id_found = 0;
srsran_csi_trs_measurements_t meas_search = {};
TESTASSERT(srsran_ssb_csi_search(ssb, buffer, sf_len, &N_id_found, &meas_search) == SRSRAN_SUCCESS);
gettimeofday(&t[2], NULL);
get_time_interval(t);
t_find_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
// Print measurement
char str[512] = {};
srsran_csi_meas_info(&meas_search, str, sizeof(str));
INFO("test_case_1 - search pci=%d %s", pci, str);
// Assert find
TESTASSERT(N_id_found == pci);
// Measure // Measure
gettimeofday(&t[1], NULL); gettimeofday(&t[1], NULL);
srsran_csi_trs_measurements_t meas = {}; srsran_csi_trs_measurements_t meas = {};
TESTASSERT(srsran_ssb_csi_measure(ssb, pci, buffer, &meas) == SRSRAN_SUCCESS); TESTASSERT(srsran_ssb_csi_measure(ssb, pci, buffer, &meas) == SRSRAN_SUCCESS);
gettimeofday(&t[2], NULL); gettimeofday(&t[2], NULL);
get_time_interval(t); get_time_interval(t);
t_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; t_meas_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
// Print measurement
char str[512];
srsran_csi_meas_info(&meas, str, sizeof(str)); srsran_csi_meas_info(&meas, str, sizeof(str));
INFO("test_case_1 - pci=%d %s", pci, str); INFO("test_case_1 - measure pci=%d %s", pci, str);
// Assert measurements // Assert measurements
TESTASSERT(fabsf(meas.rsrp_dB - 0.0f) < RSRP_MAX_ERROR); TESTASSERT(assert_measure(&meas) == SRSRAN_SUCCESS);
TESTASSERT(fabsf(meas.epre_dB - 0.0f) < EPRE_MAX_ERROR);
TESTASSERT(fabsf(meas.n0_dB - n0_dB) < N0_MAX_ERROR);
TESTASSERT(fabsf(meas.snr_dB + n0_dB) < SNR_MAX_ERROR);
TESTASSERT(fabsf(meas.cfo_hz - cfo_hz) < CFO_MAX_ERROR);
TESTASSERT(fabsf(meas.delay_us + delay_us) < DELAY_MAX_ERROR);
} }
INFO("test_case_1 - %.1f usec/measurement", (double)t_usec / (double)SRSRAN_NOF_NID_NR); INFO("test_case_1 - %.1f usec/search; Max srate %.1f MSps; %.1f usec/measurement",
(double)t_find_usec / (double)SRSRAN_NOF_NID_NR,
(double)sf_len * (double)SRSRAN_NOF_NID_NR / (double)t_find_usec,
(double)t_meas_usec / (double)SRSRAN_NOF_NID_NR);
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
@ -152,6 +183,7 @@ int main(int argc, char** argv)
srsran_ssb_args_t ssb_args = {}; srsran_ssb_args_t ssb_args = {};
ssb_args.enable_encode = true; ssb_args.enable_encode = true;
ssb_args.enable_measure = true; ssb_args.enable_measure = true;
ssb_args.enable_search = true;
if (buffer == NULL) { if (buffer == NULL) {
ERROR("Malloc"); ERROR("Malloc");

@ -188,6 +188,11 @@ int srsran_ue_cellsearch_set_nof_valid_frames(srsran_ue_cellsearch_t* q, uint32_
} }
} }
void srsran_set_detect_cp(srsran_ue_cellsearch_t* q, bool enable)
{
srsran_ue_sync_cp_en(&q->ue_sync, enable);
}
/* Decide the most likely cell based on the mode */ /* Decide the most likely cell based on the mode */
static void get_cell(srsran_ue_cellsearch_t* q, uint32_t nof_detected_frames, srsran_ue_cellsearch_result_t* found_cell) static void get_cell(srsran_ue_cellsearch_t* q, uint32_t nof_detected_frames, srsran_ue_cellsearch_result_t* found_cell)
{ {

@ -339,7 +339,8 @@ int srsran_ue_sync_set_cell(srsran_ue_sync_t* q, srsran_cell_t cell)
q->cell = cell; q->cell = cell;
q->fft_size = srsran_symbol_sz(q->cell.nof_prb); q->fft_size = srsran_symbol_sz(q->cell.nof_prb);
q->sf_len = SRSRAN_SF_LEN(q->fft_size); q->sf_len = SRSRAN_SF_LEN(q->fft_size);
srsran_sync_set_cp(&q->sfind, q->cell.cp);
srsran_sync_set_cp(&q->strack, q->cell.cp);
if (cell.id == 1000) { if (cell.id == 1000) {
/* If the cell is unkown, we search PSS/SSS in 5 ms */ /* If the cell is unkown, we search PSS/SSS in 5 ms */
q->nof_recv_sf = 5; q->nof_recv_sf = 5;
@ -508,6 +509,12 @@ float srsran_ue_sync_get_cfo(srsran_ue_sync_t* q)
return 15000 * q->cfo_current_value; return 15000 * q->cfo_current_value;
} }
void srsran_ue_sync_cp_en(srsran_ue_sync_t* q, bool enabled)
{
srsran_sync_cp_en(&q->strack, enabled);
srsran_sync_cp_en(&q->sfind, enabled);
}
void srsran_ue_sync_copy_cfo(srsran_ue_sync_t* q, srsran_ue_sync_t* src_obj) void srsran_ue_sync_copy_cfo(srsran_ue_sync_t* q, srsran_ue_sync_t* src_obj)
{ {
// Copy find object internal CFO averages // Copy find object internal CFO averages

@ -1693,21 +1693,21 @@ void srsran_vec_apply_cfo_simd(const cf_t* x, float cfo, cf_t* z, int len)
{ {
const float TWOPI = 2.0f * (float)M_PI; const float TWOPI = 2.0f * (float)M_PI;
int i = 0; int i = 0;
cf_t osc = cexpf(_Complex_I * TWOPI * cfo);
cf_t phase = 1.0f;
#if SRSRAN_SIMD_CF_SIZE #if SRSRAN_SIMD_CF_SIZE
srsran_simd_aligned cf_t _osc[SRSRAN_SIMD_CF_SIZE]; // Load initial phases and oscillator
srsran_simd_aligned cf_t _phase[SRSRAN_SIMD_CF_SIZE]; srsran_simd_aligned cf_t _phase[SRSRAN_SIMD_CF_SIZE];
_phase[0] = phase;
if (i < len - SRSRAN_SIMD_CF_SIZE + 1) { for (int k = 1; k < SRSRAN_SIMD_CF_SIZE; k++) {
for (int k = 0; k < SRSRAN_SIMD_CF_SIZE; k++) { _phase[k] = _phase[k - 1] * osc;
_osc[k] = cexpf(_Complex_I * TWOPI * cfo * SRSRAN_SIMD_CF_SIZE);
_phase[k] = cexpf(_Complex_I * TWOPI * cfo * k);
}
} }
simd_cf_t _simd_osc = srsran_simd_cfi_load(_osc); simd_cf_t _simd_osc = srsran_simd_cf_set1(_phase[SRSRAN_SIMD_CF_SIZE - 1] * osc);
simd_cf_t _simd_phase = srsran_simd_cfi_load(_phase); simd_cf_t _simd_phase = srsran_simd_cfi_load(_phase);
if (SRSRAN_IS_ALIGNED(x) && SRSRAN_IS_ALIGNED(z)) { if (SRSRAN_IS_ALIGNED(x) && SRSRAN_IS_ALIGNED(z)) {
// For aligned memory
for (; i < len - SRSRAN_SIMD_CF_SIZE + 1; i += SRSRAN_SIMD_CF_SIZE) { for (; i < len - SRSRAN_SIMD_CF_SIZE + 1; i += SRSRAN_SIMD_CF_SIZE) {
simd_cf_t a = srsran_simd_cfi_load(&x[i]); simd_cf_t a = srsran_simd_cfi_load(&x[i]);
@ -1718,6 +1718,7 @@ void srsran_vec_apply_cfo_simd(const cf_t* x, float cfo, cf_t* z, int len)
_simd_phase = srsran_simd_cf_prod(_simd_phase, _simd_osc); _simd_phase = srsran_simd_cf_prod(_simd_phase, _simd_osc);
} }
} else { } else {
// For unaligned memory
for (; i < len - SRSRAN_SIMD_F_SIZE + 1; i += SRSRAN_SIMD_F_SIZE) { for (; i < len - SRSRAN_SIMD_F_SIZE + 1; i += SRSRAN_SIMD_F_SIZE) {
simd_cf_t a = srsran_simd_cfi_loadu(&x[i]); simd_cf_t a = srsran_simd_cfi_loadu(&x[i]);
@ -1728,9 +1729,12 @@ void srsran_vec_apply_cfo_simd(const cf_t* x, float cfo, cf_t* z, int len)
_simd_phase = srsran_simd_cf_prod(_simd_phase, _simd_osc); _simd_phase = srsran_simd_cf_prod(_simd_phase, _simd_osc);
} }
} }
// Stores the next phase
srsran_simd_cfi_store(_phase, _simd_phase);
phase = _phase[0];
#endif #endif
cf_t osc = cexpf(_Complex_I * TWOPI * cfo);
cf_t phase = cexpf(_Complex_I * TWOPI * cfo * i);
for (; i < len; i++) { for (; i < len; i++) {
z[i] = x[i] * phase; z[i] = x[i] * phase;

@ -141,11 +141,6 @@ void backend_worker::process_log_entry(detail::log_entry&& entry)
assert(entry.format_func && "Invalid format function"); assert(entry.format_func && "Invalid format function");
fmt_buffer.clear(); fmt_buffer.clear();
// Already formatted strings in the foreground are passed to the formatter as the fmtstring.
if (entry.metadata.small_str.size()) {
entry.metadata.fmtstring = entry.metadata.small_str.data();
}
// Save the pointer before moving the entry. // Save the pointer before moving the entry.
auto* arg_store = entry.metadata.store; auto* arg_store = entry.metadata.store;

@ -25,6 +25,44 @@ int format_float(char* buf, std::size_t size, const char* format, int precision,
: snprintf_ptr(buf, size, format, precision, value); : snprintf_ptr(buf, size, format, precision, value);
} }
namespace {
/// This special mutex has priority inheritance to improve latency.
class pi_mutex
{
public:
pi_mutex(const pi_mutex&) = delete;
pi_mutex& operator=(const pi_mutex&) = delete;
pi_mutex()
{
::pthread_mutexattr_t mutex_attr;
::pthread_mutexattr_init(&mutex_attr);
::pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_INHERIT);
::pthread_mutex_init(&m, &mutex_attr);
}
~pi_mutex() { ::pthread_mutex_destroy(&m); }
/// Mutex lock.
void lock() { ::pthread_mutex_lock(&m); }
/// Mutex unlock.
void unlock() { ::pthread_mutex_unlock(&m); }
/// Mutex try lock. Returns true if the lock was obtained, false otherwise.
bool try_lock() { return (::pthread_mutex_trylock(&m) == 0); }
/// Accessor to the raw mutex structure.
pthread_mutex_t* raw() { return &m; }
const pthread_mutex_t* raw() const { return &m; }
private:
pthread_mutex_t m;
};
}
#define NODE_POOL_SIZE (10000u) #define NODE_POOL_SIZE (10000u)
static constexpr uint8_t memory_heap_tag = 0xAA; static constexpr uint8_t memory_heap_tag = 0xAA;
class dyn_node_pool class dyn_node_pool
@ -49,7 +87,7 @@ public:
void* alloc(std::size_t sz) { void* alloc(std::size_t sz) {
assert(sz <= dynamic_arg_list::max_pool_node_size && "Object is too large to fit in the pool"); assert(sz <= dynamic_arg_list::max_pool_node_size && "Object is too large to fit in the pool");
std::lock_guard<std::mutex> lock(m); std::lock_guard<pi_mutex> lock(m);
if (free_list.empty()) { if (free_list.empty()) {
// Tag that this allocation was performed by the heap. // Tag that this allocation was performed by the heap.
auto *p = new type; auto *p = new type;
@ -70,7 +108,6 @@ public:
return; return;
} }
std::lock_guard<std::mutex> lock(m);
uint8_t* base_ptr = reinterpret_cast<uint8_t *>(p) - 1; uint8_t* base_ptr = reinterpret_cast<uint8_t *>(p) - 1;
if (*base_ptr == memory_heap_tag) { if (*base_ptr == memory_heap_tag) {
// This pointer was allocated using the heap. // This pointer was allocated using the heap.
@ -78,13 +115,14 @@ public:
return; return;
} }
std::lock_guard<pi_mutex> lock(m);
free_list.push_back(base_ptr); free_list.push_back(base_ptr);
} }
private: private:
std::vector<type> pool; std::vector<type> pool;
std::vector<uint8_t *> free_list; std::vector<uint8_t *> free_list;
mutable std::mutex m; mutable pi_mutex m;
}; };
static dyn_node_pool node_pool; static dyn_node_pool node_pool;

@ -134,9 +134,5 @@ srslog::detail::scoped_complete_event::~scoped_complete_event()
return; return;
} }
small_str_buffer str; (*tracer)("%s %s, %u", category, name, (unsigned)diff.count());
// Limit the category and name strings to a predefined length so everything fits in a small string.
fmt::format_to(str, "{:.32} {:.16}, {}", category, name, diff.count());
str.push_back('\0');
(*tracer)(std::move(str));
} }

@ -172,7 +172,7 @@ void pdcp::del_bearer(uint32_t lcid)
} }
if (valid_lcid(lcid)) { if (valid_lcid(lcid)) {
pdcp_array.erase(lcid); pdcp_array.erase(lcid);
logger.warning("Deleted PDCP bearer %s", rrc->get_rb_name(lcid)); logger.info("Deleted PDCP bearer %s", rrc->get_rb_name(lcid));
} else { } else {
logger.warning("Can't delete bearer %s. Bearer doesn't exist.", rrc->get_rb_name(lcid)); logger.warning("Can't delete bearer %s. Bearer doesn't exist.", rrc->get_rb_name(lcid));
} }

@ -565,7 +565,7 @@ int rlc_am_lte::rlc_am_lte_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
// Section 5.2.2.3 in TS 36.311, if tx_window is full and retx_queue empty, retransmit PDU // Section 5.2.2.3 in TS 36.311, if tx_window is full and retx_queue empty, retransmit PDU
if (tx_window.size() >= RLC_AM_WINDOW_SIZE && retx_queue.empty()) { if (tx_window.size() >= RLC_AM_WINDOW_SIZE && retx_queue.empty()) {
retransmit_pdu(); retransmit_pdu(vt_a);
} }
// RETX if required // RETX if required
@ -585,11 +585,11 @@ void rlc_am_lte::rlc_am_lte_tx::timer_expired(uint32_t timeout_id)
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
if (poll_retx_timer.is_valid() && poll_retx_timer.id() == timeout_id) { if (poll_retx_timer.is_valid() && poll_retx_timer.id() == timeout_id) {
logger.debug("%s Poll reTx timer expired after %dms", RB_NAME, poll_retx_timer.duration()); logger.debug("%s Poll reTx timer expired after %dms", RB_NAME, poll_retx_timer.duration());
// Section 5.2.2.3 in TS 36.311, schedule PDU for retransmission if // Section 5.2.2.3 in TS 36.322, schedule PDU for retransmission if
// (a) both tx and retx buffer are empty, or // (a) both tx and retx buffer are empty (excluding tx'ed PDU waiting for ack), or
// (b) no new data PDU can be transmitted (tx window is full) // (b) no new data PDU can be transmitted (tx window is full)
if ((retx_queue.empty() && tx_sdu_queue.size() == 0) || tx_window.size() >= RLC_AM_WINDOW_SIZE) { if ((retx_queue.empty() && tx_sdu_queue.size() == 0) || tx_window.size() >= RLC_AM_WINDOW_SIZE) {
retransmit_pdu(); retransmit_pdu(vt_a); // TODO: TS says to send vt_s - 1 here
} }
} }
@ -600,21 +600,21 @@ void rlc_am_lte::rlc_am_lte_tx::timer_expired(uint32_t timeout_id)
} }
} }
void rlc_am_lte::rlc_am_lte_tx::retransmit_pdu() void rlc_am_lte::rlc_am_lte_tx::retransmit_pdu(uint32_t sn)
{ {
if (tx_window.empty()) { if (tx_window.empty()) {
logger.warning("%s No PDU to retransmit.", RB_NAME); logger.warning("%s No PDU to retransmit", RB_NAME);
return; return;
} }
if (not tx_window.has_sn(vt_a)) { if (not tx_window.has_sn(sn)) {
logger.warning("%s Can't retransmit unexisting SN=%d.", RB_NAME, vt_a); logger.warning("%s Can't retransmit unexisting SN=%d", RB_NAME, sn);
return; return;
} }
// select first PDU in tx window for retransmission // select first PDU in tx window for retransmission
rlc_amd_tx_pdu& pdu = tx_window[vt_a]; rlc_amd_tx_pdu& pdu = tx_window[sn];
logger.info("%s Schedule SN=%d for reTx.", RB_NAME, pdu.rlc_sn); logger.info("%s Schedule SN=%d for reTx", RB_NAME, pdu.rlc_sn);
rlc_amd_retx_t& retx = retx_queue.push(); rlc_amd_retx_t& retx = retx_queue.push();
retx.is_segment = false; retx.is_segment = false;
retx.so_start = 0; retx.so_start = 0;
@ -1263,8 +1263,8 @@ void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t no
} }
// Make sure vt_a points to valid SN // Make sure vt_a points to valid SN
if (not tx_window.empty() && not tx_window.has_sn(vt_a)) { if (not tx_window.empty()) {
logger.error("%s vt_a=%d points to invalid position in Tx window", RB_NAME, vt_a); srsran_expect(tx_window.has_sn(vt_a), "%s vt_a=%d points to invalid position in Tx window", RB_NAME, vt_a);
} }
debug_state(); debug_state();

@ -30,33 +30,35 @@ target_link_libraries(phy_dl_test srsran_phy srsran_common srsran_phy ${SEC_LIBR
set(ue_dl_min_mcs 0) set(ue_dl_min_mcs 0)
set(ue_dl_max_mcs 28) set(ue_dl_max_mcs 28)
set(ue_dl_step_mcs 7) set(ue_dl_step_mcs 7)
foreach (cp 0 1)
foreach (cell_n_prb 6 15 25 50 75 100) foreach (cell_n_prb 6 15 25 50 75 100)
foreach (allow_256 0 1) foreach (allow_256 0 1)
foreach (ue_dl_tm 1 2 3 4) foreach (ue_dl_tm 1 2 3 4)
foreach (ue_dl_mcs RANGE ${ue_dl_min_mcs} ${ue_dl_max_mcs} ${ue_dl_step_mcs}) foreach (ue_dl_mcs RANGE ${ue_dl_min_mcs} ${ue_dl_max_mcs} ${ue_dl_step_mcs})
set(phy_dl_test_args "") set(phy_dl_test_args "")
if(NOT ((${cp} EQUAL 1) AND (ue_dl_mcs GREATER 26)))
set(phy_dl_test_args ${phy_dl_test_args} -p ${cell_n_prb}) set(phy_dl_test_args ${phy_dl_test_args} -p ${cell_n_prb})
set(phy_dl_test_args ${phy_dl_test_args} -t ${ue_dl_tm}) set(phy_dl_test_args ${phy_dl_test_args} -t ${ue_dl_tm})
if (${allow_256}) set(phy_dl_test_args ${phy_dl_test_args} -E ${cp})
if (${ue_dl_mcs} EQUAL 28) if (${allow_256})
if (${cell_n_prb} EQUAL 15) if (${ue_dl_mcs} EQUAL 28)
set(ue_dl_mcs 26) if (${cell_n_prb} EQUAL 15)
else (${cell_n_prb} EQUAL 15) set(ue_dl_mcs 26)
set(ue_dl_mcs 27) else (${cell_n_prb} EQUAL 15)
endif (${cell_n_prb} EQUAL 15) set(ue_dl_mcs 27)
endif (${ue_dl_mcs} EQUAL 28) endif (${cell_n_prb} EQUAL 15)
endif (${ue_dl_mcs} EQUAL 28)
set(phy_dl_test_args ${phy_dl_test_args} -q) set(phy_dl_test_args ${phy_dl_test_args} -q)
endif (${allow_256}) endif (${allow_256})
set(phy_dl_test_args ${phy_dl_test_args} -m ${ue_dl_mcs}) set(phy_dl_test_args ${phy_dl_test_args} -m ${ue_dl_mcs})
string(REGEX REPLACE "\ " "" test_name_args ${phy_dl_test_args}) string(REGEX REPLACE "\ " "" test_name_args ${phy_dl_test_args})
add_lte_test(phy_dl_test${test_name_args} phy_dl_test ${phy_dl_test_args}) add_lte_test(phy_dl_test${test_name_args} phy_dl_test ${phy_dl_test_args})
endforeach (ue_dl_mcs) endif(NOT ((${cp} EQUAL 1) AND (ue_dl_mcs GREATER 26)))
endforeach (ue_dl_tm) endforeach (ue_dl_mcs)
endforeach (allow_256 0 1) endforeach (ue_dl_tm)
endforeach (cell_n_prb) endforeach (allow_256 0 1)
endforeach (cell_n_prb)
endforeach (cp)
add_executable(pucch_ca_test pucch_ca_test.c) add_executable(pucch_ca_test pucch_ca_test.c)
target_link_libraries(pucch_ca_test srsran_phy srsran_common srsran_phy ${SEC_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(pucch_ca_test srsran_phy srsran_common srsran_phy ${SEC_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})

@ -49,6 +49,7 @@ void usage(char* prog)
{ {
printf("Usage: %s [cfpndvs]\n", prog); printf("Usage: %s [cfpndvs]\n", prog);
printf("\t-c cell id [Default %d]\n", cell.id); printf("\t-c cell id [Default %d]\n", cell.id);
printf("\t-E extended Cyclic prefix [Default %d]\n", cell.cp);
printf("\t-f cfi [Default %d]\n", cfi); printf("\t-f cfi [Default %d]\n", cfi);
printf("\t-p cell.nof_prb [Default %d]\n", cell.nof_prb); printf("\t-p cell.nof_prb [Default %d]\n", cell.nof_prb);
printf("\t-s number of subframes to simulate [Default %d]\n", nof_subframes); printf("\t-s number of subframes to simulate [Default %d]\n", nof_subframes);
@ -93,7 +94,7 @@ void parse_args(int argc, char** argv)
nof_rx_ant = 2; nof_rx_ant = 2;
} }
while ((opt = getopt(argc, argv, "cfapndvqstm")) != -1) { while ((opt = getopt(argc, argv, "cfapndvqstmE")) != -1) {
switch (opt) { switch (opt) {
case 't': case 't':
transmission_mode = (uint32_t)strtol(argv[optind], NULL, 10) - 1; transmission_mode = (uint32_t)strtol(argv[optind], NULL, 10) - 1;
@ -120,6 +121,9 @@ void parse_args(int argc, char** argv)
case 's': case 's':
nof_subframes = (uint32_t)strtol(argv[optind], NULL, 10); nof_subframes = (uint32_t)strtol(argv[optind], NULL, 10);
break; break;
case 'E':
cell.cp = ((uint32_t)strtol(argv[optind], NULL, 10)) ? SRSRAN_CP_EXT : SRSRAN_CP_NORM;
break;
case 'd': case 'd':
print_dci_table = true; print_dci_table = true;
break; break;

@ -37,7 +37,7 @@ static detail::log_entry_metadata build_log_entry_metadata(fmt::dynamic_format_a
store->push_back(88); store->push_back(88);
} }
return {tp, {10, true}, "Text %d", store, "ABC", 'Z', small_str_buffer()}; return {tp, {10, true}, "Text %d", store, "ABC", 'Z'};
} }
static bool when_fully_filled_log_entry_then_everything_is_formatted() static bool when_fully_filled_log_entry_then_everything_is_formatted()

@ -89,7 +89,7 @@ static detail::log_entry build_log_entry(sink* s, fmt::dynamic_format_arg_store<
return {s, return {s,
[](detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer) {}, [](detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer) {},
{tp, {0, false}, "Text %d", store, "", '\0', small_str_buffer()}}; {tp, {0, false}, "Text %d", store, "", '\0'}};
} }
static bool when_backend_is_not_started_then_pushed_log_entries_are_ignored() static bool when_backend_is_not_started_then_pushed_log_entries_are_ignored()

@ -279,30 +279,6 @@ static bool when_logging_with_context_and_message_then_filled_in_log_entry_is_pu
return true; return true;
} }
static bool when_logging_with_small_string_then_filled_in_log_entry_is_pushed_into_the_backend()
{
backend_spy backend;
test_dummies::sink_dummy s;
log_channel log("id", s, backend);
small_str_buffer buf;
fmt::format_to(buf, "A {} {} {}", 1, 2, 3);
buf.push_back('\0');
log(std::move(buf));
ASSERT_EQ(backend.push_invocation_count(), 1);
const detail::log_entry& entry = backend.last_entry();
ASSERT_EQ(&s, entry.s);
ASSERT_NE(entry.format_func, nullptr);
ASSERT_NE(entry.metadata.tp.time_since_epoch().count(), 0);
ASSERT_EQ(entry.metadata.hex_dump.empty(), true);
ASSERT_EQ(std::string(entry.metadata.small_str.data()), "A 1 2 3");
return true;
}
int main() int main()
{ {
TEST_FUNCTION(when_log_channel_is_created_then_id_matches_expected_value); TEST_FUNCTION(when_log_channel_is_created_then_id_matches_expected_value);
@ -315,7 +291,6 @@ int main()
TEST_FUNCTION(when_hex_array_length_is_less_than_hex_log_max_size_then_array_length_is_used); TEST_FUNCTION(when_hex_array_length_is_less_than_hex_log_max_size_then_array_length_is_used);
TEST_FUNCTION(when_logging_with_context_then_filled_in_log_entry_is_pushed_into_the_backend); TEST_FUNCTION(when_logging_with_context_then_filled_in_log_entry_is_pushed_into_the_backend);
TEST_FUNCTION(when_logging_with_context_and_message_then_filled_in_log_entry_is_pushed_into_the_backend); TEST_FUNCTION(when_logging_with_context_and_message_then_filled_in_log_entry_is_pushed_into_the_backend);
TEST_FUNCTION(when_logging_with_small_string_then_filled_in_log_entry_is_pushed_into_the_backend);
return 0; return 0;
} }

@ -37,7 +37,7 @@ static detail::log_entry_metadata build_log_entry_metadata(fmt::dynamic_format_a
store->push_back(88); store->push_back(88);
} }
return {tp, {10, true}, "Text %d", store, "ABC", 'Z', small_str_buffer()}; return {tp, {10, true}, "Text %d", store, "ABC", 'Z'};
} }
static bool when_fully_filled_log_entry_then_everything_is_formatted() static bool when_fully_filled_log_entry_then_everything_is_formatted()

@ -92,7 +92,7 @@ public:
class ul_writer : public thread class ul_writer : public thread
{ {
public: public:
ul_writer(rlc_am_lte* rlc_) : rlc(rlc_), running(false), thread("UL_WRITER") {} ul_writer(rlc_am_lte* rlc_) : rlc(rlc_), thread("UL_WRITER") {}
~ul_writer() { stop(); } ~ul_writer() { stop(); }
void stop() void stop()
{ {
@ -128,8 +128,8 @@ private:
running = false; running = false;
} }
rlc_am_lte* rlc; rlc_am_lte* rlc = nullptr;
bool running; std::atomic<bool> running = {false};
}; };
int basic_test_tx(rlc_am_lte* rlc, byte_buffer_t pdu_bufs[NBUFS]) int basic_test_tx(rlc_am_lte* rlc, byte_buffer_t pdu_bufs[NBUFS])

@ -254,7 +254,7 @@ private:
pcap->write_ul_ccch(pdu->msg, pdu_len); pcap->write_ul_ccch(pdu->msg, pdu_len);
} }
} else { } else {
logger.warning(pdu->msg, pdu->N_bytes, "Dropping RLC PDU (%d B)", pdu->N_bytes); logger.info(pdu->msg, pdu->N_bytes, "Dropping RLC PDU (%d B)", pdu->N_bytes);
skip_action = true; // Avoid drop duplicating this PDU skip_action = true; // Avoid drop duplicating this PDU
} }
@ -263,7 +263,7 @@ private:
it++; it++;
skip_action = false; // Allow action on the next PDU skip_action = false; // Allow action on the next PDU
} else { } else {
logger.warning(pdu->msg, pdu->N_bytes, "Duplicating RLC PDU (%d B)", pdu->N_bytes); logger.info(pdu->msg, pdu->N_bytes, "Duplicating RLC PDU (%d B)", pdu->N_bytes);
skip_action = true; // Avoid drop of this PDU skip_action = true; // Avoid drop of this PDU
} }
} }
@ -308,7 +308,7 @@ private:
rlc_interface_mac* rlc1 = nullptr; rlc_interface_mac* rlc1 = nullptr;
rlc_interface_mac* rlc2 = nullptr; rlc_interface_mac* rlc2 = nullptr;
bool run_enable = false; std::atomic<bool> run_enable = {false};
stress_test_args_t args = {}; stress_test_args_t args = {};
rlc_pcap* pcap = nullptr; rlc_pcap* pcap = nullptr;
uint32_t lcid = 0; uint32_t lcid = 0;
@ -436,7 +436,8 @@ private:
} }
} }
bool run_enable = true; std::atomic<bool> run_enable = {true};
/// Tx uses thread-local PDCP SN to set SDU content, the Rx uses this variable to check received SDUs /// Tx uses thread-local PDCP SN to set SDU content, the Rx uses this variable to check received SDUs
uint8_t next_expected_sdu = 0; uint8_t next_expected_sdu = 0;
uint64_t rx_pdus = 0; uint64_t rx_pdus = 0;

@ -332,6 +332,8 @@ enable = false
#max_prach_offset_us = 30 #max_prach_offset_us = 30
#nof_prealloc_ues = 8 #nof_prealloc_ues = 8
#rlf_release_timer_ms = 4000 #rlf_release_timer_ms = 4000
#lcid_padding = 3
#eea_pref_list = EEA0, EEA2, EEA1 #eea_pref_list = EEA0, EEA2, EEA1
#eia_pref_list = EIA2, EIA1, EIA0 #eia_pref_list = EIA2, EIA1, EIA0
#gtpu_tunnel_timeout = 0 #gtpu_tunnel_timeout = 0
#extended_cp = false

@ -51,6 +51,10 @@ constexpr uint32_t drb_to_lcid(lte_drb drb_id)
{ {
return srb_to_lcid(lte_srb::srb2) + static_cast<uint32_t>(drb_id); return srb_to_lcid(lte_srb::srb2) + static_cast<uint32_t>(drb_id);
} }
constexpr lte_drb lte_lcid_to_drb(uint32_t lcid)
{
return srsran::is_lte_drb(lcid) ? static_cast<lte_drb>(lcid - srb_to_lcid(lte_srb::srb2)) : lte_drb::invalid;
}
// Cat 3 UE - Max number of DL-SCH transport block bits received within a TTI // Cat 3 UE - Max number of DL-SCH transport block bits received within a TTI
// 3GPP 36.306 Table 4.1.1 // 3GPP 36.306 Table 4.1.1

@ -146,6 +146,8 @@ public:
// eNodeB command interface // eNodeB command interface
void cmd_cell_gain(uint32_t cell_id, float gain) override; void cmd_cell_gain(uint32_t cell_id, float gain) override;
void toggle_padding() override;
private: private:
const static int ENB_POOL_SIZE = 1024 * 10; const static int ENB_POOL_SIZE = 1024 * 10;

@ -72,7 +72,7 @@ struct phy_args_t {
bool pusch_meas_ta = true; bool pusch_meas_ta = true;
bool pucch_meas_ta = true; bool pucch_meas_ta = true;
uint32_t nof_prach_threads = 1; uint32_t nof_prach_threads = 1;
bool extended_cp = false;
srsran::channel::args_t dl_channel_args; srsran::channel::args_t dl_channel_args;
srsran::channel::args_t ul_channel_args; srsran::channel::args_t ul_channel_args;

@ -101,6 +101,7 @@ public:
virtual void stop() = 0; virtual void stop() = 0;
virtual void toggle_padding() = 0;
// eNB metrics interface // eNB metrics interface
virtual bool get_metrics(stack_metrics_t* metrics) = 0; virtual bool get_metrics(stack_metrics_t* metrics) = 0;
}; };

@ -29,11 +29,11 @@
#include "mac/mac.h" #include "mac/mac.h"
#include "rrc/rrc.h" #include "rrc/rrc.h"
#include "s1ap/s1ap.h"
#include "srsran/common/task_scheduler.h" #include "srsran/common/task_scheduler.h"
#include "upper/gtpu.h" #include "upper/gtpu.h"
#include "upper/pdcp.h" #include "upper/pdcp.h"
#include "upper/rlc.h" #include "upper/rlc.h"
#include "upper/s1ap.h"
#include "enb_stack_base.h" #include "enb_stack_base.h"
#include "srsran/common/mac_pcap_net.h" #include "srsran/common/mac_pcap_net.h"
@ -100,6 +100,7 @@ public:
{ {
mac.set_sched_dl_tti_mask(tti_mask, nof_sfs); mac.set_sched_dl_tti_mask(tti_mask, nof_sfs);
} }
void toggle_padding() override { mac.toggle_padding(); }
void tti_clock() override; void tti_clock() override;
private: private:

@ -27,12 +27,12 @@
#ifndef SRSRAN_GNB_STACK_NR_H #ifndef SRSRAN_GNB_STACK_NR_H
#define SRSRAN_GNB_STACK_NR_H #define SRSRAN_GNB_STACK_NR_H
#include "s1ap/s1ap.h"
#include "srsenb/hdr/stack/mac/mac_nr.h" #include "srsenb/hdr/stack/mac/mac_nr.h"
#include "srsenb/hdr/stack/rrc/rrc_nr.h" #include "srsenb/hdr/stack/rrc/rrc_nr.h"
#include "srsenb/hdr/stack/upper/pdcp_nr.h" #include "srsenb/hdr/stack/upper/pdcp_nr.h"
#include "srsenb/hdr/stack/upper/rlc_nr.h" #include "srsenb/hdr/stack/upper/rlc_nr.h"
#include "upper/gtpu.h" #include "upper/gtpu.h"
#include "upper/s1ap.h"
#include "upper/sdap.h" #include "upper/sdap.h"
#include "enb_stack_base.h" #include "enb_stack_base.h"
@ -80,6 +80,8 @@ public:
// MAC interface to trigger processing of received PDUs // MAC interface to trigger processing of received PDUs
void process_pdus() final; void process_pdus() final;
void toggle_padding() { srsran::console("padding not available for NR\n"); }
private: private:
void run_thread() final; void run_thread() final;
void run_tti_impl(uint32_t tti); void run_tti_impl(uint32_t tti);

@ -103,6 +103,11 @@ public:
bool process_pdus(); bool process_pdus();
void get_metrics(mac_metrics_t& metrics); void get_metrics(mac_metrics_t& metrics);
void toggle_padding();
void add_padding();
void write_mcch(const srsran::sib2_mbms_t* sib2_, void write_mcch(const srsran::sib2_mbms_t* sib2_,
const srsran::sib13_t* sib13_, const srsran::sib13_t* sib13_,
const srsran::mcch_msg_t* mcch_, const srsran::mcch_msg_t* mcch_,
@ -185,8 +190,9 @@ private:
uint8_t mtch_payload_buffer[mtch_payload_len] = {}; uint8_t mtch_payload_buffer[mtch_payload_len] = {};
// pointer to MAC PCAP object // pointer to MAC PCAP object
srsran::mac_pcap* pcap = nullptr; srsran::mac_pcap* pcap = nullptr;
srsran::mac_pcap_net* pcap_net = nullptr; srsran::mac_pcap_net* pcap_net = nullptr;
bool do_padding = false;
// Number of rach preambles detected for a cc. // Number of rach preambles detected for a cc.
std::vector<uint32_t> detected_rachs; std::vector<uint32_t> detected_rachs;

@ -90,29 +90,6 @@ public:
dl_lb_nof_re_table nof_re_lb_table; dl_lb_nof_re_table nof_re_lb_table;
}; };
//! Bitmask used for CCE allocations
using pdcch_mask_t = srsran::bounded_bitset<sched_interface::max_cce, true>;
//! Bitmask that stores the allocared DL RBGs
using rbgmask_t = srsran::bounded_bitset<25, true>;
//! Bitmask that stores the allocated UL PRBs
using prbmask_t = srsran::bounded_bitset<100, true>;
//! Struct to express a {min,...,max} range of RBGs
struct prb_interval;
struct rbg_interval : public srsran::interval<uint32_t> {
using interval::interval;
static rbg_interval rbgmask_to_rbgs(const rbgmask_t& mask);
};
/// Struct to express a {min,...,max} range of PRBs
struct prb_interval : public srsran::interval<uint32_t> {
using interval::interval;
static prb_interval rbgs_to_prbs(const rbg_interval& rbgs, uint32_t cell_nof_prb);
static prb_interval riv_to_prbs(uint32_t riv, uint32_t nof_prbs, int nof_vrbs = -1);
};
/// Type of Allocation stored in PDSCH/PUSCH /// Type of Allocation stored in PDSCH/PUSCH
enum class alloc_type_t { DL_BC, DL_PCCH, DL_RAR, DL_DATA, UL_DATA }; enum class alloc_type_t { DL_BC, DL_PCCH, DL_RAR, DL_DATA, UL_DATA };
inline bool is_dl_ctrl_alloc(alloc_type_t a) inline bool is_dl_ctrl_alloc(alloc_type_t a)
@ -122,13 +99,4 @@ inline bool is_dl_ctrl_alloc(alloc_type_t a)
} // namespace srsenb } // namespace srsenb
namespace fmt {
template <>
struct formatter<srsenb::rbg_interval> : public formatter<srsran::interval<uint32_t> > {};
template <>
struct formatter<srsenb::prb_interval> : public formatter<srsran::interval<uint32_t> > {};
} // namespace fmt
#endif // SRSRAN_SCHED_COMMON_H #endif // SRSRAN_SCHED_COMMON_H

@ -47,74 +47,6 @@ inline uint32_t get_nof_retx(uint32_t rv_idx)
return nof_retxs[rv_idx % 4]; return nof_retxs[rv_idx % 4];
} }
/// convert cell nof PRBs to nof RBGs
inline uint32_t cell_nof_prb_to_rbg(uint32_t nof_prbs)
{
switch (nof_prbs) {
case 6:
return 6;
case 15:
return 8;
case 25:
return 13;
case 50:
return 17;
case 75:
return 19;
case 100:
return 25;
default:
srslog::fetch_basic_logger("MAC").error("Provided nof PRBs not valid");
return 0;
}
}
/// convert cell nof RBGs to nof PRBs
inline uint32_t cell_nof_rbg_to_prb(uint32_t nof_rbgs)
{
switch (nof_rbgs) {
case 6:
return 6;
case 8:
return 15;
case 13:
return 25;
case 17:
return 50;
case 19:
return 75;
case 25:
return 100;
default:
srslog::fetch_basic_logger("MAC").error("Provided nof PRBs not valid");
return 0;
}
}
/**
* Count number of PRBs present in a DL RBG mask
* @param cell_nof_prb cell nof prbs
* @param P cell ratio prb/rbg
* @param bitmask DL RBG mask
* @return number of prbs
*/
inline uint32_t count_prb_per_tb(const rbgmask_t& bitmask)
{
uint32_t Nprb = cell_nof_rbg_to_prb(bitmask.size());
uint32_t P = srsran_ra_type0_P(Nprb);
uint32_t nof_prb = P * bitmask.count();
if (bitmask.test(bitmask.size() - 1)) {
nof_prb -= bitmask.size() * P - Nprb;
}
return nof_prb;
}
inline uint32_t count_prb_per_tb_approx(uint32_t nof_rbgs, uint32_t cell_nof_prb)
{
uint32_t P = srsran_ra_type0_P(cell_nof_prb);
return std::min(nof_rbgs * P, cell_nof_prb);
}
cce_frame_position_table generate_cce_location_table(uint16_t rnti, const sched_cell_params_t& cell_cfg); cce_frame_position_table generate_cce_location_table(uint16_t rnti, const sched_cell_params_t& cell_cfg);
/** /**
@ -150,12 +82,6 @@ uint32_t get_aggr_level(uint32_t nof_bits,
uint32_t cell_nof_prb, uint32_t cell_nof_prb,
bool use_tbs_index_alt); bool use_tbs_index_alt);
/*******************************************************
* RB mask helper functions
*******************************************************/
bool is_contiguous(const rbgmask_t& mask);
/******************************************************* /*******************************************************
* sched_interface helper functions * sched_interface helper functions
*******************************************************/ *******************************************************/

@ -23,6 +23,7 @@
#define SRSRAN_SCHED_DCI_H #define SRSRAN_SCHED_DCI_H
#include "../sched_common.h" #include "../sched_common.h"
#include "srsenb/hdr/stack/mac/sched_phy_ch/sched_phy_resource.h"
#include "srsran/adt/bounded_vector.h" #include "srsran/adt/bounded_vector.h"
namespace srsenb { namespace srsenb {

@ -0,0 +1,174 @@
/**
*
* \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_SCHED_PHY_RESOURCE_H
#define SRSRAN_SCHED_PHY_RESOURCE_H
#include "srsran/adt/bounded_bitset.h"
#include "srsran/adt/interval.h"
#include "srsran/common/srsran_assert.h"
extern "C" {
#include "srsran/phy/phch/ra.h"
}
// Description: This file defines the types associated with representing the allocation masks/intervals for RBGs, PRBs
// and PDCCH CCEs, and provides some function helpers and algorithms to handle these types.
constexpr uint32_t MAX_NOF_RBGS = 25;
constexpr uint32_t MAX_NOF_PRBS = 100;
constexpr uint32_t MAX_NOF_CCES = 128;
namespace srsenb {
/// convert cell nof PRBs to nof RBGs
inline uint32_t cell_nof_prb_to_rbg(uint32_t nof_prbs)
{
switch (nof_prbs) {
case 6:
return 6;
case 15:
return 8;
case 25:
return 13;
case 50:
return 17;
case 75:
return 19;
case 100:
return 25;
default:
srsran_terminate("Provided nof PRBs not valid");
}
return 0;
}
/// convert cell nof RBGs to nof PRBs
inline uint32_t cell_nof_rbg_to_prb(uint32_t nof_rbgs)
{
switch (nof_rbgs) {
case 6:
return 6;
case 8:
return 15;
case 13:
return 25;
case 17:
return 50;
case 19:
return 75;
case 25:
return 100;
default:
srsran_terminate("Provided nof PRBs not valid");
}
return 0;
}
/// Bitmask used for CCE allocations
using pdcch_mask_t = srsran::bounded_bitset<MAX_NOF_CCES, true>;
/// Bitmask that stores the allocared DL RBGs
using rbgmask_t = srsran::bounded_bitset<MAX_NOF_RBGS, true>;
/// Bitmask that stores the allocated UL PRBs
using prbmask_t = srsran::bounded_bitset<MAX_NOF_PRBS, true>;
/// Struct to express a {min,...,max} range of RBGs
struct prb_interval;
struct rbg_interval : public srsran::interval<uint32_t> {
using interval::interval;
static rbg_interval find_first_interval(const rbgmask_t& mask);
static rbg_interval prbs_to_rbgs(const prb_interval& prbs, uint32_t cell_nof_prb);
};
/// Struct to express a {min,...,max} range of PRBs
struct prb_interval : public srsran::interval<uint32_t> {
using interval::interval;
static prb_interval rbgs_to_prbs(const rbg_interval& rbgs, uint32_t cell_nof_prb)
{
uint32_t P = srsran_ra_type0_P(cell_nof_prb);
return prb_interval{rbgs.start() * P, std::min(rbgs.stop() * P, cell_nof_prb)};
}
static prb_interval riv_to_prbs(uint32_t riv, uint32_t nof_prbs, int nof_vrbs = -1);
};
inline rbg_interval rbg_interval::prbs_to_rbgs(const prb_interval& prbs, uint32_t cell_nof_prb)
{
uint32_t P = srsran_ra_type0_P(cell_nof_prb);
return rbg_interval{prbs.start() / P, srsran::ceil_div(prbs.stop(), P)};
}
/*******************************************************
* helper functions
*******************************************************/
/// If the RBG mask one bits are all contiguous
inline bool is_contiguous(const rbgmask_t& mask)
{
return rbg_interval::find_first_interval(mask).length() == mask.count();
}
/// Count number of PRBs present in a DL RBG mask
inline uint32_t count_prb_per_tb(const rbgmask_t& bitmask)
{
uint32_t Nprb = cell_nof_rbg_to_prb(bitmask.size());
uint32_t P = srsran_ra_type0_P(Nprb);
uint32_t nof_prb = P * bitmask.count();
if (bitmask.test(bitmask.size() - 1)) {
nof_prb -= bitmask.size() * P - Nprb;
}
return nof_prb;
}
/// Estimate of number of PRBs in DL grant given Nof RBGs
inline uint32_t count_prb_per_tb_approx(uint32_t nof_rbgs, uint32_t cell_nof_prb)
{
uint32_t P = srsran_ra_type0_P(cell_nof_prb);
return std::min(nof_rbgs * P, cell_nof_prb);
}
/**
* Finds a contiguous interval of "zeroed"/available RBG resources
* @param max_nof_rbgs maximum number of RBGs
* @param current_mask bitmask of occupied RBGs, used to search for available RBGs
* @return interval with found RBGs. If a valid interval wasn't found, interval.length() == 0
*/
rbg_interval find_empty_rbg_interval(uint32_t max_nof_rbgs, const rbgmask_t& current_mask);
/**
* Finds a bitmask of "zeroed"/available RBG resources
* @param max_nof_rbgs maximum number of RBGs
* @param current_mask bitmask of occupied RBGs, used to search for available RBGs
* @return bitmask of found RBGs. If a valid mask wasn't found, bitmask::size() == 0
*/
rbgmask_t find_available_rbgmask(uint32_t max_nof_rbgs, bool is_contiguous, const rbgmask_t& current_mask);
/**
* Finds a range of L contiguous PRBs that are empty
* @param L Max length of the requested UL PRBs
* @param current_mask input PRB mask where to search for available PRBs
* @return found interval of PRBs
*/
prb_interval find_contiguous_ul_prbs(uint32_t L, const prbmask_t& current_mask);
} // namespace srsenb
namespace fmt {
template <>
struct formatter<srsenb::rbg_interval> : public formatter<srsran::interval<uint32_t> > {};
template <>
struct formatter<srsenb::prb_interval> : public formatter<srsran::interval<uint32_t> > {};
} // namespace fmt
#endif // SRSRAN_SCHED_PHY_RESOURCE_H

@ -23,6 +23,7 @@
#define SRSRAN_SCHED_RESULT_H #define SRSRAN_SCHED_RESULT_H
#include "../sched_common.h" #include "../sched_common.h"
#include "srsenb/hdr/stack/mac/sched_phy_ch/sched_phy_resource.h"
namespace srsenb { namespace srsenb {

@ -91,10 +91,11 @@ public:
/// Get total pending bytes to be transmitted in DL. /// Get total pending bytes to be transmitted in DL.
/// The amount of CEs to transmit depends on whether enb_cc_idx is UE's PCell /// The amount of CEs to transmit depends on whether enb_cc_idx is UE's PCell
uint32_t get_pending_dl_bytes(uint32_t enb_cc_idx); uint32_t get_pending_dl_bytes(uint32_t enb_cc_idx);
rbg_interval get_required_dl_rbgs(uint32_t enb_cc_idx); srsran::interval<uint32_t> get_requested_dl_bytes(uint32_t enb_cc_idx);
uint32_t get_pending_dl_rlc_data() const; rbg_interval get_required_dl_rbgs(uint32_t enb_cc_idx);
uint32_t get_expected_dl_bitrate(uint32_t enb_cc_idx, int nof_rbgs = -1) const; uint32_t get_pending_dl_rlc_data() const;
uint32_t get_expected_dl_bitrate(uint32_t enb_cc_idx, int nof_rbgs = -1) const;
uint32_t get_pending_ul_data_total(tti_point tti_tx_ul, int this_enb_cc_idx); uint32_t get_pending_ul_data_total(tti_point tti_tx_ul, int this_enb_cc_idx);
uint32_t get_pending_ul_new_data(tti_point tti_tx_ul, int this_enb_cc_idx); uint32_t get_pending_ul_new_data(tti_point tti_tx_ul, int this_enb_cc_idx);
@ -148,8 +149,6 @@ public:
bool pusch_enabled(tti_point tti_rx, uint32_t enb_cc_idx, bool needs_pdcch) const; bool pusch_enabled(tti_point tti_rx, uint32_t enb_cc_idx, bool needs_pdcch) const;
private: private:
srsran::interval<uint32_t> get_requested_dl_bytes(uint32_t enb_cc_idx);
bool is_sr_triggered(); bool is_sr_triggered();
tbs_info allocate_new_dl_mac_pdu(sched_interface::dl_sched_data_t* data, tbs_info allocate_new_dl_mac_pdu(sched_interface::dl_sched_data_t* data,
@ -162,7 +161,7 @@ private:
tbs_info compute_mcs_and_tbs(uint32_t enb_cc_idx, tbs_info compute_mcs_and_tbs(uint32_t enb_cc_idx,
tti_point tti_tx_dl, tti_point tti_tx_dl,
uint32_t nof_alloc_prbs, const rbgmask_t& rbgs,
uint32_t cfi, uint32_t cfi,
const srsran_dci_dl_t& dci); const srsran_dci_dl_t& dci);

@ -0,0 +1,240 @@
/**
*
* \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_SCHED_DL_CQI_H
#define SRSRAN_SCHED_DL_CQI_H
#include "srsenb/hdr/stack/mac/sched_common.h"
#include "srsenb/hdr/stack/mac/sched_helpers.h"
#include "srsenb/hdr/stack/mac/sched_phy_ch/sched_phy_resource.h"
#include "srsran/adt/accumulators.h"
#include "srsran/common/common_lte.h"
#include "srsran/phy/phch/cqi.h"
namespace srsenb {
/**
* Class that handles DL CQI state of a given {rnti,sector}
* - The cell bandwidth is divided into J parts. J = f(nof_cell_prbs)
* - UE reports wideband CQI every H.Np msec, where Np is the CQI period and H=JK + 1, where K is configured in RRC
* - Thus, for K==0, only wideband CQI is active
*/
class sched_dl_cqi
{
public:
sched_dl_cqi(uint32_t cell_nof_prb_, uint32_t K_, uint32_t init_dl_cqi) :
cell_nof_prb(cell_nof_prb_),
cell_nof_rbg(cell_nof_prb_to_rbg(cell_nof_prb_)),
K(K_),
wb_cqi_avg(init_dl_cqi),
bp_list(nof_bandwidth_parts(cell_nof_prb_), bandwidth_part_context(init_dl_cqi)),
subband_cqi(std::max(1, srsran_cqi_hl_get_no_subbands(cell_nof_prb)), 0)
{
srsran_assert(K <= 4, "K=%d outside of {0, 4}", K);
srsran_assert(K == 0 or cell_nof_prb_ > 6, "K > 0 not allowed for nof_prbs=6");
}
/// Set K value from upper layers. See TS 36.331, CQI-ReportPeriodic
void set_K(uint32_t K_)
{
srsran_assert(K <= 4, "K=%d outside of {0, 4}", K);
srsran_assert(K == 0 or cell_nof_prb > 6, "K > 0 not allowed for nof_prbs=6");
K = K_;
}
/// Update wideband CQI
void cqi_wb_info(tti_point tti, uint32_t cqi_value)
{
if (cqi_value > 0) {
last_pos_cqi_tti = tti;
}
last_wb_tti = tti;
wb_cqi_avg = static_cast<float>(cqi_value);
}
/// Update subband CQI for subband "sb_index"
void cqi_sb_info(tti_point tti, uint32_t sb_index, uint32_t cqi_value)
{
if (cqi_value > 0) {
last_pos_cqi_tti = tti;
}
uint32_t bp_idx = get_bp_index(sb_index);
bp_list[bp_idx].last_feedback_tti = tti;
bp_list[bp_idx].last_cqi_subband_idx = sb_index;
bp_list[bp_idx].cqi_val = static_cast<float>(cqi_value);
// just cap all sub-bands in the same bandwidth part
srsran::interval<uint32_t> interv = get_bp_sb_indexes(bp_idx);
for (uint32_t sb_index2 = interv.start(); sb_index2 < interv.stop(); ++sb_index2) {
subband_cqi[sb_index2] = bp_list[bp_idx].cqi_val;
}
}
/// Resets CQI to provided value
void reset_cqi(uint32_t dl_cqi)
{
last_pos_cqi_tti = {};
last_wb_tti = {};
wb_cqi_avg = dl_cqi;
for (bandwidth_part_context& bp : bp_list) {
bp = bandwidth_part_context(dl_cqi);
}
}
int get_avg_cqi() const { return get_grant_avg_cqi(rbg_interval(0, cell_nof_rbg)); }
/// Get CQI of RBG
int get_rbg_cqi(uint32_t rbg) const
{
if (not subband_cqi_enabled()) {
return static_cast<int>(wb_cqi_avg);
}
uint32_t sb_idx = rbg_to_sb_index(rbg);
return get_subband_cqi_(sb_idx);
}
/// Get average CQI in given RBG interval
int get_grant_avg_cqi(rbg_interval interv) const
{
if (not subband_cqi_enabled()) {
return static_cast<int>(wb_cqi_avg);
}
float cqi = 0;
uint32_t sbstart = rbg_to_sb_index(interv.start()), sbend = rbg_to_sb_index(interv.stop() - 1) + 1;
for (uint32_t sb = sbstart; sb < sbend; ++sb) {
cqi += get_subband_cqi_(sb);
}
return static_cast<int>(cqi / (sbend - sbstart));
}
/// Get average CQI in given PRB interval
int get_grant_avg_cqi(prb_interval prb_interv) const
{
return get_grant_avg_cqi(rbg_interval::prbs_to_rbgs(prb_interv, cell_nof_prb));
}
/// Get average CQI in given RBG mask
int get_grant_avg_cqi(const rbgmask_t& mask) const
{
if (not subband_cqi_enabled()) {
return static_cast<int>(wb_cqi_avg);
}
float cqi = 0;
uint32_t count = 0;
for (int rbg = mask.find_lowest(0, mask.size()); rbg != -1; rbg = mask.find_lowest(rbg, mask.size())) {
uint32_t sb = rbg_to_sb_index(rbg);
cqi += get_subband_cqi_(sb);
count++;
rbg = static_cast<int>(((sb + 1U) * cell_nof_rbg + N() - 1U) / N()); // skip to next subband index
}
return static_cast<int>(cqi / count);
}
/// Get CQI-optimal RBG mask with at most "req_rbgs" RBGs
rbgmask_t get_optim_rbgmask(uint32_t req_rbgs, bool max_min_flag = true) const
{
rbgmask_t rbgmask(cell_nof_rbg);
return get_optim_rbgmask(rbgmask, req_rbgs, max_min_flag);
}
rbgmask_t get_optim_rbgmask(const rbgmask_t& dl_mask, uint32_t req_rbgs, bool max_min_flag = true) const;
/// TS 36.321, 7.2.2 - Parameter N
uint32_t nof_subbands() const { return subband_cqi.size(); }
/// TS 36.321, 7.2.2 - Parameter J
uint32_t nof_bandwidth_parts() const { return bp_list.size(); }
bool subband_cqi_enabled() const { return K > 0; }
bool is_cqi_info_received() const { return last_pos_cqi_tti.is_valid(); }
tti_point last_cqi_info_tti() const { return last_pos_cqi_tti; }
int get_wb_cqi_info() const { return wb_cqi_avg; }
uint32_t rbg_to_sb_index(uint32_t rbg_index) const { return rbg_index * N() / cell_nof_rbg; }
/// Get CQI of given subband index
int get_subband_cqi(uint32_t subband_index) const
{
if (subband_cqi_enabled()) {
return get_wb_cqi_info();
}
return bp_list[get_bp_index(subband_index)].last_feedback_tti.is_valid() ? subband_cqi[subband_index] : wb_cqi_avg;
}
private:
static const uint32_t max_subband_size = 8;
static const uint32_t max_nof_subbands = 13;
static const uint32_t max_bandwidth_parts = 4;
/// TS 36.321, Table 7.2.2-2
static uint32_t nof_bandwidth_parts(uint32_t nof_prb)
{
static const uint32_t nrb[] = {0, 2, 2, 3, 4, 4};
return nrb[srsran::lte_cell_nof_prb_to_index(nof_prb)];
}
uint32_t J() const { return nof_bandwidth_parts(); }
uint32_t N() const { return nof_subbands(); }
uint32_t get_bp_index(uint32_t sb_index) const { return sb_index * J() / N(); }
uint32_t prb_to_sb_index(uint32_t prb_index) const { return prb_index * N() / cell_nof_prb; }
srsran::interval<uint32_t> get_bp_sb_indexes(uint32_t bp_idx) const
{
return srsran::interval<uint32_t>{bp_idx * N() / J(), (bp_idx + 1) * N() / J()};
}
float get_subband_cqi_(uint32_t sb_idx) const
{
return bp_list[get_bp_index(sb_idx)].last_feedback_tti.is_valid() ? subband_cqi[sb_idx] : wb_cqi_avg;
}
uint32_t cell_nof_prb;
uint32_t cell_nof_rbg;
uint32_t K; ///< set in RRC
/// context of bandwidth part
struct bandwidth_part_context {
tti_point last_feedback_tti{};
uint32_t last_cqi_subband_idx;
float cqi_val;
explicit bandwidth_part_context(uint32_t init_dl_cqi) : cqi_val(init_dl_cqi), last_cqi_subband_idx(max_nof_subbands)
{}
};
tti_point last_pos_cqi_tti;
tti_point last_wb_tti;
float wb_cqi_avg;
srsran::bounded_vector<bandwidth_part_context, max_bandwidth_parts> bp_list;
srsran::bounded_vector<float, max_nof_subbands> subband_cqi;
};
/// Get {RBG index, CQI} tuple which correspond to the set RBG with the lowest CQI
std::tuple<uint32_t, int> find_min_cqi_rbg(const rbgmask_t& mask, const sched_dl_cqi& dl_cqi);
/// Returns the same RBG mask, but with the RBGs of the subband with the lowest CQI reset
rbgmask_t remove_min_cqi_subband(const rbgmask_t& rbgmask, const sched_dl_cqi& dl_cqi);
/// Returns the same RBG mask, but with the RBG with the lowest CQI reset
rbgmask_t remove_min_cqi_rbg(const rbgmask_t& rbgmask, const sched_dl_cqi& dl_cqi);
} // namespace srsenb
#endif // SRSRAN_SCHED_DL_CQI_H

@ -23,6 +23,7 @@
#define SRSRAN_SCHED_UE_CELL_H #define SRSRAN_SCHED_UE_CELL_H
#include "../sched_common.h" #include "../sched_common.h"
#include "sched_dl_cqi.h"
#include "sched_harq.h" #include "sched_harq.h"
#include "srsenb/hdr/stack/mac/sched_phy_ch/sched_dci.h" #include "srsenb/hdr/stack/mac/sched_phy_ch/sched_dci.h"
#include "tpc.h" #include "tpc.h"
@ -41,7 +42,7 @@ struct sched_ue_cell {
void clear_feedback(); void clear_feedback();
void finish_tti(tti_point tti_rx); void finish_tti(tti_point tti_rx);
void set_dl_cqi(tti_point tti_rx, uint32_t dl_cqi_); void set_dl_wb_cqi(tti_point tti_rx, uint32_t dl_cqi_);
bool configured() const { return ue_cc_idx >= 0; } bool configured() const { return ue_cc_idx >= 0; }
int get_ue_cc_idx() const { return ue_cc_idx; } int get_ue_cc_idx() const { return ue_cc_idx; }
@ -66,23 +67,21 @@ struct sched_ue_cell {
tpc tpc_fsm; tpc tpc_fsm;
/// UCI Feedback /// UCI Feedback
uint32_t dl_ri = 0; const sched_dl_cqi& dl_cqi() const { return dl_cqi_ctxt; }
tti_point dl_ri_tti_rx{}; uint32_t dl_ri = 0;
uint32_t dl_pmi = 0; tti_point dl_ri_tti_rx{};
tti_point dl_pmi_tti_rx{}; uint32_t dl_pmi = 0;
uint32_t dl_cqi = 1; tti_point dl_pmi_tti_rx{};
tti_point dl_cqi_tti_rx{0}; uint32_t ul_cqi = 1;
uint32_t ul_cqi = 1; tti_point ul_cqi_tti_rx{};
tti_point ul_cqi_tti_rx{};
bool dl_cqi_rx = false;
uint32_t max_mcs_dl = 28, max_mcs_ul = 28; uint32_t max_mcs_dl = 28, max_mcs_ul = 28;
uint32_t max_aggr_level = 3; uint32_t max_aggr_level = 3;
int fixed_mcs_ul = 0, fixed_mcs_dl = 0; int fixed_mcs_ul = 0, fixed_mcs_dl = 0;
private: private:
srslog::basic_logger& logger; // args
srslog::basic_logger& logger;
const sched_interface::ue_cfg_t* ue_cfg = nullptr; const sched_interface::ue_cfg_t* ue_cfg = nullptr;
tti_point cfg_tti; tti_point cfg_tti;
int ue_cc_idx = -1; int ue_cc_idx = -1;
@ -90,17 +89,22 @@ private:
// state // state
tti_point current_tti; tti_point current_tti;
cc_st cc_state_ = cc_st::idle; cc_st cc_state_ = cc_st::idle;
sched_dl_cqi dl_cqi_ctxt;
}; };
/************************************************************* /*************************************************************
* TBS/MCS derivation * TBS/MCS derivation
************************************************************/ ************************************************************/
/// Compute DL grant optimal TBS and MCS given UE cell context and DL grant parameters
tbs_info cqi_to_tbs_dl(const sched_ue_cell& cell, tbs_info cqi_to_tbs_dl(const sched_ue_cell& cell,
uint32_t nof_prb, const rbgmask_t& rbgs,
uint32_t nof_re, uint32_t nof_re,
srsran_dci_format_t dci_format, srsran_dci_format_t dci_format,
int req_bytes = -1); uint32_t req_bytes = std::numeric_limits<uint32_t>::max());
/// Compute UL grant optimal TBS and MCS given UE cell context and UL grant parameters
tbs_info tbs_info
cqi_to_tbs_ul(const sched_ue_cell& cell, uint32_t nof_prb, uint32_t nof_re, int req_bytes = -1, int explicit_mcs = -1); cqi_to_tbs_ul(const sched_ue_cell& cell, uint32_t nof_prb, uint32_t nof_re, int req_bytes = -1, int explicit_mcs = -1);
@ -110,6 +114,19 @@ int get_required_prb_dl(const sched_ue_cell& cell,
uint32_t req_bytes); uint32_t req_bytes);
uint32_t get_required_prb_ul(const sched_ue_cell& cell, uint32_t req_bytes); uint32_t get_required_prb_ul(const sched_ue_cell& cell, uint32_t req_bytes);
tbs_info compute_mcs_and_tbs_lower_bound(const sched_ue_cell& ue_cell,
tti_point tti_tx_dl,
const rbgmask_t& rbg_mask,
srsran_dci_format_t dci_format);
bool find_optimal_rbgmask(const sched_ue_cell& ue_cell,
tti_point tti_tx_dl,
const rbgmask_t& dl_mask,
srsran_dci_format_t dci_format,
srsran::interval<uint32_t> req_bytes,
tbs_info& tb,
rbgmask_t& newtxmask);
} // namespace srsenb } // namespace srsenb
#endif // SRSRAN_SCHED_UE_CELL_H #endif // SRSRAN_SCHED_UE_CELL_H

@ -23,6 +23,7 @@
#define SRSRAN_SCHED_BASE_H #define SRSRAN_SCHED_BASE_H
#include "srsenb/hdr/stack/mac/sched_grid.h" #include "srsenb/hdr/stack/mac/sched_grid.h"
#include "srsenb/hdr/stack/mac/sched_phy_ch/sched_phy_resource.h"
namespace srsenb { namespace srsenb {
@ -43,25 +44,6 @@ protected:
/**************** Helper methods ****************/ /**************** Helper methods ****************/
rbg_interval find_empty_rbg_interval(uint32_t max_nof_rbgs, const rbgmask_t& current_mask);
/**
* Finds a bitmask of available RBG resources for a given UE in a greedy fashion
* @param ue UE being allocated
* @param is_contiguous whether to find a contiguous range of RBGs
* @param current_mask bitmask of occupied RBGs, where to search for available RBGs
* @return bitmask of found RBGs. If a valid mask wasn't found, bitmask::size() == 0
*/
rbgmask_t compute_rbgmask_greedy(uint32_t max_nof_rbgs, bool is_contiguous, const rbgmask_t& current_mask);
/**
* Finds a range of L contiguous PRBs that are empty
* @param L Size of the requested UL PRBs
* @param current_mask input prb mask where to search for available PRBs
* @return found interval of PRBs
*/
prb_interval find_contiguous_ul_prbs(uint32_t L, const prbmask_t& current_mask);
const dl_harq_proc* get_dl_retx_harq(sched_ue& user, sf_sched* tti_sched); const dl_harq_proc* get_dl_retx_harq(sched_ue& user, sf_sched* tti_sched);
const dl_harq_proc* get_dl_newtx_harq(sched_ue& user, sf_sched* tti_sched); const dl_harq_proc* get_dl_newtx_harq(sched_ue& user, sf_sched* tti_sched);
const ul_harq_proc* get_ul_retx_harq(sched_ue& user, sf_sched* tti_sched); const ul_harq_proc* get_ul_retx_harq(sched_ue& user, sf_sched* tti_sched);

@ -153,6 +153,7 @@ public:
void start_ta() { ta_fsm.start(); }; void start_ta() { ta_fsm.start(); };
uint32_t set_ta_us(float ta_us) { return ta_fsm.push_value(ta_us); }; uint32_t set_ta_us(float ta_us) { return ta_fsm.push_value(ta_us); };
void tic(); void tic();
void trigger_padding(int lcid);
uint8_t* generate_pdu(uint32_t ue_cc_idx, uint8_t* generate_pdu(uint32_t ue_cc_idx,
uint32_t harq_pid, uint32_t harq_pid,

@ -129,34 +129,27 @@ public:
uint32_t get_nof_users(); uint32_t get_nof_users();
// logging // logging
typedef enum { Rx = 0, Tx, toS1AP, fromS1AP } direction_t; enum direction_t { Rx = 0, Tx, toS1AP, fromS1AP };
template <class T> template <class T>
void log_rrc_message(const std::string& source, void log_rrc_message(const direction_t dir,
const direction_t dir, uint16_t rnti,
const srsran::byte_buffer_t* pdu, uint32_t lcid,
const T& msg,
const std::string& msg_type)
{
log_rrc_message(source, dir, srsran::make_span(*pdu), msg, msg_type);
}
template <class T>
void log_rrc_message(const std::string& source,
const direction_t dir,
srsran::const_byte_span pdu, srsran::const_byte_span pdu,
const T& msg, const T& msg,
const std::string& msg_type) const char* msg_type)
{ {
static const char* dir_str[] = {"Rx", "Tx", "S1AP Tx", "S1AP Rx"}; log_rxtx_pdu_impl(dir, rnti, lcid, pdu, msg_type);
if (logger.debug.enabled()) { if (logger.debug.enabled()) {
asn1::json_writer json_writer; asn1::json_writer json_writer;
msg.to_json(json_writer); msg.to_json(json_writer);
logger.debug(
pdu.data(), pdu.size(), "%s - %s %s (%zd B)", source.c_str(), dir_str[dir], msg_type.c_str(), pdu.size());
logger.debug("Content:\n%s", json_writer.to_string().c_str()); logger.debug("Content:\n%s", json_writer.to_string().c_str());
} else if (logger.info.enabled()) {
logger.info("%s - %s %s (%zd B)", source.c_str(), dir_str[dir], msg_type.c_str(), pdu.size());
} }
} }
template <class T>
void log_broadcast_rrc_message(uint16_t rnti, srsran::const_byte_span pdu, const T& msg, const char* msg_type)
{
log_rrc_message(Tx, rnti, -1, pdu, msg, msg_type);
}
private: private:
class ue; class ue;
@ -185,19 +178,22 @@ private:
int pack_mcch(); int pack_mcch();
void config_mac(); void config_mac();
void parse_ul_dcch(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu); void parse_ul_dcch(ue& ue, uint32_t lcid, srsran::unique_byte_buffer_t pdu);
void parse_ul_ccch(uint16_t rnti, srsran::unique_byte_buffer_t pdu); void parse_ul_ccch(ue& ue, srsran::unique_byte_buffer_t pdu);
void send_rrc_connection_reject(uint16_t rnti); void send_rrc_connection_reject(uint16_t rnti);
const static int mcch_payload_len = 3000; const static int mcch_payload_len = 3000;
int current_mcch_length = 0; int current_mcch_length = 0;
uint8_t mcch_payload_buffer[mcch_payload_len] = {}; uint8_t mcch_payload_buffer[mcch_payload_len] = {};
typedef struct { struct rrc_pdu {
uint16_t rnti; uint16_t rnti;
uint32_t lcid; uint32_t lcid;
uint32_t arg; uint32_t arg;
srsran::unique_byte_buffer_t pdu; srsran::unique_byte_buffer_t pdu;
} rrc_pdu; };
void log_rx_pdu_fail(uint16_t rnti, uint32_t lcid, srsran::const_byte_span pdu, const char* cause);
void
log_rxtx_pdu_impl(direction_t dir, uint16_t rnti, uint32_t lcid, srsran::const_byte_span pdu, const char* msg_type);
const static uint32_t LCID_EXIT = 0xffff0000; const static uint32_t LCID_EXIT = 0xffff0000;
const static uint32_t LCID_REM_USER = 0xffff0001; const static uint32_t LCID_REM_USER = 0xffff0001;

@ -78,7 +78,8 @@ public:
uint32_t teid_in = 0; uint32_t teid_in = 0;
uint32_t addr = 0; uint32_t addr = 0;
}; };
uint8_t id = 0; uint8_t id = 0;
uint8_t lcid = 0;
asn1::s1ap::erab_level_qos_params_s qos_params; asn1::s1ap::erab_level_qos_params_s qos_params;
asn1::bounded_bitstring<1, 160, true, true> address; asn1::bounded_bitstring<1, 160, true, true> address;
uint32_t teid_out = 0; uint32_t teid_out = 0;

@ -240,7 +240,7 @@ bool paging_manager::read_pdu_pcch(tti_point tti_tx_dl, const Callable& func)
} }
// Call callable for existing PCCH pdu // Call callable for existing PCCH pdu
if (func(*pending_pcch->pdu, pending_pcch->pcch_msg, pending_pcch->tti_tx_dl.is_valid())) { if (func(*pending_pcch->pdu, pending_pcch->pcch_msg, not pending_pcch->is_tx())) {
pending_pcch->tti_tx_dl = tti_tx_dl; pending_pcch->tti_tx_dl = tti_tx_dl;
return true; return true;
} }

@ -63,7 +63,7 @@ public:
struct tunnel { struct tunnel {
uint16_t rnti = SRSRAN_INVALID_RNTI; uint16_t rnti = SRSRAN_INVALID_RNTI;
uint32_t lcid = srsran::MAX_NOF_BEARERS; uint32_t lcid = srsran::INVALID_LCID;
uint32_t teid_in = 0; uint32_t teid_in = 0;
uint32_t teid_out = 0; uint32_t teid_out = 0;
uint32_t spgw_addr = 0; uint32_t spgw_addr = 0;

@ -38,10 +38,10 @@ target_link_libraries(enb_cfg_parser ${LIBCONFIGPP_LIBRARIES})
add_executable(srsenb main.cc enb.cc metrics_stdout.cc metrics_csv.cc metrics_json.cc) add_executable(srsenb main.cc enb.cc metrics_stdout.cc metrics_csv.cc metrics_json.cc)
set(SRSENB_SOURCES srsenb_phy srsenb_stack srsenb_common srsenb_upper srsenb_mac srsenb_rrc srslog system) set(SRSENB_SOURCES srsenb_phy srsenb_stack srsenb_common srsenb_s1ap srsenb_upper srsenb_mac srsenb_rrc srslog system)
set(SRSRAN_SOURCES srsran_common srsran_mac srsran_phy srsran_upper srsran_radio rrc_asn1 s1ap_asn1 enb_cfg_parser srslog system) set(SRSRAN_SOURCES srsran_common srsran_mac srsran_phy srsran_upper srsran_radio rrc_asn1 s1ap_asn1 enb_cfg_parser srslog system)
set(SRSENB_SOURCES ${SRSENB_SOURCES} srsgnb_phy srsgnb_stack srsgnb_upper srsgnb_mac srsgnb_rrc) set(SRSENB_SOURCES ${SRSENB_SOURCES} srsgnb_phy srsgnb_stack srsgnb_ngap srsgnb_upper srsgnb_mac srsgnb_rrc)
set(SRSRAN_SOURCES ${SRSRAN_SOURCES} rrc_nr_asn1 ngap_nr_asn1) set(SRSRAN_SOURCES ${SRSRAN_SOURCES} rrc_nr_asn1 ngap_nr_asn1)
target_link_libraries(srsenb ${SRSENB_SOURCES} target_link_libraries(srsenb ${SRSENB_SOURCES}

@ -236,4 +236,9 @@ std::string enb::get_build_string()
return ss.str(); return ss.str();
} }
void enb::toggle_padding()
{
stack->toggle_padding();
}
} // namespace srsenb } // namespace srsenb

@ -834,7 +834,7 @@ namespace enb_conf_sections {
int parse_cell_cfg(all_args_t* args_, srsran_cell_t* cell) int parse_cell_cfg(all_args_t* args_, srsran_cell_t* cell)
{ {
cell->frame_type = SRSRAN_FDD; cell->frame_type = SRSRAN_FDD;
cell->cp = SRSRAN_CP_NORM; cell->cp = args_->phy.extended_cp ? SRSRAN_CP_EXT : SRSRAN_CP_NORM;
cell->nof_ports = args_->enb.nof_ports; cell->nof_ports = args_->enb.nof_ports;
cell->nof_prb = args_->enb.n_prb; cell->nof_prb = args_->enb.n_prb;
// PCI not configured yet // PCI not configured yet

@ -225,10 +225,12 @@ void parse_args(all_args_t* args, int argc, char* argv[])
("expert.eea_pref_list", bpo::value<string>(&args->general.eea_pref_list)->default_value("EEA0, EEA2, EEA1"), "Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1).") ("expert.eea_pref_list", bpo::value<string>(&args->general.eea_pref_list)->default_value("EEA0, EEA2, EEA1"), "Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1).")
("expert.eia_pref_list", bpo::value<string>(&args->general.eia_pref_list)->default_value("EIA2, EIA1, EIA0"), "Ordered preference list for the selection of integrity algorithm (EIA) (default: EIA2, EIA1, EIA0).") ("expert.eia_pref_list", bpo::value<string>(&args->general.eia_pref_list)->default_value("EIA2, EIA1, EIA0"), "Ordered preference list for the selection of integrity algorithm (EIA) (default: EIA2, EIA1, EIA0).")
("expert.nof_prealloc_ues", bpo::value<uint32_t>(&args->stack.mac.nof_prealloc_ues)->default_value(8), "Number of UE resources to preallocate during eNB initialization") ("expert.nof_prealloc_ues", bpo::value<uint32_t>(&args->stack.mac.nof_prealloc_ues)->default_value(8), "Number of UE resources to preallocate during eNB initialization")
("expert.lcid_padding", bpo::value<int>(&args->stack.mac.lcid_padding)->default_value(3), "LCID on which to put MAC padding")
("expert.max_mac_dl_kos", bpo::value<uint32_t>(&args->general.max_mac_dl_kos)->default_value(100), "Maximum number of consecutive KOs in DL before triggering the UE's release") ("expert.max_mac_dl_kos", bpo::value<uint32_t>(&args->general.max_mac_dl_kos)->default_value(100), "Maximum number of consecutive KOs in DL before triggering the UE's release")
("expert.max_mac_ul_kos", bpo::value<uint32_t>(&args->general.max_mac_ul_kos)->default_value(100), "Maximum number of consecutive KOs in UL before triggering the UE's release") ("expert.max_mac_ul_kos", bpo::value<uint32_t>(&args->general.max_mac_ul_kos)->default_value(100), "Maximum number of consecutive KOs in UL before triggering the UE's release")
("expert.gtpu_tunnel_timeout", bpo::value<uint32_t>(&args->stack.gtpu_indirect_tunnel_timeout_msec)->default_value(0), "Maximum time that GTPU takes to release indirect forwarding tunnel since the last received GTPU PDU. (0 for infinity)") ("expert.gtpu_tunnel_timeout", bpo::value<uint32_t>(&args->stack.gtpu_indirect_tunnel_timeout_msec)->default_value(0), "Maximum time that GTPU takes to release indirect forwarding tunnel since the last received GTPU PDU. (0 for infinity)")
("expert.rlf_release_timer_ms", bpo::value<uint32_t>(&args->general.rlf_release_timer_ms)->default_value(4000), "Time taken by eNB to release UE context after it detects an RLF") ("expert.rlf_release_timer_ms", bpo::value<uint32_t>(&args->general.rlf_release_timer_ms)->default_value(4000), "Time taken by eNB to release UE context after it detects an RLF")
("expert.extended_cp", bpo::value<bool>(&args->phy.extended_cp)->default_value(false), "Use extended cyclic prefix")
// eMBMS section // eMBMS section
@ -437,6 +439,7 @@ void parse_args(all_args_t* args, int argc, char* argv[])
} }
static bool do_metrics = false; static bool do_metrics = false;
static bool do_padding = false;
static void* input_loop(metrics_stdout* metrics, srsenb::enb_command_interface* control) static void* input_loop(metrics_stdout* metrics, srsenb::enb_command_interface* control)
{ {
@ -461,6 +464,14 @@ static void* input_loop(metrics_stdout* metrics, srsenb::enb_command_interface*
cout << "Enter t to restart trace." << endl; cout << "Enter t to restart trace." << endl;
} }
metrics->toggle_print(do_metrics); metrics->toggle_print(do_metrics);
} else if (cmd[0] == "p") {
do_padding = !do_padding;
if (do_padding) {
cout << "Enter p to stop padding." << endl;
} else {
cout << "Enter p to restart padding." << endl;
}
control->toggle_padding();
} else if (cmd[0] == "q") { } else if (cmd[0] == "q") {
raise(SIGTERM); raise(SIGTERM);
} else if (cmd[0] == "cell_gain") { } else if (cmd[0] == "cell_gain") {
@ -480,6 +491,7 @@ static void* input_loop(metrics_stdout* metrics, srsenb::enb_command_interface*
cout << " t: starts console trace" << endl; cout << " t: starts console trace" << endl;
cout << " q: quit srsenb" << endl; cout << " q: quit srsenb" << endl;
cout << " cell_gain: set relative cell gain" << endl; cout << " cell_gain: set relative cell gain" << endl;
cout << " p: starts MAC padding" << endl;
cout << endl; cout << endl;
} }
} }

@ -218,10 +218,6 @@ void sf_worker::work_imp()
} }
} }
// Make sure CFI is in the right range
dl_grants[0].cfi = SRSRAN_MAX(dl_grants[0].cfi, 1);
dl_grants[0].cfi = SRSRAN_MIN(dl_grants[0].cfi, 3);
// Get UL scheduling for the TX TTI from MAC // Get UL scheduling for the TX TTI from MAC
if (stack->get_ul_sched(tti_tx_ul, ul_grants_tx) < 0) { if (stack->get_ul_sched(tti_tx_ul, ul_grants_tx) < 0) {
Error("Getting UL scheduling from MAC"); Error("Getting UL scheduling from MAC");
@ -239,7 +235,11 @@ void sf_worker::work_imp()
// Process DL // Process DL
for (uint32_t cc = 0; cc < cc_workers.size(); cc++) { for (uint32_t cc = 0; cc < cc_workers.size(); cc++) {
// Select CFI and make sure it is in the right range
dl_sf.cfi = dl_grants[cc].cfi; dl_sf.cfi = dl_grants[cc].cfi;
dl_sf.cfi = SRSRAN_MAX(dl_sf.cfi, 1);
dl_sf.cfi = SRSRAN_MIN(dl_sf.cfi, 3);
cc_workers[cc]->work_dl(dl_sf, dl_grants[cc], ul_grants_tx[cc], &mbsfn_cfg); cc_workers[cc]->work_dl(dl_sf, dl_grants[cc], ul_grants_tx[cc], &mbsfn_cfg);
} }

@ -20,6 +20,8 @@
add_subdirectory(mac) add_subdirectory(mac)
add_subdirectory(rrc) add_subdirectory(rrc)
add_subdirectory(s1ap)
add_subdirectory(ngap)
add_subdirectory(upper) add_subdirectory(upper)
set(SOURCES enb_stack_lte.cc) set(SOURCES enb_stack_lte.cc)

@ -21,7 +21,9 @@
add_subdirectory(schedulers) add_subdirectory(schedulers)
set(SOURCES mac.cc ue.cc sched.cc sched_carrier.cc sched_grid.cc sched_ue_ctrl/sched_harq.cc sched_ue.cc set(SOURCES mac.cc ue.cc sched.cc sched_carrier.cc sched_grid.cc sched_ue_ctrl/sched_harq.cc sched_ue.cc
sched_ue_ctrl/sched_lch.cc sched_ue_ctrl/sched_ue_cell.cc sched_phy_ch/sf_cch_allocator.cc sched_phy_ch/sched_dci.cc sched_helpers.cc) sched_ue_ctrl/sched_lch.cc sched_ue_ctrl/sched_ue_cell.cc sched_ue_ctrl/sched_dl_cqi.cc
sched_phy_ch/sf_cch_allocator.cc sched_phy_ch/sched_dci.cc sched_phy_ch/sched_phy_resource.cc
sched_helpers.cc)
add_library(srsenb_mac STATIC ${SOURCES} $<TARGET_OBJECTS:mac_schedulers>) add_library(srsenb_mac STATIC ${SOURCES} $<TARGET_OBJECTS:mac_schedulers>)
set(SOURCES mac_nr.cc) set(SOURCES mac_nr.cc)

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

Loading…
Cancel
Save