mirror of https://github.com/PentHertz/srsLTE.git
Merged with next. Attach successful
This commit is contained in:
commit
a51b6b09dd
|
@ -42,6 +42,8 @@ include(SRSLTEPackage) #setup cpack
|
|||
|
||||
include(CTest)
|
||||
set(CTEST_MEMORYCHECK_COMMAND valgrind)
|
||||
set(CTEST_MEMORYCHECK_COMMAND_OPTIONS "--trace-children=yes --leak-check=full" )
|
||||
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/CTestCustom.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/CTestCustom.cmake"
|
||||
|
@ -74,8 +76,30 @@ set(GCC_ARCH native CACHE STRING "GCC compile for specific architecture.")
|
|||
########################################################################
|
||||
# Find dependencies
|
||||
########################################################################
|
||||
|
||||
# Threads
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
# FFT
|
||||
find_package(MKL)
|
||||
if(MKL_FOUND)
|
||||
include_directories(${MKL_INCLUDE_DIRS})
|
||||
link_directories(${MKL_LIBRARY_DIRS})
|
||||
set(FFT_LIBRARIES "${MKL_STATIC_LIBRARIES}") # Static by default
|
||||
else(MKL_FOUND)
|
||||
find_package(FFTW3F REQUIRED)
|
||||
if(FFTW3F_FOUND)
|
||||
include_directories(${FFTW3F_INCLUDE_DIRS})
|
||||
link_directories(${FFTW3F_LIBRARY_DIRS})
|
||||
if(BUILD_STATIC)
|
||||
set(FFT_LIBRARIES "${FFTW3F_STATIC_LIBRARIES}")
|
||||
else(BUILD_STATIC)
|
||||
set(FFT_LIBRARIES "${FFTW3F_LIBRARIES}")
|
||||
endif(BUILD_STATIC)
|
||||
endif(FFTW3F_FOUND)
|
||||
endif(MKL_FOUND)
|
||||
|
||||
# Crypto
|
||||
find_package(Polarssl)
|
||||
if (POLARSSL_FOUND)
|
||||
set(SEC_INCLUDE_DIRS "${POLARSSL_INCLUDE_DIRS}")
|
||||
|
@ -100,12 +124,14 @@ else(POLARSSL_FOUND)
|
|||
endif (MBEDTLS_FOUND)
|
||||
endif(POLARSSL_FOUND)
|
||||
|
||||
# UHD
|
||||
find_package(UHD)
|
||||
if(UHD_FOUND)
|
||||
include_directories(${UHD_INCLUDE_DIRS})
|
||||
link_directories(${UHD_LIBRARY_DIRS})
|
||||
endif(UHD_FOUND)
|
||||
|
||||
# BladeRF
|
||||
if(ENABLE_BLADERF)
|
||||
find_package(bladeRF)
|
||||
if(BLADERF_FOUND)
|
||||
|
@ -114,6 +140,7 @@ if(ENABLE_BLADERF)
|
|||
endif(BLADERF_FOUND)
|
||||
endif(ENABLE_BLADERF)
|
||||
|
||||
# Soapy
|
||||
find_package(SoapySDR)
|
||||
if(SOAPYSDR_FOUND)
|
||||
include_directories(${SOAPYSDR_INCLUDE_DIRS})
|
||||
|
@ -127,8 +154,8 @@ else(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND)
|
|||
add_definitions(-DDISABLE_RF)
|
||||
endif(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND)
|
||||
|
||||
# Boost
|
||||
if(ENABLE_SRSUE OR ENABLE_SRSENB)
|
||||
# Find Boost
|
||||
if(BUILD_STATIC)
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
endif(BUILD_STATIC)
|
||||
|
@ -152,7 +179,7 @@ if(ENABLE_SRSUE OR ENABLE_SRSENB)
|
|||
find_package(Boost "1.35" COMPONENTS ${BOOST_REQUIRED_COMPONENTS})
|
||||
endif(ENABLE_SRSUE OR ENABLE_SRSENB)
|
||||
|
||||
|
||||
# srsGUI
|
||||
if(ENABLE_GUI)
|
||||
find_package(SRSGUI)
|
||||
if(SRSGUI_FOUND)
|
||||
|
@ -162,6 +189,7 @@ if(ENABLE_GUI)
|
|||
endif(SRSGUI_FOUND)
|
||||
endif(ENABLE_GUI)
|
||||
|
||||
# VOLK
|
||||
include(CheckFunctionExistsMath)
|
||||
if(ENABLE_VOLK)
|
||||
find_package(Volk)
|
||||
|
@ -176,7 +204,6 @@ else(ENABLE_VOLK)
|
|||
message(STATUS "VOLK library disabled")
|
||||
endif(ENABLE_VOLK)
|
||||
|
||||
|
||||
########################################################################
|
||||
# Install Dirs
|
||||
########################################################################
|
||||
|
|
|
@ -7,7 +7,7 @@ SET(CTEST_CUSTOM_MEMCHECK_IGNORE
|
|||
realplot_test
|
||||
complexplot_test
|
||||
|
||||
# Ignore these to, they take too lonk
|
||||
# Ignore these to, they take too long
|
||||
fft_normal
|
||||
fft_extened
|
||||
chest_test_all_cellids
|
||||
|
|
|
@ -15,6 +15,12 @@ find_path(FFTW3F_INCLUDE_DIR
|
|||
PATHS /usr/local/include
|
||||
/usr/include )
|
||||
|
||||
find_library(FFTW3F_STATIC_LIBRARY
|
||||
NAMES fftw3f.a libfftw3f.a libfftw3f-3.a
|
||||
HINTS ${PC_FFTW3F_LIBDIR} ${PC_FFTW3F_LIBRARY_DIRS} $ENV{FFTW3_DIR}/lib
|
||||
PATHS /usr/local/lib
|
||||
/usr/lib)
|
||||
|
||||
find_library(FFTW3F_LIBRARY
|
||||
NAMES fftw3f libfftw3f libfftw3f-3
|
||||
HINTS ${PC_FFTW3F_LIBDIR} ${PC_FFTW3F_LIBRARY_DIRS} $ENV{FFTW3_DIR}/lib
|
||||
|
@ -22,12 +28,17 @@ find_library(FFTW3F_LIBRARY
|
|||
/usr/lib)
|
||||
|
||||
set(FFTW3F_LIBRARIES ${FFTW3F_LIBRARY} )
|
||||
set(FFTW3F_STATIC_LIBRARIES ${FFTW3F_STATIC_LIBRARY} )
|
||||
set(FFTW3F_INCLUDE_DIRS ${FFTW3F_INCLUDE_DIR} )
|
||||
|
||||
message(STATUS "FFTW3F LIBRARIES: " ${FFTW3F_LIBRARIES})
|
||||
message(STATUS "FFTW3F STATIC LIBRARIES: " ${FFTW3F_STATIC_LIBRARIES})
|
||||
message(STATUS "FFTW3F INCLUDE DIRS: " ${FFTW3F_INCLUDE_DIRS})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
# handle the QUIETLY and REQUIRED arguments and set FFTW3F_FOUND to TRUE
|
||||
# if all listed variables are TRUE
|
||||
find_package_handle_standard_args(fftw3f DEFAULT_MSG
|
||||
FFTW3F_LIBRARY FFTW3F_INCLUDE_DIR)
|
||||
FFTW3F_LIBRARY FFTW3F_STATIC_LIBRARY FFTW3F_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(FFTW3F_INCLUDE_DIR FFTW3F_LIBRARY )
|
||||
mark_as_advanced(FFTW3F_INCLUDE_DIR FFTW3F_STATIC_LIBRARY FFTW3F_LIBRARY )
|
||||
|
|
|
@ -49,5 +49,5 @@ message(STATUS "POLARSSL STATIC LIBRARIES: " ${POLARSSL_STATIC_LIBRARIES})
|
|||
message(STATUS "POLARSSL INCLUDE DIRS: " ${POLARSSL_INCLUDE_DIRS})
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(POLARSSL DEFAULT_MSG POLARSSL_LIBRARIES POLARSSL_STATIC_LIBRARIES POLARSSL_INCLUDE_DIRS)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(POLARSSL DEFAULT_MSG POLARSSL_LIBRARIES POLARSSL_INCLUDE_DIRS)
|
||||
MARK_AS_ADVANCED(POLARSSL_STATIC_LIBRARIES POLARSSL_LIBRARIES POLARSSL_INCLUDE_DIRS)
|
||||
|
|
|
@ -65,7 +65,7 @@ if(RF_FOUND)
|
|||
add_executable(cell_search cell_search.c)
|
||||
target_link_libraries(cell_search srslte_phy srslte_rf)
|
||||
|
||||
add_executable(cell_measurement cell_measurement.c ../include/srslte/common/logger_stdout.h)
|
||||
add_executable(cell_measurement cell_measurement.c)
|
||||
target_link_libraries(cell_measurement srslte_phy srslte_rf)
|
||||
|
||||
add_executable(usrp_capture usrp_capture.c)
|
||||
|
|
|
@ -156,6 +156,9 @@ public:
|
|||
return pool->allocate(debug_name);
|
||||
}
|
||||
void deallocate(byte_buffer_t *b) {
|
||||
if(!b) {
|
||||
return;
|
||||
}
|
||||
b->reset();
|
||||
pool->deallocate(b);
|
||||
}
|
||||
|
|
|
@ -83,32 +83,6 @@ static const char error_text[ERROR_N_ITEMS][20] = { "None",
|
|||
"Can't start",
|
||||
"Already started"};
|
||||
|
||||
typedef enum{
|
||||
RB_ID_SRB0 = 0,
|
||||
RB_ID_SRB1,
|
||||
RB_ID_SRB2,
|
||||
RB_ID_DRB1,
|
||||
RB_ID_DRB2,
|
||||
RB_ID_DRB3,
|
||||
RB_ID_DRB4,
|
||||
RB_ID_DRB5,
|
||||
RB_ID_DRB6,
|
||||
RB_ID_DRB7,
|
||||
RB_ID_DRB8,
|
||||
RB_ID_N_ITEMS,
|
||||
}rb_id_t;
|
||||
static const char rb_id_text[RB_ID_N_ITEMS][20] = { "SRB0",
|
||||
"SRB1",
|
||||
"SRB2",
|
||||
"DRB1",
|
||||
"DRB2",
|
||||
"DRB3",
|
||||
"DRB4",
|
||||
"DRB5",
|
||||
"DRB6",
|
||||
"DRB7",
|
||||
"DRB8"};
|
||||
|
||||
/******************************************************************************
|
||||
* Byte and Bit buffers
|
||||
*
|
||||
|
|
|
@ -28,9 +28,43 @@
|
|||
#define INTERFACE_COMMON_H
|
||||
|
||||
#include "srslte/common/timers.h"
|
||||
#include "srslte/common/security.h"
|
||||
#include "srslte/asn1/liblte_rrc.h"
|
||||
|
||||
|
||||
namespace srslte {
|
||||
|
||||
class srslte_nas_config_t
|
||||
{
|
||||
public:
|
||||
srslte_nas_config_t(uint32_t lcid_ = 0)
|
||||
:lcid(lcid_)
|
||||
{}
|
||||
|
||||
uint32_t lcid;
|
||||
};
|
||||
|
||||
|
||||
class srslte_pdcp_config_t
|
||||
{
|
||||
public:
|
||||
srslte_pdcp_config_t(bool is_control_ = false, bool is_data_ = false, uint8_t direction_ = SECURITY_DIRECTION_UPLINK)
|
||||
:direction(direction_)
|
||||
,is_control(is_control_)
|
||||
,is_data(is_data_)
|
||||
,do_security(false)
|
||||
,sn_len(12) {}
|
||||
|
||||
uint8_t direction;
|
||||
bool is_control;
|
||||
bool is_data;
|
||||
bool do_security;
|
||||
uint8_t sn_len;
|
||||
|
||||
// TODO: Support the following configurations
|
||||
// bool do_rohc;
|
||||
};
|
||||
|
||||
class mac_interface_timers
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -34,6 +34,7 @@ public:
|
|||
bool init(metrics_interface<metrics_t> *m_, float report_period_secs=1.0) {
|
||||
m = m_;
|
||||
start_periodic(report_period_secs*1e6);
|
||||
return true;
|
||||
}
|
||||
void stop() {
|
||||
thread_cancel();
|
||||
|
@ -47,7 +48,7 @@ private:
|
|||
void run_period() {
|
||||
metrics_t metric;
|
||||
m->get_metrics(metric);
|
||||
for (int i=0;i<listeners.size();i++) {
|
||||
for (uint32_t i=0;i<listeners.size();i++) {
|
||||
listeners[i]->set_metrics(metric);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,11 +97,13 @@ public:
|
|||
bool running;
|
||||
};
|
||||
|
||||
timers(uint32_t nof_timers_) : timer_list(nof_timers_) {
|
||||
timers(uint32_t nof_timers_) : timer_list(nof_timers_),used_timers(nof_timers_) {
|
||||
nof_timers = nof_timers_;
|
||||
next_timer = 0;
|
||||
nof_used_timers = 0;
|
||||
for (uint32_t i=0;i<nof_timers;i++) {
|
||||
timer_list[i].id = i;
|
||||
timer_list[i].id = i;
|
||||
used_timers[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,17 +135,36 @@ public:
|
|||
return NULL;
|
||||
}
|
||||
}
|
||||
uint32_t get_unique_id() {
|
||||
if (next_timer == nof_timers){
|
||||
printf("No more unique timer ids (Only %d timers available)\n", nof_timers);
|
||||
next_timer = 0;
|
||||
void release_id(uint32_t i) {
|
||||
if (nof_used_timers > 0 && i < nof_timers) {
|
||||
used_timers[i] = false;
|
||||
nof_used_timers--;
|
||||
} else {
|
||||
fprintf(stderr, "Error releasing timer: nof_used_timers=%d, nof_timers=%d\n", nof_used_timers, nof_timers);
|
||||
}
|
||||
}
|
||||
uint32_t get_unique_id() {
|
||||
if (nof_used_timers >= nof_timers) {
|
||||
fprintf(stderr, "Error getting uinque timer id: no more timers available\n");
|
||||
return 0;
|
||||
} else {
|
||||
while(used_timers[next_timer]) {
|
||||
next_timer++;
|
||||
if (next_timer >= nof_timers) {
|
||||
next_timer=0;
|
||||
}
|
||||
}
|
||||
used_timers[next_timer] = true;
|
||||
nof_used_timers++;
|
||||
return next_timer;
|
||||
}
|
||||
return next_timer++;
|
||||
}
|
||||
private:
|
||||
uint32_t nof_timers;
|
||||
uint32_t next_timer;
|
||||
std::vector<timer> timer_list;
|
||||
uint32_t nof_used_timers;
|
||||
uint32_t nof_timers;
|
||||
std::vector<timer> timer_list;
|
||||
std::vector<bool> used_timers;
|
||||
};
|
||||
|
||||
} // namespace srslte
|
||||
|
|
|
@ -28,7 +28,9 @@
|
|||
|
||||
#include "srslte/common/common.h"
|
||||
#include "srslte/common/security.h"
|
||||
#include "srslte/common/interfaces_common.h"
|
||||
#include "srslte/interfaces/sched_interface.h"
|
||||
#include "srslte/upper/rlc_interface.h"
|
||||
#include "srslte/asn1/liblte_rrc.h"
|
||||
#include "srslte/asn1/liblte_s1ap.h"
|
||||
|
||||
|
@ -156,7 +158,7 @@ public:
|
|||
virtual void add_user(uint16_t rnti) = 0;
|
||||
virtual void rem_user(uint16_t rnti) = 0;
|
||||
virtual void add_bearer(uint16_t rnti, uint32_t lcid) = 0;
|
||||
virtual void add_bearer(uint16_t rnti, uint32_t lcid, LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg) = 0;
|
||||
virtual void add_bearer(uint16_t rnti, uint32_t lcid, srslte::srslte_rlc_config_t cnfg) = 0;
|
||||
};
|
||||
|
||||
// PDCP interface for GTPU
|
||||
|
@ -174,7 +176,7 @@ public:
|
|||
virtual void add_user(uint16_t rnti) = 0;
|
||||
virtual void rem_user(uint16_t rnti) = 0;
|
||||
virtual void write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *sdu) = 0;
|
||||
virtual void add_bearer(uint16_t rnti, uint32_t lcid, LIBLTE_RRC_PDCP_CONFIG_STRUCT *cnfg=NULL) = 0;
|
||||
virtual void add_bearer(uint16_t rnti, uint32_t lcid, srslte::srslte_pdcp_config_t cnfg) = 0;
|
||||
virtual void config_security(uint16_t rnti,
|
||||
uint32_t lcid,
|
||||
uint8_t *k_rrc_enc_,
|
||||
|
|
|
@ -94,7 +94,8 @@ public:
|
|||
typedef struct {
|
||||
int priority;
|
||||
int bsd;
|
||||
int pbr;
|
||||
int pbr;
|
||||
int group;
|
||||
enum {IDLE = 0, UL, DL, BOTH} direction;
|
||||
} ue_bearer_cfg_t;
|
||||
|
||||
|
@ -231,7 +232,7 @@ public:
|
|||
/* UL information */
|
||||
virtual int ul_crc_info(uint32_t tti, uint16_t rnti, bool crc) = 0;
|
||||
virtual int ul_sr_info(uint32_t tti, uint16_t rnti) = 0;
|
||||
virtual int ul_bsr(uint16_t rnti, uint32_t lcid, uint32_t bsr) = 0;
|
||||
virtual int ul_bsr(uint16_t rnti, uint32_t lcid, uint32_t bsr, bool set_value = true) = 0;
|
||||
virtual int ul_recv_len(uint16_t rnti, uint32_t lcid, uint32_t len) = 0;
|
||||
virtual int ul_phr(uint16_t rnti, int phr) = 0;
|
||||
virtual int ul_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi, uint32_t ul_ch_code) = 0;
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "srslte/common/interfaces_common.h"
|
||||
#include "srslte/common/common.h"
|
||||
#include "srslte/common/security.h"
|
||||
#include "srslte/upper/rlc_interface.h"
|
||||
|
||||
namespace srsue {
|
||||
|
||||
|
@ -113,11 +114,16 @@ public:
|
|||
};
|
||||
|
||||
// RRC interface for MAC
|
||||
class rrc_interface_mac
|
||||
class rrc_interface_mac_common
|
||||
{
|
||||
public:
|
||||
virtual void ra_problem() = 0;
|
||||
};
|
||||
|
||||
class rrc_interface_mac : public rrc_interface_mac_common
|
||||
{
|
||||
public:
|
||||
virtual void release_pucch_srs() = 0;
|
||||
virtual void ra_problem() = 0;
|
||||
};
|
||||
|
||||
// RRC interface for PHY
|
||||
|
@ -139,7 +145,7 @@ public:
|
|||
virtual void enable_capabilities() = 0;
|
||||
virtual void plmn_search() = 0;
|
||||
virtual void plmn_select(LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id) = 0;
|
||||
|
||||
virtual std::string get_rb_name(uint32_t lcid) = 0;
|
||||
};
|
||||
|
||||
// RRC interface for PDCP
|
||||
|
@ -150,6 +156,7 @@ public:
|
|||
virtual void write_pdu_bcch_bch(srslte::byte_buffer_t *pdu) = 0;
|
||||
virtual void write_pdu_bcch_dlsch(srslte::byte_buffer_t *pdu) = 0;
|
||||
virtual void write_pdu_pcch(srslte::byte_buffer_t *pdu) = 0;
|
||||
virtual std::string get_rb_name(uint32_t lcid) = 0;
|
||||
};
|
||||
|
||||
// RRC interface for RLC
|
||||
|
@ -157,6 +164,7 @@ class rrc_interface_rlc
|
|||
{
|
||||
public:
|
||||
virtual void max_retx_attempted() = 0;
|
||||
virtual std::string get_rb_name(uint32_t lcid) = 0;
|
||||
};
|
||||
|
||||
// PDCP interface for GW
|
||||
|
@ -172,7 +180,7 @@ class pdcp_interface_rrc
|
|||
public:
|
||||
virtual void reset() = 0;
|
||||
virtual void write_sdu(uint32_t lcid, srslte::byte_buffer_t *sdu) = 0;
|
||||
virtual void add_bearer(uint32_t lcid, LIBLTE_RRC_PDCP_CONFIG_STRUCT *cnfg=NULL) = 0;
|
||||
virtual void add_bearer(uint32_t lcid, srslte::srslte_pdcp_config_t cnfg = srslte::srslte_pdcp_config_t()) = 0;
|
||||
virtual void config_security(uint32_t lcid,
|
||||
uint8_t *k_rrc_enc_,
|
||||
uint8_t *k_rrc_int_,
|
||||
|
@ -197,7 +205,7 @@ class rlc_interface_rrc
|
|||
public:
|
||||
virtual void reset() = 0;
|
||||
virtual void add_bearer(uint32_t lcid) = 0;
|
||||
virtual void add_bearer(uint32_t lcid, LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg) = 0;
|
||||
virtual void add_bearer(uint32_t lcid, srslte::srslte_rlc_config_t cnfg) = 0;
|
||||
};
|
||||
|
||||
// RLC interface for PDCP
|
||||
|
@ -234,6 +242,31 @@ public:
|
|||
};
|
||||
|
||||
|
||||
//BSR interface for MUX
|
||||
class bsr_interface_mux
|
||||
{
|
||||
public:
|
||||
typedef enum {
|
||||
LONG_BSR,
|
||||
SHORT_BSR,
|
||||
TRUNC_BSR
|
||||
} bsr_format_t;
|
||||
|
||||
typedef struct {
|
||||
bsr_format_t format;
|
||||
uint32_t buff_size[4];
|
||||
} bsr_t;
|
||||
|
||||
/* MUX calls BSR to check if it can fit a BSR into PDU */
|
||||
virtual bool need_to_send_bsr_on_ul_grant(uint32_t grant_size, bsr_t *bsr) = 0;
|
||||
|
||||
/* MUX calls BSR to let it generate a padding BSR if there is space in PDU */
|
||||
virtual bool generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t *bsr) = 0;
|
||||
|
||||
/* MAX calls BSR to set the Tx TTI */
|
||||
virtual void set_tx_tti(uint32_t tti) = 0;
|
||||
};
|
||||
|
||||
|
||||
/** MAC interface
|
||||
*
|
||||
|
@ -279,6 +312,7 @@ public:
|
|||
uint32_t rv;
|
||||
uint16_t rnti;
|
||||
uint32_t current_tx_nb;
|
||||
int32_t tti_offset; // relative offset between grant and UL tx/HARQ rx
|
||||
srslte_softbuffer_tx_t *softbuffer;
|
||||
srslte_phy_grant_t phy_grant;
|
||||
uint8_t *payload_ptr;
|
||||
|
@ -313,9 +347,27 @@ public:
|
|||
|
||||
};
|
||||
|
||||
/* Interface RRC -> MAC shared between different RATs */
|
||||
class mac_interface_rrc_common
|
||||
{
|
||||
public:
|
||||
// Class to handle UE specific RNTIs between RRC and MAC
|
||||
typedef struct {
|
||||
uint16_t crnti;
|
||||
uint16_t temp_rnti;
|
||||
uint16_t tpc_rnti;
|
||||
uint16_t sps_rnti;
|
||||
uint64_t contention_id;
|
||||
} ue_rnti_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t max_harq_msg3_tx;
|
||||
uint32_t max_harq_tx;
|
||||
} ul_harq_params_t;
|
||||
};
|
||||
|
||||
/* Interface RRC -> MAC */
|
||||
class mac_interface_rrc
|
||||
class mac_interface_rrc : public mac_interface_rrc_common
|
||||
{
|
||||
public:
|
||||
|
||||
|
@ -323,19 +375,10 @@ public:
|
|||
LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT main;
|
||||
LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT rach;
|
||||
LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT sr;
|
||||
ul_harq_params_t ul_harq_params;
|
||||
uint32_t prach_config_index;
|
||||
} mac_cfg_t;
|
||||
|
||||
|
||||
// Class to handle UE specific RNTIs between RRC and MAC
|
||||
typedef struct {
|
||||
uint16_t crnti;
|
||||
uint16_t temp_rnti;
|
||||
uint16_t tpc_rnti;
|
||||
uint16_t sps_rnti;
|
||||
uint64_t contention_id;
|
||||
} ue_rnti_t;
|
||||
|
||||
/* Instructs the MAC to start receiving BCCH */
|
||||
virtual void bcch_start_rx() = 0;
|
||||
virtual void bcch_stop_rx() = 0;
|
||||
|
@ -395,45 +438,50 @@ typedef struct {
|
|||
float estimator_fil_w;
|
||||
bool rssi_sensor_enabled;
|
||||
} phy_args_t;
|
||||
|
||||
/* Interface MAC -> PHY */
|
||||
class phy_interface_mac
|
||||
|
||||
|
||||
/* RAT agnostic Interface MAC -> PHY */
|
||||
class phy_interface_mac_common
|
||||
{
|
||||
public:
|
||||
/* Configure PRACH using parameters written by RRC */
|
||||
virtual void configure_prach_params() = 0;
|
||||
|
||||
/* Start synchronization with strongest cell in the current carrier frequency */
|
||||
virtual bool sync_status() = 0;
|
||||
|
||||
/* Sets a C-RNTI allowing the PHY to pregenerate signals if necessary */
|
||||
virtual void set_crnti(uint16_t rnti) = 0;
|
||||
|
||||
virtual void set_crnti(uint16_t rnti) = 0;
|
||||
|
||||
/* Time advance commands */
|
||||
virtual void set_timeadv_rar(uint32_t ta_cmd) = 0;
|
||||
virtual void set_timeadv(uint32_t ta_cmd) = 0;
|
||||
|
||||
/* Sets RAR grant payload */
|
||||
virtual void set_rar_grant(uint32_t tti, uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN]) = 0;
|
||||
|
||||
virtual uint32_t get_current_tti() = 0;
|
||||
|
||||
virtual float get_phr() = 0;
|
||||
virtual float get_pathloss_db() = 0;
|
||||
};
|
||||
|
||||
/* Interface MAC -> PHY */
|
||||
class phy_interface_mac : public phy_interface_mac_common
|
||||
{
|
||||
public:
|
||||
/* Configure PRACH using parameters written by RRC */
|
||||
virtual void configure_prach_params() = 0;
|
||||
|
||||
virtual void prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm) = 0;
|
||||
virtual int prach_tx_tti() = 0;
|
||||
|
||||
/* Indicates the transmission of a SR signal in the next opportunity */
|
||||
virtual void sr_send() = 0;
|
||||
virtual int sr_last_tx_tti() = 0;
|
||||
|
||||
/* Time advance commands */
|
||||
virtual void set_timeadv_rar(uint32_t ta_cmd) = 0;
|
||||
virtual void set_timeadv(uint32_t ta_cmd) = 0;
|
||||
|
||||
/* Sets RAR grant payload */
|
||||
virtual void set_rar_grant(uint32_t tti, uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN]) = 0;
|
||||
|
||||
/* Instruct the PHY to decode PDCCH with the CRC scrambled with given RNTI */
|
||||
virtual void pdcch_ul_search(srslte_rnti_type_t rnti_type, uint16_t rnti, int tti_start = -1, int tti_end = -1) = 0;
|
||||
virtual void pdcch_dl_search(srslte_rnti_type_t rnti_type, uint16_t rnti, int tti_start = -1, int tti_end = -1) = 0;
|
||||
virtual void pdcch_ul_search_reset() = 0;
|
||||
virtual void pdcch_dl_search_reset() = 0;
|
||||
|
||||
virtual uint32_t get_current_tti() = 0;
|
||||
|
||||
virtual float get_phr() = 0;
|
||||
virtual float get_pathloss_db() = 0;
|
||||
|
||||
};
|
||||
|
||||
class phy_interface_rrc
|
||||
|
|
|
@ -242,6 +242,9 @@ SRSLTE_API uint32_t srslte_N_ta_new_rar(uint32_t ta);
|
|||
SRSLTE_API uint32_t srslte_N_ta_new(uint32_t N_ta_old,
|
||||
uint32_t ta);
|
||||
|
||||
SRSLTE_API float srslte_coderate(uint32_t tbs,
|
||||
uint32_t nof_re);
|
||||
|
||||
SRSLTE_API char *srslte_cp_string(srslte_cp_t cp);
|
||||
|
||||
SRSLTE_API char *srslte_mod_string(srslte_mod_t mod);
|
||||
|
|
|
@ -48,13 +48,13 @@
|
|||
// The constant SRSLTE_TDEC_NPAR defines the maximum number of parallel CB supported by all SIMD decoders
|
||||
#ifdef ENABLE_SIMD_INTER
|
||||
#include "srslte/phy/fec/turbodecoder_simd_inter.h"
|
||||
#if LV_HAVE_AVX2
|
||||
#ifdef LV_HAVE_AVX2
|
||||
#define SRSLTE_TDEC_NPAR_INTRA 2
|
||||
#else
|
||||
#define SRSLTE_TDEC_NPAR_INTRA 1
|
||||
#endif
|
||||
#else
|
||||
#if LV_HAVE_AVX2
|
||||
#ifdef LV_HAVE_AVX2
|
||||
#define SRSLTE_TDEC_NPAR 2
|
||||
#else
|
||||
#define SRSLTE_TDEC_NPAR 1
|
||||
|
|
|
@ -107,10 +107,7 @@ SRSLTE_API int srslte_pdsch_set_rnti(srslte_pdsch_t *q,
|
|||
SRSLTE_API void srslte_pdsch_free_rnti(srslte_pdsch_t *q,
|
||||
uint16_t rnti);
|
||||
|
||||
SRSLTE_API float srslte_pdsch_coderate(uint32_t tbs,
|
||||
uint32_t nof_re);
|
||||
|
||||
SRSLTE_API int srslte_pdsch_cfg(srslte_pdsch_cfg_t *cfg,
|
||||
SRSLTE_API int srslte_pdsch_cfg(srslte_pdsch_cfg_t *cfg,
|
||||
srslte_cell_t cell,
|
||||
srslte_ra_dl_grant_t *grant,
|
||||
uint32_t cfi,
|
||||
|
|
|
@ -108,7 +108,6 @@ typedef struct SRSLTE_API {
|
|||
bool group_hopping_en;
|
||||
|
||||
float threshold_format1;
|
||||
float threshold_format1a;
|
||||
float last_corr;
|
||||
uint32_t last_n_prb;
|
||||
uint32_t last_n_pucch;
|
||||
|
@ -127,9 +126,8 @@ SRSLTE_API bool srslte_pucch_set_cfg(srslte_pucch_t* q,
|
|||
srslte_pucch_cfg_t* cfg,
|
||||
bool group_hopping_en);
|
||||
|
||||
SRSLTE_API void srslte_pucch_set_threshold(srslte_pucch_t *q,
|
||||
float format1,
|
||||
float format1a);
|
||||
SRSLTE_API void srslte_pucch_set_threshold(srslte_pucch_t *q,
|
||||
float format1_threshold);
|
||||
|
||||
SRSLTE_API int srslte_pucch_set_crnti(srslte_pucch_t *q,
|
||||
uint16_t c_rnti);
|
||||
|
|
|
@ -63,7 +63,7 @@ typedef struct {
|
|||
typedef struct {
|
||||
srslte_sequence_t seq[SRSLTE_NSUBFRAMES_X_FRAME];
|
||||
bool sequence_generated;
|
||||
} srslte_pusch_user_t;
|
||||
} srslte_pusch_user_t;
|
||||
|
||||
/* PUSCH object */
|
||||
typedef struct SRSLTE_API {
|
||||
|
|
|
@ -152,6 +152,9 @@ SRSLTE_API void srslte_vec_conj_cc(cf_t *x, cf_t *y, uint32_t len);
|
|||
/* average vector power */
|
||||
SRSLTE_API float srslte_vec_avg_power_cf(cf_t *x, uint32_t len);
|
||||
|
||||
/* Correlation between complex vectors x and y */
|
||||
SRSLTE_API float srslte_vec_corr_ccc(cf_t *x, cf_t *y, uint32_t len);
|
||||
|
||||
/* return the index of the maximum value in the vector */
|
||||
SRSLTE_API uint32_t srslte_vec_max_fi(float *x, uint32_t len);
|
||||
SRSLTE_API uint32_t srslte_vec_max_abs_ci(cf_t *x, uint32_t len);
|
||||
|
|
|
@ -46,7 +46,7 @@ class gw
|
|||
{
|
||||
public:
|
||||
gw();
|
||||
void init(srsue::pdcp_interface_gw *pdcp_, srsue::ue_interface *ue_, log *gw_log_);
|
||||
void init(srsue::pdcp_interface_gw *pdcp_, srsue::ue_interface *ue_, log *gw_log_, uint32_t lcid_);
|
||||
void stop();
|
||||
|
||||
void get_metrics(gw_metrics_t &m);
|
||||
|
@ -56,11 +56,11 @@ public:
|
|||
|
||||
// NAS interface
|
||||
error_t setup_if_addr(uint32_t ip_addr, char *err_str);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
static const int GW_THREAD_PRIO = 7;
|
||||
|
||||
|
||||
static const int GW_THREAD_PRIO = 7;
|
||||
|
||||
srsue::pdcp_interface_gw *pdcp;
|
||||
srsue::ue_interface *ue;
|
||||
|
||||
|
@ -72,6 +72,7 @@ private:
|
|||
struct ifreq ifr;
|
||||
int32 sock;
|
||||
bool if_up;
|
||||
uint32_t lcid;
|
||||
|
||||
long ul_tput_bytes;
|
||||
long dl_tput_bytes;
|
||||
|
|
|
@ -46,13 +46,14 @@ public:
|
|||
srsue::rrc_interface_pdcp *rrc_,
|
||||
srsue::gw_interface_pdcp *gw_,
|
||||
log *pdcp_log_,
|
||||
uint32_t lcid_,
|
||||
uint8_t direction_);
|
||||
void stop();
|
||||
|
||||
// RRC interface
|
||||
void reset();
|
||||
void write_sdu(uint32_t lcid, byte_buffer_t *sdu);
|
||||
void add_bearer(uint32_t lcid, LIBLTE_RRC_PDCP_CONFIG_STRUCT *cnfg = NULL);
|
||||
void add_bearer(uint32_t lcid, srslte_pdcp_config_t cnfg = srslte_pdcp_config_t());
|
||||
void config_security(uint32_t lcid,
|
||||
uint8_t *k_rrc_enc,
|
||||
uint8_t *k_rrc_int,
|
||||
|
@ -72,6 +73,7 @@ private:
|
|||
|
||||
log *pdcp_log;
|
||||
pdcp_entity pdcp_array[SRSLTE_N_RADIO_BEARERS];
|
||||
uint32_t lcid; // default LCID that is maintained active by PDCP instance
|
||||
uint8_t direction;
|
||||
|
||||
bool valid_lcid(uint32_t lcid);
|
||||
|
|
|
@ -67,9 +67,7 @@ public:
|
|||
srsue::gw_interface_pdcp *gw_,
|
||||
srslte::log *log_,
|
||||
uint32_t lcid_,
|
||||
uint8_t direction_,
|
||||
LIBLTE_RRC_PDCP_CONFIG_STRUCT *cnfg = NULL
|
||||
);
|
||||
srslte_pdcp_config_t cfg_);
|
||||
void reset();
|
||||
|
||||
bool is_active();
|
||||
|
@ -94,12 +92,7 @@ private:
|
|||
|
||||
bool active;
|
||||
uint32_t lcid;
|
||||
bool do_security;
|
||||
u_int8_t direction;
|
||||
|
||||
uint8_t sn_len;
|
||||
// TODO: Support the following configurations
|
||||
// bool do_rohc;
|
||||
srslte_pdcp_config_t cfg;
|
||||
|
||||
uint32_t rx_count;
|
||||
uint32_t tx_count;
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "srslte/common/msg_queue.h"
|
||||
#include "srslte/upper/rlc_entity.h"
|
||||
#include "srslte/upper/rlc_metrics.h"
|
||||
#include "srslte/upper/rlc_common.h"
|
||||
|
||||
namespace srslte {
|
||||
|
||||
|
@ -55,13 +56,15 @@ public:
|
|||
srsue::rrc_interface_rlc *rrc_,
|
||||
srsue::ue_interface *ue_,
|
||||
log *rlc_log_,
|
||||
mac_interface_timers *mac_timers_);
|
||||
mac_interface_timers *mac_timers_,
|
||||
uint32_t lcid_);
|
||||
void stop();
|
||||
|
||||
void get_metrics(rlc_metrics_t &m);
|
||||
|
||||
// PDCP interface
|
||||
void write_sdu(uint32_t lcid, byte_buffer_t *sdu);
|
||||
std::string get_rb_name(uint32_t lcid);
|
||||
|
||||
// MAC interface
|
||||
uint32_t get_buffer_state(uint32_t lcid);
|
||||
|
@ -74,8 +77,9 @@ public:
|
|||
|
||||
// RRC interface
|
||||
void reset();
|
||||
void empty_queue();
|
||||
void add_bearer(uint32_t lcid);
|
||||
void add_bearer(uint32_t lcid, LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg);
|
||||
void add_bearer(uint32_t lcid, srslte_rlc_config_t cnfg);
|
||||
|
||||
private:
|
||||
void reset_metrics();
|
||||
|
@ -87,6 +91,7 @@ private:
|
|||
srslte::mac_interface_timers *mac_timers;
|
||||
srsue::ue_interface *ue;
|
||||
srslte::rlc_entity rlc_array[SRSLTE_N_RADIO_BEARERS];
|
||||
uint32_t default_lcid;
|
||||
|
||||
long ul_tput_bytes[SRSLTE_N_RADIO_BEARERS];
|
||||
long dl_tput_bytes[SRSLTE_N_RADIO_BEARERS];
|
||||
|
|
|
@ -76,7 +76,7 @@ public:
|
|||
srsue::pdcp_interface_rlc *pdcp_,
|
||||
srsue::rrc_interface_rlc *rrc_,
|
||||
mac_interface_timers *mac_timers);
|
||||
void configure(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg);
|
||||
void configure(srslte_rlc_config_t cnfg);
|
||||
void reset();
|
||||
void empty_queue();
|
||||
|
||||
|
@ -128,15 +128,7 @@ private:
|
|||
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
|
||||
***************************************************************************/
|
||||
|
||||
// TX configs
|
||||
int32_t t_poll_retx; // Poll retx timeout (ms)
|
||||
int32_t poll_pdu; // Insert poll bit after this many PDUs
|
||||
int32_t poll_byte; // Insert poll bit after this much data (KB)
|
||||
uint32_t max_retx_thresh; // Max number of retx
|
||||
|
||||
// RX configs
|
||||
int32_t t_reordering; // Timer used by rx to detect PDU loss (ms)
|
||||
int32_t t_status_prohibit; // Timer used by rx to prohibit tx of status PDU (ms)
|
||||
srslte_rlc_am_config_t cfg;
|
||||
|
||||
/****************************************************************************
|
||||
* State variables and counters
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#ifndef RLC_COMMON_H
|
||||
#define RLC_COMMON_H
|
||||
|
||||
#include "srslte/upper/rlc_interface.h"
|
||||
|
||||
namespace srslte {
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -66,14 +68,6 @@ typedef enum{
|
|||
static const char rlc_dc_field_text[RLC_DC_FIELD_N_ITEMS][20] = {"Control PDU",
|
||||
"Data PDU"};
|
||||
|
||||
typedef enum{
|
||||
RLC_UMD_SN_SIZE_5_BITS = 0,
|
||||
RLC_UMD_SN_SIZE_10_BITS,
|
||||
RLC_UMD_SN_SIZE_N_ITEMS,
|
||||
}rlc_umd_sn_size_t;
|
||||
static const char rlc_umd_sn_size_text[RLC_UMD_SN_SIZE_N_ITEMS][20] = {"5 bits", "10 bits"};
|
||||
static const uint16_t rlc_umd_sn_size_num[RLC_UMD_SN_SIZE_N_ITEMS] = {5, 10};
|
||||
|
||||
// UMD PDU Header
|
||||
typedef struct{
|
||||
uint8_t fi; // Framing info
|
||||
|
@ -162,7 +156,7 @@ public:
|
|||
srsue::pdcp_interface_rlc *pdcp_,
|
||||
srsue::rrc_interface_rlc *rrc_,
|
||||
srslte::mac_interface_timers *mac_timers_) = 0;
|
||||
virtual void configure(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg) = 0;
|
||||
virtual void configure(srslte_rlc_config_t cnfg) = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual void empty_queue() = 0;
|
||||
|
||||
|
|
|
@ -54,8 +54,9 @@ public:
|
|||
srsue::rrc_interface_rlc *rrc_,
|
||||
mac_interface_timers *mac_timers_);
|
||||
|
||||
void configure(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg);
|
||||
void configure(srslte_rlc_config_t cnfg);
|
||||
void reset();
|
||||
void empty_queue();
|
||||
bool active();
|
||||
|
||||
rlc_mode_t get_mode();
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
/**
|
||||
*
|
||||
* \section COPYRIGHT
|
||||
*
|
||||
* Copyright 2013-2015 Software Radio Systems Limited
|
||||
*
|
||||
* \section LICENSE
|
||||
*
|
||||
* This file is part of the srsUE library.
|
||||
*
|
||||
* srsUE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* srsUE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* A copy of the GNU Affero General Public License can be found in
|
||||
* the LICENSE file in the top-level directory of this distribution
|
||||
* and at http://www.gnu.org/licenses/.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef RLC_INTERFACE_H
|
||||
#define RLC_INTERFACE_H
|
||||
|
||||
// for custom constructors
|
||||
#include "srslte/asn1/liblte_rrc.h"
|
||||
|
||||
namespace srslte {
|
||||
|
||||
|
||||
typedef enum{
|
||||
RLC_UMD_SN_SIZE_5_BITS = 0,
|
||||
RLC_UMD_SN_SIZE_10_BITS,
|
||||
RLC_UMD_SN_SIZE_N_ITEMS,
|
||||
}rlc_umd_sn_size_t;
|
||||
static const char rlc_umd_sn_size_text[RLC_UMD_SN_SIZE_N_ITEMS][20] = {"5 bits", "10 bits"};
|
||||
static const uint16_t rlc_umd_sn_size_num[RLC_UMD_SN_SIZE_N_ITEMS] = {5, 10};
|
||||
|
||||
|
||||
typedef struct {
|
||||
/****************************************************************************
|
||||
* Configurable parameters
|
||||
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
|
||||
***************************************************************************/
|
||||
|
||||
// TX configs
|
||||
int32_t t_poll_retx; // Poll retx timeout (ms)
|
||||
int32_t poll_pdu; // Insert poll bit after this many PDUs
|
||||
int32_t poll_byte; // Insert poll bit after this much data (KB)
|
||||
uint32_t max_retx_thresh; // Max number of retx
|
||||
|
||||
// RX configs
|
||||
int32_t t_reordering; // Timer used by rx to detect PDU loss (ms)
|
||||
int32_t t_status_prohibit; // Timer used by rx to prohibit tx of status PDU (ms)
|
||||
} srslte_rlc_am_config_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
/****************************************************************************
|
||||
* Configurable parameters
|
||||
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
|
||||
***************************************************************************/
|
||||
|
||||
int32_t t_reordering; // Timer used by rx to detect PDU loss (ms)
|
||||
rlc_umd_sn_size_t tx_sn_field_length; // Number of bits used for tx (UL) sequence number
|
||||
rlc_umd_sn_size_t rx_sn_field_length; // Number of bits used for rx (DL) sequence number
|
||||
|
||||
uint32_t rx_window_size;
|
||||
uint32_t rx_mod; // Rx counter modulus
|
||||
uint32_t tx_mod; // Tx counter modulus
|
||||
} srslte_rlc_um_config_t;
|
||||
|
||||
|
||||
class srslte_rlc_config_t
|
||||
{
|
||||
public:
|
||||
LIBLTE_RRC_RLC_MODE_ENUM rlc_mode;
|
||||
srslte_rlc_am_config_t am;
|
||||
srslte_rlc_um_config_t um;
|
||||
|
||||
// Constructor based on liblte's RLC config
|
||||
srslte_rlc_config_t(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg) : rlc_mode(cnfg->rlc_mode), am(), um()
|
||||
{
|
||||
switch(rlc_mode)
|
||||
{
|
||||
case LIBLTE_RRC_RLC_MODE_AM:
|
||||
am.t_poll_retx = liblte_rrc_t_poll_retransmit_num[cnfg->ul_am_rlc.t_poll_retx];
|
||||
am.poll_pdu = liblte_rrc_poll_pdu_num[cnfg->ul_am_rlc.poll_pdu];
|
||||
am.poll_byte = liblte_rrc_poll_byte_num[cnfg->ul_am_rlc.poll_byte]*1000; // KB
|
||||
am.max_retx_thresh = liblte_rrc_max_retx_threshold_num[cnfg->ul_am_rlc.max_retx_thresh];
|
||||
am.t_reordering = liblte_rrc_t_reordering_num[cnfg->dl_am_rlc.t_reordering];
|
||||
am.t_status_prohibit = liblte_rrc_t_status_prohibit_num[cnfg->dl_am_rlc.t_status_prohibit];
|
||||
break;
|
||||
case LIBLTE_RRC_RLC_MODE_UM_BI:
|
||||
um.t_reordering = liblte_rrc_t_reordering_num[cnfg->dl_um_bi_rlc.t_reordering];
|
||||
um.rx_sn_field_length = (rlc_umd_sn_size_t)cnfg->dl_um_bi_rlc.sn_field_len;
|
||||
um.rx_window_size = (RLC_UMD_SN_SIZE_5_BITS == um.rx_sn_field_length) ? 16 : 512;
|
||||
um.rx_mod = (RLC_UMD_SN_SIZE_5_BITS == um.rx_sn_field_length) ? 32 : 1024;
|
||||
um.tx_sn_field_length = (rlc_umd_sn_size_t)cnfg->ul_um_bi_rlc.sn_field_len;
|
||||
um.tx_mod = (RLC_UMD_SN_SIZE_5_BITS == um.tx_sn_field_length) ? 32 : 1024;
|
||||
break;
|
||||
case LIBLTE_RRC_RLC_MODE_UM_UNI_UL:
|
||||
um.tx_sn_field_length = (rlc_umd_sn_size_t)cnfg->ul_um_uni_rlc.sn_field_len;
|
||||
um.tx_mod = (RLC_UMD_SN_SIZE_5_BITS == um.tx_sn_field_length) ? 32 : 1024;
|
||||
break;
|
||||
case LIBLTE_RRC_RLC_MODE_UM_UNI_DL:
|
||||
um.t_reordering = liblte_rrc_t_reordering_num[cnfg->dl_um_uni_rlc.t_reordering];
|
||||
um.rx_sn_field_length = (rlc_umd_sn_size_t)cnfg->dl_um_uni_rlc.sn_field_len;
|
||||
um.rx_window_size = (RLC_UMD_SN_SIZE_5_BITS == um.rx_sn_field_length) ? 16 : 512;
|
||||
um.rx_mod = (RLC_UMD_SN_SIZE_5_BITS == um.rx_sn_field_length) ? 32 : 1024;
|
||||
break;
|
||||
default:
|
||||
// Handle default case
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace srslte
|
||||
|
||||
#endif // RLC_INTERFACE_H
|
|
@ -46,7 +46,7 @@ public:
|
|||
srsue::pdcp_interface_rlc *pdcp_,
|
||||
srsue::rrc_interface_rlc *rrc_,
|
||||
mac_interface_timers *mac_timers);
|
||||
void configure(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg);
|
||||
void configure(srslte_rlc_config_t cnfg);
|
||||
void reset();
|
||||
void empty_queue();
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ public:
|
|||
srsue::pdcp_interface_rlc *pdcp_,
|
||||
srsue::rrc_interface_rlc *rrc_,
|
||||
mac_interface_timers *mac_timers_);
|
||||
void configure(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg);
|
||||
void configure(srslte_rlc_config_t cnfg);
|
||||
void reset();
|
||||
void empty_queue();
|
||||
|
||||
|
@ -92,9 +92,6 @@ private:
|
|||
|
||||
// Rx window
|
||||
std::map<uint32_t, rlc_umd_pdu_t> rx_window;
|
||||
uint32_t rx_window_size;
|
||||
uint32_t rx_mod; // Rx counter modulus
|
||||
uint32_t tx_mod; // Tx counter modulus
|
||||
|
||||
// RX SDU buffers
|
||||
byte_buffer_t *rx_sdu;
|
||||
|
@ -108,9 +105,7 @@ private:
|
|||
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
|
||||
***************************************************************************/
|
||||
|
||||
int32_t t_reordering; // Timer used by rx to detect PDU loss (ms)
|
||||
rlc_umd_sn_size_t tx_sn_field_length; // Number of bits used for tx (UL) sequence number
|
||||
rlc_umd_sn_size_t rx_sn_field_length; // Number of bits used for rx (DL) sequence number
|
||||
srslte_rlc_um_config_t cfg;
|
||||
|
||||
/****************************************************************************
|
||||
* State variables and counters
|
||||
|
|
|
@ -2006,6 +2006,7 @@ LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_object_eutra_ie(LIBLTE_RRC_MEAS_OBJECT_EU
|
|||
liblte_value_2_bits(0, ie_ptr, 1);
|
||||
|
||||
// Optional indicators
|
||||
liblte_value_2_bits(meas_obj_eutra->offset_freq_not_default, ie_ptr, 1);
|
||||
liblte_value_2_bits(meas_obj_eutra->cells_to_remove_list_present, ie_ptr, 1);
|
||||
if(0 != meas_obj_eutra->N_cells_to_add_mod)
|
||||
{
|
||||
|
@ -2035,7 +2036,10 @@ LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_object_eutra_ie(LIBLTE_RRC_MEAS_OBJECT_EU
|
|||
liblte_rrc_pack_neigh_cell_config_ie(meas_obj_eutra->neigh_cell_cnfg, ie_ptr);
|
||||
|
||||
// Offset Freq
|
||||
liblte_rrc_pack_q_offset_range_ie(meas_obj_eutra->offset_freq, ie_ptr);
|
||||
if(meas_obj_eutra->offset_freq_not_default)
|
||||
{
|
||||
liblte_rrc_pack_q_offset_range_ie(meas_obj_eutra->offset_freq, ie_ptr);
|
||||
}
|
||||
|
||||
// Cells To Remove List
|
||||
if(meas_obj_eutra->cells_to_remove_list_present)
|
||||
|
@ -2329,6 +2333,9 @@ LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_object_to_add_mod_list_ie(LIBLTE_RRC_MEAS
|
|||
// Meas Object ID
|
||||
liblte_rrc_pack_meas_object_id_ie(list->meas_obj_list[i].meas_obj_id, ie_ptr);
|
||||
|
||||
// Meas Object Choice Extension
|
||||
liblte_value_2_bits(0, ie_ptr, 1); // Choice from before extension marker
|
||||
|
||||
// Meas Object Choice
|
||||
liblte_value_2_bits(list->meas_obj_list[i].meas_obj_type, ie_ptr, 2);
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
// Table 6.1.3.1-1 Buffer size levels for BSR
|
||||
static uint32_t btable[64] = {
|
||||
0, 5, 10, 12, 14, 17, 19, 22, 26, 31, 36, 42, 49, 57, 67, 78, 91, 107, 125, 146, 171, 200, 234, 274, 321, 376, 440, 515, 603, 706, 826, 967, 1132,
|
||||
0, 1, 10, 12, 14, 17, 19, 22, 26, 31, 36, 42, 49, 57, 67, 78, 91, 107, 125, 146, 171, 200, 234, 274, 321, 376, 440, 515, 603, 706, 826, 967, 1132,
|
||||
1326, 1552, 1817, 2127, 2490, 2915, 3413, 3995, 4667, 5476, 6411, 7505, 8787, 10287, 12043, 14099, 16507, 19325, 22624, 26487, 31009, 36304,
|
||||
42502, 49759, 58255, 68201, 79846, 93479, 109439, 128125, 150000};
|
||||
|
||||
|
@ -428,12 +428,16 @@ int sch_subh::get_bsr(uint32_t buff_size[4])
|
|||
buff_size[2] = (payload[1]&0x0F) << 4 | (payload[1]&0xC0) >> 6;
|
||||
buff_size[3] = (payload[2]&0x3F);
|
||||
} else {
|
||||
uint32_t nonzero_lcg = (payload[0]&0xc0) >> 6;
|
||||
nonzero_lcg = (payload[0]&0xc0) >> 6;
|
||||
buff_size[nonzero_lcg%4] = payload[0]&0x3f;
|
||||
}
|
||||
for (int i=0;i<4;i++) {
|
||||
if (buff_size[i]) {
|
||||
buff_size[i] = btable[buff_size[i]%64];
|
||||
if (buff_size[i]<63) {
|
||||
buff_size[i] = btable[1+buff_size[i]];
|
||||
} else {
|
||||
buff_size[i] = btable[63];
|
||||
}
|
||||
}
|
||||
}
|
||||
return nonzero_lcg;
|
||||
|
@ -515,8 +519,8 @@ bool sch_subh::set_bsr(uint32_t buff_size[4], sch_subh::cetype format)
|
|||
bool sch_subh::set_c_rnti(uint16_t crnti)
|
||||
{
|
||||
if (((sch_pdu*)parent)->has_space_ce(2)) {
|
||||
w_payload_ce[0] = (uint8_t) (crnti&0xff00)>>8;
|
||||
w_payload_ce[1] = (uint8_t) (crnti&0x00ff);
|
||||
w_payload_ce[0] = (uint8_t) ((crnti&0xff00)>>8);
|
||||
w_payload_ce[1] = (uint8_t) ((crnti&0x00ff));
|
||||
lcid = CRNTI;
|
||||
((sch_pdu*)parent)->update_space_ce(2);
|
||||
nof_bytes = 2;
|
||||
|
|
|
@ -55,32 +55,7 @@ set(srslte_srcs $<TARGET_OBJECTS:srslte_agc>
|
|||
)
|
||||
|
||||
add_library(srslte_phy STATIC ${srslte_srcs})
|
||||
set_target_properties(srslte_phy PROPERTIES
|
||||
VERSION ${SRSLTE_VERSION_MAJOR}.${SRSLTE_VERSION_MINOR})
|
||||
|
||||
find_package(MKL)
|
||||
if(MKL_FOUND)
|
||||
include_directories(${MKL_INCLUDE_DIRS})
|
||||
link_directories(${MKL_LIBRARY_DIRS})
|
||||
else(MKL_FOUND)
|
||||
find_package(FFTW3F REQUIRED)
|
||||
if(FFTW3F_FOUND)
|
||||
include_directories(${FFTW3F_INCLUDE_DIRS})
|
||||
link_directories(${FFTW3F_LIBRARY_DIRS})
|
||||
endif(FFTW3F_FOUND)
|
||||
endif(MKL_FOUND)
|
||||
|
||||
|
||||
if(MKL_FOUND)
|
||||
if(BUILD_STATIC)
|
||||
target_link_libraries(srslte_phy ${MKL_STATIC_LIBRARIES})
|
||||
else(BUILD_STATIC)
|
||||
target_link_libraries(srslte_phy ${MKL_LIBRARIES})
|
||||
endif(BUILD_STATIC)
|
||||
else(MKL_FOUND)
|
||||
target_link_libraries(srslte_phy ${FFTW3F_LIBRARIES})
|
||||
endif(MKL_FOUND)
|
||||
|
||||
target_link_libraries(srslte_phy ${FFT_LIBRARIES})
|
||||
|
||||
if(VOLK_FOUND)
|
||||
target_link_libraries(srslte_phy ${VOLK_LIBRARIES})
|
||||
|
|
|
@ -178,6 +178,11 @@ uint32_t srslte_N_ta_new(uint32_t N_ta_old, uint32_t ta) {
|
|||
}
|
||||
}
|
||||
|
||||
float srslte_coderate(uint32_t tbs, uint32_t nof_re)
|
||||
{
|
||||
return (float) (tbs + 24)/(nof_re);
|
||||
}
|
||||
|
||||
/* Returns the new time advance as indicated by the random access response
|
||||
* as specified in Section 4.2.3 of 36.213 */
|
||||
uint32_t srslte_N_ta_new_rar(uint32_t ta) {
|
||||
|
|
|
@ -73,8 +73,8 @@ int srslte_enb_ul_init(srslte_enb_ul_t *q,
|
|||
goto clean_exit;
|
||||
}
|
||||
|
||||
srslte_pucch_set_threshold(&q->pucch, 0.5, 0.5);
|
||||
|
||||
srslte_pucch_set_threshold(&q->pucch, 0.8);
|
||||
|
||||
if (srslte_chest_ul_init(&q->chest, max_prb)) {
|
||||
fprintf(stderr, "Error initiating channel estimator\n");
|
||||
goto clean_exit;
|
||||
|
@ -174,8 +174,6 @@ int srslte_enb_ul_set_cell(srslte_enb_ul_t *q, srslte_cell_t cell,
|
|||
srslte_prach_set_detect_factor(&q->prach, 60);
|
||||
}
|
||||
|
||||
srslte_pucch_set_threshold(&q->pucch, 0.5, 0.5);
|
||||
|
||||
if (srslte_chest_ul_set_cell(&q->chest, cell)) {
|
||||
fprintf(stderr, "Error initiating channel estimator\n");
|
||||
return SRSLTE_ERROR;
|
||||
|
|
|
@ -328,6 +328,9 @@ void srslte_tdec_gen_iteration(srslte_tdec_gen_t * h, float * input, uint32_t lo
|
|||
} else {
|
||||
fprintf(stderr, "Error CB index not set (call srslte_tdec_gen_reset() first\n");
|
||||
}
|
||||
|
||||
// Increase number of iterations
|
||||
h->n_iter++;
|
||||
}
|
||||
|
||||
int srslte_tdec_gen_reset(srslte_tdec_gen_t * h, uint32_t long_cb)
|
||||
|
@ -339,6 +342,7 @@ int srslte_tdec_gen_reset(srslte_tdec_gen_t * h, uint32_t long_cb)
|
|||
}
|
||||
memset(h->w, 0, sizeof(float) * long_cb);
|
||||
h->current_cbidx = srslte_cbsegm_cbindex(long_cb);
|
||||
h->current_cb_len = long_cb;
|
||||
if (h->current_cbidx < 0) {
|
||||
fprintf(stderr, "Invalid CB length %d\n", long_cb);
|
||||
return -1;
|
||||
|
|
|
@ -27,20 +27,9 @@
|
|||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <srslte/phy/phch/pdsch.h>
|
||||
#include <srslte/phy/phch/pdsch_cfg.h>
|
||||
#include <srslte/phy/phch/ra.h>
|
||||
|
||||
#include "prb_dl.h"
|
||||
#include "srslte/phy/phch/pdsch.h"
|
||||
#include "srslte/phy/phch/sch.h"
|
||||
#include "srslte/phy/common/phy_common.h"
|
||||
#include "srslte/phy/utils/bit.h"
|
||||
#include "prb_dl.h"
|
||||
#include "srslte/phy/utils/debug.h"
|
||||
#include "srslte/phy/utils/vector.h"
|
||||
|
||||
|
@ -48,7 +37,6 @@
|
|||
#define MAX_PDSCH_RE(cp) (2 * SRSLTE_CP_NSYMB(cp) * 12)
|
||||
|
||||
|
||||
|
||||
const static srslte_mod_t modulations[4] =
|
||||
{ SRSLTE_MOD_BPSK, SRSLTE_MOD_QPSK, SRSLTE_MOD_16QAM, SRSLTE_MOD_64QAM };
|
||||
|
||||
|
@ -60,12 +48,7 @@ extern int indices[100000];
|
|||
extern int indices_ptr;
|
||||
#endif
|
||||
|
||||
float srslte_pdsch_coderate(uint32_t tbs, uint32_t nof_re)
|
||||
{
|
||||
return (float) (tbs + 24)/(nof_re);
|
||||
}
|
||||
|
||||
int srslte_pdsch_cp(srslte_pdsch_t *q, cf_t *input, cf_t *output, srslte_ra_dl_grant_t *grant, uint32_t lstart_grant, uint32_t nsubframe, bool put)
|
||||
int srslte_pdsch_cp(srslte_pdsch_t *q, cf_t *input, cf_t *output, srslte_ra_dl_grant_t *grant, uint32_t lstart_grant, uint32_t nsubframe, bool put)
|
||||
{
|
||||
uint32_t s, n, l, lp, lstart, lend, nof_refs;
|
||||
bool is_pbch, is_sss;
|
||||
|
@ -257,7 +240,7 @@ int pdsch_init_multi(srslte_pdsch_t *q, uint32_t max_prb, uint32_t nof_rx_antenn
|
|||
}
|
||||
}
|
||||
}
|
||||
for (int j=0;j<q->nof_rx_antennas;j++) {
|
||||
for (int j=0;j<SRSLTE_MAX(q->nof_rx_antennas, q->cell.nof_ports);j++) {
|
||||
q->symbols[j] = srslte_vec_malloc(sizeof(cf_t) * q->max_re);
|
||||
if (!q->symbols[j]) {
|
||||
goto clean;
|
||||
|
|
|
@ -214,15 +214,15 @@ uint32_t srslte_pucch_get_npucch(uint32_t n_cce, srslte_pucch_format_t format, b
|
|||
{
|
||||
uint32_t n_pucch = 0;
|
||||
if (has_scheduling_request) {
|
||||
n_pucch = pucch_sched->n_pucch_sr;
|
||||
n_pucch = pucch_sched->n_pucch_sr;
|
||||
} else if (format < SRSLTE_PUCCH_FORMAT_2) {
|
||||
if (pucch_sched->sps_enabled) {
|
||||
n_pucch = pucch_sched->n_pucch_1[pucch_sched->tpc_for_pucch%4];
|
||||
} else {
|
||||
n_pucch = n_cce + pucch_sched->N_pucch_1;
|
||||
n_pucch = n_cce + pucch_sched->N_pucch_1;
|
||||
}
|
||||
} else {
|
||||
n_pucch = pucch_sched->n_pucch_2;
|
||||
n_pucch = pucch_sched->n_pucch_2;
|
||||
}
|
||||
return n_pucch;
|
||||
}
|
||||
|
@ -411,9 +411,8 @@ static int pucch_get(srslte_pucch_t *q, srslte_pucch_format_t format, uint32_t n
|
|||
return pucch_cp(q, format, n_pucch, input, z, true);
|
||||
}
|
||||
|
||||
void srslte_pucch_set_threshold(srslte_pucch_t *q, float format1, float format1a) {
|
||||
q->threshold_format1 = format1;
|
||||
q->threshold_format1a = format1a;
|
||||
void srslte_pucch_set_threshold(srslte_pucch_t *q, float format1_threshold) {
|
||||
q->threshold_format1 = format1_threshold;
|
||||
}
|
||||
|
||||
/** Initializes the PDCCH transmitter and receiver */
|
||||
|
@ -435,6 +434,8 @@ int srslte_pucch_init(srslte_pucch_t *q) {
|
|||
|
||||
srslte_uci_cqi_pucch_init(&q->cqi);
|
||||
|
||||
q->threshold_format1 = 0.8;
|
||||
|
||||
ret = SRSLTE_SUCCESS;
|
||||
}
|
||||
clean_exit:
|
||||
|
@ -616,6 +617,10 @@ static int uci_mod_bits(srslte_pucch_t *q, srslte_pucch_format_t format, uint8_t
|
|||
// Declare this here, since we can not include refsignal_ul.h
|
||||
void srslte_refsignal_r_uv_arg_1prb(float *arg, uint32_t u);
|
||||
|
||||
|
||||
float tmp_alpha;
|
||||
uint32_t tmp_noc, tmp_nprime, tmp_woc;
|
||||
|
||||
static int pucch_encode_(srslte_pucch_t* q, srslte_pucch_format_t format,
|
||||
uint32_t n_pucch, uint32_t sf_idx, uint16_t rnti,
|
||||
uint8_t bits[SRSLTE_PUCCH_MAX_BITS], cf_t z[SRSLTE_PUCCH_MAX_SYMBOLS], bool signal_only)
|
||||
|
@ -659,8 +664,14 @@ static int pucch_encode_(srslte_pucch_t* q, srslte_pucch_format_t format,
|
|||
if (n_prime_ns%2) {
|
||||
S_ns = M_PI/2;
|
||||
}
|
||||
DEBUG("PUCCH d_0: %.1f+%.1fi, alpha: %.1f, n_oc: %d, n_prime_ns: %d, n_rb_2=%d\n",
|
||||
DEBUG("PUCCH d_0: %.1f+%.1fi, alpha: %.1f, n_oc: %d, n_prime_ns: %d, n_rb_2=%d\n",
|
||||
__real__ q->d[0], __imag__ q->d[0], alpha, n_oc, n_prime_ns, q->pucch_cfg.n_rb_2);
|
||||
|
||||
tmp_alpha = alpha;
|
||||
tmp_noc = n_oc;
|
||||
tmp_nprime = n_prime_ns;
|
||||
tmp_woc = w_n_oc[N_sf_widx][n_oc%3][m];
|
||||
|
||||
for (uint32_t n=0;n<SRSLTE_PUCCH_N_SEQ;n++) {
|
||||
z[(ns%2)*N_sf_0*SRSLTE_PUCCH_N_SEQ+m*SRSLTE_PUCCH_N_SEQ+n] =
|
||||
q->d[0]*cexpf(I*(w_n_oc[N_sf_widx][n_oc%3][m]+q->tmp_arg[n]+alpha*n+S_ns));
|
||||
|
@ -771,7 +782,7 @@ int srslte_pucch_decode(srslte_pucch_t* q, srslte_pucch_format_t format,
|
|||
case SRSLTE_PUCCH_FORMAT_1:
|
||||
bzero(bits, SRSLTE_PUCCH_MAX_BITS*sizeof(uint8_t));
|
||||
pucch_encode(q, format, n_pucch, sf_idx, rnti, bits, q->z_tmp);
|
||||
corr = crealf(srslte_vec_dot_prod_conj_ccc(q->z, q->z_tmp, nof_re))/nof_re;
|
||||
corr = srslte_vec_corr_ccc(q->z, q->z_tmp, nof_re);
|
||||
if (corr >= q->threshold_format1) {
|
||||
ret = 1;
|
||||
} else {
|
||||
|
@ -782,11 +793,11 @@ int srslte_pucch_decode(srslte_pucch_t* q, srslte_pucch_format_t format,
|
|||
break;
|
||||
case SRSLTE_PUCCH_FORMAT_1A:
|
||||
bzero(bits, SRSLTE_PUCCH_MAX_BITS*sizeof(uint8_t));
|
||||
ret = 0;
|
||||
ret = 0;
|
||||
for (int b=0;b<2;b++) {
|
||||
bits[0] = b;
|
||||
bits[0] = b;
|
||||
pucch_encode(q, format, n_pucch, sf_idx, rnti, bits, q->z_tmp);
|
||||
corr = crealf(srslte_vec_dot_prod_conj_ccc(q->z, q->z_tmp, nof_re))/nof_re;
|
||||
corr = srslte_vec_corr_ccc(q->z, q->z_tmp, nof_re);
|
||||
if (corr > corr_max) {
|
||||
corr_max = corr;
|
||||
b_max = b;
|
||||
|
@ -794,7 +805,7 @@ int srslte_pucch_decode(srslte_pucch_t* q, srslte_pucch_format_t format,
|
|||
if (corr_max > q->threshold_format1) { // check with format1 in case ack+sr because ack only is binary
|
||||
ret = 1;
|
||||
}
|
||||
DEBUG("format1a b=%d, corr=%f, nof_re=%d, th=%f\n", b, corr, nof_re, q->threshold_format1a);
|
||||
DEBUG("format1a b=%d, corr=%f, nof_re=%d\n", b, corr, nof_re);
|
||||
}
|
||||
q->last_corr = corr_max;
|
||||
bits[0] = b_max;
|
||||
|
|
|
@ -512,7 +512,7 @@ int srslte_pusch_encode(srslte_pusch_t *q, srslte_pusch_cfg_t *cfg, srslte_softb
|
|||
// Run scrambling
|
||||
srslte_scrambling_bytes(seq, (uint8_t*) q->q, cfg->nbits.nof_bits);
|
||||
|
||||
// Correct UCI placeholder/repetition bits
|
||||
// Correct UCI placeholder/repetition bits
|
||||
uint8_t *d = q->q;
|
||||
for (int i = 0; i < q->ul_sch.nof_ri_ack_bits; i++) {
|
||||
if (q->ul_sch.ack_ri_bits[i].type == UCI_BIT_PLACEHOLDER) {
|
||||
|
@ -592,7 +592,7 @@ int srslte_pusch_decode(srslte_pusch_t *q,
|
|||
// Generate scrambling sequence if not pre-generated
|
||||
srslte_sequence_t *seq = get_user_sequence(q, rnti, cfg->sf_idx, cfg->nbits.nof_bits);
|
||||
|
||||
// Decode RI/HARQ bits before descrambling
|
||||
// Decode RI/HARQ bits before descrambling
|
||||
if (srslte_ulsch_uci_decode_ri_ack(&q->ul_sch, cfg, softbuffer, q->q, seq->c, uci_data)) {
|
||||
fprintf(stderr, "Error decoding RI/HARQ bits\n");
|
||||
return SRSLTE_ERROR;
|
||||
|
|
|
@ -380,7 +380,7 @@ int decode_cqi_long(srslte_uci_cqi_pusch_t *q, int16_t *q_bits, uint32_t Q,
|
|||
ret = srslte_crc_checksum(&q->crc, q->tmp_cqi, nof_bits + 8);
|
||||
if (ret == 0) {
|
||||
memcpy(data, q->tmp_cqi, nof_bits*sizeof(uint8_t));
|
||||
ret = 1;
|
||||
ret = 1;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
|
|
|
@ -43,9 +43,15 @@ typedef struct {
|
|||
SoapySDRRange *ranges;
|
||||
SoapySDRStream *rxStream;
|
||||
SoapySDRStream *txStream;
|
||||
bool tx_stream_active;
|
||||
bool rx_stream_active;
|
||||
} rf_soapy_handler_t;
|
||||
|
||||
|
||||
cf_t zero_mem[64*1024];
|
||||
|
||||
|
||||
|
||||
int soapy_error(void *h)
|
||||
{
|
||||
return 0;
|
||||
|
@ -82,6 +88,7 @@ void rf_soapy_register_error_handler(void *notused, srslte_rf_error_handler_t ne
|
|||
}
|
||||
|
||||
|
||||
|
||||
char* rf_soapy_devname(void* h)
|
||||
{
|
||||
return "soapy";
|
||||
|
@ -110,10 +117,11 @@ void rf_soapy_set_rx_cal(void *h, srslte_rf_cal_t *cal)
|
|||
int rf_soapy_start_rx_stream(void *h)
|
||||
{
|
||||
rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h;
|
||||
|
||||
if (SoapySDRDevice_activateStream(handler->device, handler->rxStream, 0, 0, 0) != 0)
|
||||
return SRSLTE_ERROR;
|
||||
|
||||
if(handler->rx_stream_active == false){
|
||||
if(SoapySDRDevice_activateStream(handler->device, handler->rxStream, 0, 0, 0) != 0)
|
||||
return SRSLTE_ERROR;
|
||||
handler->rx_stream_active = true;
|
||||
}
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -121,14 +129,11 @@ int rf_soapy_start_rx_stream(void *h)
|
|||
int rf_soapy_start_tx_stream(void *h)
|
||||
{
|
||||
rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h;
|
||||
if (SoapySDRDevice_setupStream(handler->device, &(handler->txStream), SOAPY_SDR_TX, SOAPY_SDR_CF32, NULL, 0, NULL) != 0) {
|
||||
printf("setupStream fail: %s\n", SoapySDRDevice_lastError());
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
if(SoapySDRDevice_activateStream(handler->device, handler->txStream, 0, 0, 0) != 0)
|
||||
return SRSLTE_ERROR;
|
||||
|
||||
if(handler->tx_stream_active == false){
|
||||
if(SoapySDRDevice_activateStream(handler->device, handler->txStream, 0, 0, 0) != 0)
|
||||
return SRSLTE_ERROR;
|
||||
handler->tx_stream_active = true;
|
||||
}
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -138,7 +143,8 @@ int rf_soapy_stop_rx_stream(void *h)
|
|||
rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h;
|
||||
if (SoapySDRDevice_deactivateStream(handler->device, handler->rxStream, 0, 0) != 0)
|
||||
return SRSLTE_ERROR;
|
||||
|
||||
|
||||
handler->rx_stream_active = false;
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -149,6 +155,8 @@ int rf_soapy_stop_tx_stream(void *h)
|
|||
if(SoapySDRDevice_deactivateStream(handler->device, handler->txStream, 0, 0) != 0)
|
||||
return SRSLTE_ERROR;
|
||||
|
||||
|
||||
handler->tx_stream_active = false;
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -210,12 +218,26 @@ int rf_soapy_open_multi(char *args, void **h, uint32_t nof_rx_antennas)
|
|||
bzero(handler, sizeof(rf_soapy_handler_t));
|
||||
*h = handler;
|
||||
handler->device = sdr;
|
||||
|
||||
if (SoapySDRDevice_setupStream(handler->device, &(handler->rxStream), SOAPY_SDR_RX, SOAPY_SDR_CF32, NULL, 0, NULL) != 0) {
|
||||
printf("setupStream fail: %s\n", SoapySDRDevice_lastError());
|
||||
return SRSLTE_ERROR;
|
||||
handler->tx_stream_active = false;
|
||||
handler->rx_stream_active = false;
|
||||
|
||||
|
||||
if(SoapySDRDevice_getNumChannels(handler->device,SOAPY_SDR_RX) > 0){
|
||||
printf("setting up RX stream\n");
|
||||
if(SoapySDRDevice_setupStream(handler->device, &(handler->rxStream), SOAPY_SDR_RX, SOAPY_SDR_CF32, NULL, 0, NULL) != 0) {
|
||||
printf("Rx setupStream fail: %s\n", SoapySDRDevice_lastError());
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(SoapySDRDevice_getNumChannels(handler->device,SOAPY_SDR_TX) > 0){
|
||||
printf("setting up TX stream\n");
|
||||
if (SoapySDRDevice_setupStream(handler->device, &(handler->txStream), SOAPY_SDR_TX, SOAPY_SDR_CF32, NULL, 0, NULL) != 0) {
|
||||
printf("Tx setupStream fail: %s\n", SoapySDRDevice_lastError());
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -300,7 +322,7 @@ double rf_soapy_set_tx_gain(void *h, double gain)
|
|||
printf("setGain fail: %s\n", SoapySDRDevice_lastError());
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
return rf_soapy_get_rx_gain(h);
|
||||
return rf_soapy_get_tx_gain(h);
|
||||
}
|
||||
|
||||
|
||||
|
@ -338,7 +360,7 @@ double rf_soapy_set_tx_freq(void *h, double freq)
|
|||
printf("setFrequency fail: %s\n", SoapySDRDevice_lastError());
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
return SoapySDRDevice_getFrequency(handler->device, SOAPY_SDR_RX, 0);
|
||||
return SoapySDRDevice_getFrequency(handler->device, SOAPY_SDR_TX, 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -355,10 +377,7 @@ int rf_soapy_recv_with_time_multi(void *h,
|
|||
double *frac_secs)
|
||||
{
|
||||
rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h;
|
||||
//void *buffs[] = {buff}; //array of buffers
|
||||
|
||||
int flags; //flags set by receive operation
|
||||
|
||||
int num_channels = 1; // temp
|
||||
|
||||
int trials = 0;
|
||||
|
@ -368,16 +387,14 @@ int rf_soapy_recv_with_time_multi(void *h,
|
|||
do {
|
||||
size_t rx_samples = nsamples;
|
||||
|
||||
if (rx_samples > nsamples - n)
|
||||
{
|
||||
if (rx_samples > nsamples - n){
|
||||
rx_samples = nsamples - n;
|
||||
}
|
||||
void *buffs_ptr[4];
|
||||
for (int i=0; i<num_channels; i++)
|
||||
{
|
||||
for (int i=0; i<num_channels; i++){
|
||||
cf_t *data_c = (cf_t*) data[i];
|
||||
buffs_ptr[i] = &data_c[n];
|
||||
} //(void*)(&data)
|
||||
}
|
||||
ret = SoapySDRDevice_readStream(handler->device, handler->rxStream, buffs_ptr , rx_samples, &flags, &timeNs, 1000000);
|
||||
if(ret < 0) {
|
||||
// continue when getting overflows
|
||||
|
@ -413,23 +430,49 @@ int rf_soapy_recv_with_time(void *h,
|
|||
|
||||
|
||||
int rf_soapy_send_timed(void *h,
|
||||
void *data,
|
||||
int nsamples,
|
||||
time_t secs,
|
||||
double frac_secs,
|
||||
bool has_time_spec,
|
||||
bool blocking,
|
||||
bool is_start_of_burst,
|
||||
bool is_end_of_burst)
|
||||
void *data,
|
||||
int nsamples,
|
||||
time_t secs,
|
||||
double frac_secs,
|
||||
bool has_time_spec,
|
||||
bool blocking,
|
||||
bool is_start_of_burst,
|
||||
bool is_end_of_burst)
|
||||
{
|
||||
int flags;
|
||||
long long timeNs;
|
||||
rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h;
|
||||
timeNs = secs * 1000000000;
|
||||
timeNs = timeNs + (frac_secs * 1000000000);
|
||||
int ret = SoapySDRDevice_writeStream(handler->device, handler->txStream, data, nsamples, &flags, timeNs, 100000);
|
||||
if(ret != nsamples)
|
||||
return SRSLTE_ERROR;
|
||||
|
||||
return ret;
|
||||
|
||||
int flags;
|
||||
long long timeNs;
|
||||
int trials = 0;
|
||||
int ret = 0;
|
||||
rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h;
|
||||
timeNs = secs * 1000000000;
|
||||
timeNs = timeNs + (frac_secs * 1000000000);
|
||||
int n = 0;
|
||||
|
||||
if(!handler->tx_stream_active){
|
||||
rf_soapy_start_tx_stream(h);
|
||||
}
|
||||
|
||||
|
||||
cf_t *data_c = (cf_t*) data;
|
||||
do{
|
||||
size_t tx_samples = nsamples;
|
||||
if (tx_samples > nsamples - n) {
|
||||
tx_samples = nsamples - n;
|
||||
}
|
||||
void *buff = (void*) &data_c[n];
|
||||
const void *buffs_ptr[1] = {buff};
|
||||
ret = SoapySDRDevice_writeStream(handler->device, handler->txStream, buffs_ptr, tx_samples, &flags, timeNs, 10000);
|
||||
if(ret < 0)
|
||||
return SRSLTE_ERROR;
|
||||
|
||||
n += ret;
|
||||
trials++;
|
||||
}while (n < nsamples && trials < 100);
|
||||
|
||||
if(ret != nsamples)
|
||||
return SRSLTE_ERROR;
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
|
|
@ -328,7 +328,7 @@ int rf_uhd_open_multi(char *args, void **h, uint32_t nof_rx_antennas)
|
|||
if (args[0]=='\0') {
|
||||
if (find_string(devices_str, "type=b200") && !strstr(args, "recv_frame_size")) {
|
||||
// If B200 is available, use it
|
||||
args = "type=b200";
|
||||
args = "type=b200,master_clock_rate=30.72e6";
|
||||
handler->devname = DEVNAME_B200;
|
||||
} else if (find_string(devices_str, "type=x300")) {
|
||||
// Else if X300 is available, set master clock rate now (can't be changed later)
|
||||
|
@ -344,6 +344,8 @@ int rf_uhd_open_multi(char *args, void **h, uint32_t nof_rx_antennas)
|
|||
handler->dynamic_rate = false;
|
||||
handler->devname = DEVNAME_X300;
|
||||
} else if (strstr(args, "type=b200")) {
|
||||
snprintf(args2, sizeof(args2), "%s,master_clock_rate=30.72e6", args);
|
||||
args = args2;
|
||||
handler->devname = DEVNAME_B200;
|
||||
}
|
||||
}
|
||||
|
@ -399,7 +401,11 @@ int rf_uhd_open_multi(char *args, void **h, uint32_t nof_rx_antennas)
|
|||
};
|
||||
|
||||
handler->nof_rx_channels = nof_rx_antennas;
|
||||
handler->nof_tx_channels = 1;
|
||||
handler->nof_tx_channels = 1;
|
||||
|
||||
/* Set default rate to avoid decimation warnings */
|
||||
uhd_usrp_set_rx_rate(handler->usrp, 1.92e6, 0);
|
||||
uhd_usrp_set_tx_rate(handler->usrp, 1.92e6, 0);
|
||||
|
||||
/* Initialize rx and tx stremers */
|
||||
uhd_rx_streamer_make(&handler->rx_stream);
|
||||
|
|
|
@ -101,7 +101,7 @@ int srslte_ue_cellsearch_init_multi(srslte_ue_cellsearch_t * q, uint32_t max_fra
|
|||
{
|
||||
int ret = SRSLTE_ERROR_INVALID_INPUTS;
|
||||
|
||||
if (q != NULL) {
|
||||
if (q != NULL && nof_rx_antennas > 0) {
|
||||
ret = SRSLTE_ERROR;
|
||||
srslte_cell_t cell;
|
||||
|
||||
|
|
|
@ -681,6 +681,15 @@ float srslte_vec_avg_power_cf(cf_t *x, uint32_t len) {
|
|||
return crealf(srslte_vec_dot_prod_conj_ccc(x,x,len)) / len;
|
||||
}
|
||||
|
||||
// Correlation assumes zero-mean x and y
|
||||
float srslte_vec_corr_ccc(cf_t *x, cf_t *y, uint32_t len) {
|
||||
// return crealf(srslte_vec_dot_prod_conj_ccc(x,y,len)) / len;
|
||||
float s_x = crealf(srslte_vec_dot_prod_conj_ccc(x, x, len))/len;
|
||||
float s_y = crealf(srslte_vec_dot_prod_conj_ccc(y, y, len))/len;
|
||||
float cov = crealf(srslte_vec_dot_prod_conj_ccc(x, y, len))/len;
|
||||
return cov/(sqrt(s_x*s_y));
|
||||
}
|
||||
|
||||
void srslte_vec_abs_cf(cf_t *x, float *abs, uint32_t len) {
|
||||
#ifndef HAVE_VOLK_MAG_FUNCTION
|
||||
int i;
|
||||
|
|
|
@ -333,7 +333,7 @@ void radio::set_tx_srate(float srate)
|
|||
} else if (srate_khz == 15.36e3) {
|
||||
nsamples = 131;
|
||||
} else if (srate_khz == 23.04e3) {
|
||||
nsamples = 175;
|
||||
nsamples = 150;
|
||||
} else {
|
||||
/* Interpolate from known values */
|
||||
printf("\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n", cur_tx_srate);
|
||||
|
@ -353,7 +353,7 @@ void radio::set_tx_srate(float srate)
|
|||
} else if (srate_khz == 15.36e3) {
|
||||
nsamples = 86;
|
||||
} else if (srate_khz == 23.04e3) {
|
||||
nsamples = 119;
|
||||
nsamples = 110;
|
||||
} else {
|
||||
/* Interpolate from known values */
|
||||
printf("\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n", cur_tx_srate);
|
||||
|
|
|
@ -44,12 +44,13 @@ gw::gw()
|
|||
:if_up(false)
|
||||
{}
|
||||
|
||||
void gw::init(srsue::pdcp_interface_gw *pdcp_, srsue::ue_interface *ue_, log *gw_log_)
|
||||
void gw::init(srsue::pdcp_interface_gw *pdcp_, srsue::ue_interface *ue_, log *gw_log_, uint32_t lcid_)
|
||||
{
|
||||
pool = byte_buffer_pool::get_instance();
|
||||
pdcp = pdcp_;
|
||||
ue = ue_;
|
||||
gw_log = gw_log_;
|
||||
lcid = lcid_;
|
||||
run_enable = true;
|
||||
|
||||
gettimeofday(&metrics_time[1], NULL);
|
||||
|
@ -255,7 +256,7 @@ void gw::run_thread()
|
|||
// Send PDU directly to PDCP
|
||||
pdu->set_timestamp();
|
||||
ul_tput_bytes += pdu->N_bytes;
|
||||
pdcp->write_sdu(RB_ID_DRB1, pdu);
|
||||
pdcp->write_sdu(lcid, pdu);
|
||||
|
||||
do {
|
||||
pdu = pool_allocate;
|
||||
|
|
|
@ -32,15 +32,22 @@ namespace srslte {
|
|||
pdcp::pdcp()
|
||||
{}
|
||||
|
||||
void pdcp::init(srsue::rlc_interface_pdcp *rlc_, srsue::rrc_interface_pdcp *rrc_, srsue::gw_interface_pdcp *gw_, log *pdcp_log_, uint8_t direction_)
|
||||
void pdcp::init(srsue::rlc_interface_pdcp *rlc_, srsue::rrc_interface_pdcp *rrc_, srsue::gw_interface_pdcp *gw_, log *pdcp_log_, uint32_t lcid_, uint8_t direction_)
|
||||
{
|
||||
rlc = rlc_;
|
||||
rrc = rrc_;
|
||||
gw = gw_;
|
||||
pdcp_log = pdcp_log_;
|
||||
lcid = lcid_;
|
||||
direction = direction_;
|
||||
|
||||
pdcp_array[0].init(rlc, rrc, gw, pdcp_log, RB_ID_SRB0, direction); // SRB0
|
||||
// Default config
|
||||
srslte_pdcp_config_t cnfg;
|
||||
cnfg.is_control = false;
|
||||
cnfg.is_data = false;
|
||||
cnfg.direction = direction_;
|
||||
|
||||
pdcp_array[0].init(rlc, rrc, gw, pdcp_log, lcid, cnfg);
|
||||
}
|
||||
|
||||
void pdcp::stop()
|
||||
|
@ -52,7 +59,7 @@ void pdcp::reset()
|
|||
pdcp_array[i].reset();
|
||||
}
|
||||
|
||||
pdcp_array[0].init(rlc, rrc, gw, pdcp_log, RB_ID_SRB0, direction); // SRB0
|
||||
pdcp_array[0].init(rlc, rrc, gw, pdcp_log, lcid, direction);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
|
@ -64,17 +71,17 @@ void pdcp::write_sdu(uint32_t lcid, byte_buffer_t *sdu)
|
|||
pdcp_array[lcid].write_sdu(sdu);
|
||||
}
|
||||
|
||||
void pdcp::add_bearer(uint32_t lcid, LIBLTE_RRC_PDCP_CONFIG_STRUCT *cnfg)
|
||||
void pdcp::add_bearer(uint32_t lcid, srslte_pdcp_config_t cfg)
|
||||
{
|
||||
if(lcid < 0 || lcid >= SRSLTE_N_RADIO_BEARERS) {
|
||||
if(lcid >= SRSLTE_N_RADIO_BEARERS) {
|
||||
pdcp_log->error("Radio bearer id must be in [0:%d] - %d\n", SRSLTE_N_RADIO_BEARERS, lcid);
|
||||
return;
|
||||
}
|
||||
if (!pdcp_array[lcid].is_active()) {
|
||||
pdcp_array[lcid].init(rlc, rrc, gw, pdcp_log, lcid, direction, cnfg);
|
||||
pdcp_log->info("Added bearer %s\n", rb_id_text[lcid]);
|
||||
pdcp_array[lcid].init(rlc, rrc, gw, pdcp_log, lcid, cfg);
|
||||
pdcp_log->info("Added bearer %s\n", rrc->get_rb_name(lcid).c_str());
|
||||
} else {
|
||||
pdcp_log->warning("Bearer %s already configured. Reconfiguration not supported\n", rb_id_text[lcid]);
|
||||
pdcp_log->warning("Bearer %s already configured. Reconfiguration not supported\n", rrc->get_rb_name(lcid).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,7 +123,7 @@ void pdcp::write_pdu_pcch(byte_buffer_t *sdu)
|
|||
*******************************************************************************/
|
||||
bool pdcp::valid_lcid(uint32_t lcid)
|
||||
{
|
||||
if(lcid < 0 || lcid >= SRSLTE_N_RADIO_BEARERS) {
|
||||
if(lcid >= SRSLTE_N_RADIO_BEARERS) {
|
||||
pdcp_log->error("Radio bearer id must be in [0:%d] - %d", SRSLTE_N_RADIO_BEARERS, lcid);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -34,8 +34,6 @@ pdcp_entity::pdcp_entity()
|
|||
:active(false)
|
||||
,tx_count(0)
|
||||
,rx_count(0)
|
||||
,do_security(false)
|
||||
,sn_len(12)
|
||||
{
|
||||
pool = byte_buffer_pool::get_instance();
|
||||
}
|
||||
|
@ -45,38 +43,27 @@ void pdcp_entity::init(srsue::rlc_interface_pdcp *rlc_,
|
|||
srsue::gw_interface_pdcp *gw_,
|
||||
srslte::log *log_,
|
||||
uint32_t lcid_,
|
||||
u_int8_t direction_,
|
||||
LIBLTE_RRC_PDCP_CONFIG_STRUCT *cnfg)
|
||||
srslte_pdcp_config_t cfg_)
|
||||
{
|
||||
rlc = rlc_;
|
||||
rrc = rrc_;
|
||||
gw = gw_;
|
||||
log = log_;
|
||||
lcid = lcid_;
|
||||
direction = direction_;
|
||||
cfg = cfg_;
|
||||
active = true;
|
||||
|
||||
tx_count = 0;
|
||||
rx_count = 0;
|
||||
do_security = false;
|
||||
|
||||
if(cnfg)
|
||||
{
|
||||
if(cnfg->rlc_um_pdcp_sn_size_present) {
|
||||
if(LIBLTE_RRC_PDCP_SN_SIZE_7_BITS == cnfg->rlc_um_pdcp_sn_size) {
|
||||
sn_len = 7;
|
||||
}
|
||||
}
|
||||
// TODO: handle remainder of cnfg
|
||||
}
|
||||
log->debug("Init %s\n", rb_id_text[lcid]);
|
||||
log->debug("Init %s\n", rrc->get_rb_name(lcid).c_str());
|
||||
}
|
||||
|
||||
void pdcp_entity::reset()
|
||||
{
|
||||
active = false;
|
||||
if(log)
|
||||
log->debug("Reset %s\n", rb_id_text[lcid]);
|
||||
log->debug("Reset %s\n", rrc->get_rb_name(lcid).c_str());
|
||||
}
|
||||
|
||||
bool pdcp_entity::is_active()
|
||||
|
@ -87,44 +74,32 @@ bool pdcp_entity::is_active()
|
|||
// RRC interface
|
||||
void pdcp_entity::write_sdu(byte_buffer_t *sdu)
|
||||
{
|
||||
log->info_hex(sdu->msg, sdu->N_bytes, "TX %s SDU, do_security = %s", rb_id_text[lcid], (do_security)?"true":"false");
|
||||
log->info_hex(sdu->msg, sdu->N_bytes, "TX %s SDU, do_security = %s", rrc->get_rb_name(lcid).c_str(), (cfg.do_security)?"true":"false");
|
||||
|
||||
// Handle SRB messages
|
||||
switch(lcid)
|
||||
{
|
||||
case RB_ID_SRB0:
|
||||
rlc->write_sdu(lcid, sdu);
|
||||
break;
|
||||
case RB_ID_SRB1: // Intentional fall-through
|
||||
case RB_ID_SRB2:
|
||||
if (cfg.is_control) {
|
||||
pdcp_pack_control_pdu(tx_count, sdu);
|
||||
if(do_security)
|
||||
if(cfg.do_security)
|
||||
{
|
||||
integrity_generate(&k_rrc_int[16],
|
||||
tx_count,
|
||||
lcid-1,
|
||||
direction,
|
||||
cfg.direction,
|
||||
sdu->msg,
|
||||
sdu->N_bytes-4,
|
||||
&sdu->msg[sdu->N_bytes-4]);
|
||||
}
|
||||
tx_count++;
|
||||
rlc->write_sdu(lcid, sdu);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle DRB messages
|
||||
if(lcid >= RB_ID_DRB1)
|
||||
{
|
||||
if(12 == sn_len)
|
||||
{
|
||||
if (cfg.is_data) {
|
||||
if(12 == cfg.sn_len) {
|
||||
pdcp_pack_data_pdu_long_sn(tx_count++, sdu);
|
||||
} else {
|
||||
pdcp_pack_data_pdu_short_sn(tx_count++, sdu);
|
||||
}
|
||||
rlc->write_sdu(lcid, sdu);
|
||||
}
|
||||
|
||||
rlc->write_sdu(lcid, sdu);
|
||||
}
|
||||
|
||||
void pdcp_entity::config_security(uint8_t *k_rrc_enc_,
|
||||
|
@ -132,7 +107,7 @@ void pdcp_entity::config_security(uint8_t *k_rrc_enc_,
|
|||
CIPHERING_ALGORITHM_ID_ENUM cipher_algo_,
|
||||
INTEGRITY_ALGORITHM_ID_ENUM integ_algo_)
|
||||
{
|
||||
do_security = true;
|
||||
cfg.do_security = true;
|
||||
for(int i=0; i<32; i++)
|
||||
{
|
||||
k_rrc_enc[i] = k_rrc_enc_[i];
|
||||
|
@ -145,37 +120,32 @@ void pdcp_entity::config_security(uint8_t *k_rrc_enc_,
|
|||
// RLC interface
|
||||
void pdcp_entity::write_pdu(byte_buffer_t *pdu)
|
||||
{
|
||||
// Handle SRB messages
|
||||
switch(lcid)
|
||||
{
|
||||
case RB_ID_SRB0:
|
||||
// Simply pass on to RRC
|
||||
log->info_hex(pdu->msg, pdu->N_bytes, "RX %s PDU", rb_id_text[lcid]);
|
||||
rrc->write_pdu(RB_ID_SRB0, pdu);
|
||||
break;
|
||||
case RB_ID_SRB1: // Intentional fall-through
|
||||
case RB_ID_SRB2:
|
||||
uint32_t sn;
|
||||
log->info_hex(pdu->msg, pdu->N_bytes, "RX %s PDU", rb_id_text[lcid]);
|
||||
pdcp_unpack_control_pdu(pdu, &sn);
|
||||
log->info_hex(pdu->msg, pdu->N_bytes, "RX %s SDU SN: %d",
|
||||
rb_id_text[lcid], sn);
|
||||
rrc->write_pdu(lcid, pdu);
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle DRB messages
|
||||
if(lcid >= RB_ID_DRB1)
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
if (cfg.is_data) {
|
||||
uint32_t sn;
|
||||
if(12 == sn_len)
|
||||
if(12 == cfg.sn_len)
|
||||
{
|
||||
pdcp_unpack_data_pdu_long_sn(pdu, &sn);
|
||||
} else {
|
||||
pdcp_unpack_data_pdu_short_sn(pdu, &sn);
|
||||
}
|
||||
log->info_hex(pdu->msg, pdu->N_bytes, "RX %s PDU: %d", rb_id_text[lcid], sn);
|
||||
log->info_hex(pdu->msg, pdu->N_bytes, "RX %s PDU: %d", rrc->get_rb_name(lcid).c_str(), sn);
|
||||
gw->write_pdu(lcid, pdu);
|
||||
} else {
|
||||
if (cfg.is_control) {
|
||||
uint32_t sn;
|
||||
pdcp_unpack_control_pdu(pdu, &sn);
|
||||
log->info_hex(pdu->msg, pdu->N_bytes, "RX %s SDU SN: %d",
|
||||
rrc->get_rb_name(lcid).c_str(), sn);
|
||||
} else {
|
||||
log->info_hex(pdu->msg, pdu->N_bytes, "RX %s PDU", rrc->get_rb_name(lcid).c_str());
|
||||
}
|
||||
// pass to RRC
|
||||
rrc->write_pdu(lcid, pdu);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,18 +41,20 @@ void rlc::init(srsue::pdcp_interface_rlc *pdcp_,
|
|||
srsue::rrc_interface_rlc *rrc_,
|
||||
srsue::ue_interface *ue_,
|
||||
log *rlc_log_,
|
||||
mac_interface_timers *mac_timers_)
|
||||
mac_interface_timers *mac_timers_,
|
||||
uint32_t lcid_)
|
||||
{
|
||||
pdcp = pdcp_;
|
||||
rrc = rrc_;
|
||||
ue = ue_;
|
||||
rlc_log = rlc_log_;
|
||||
mac_timers = mac_timers_;
|
||||
default_lcid = lcid_;
|
||||
|
||||
gettimeofday(&metrics_time[1], NULL);
|
||||
reset_metrics();
|
||||
|
||||
rlc_array[0].init(RLC_MODE_TM, rlc_log, RB_ID_SRB0, pdcp, rrc, mac_timers); // SRB0
|
||||
rlc_array[0].init(RLC_MODE_TM, rlc_log, default_lcid, pdcp, rrc, mac_timers); // SRB0
|
||||
}
|
||||
|
||||
void rlc::reset_metrics()
|
||||
|
@ -97,7 +99,15 @@ void rlc::reset()
|
|||
rlc_array[i].reset();
|
||||
}
|
||||
|
||||
rlc_array[0].init(RLC_MODE_TM, rlc_log, RB_ID_SRB0, pdcp, rrc, mac_timers); // SRB0
|
||||
rlc_array[0].init(RLC_MODE_TM, rlc_log, default_lcid, pdcp, rrc, mac_timers); // SRB0
|
||||
}
|
||||
|
||||
void rlc::empty_queue()
|
||||
{
|
||||
for(uint32_t i=0; i<SRSLTE_N_RADIO_BEARERS; i++) {
|
||||
if(rlc_array[i].active())
|
||||
rlc_array[i].empty_queue();
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
|
@ -110,6 +120,11 @@ void rlc::write_sdu(uint32_t lcid, byte_buffer_t *sdu)
|
|||
}
|
||||
}
|
||||
|
||||
std::string rlc::get_rb_name(uint32_t lcid)
|
||||
{
|
||||
return rrc->get_rb_name(lcid);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
MAC interface
|
||||
*******************************************************************************/
|
||||
|
@ -186,11 +201,10 @@ void rlc::write_pdu_pcch(uint8_t *payload, uint32_t nof_bytes)
|
|||
*******************************************************************************/
|
||||
void rlc::add_bearer(uint32_t lcid)
|
||||
{
|
||||
// No config provided - use defaults for lcid
|
||||
LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg;
|
||||
if(RB_ID_SRB1 == lcid || RB_ID_SRB2 == lcid)
|
||||
{
|
||||
// No config provided - use defaults for SRB1 and SRB2
|
||||
if(lcid < 3) {
|
||||
if (!rlc_array[lcid].active()) {
|
||||
LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg;
|
||||
cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM;
|
||||
cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS45;
|
||||
cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_INFINITY;
|
||||
|
@ -198,28 +212,27 @@ void rlc::add_bearer(uint32_t lcid)
|
|||
cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4;
|
||||
cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS35;
|
||||
cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS0;
|
||||
add_bearer(lcid, &cnfg);
|
||||
add_bearer(lcid, srslte_rlc_config_t(&cnfg));
|
||||
} else {
|
||||
rlc_log->warning("Bearer %s already configured. Reconfiguration not supported\n", rb_id_text[lcid]);
|
||||
rlc_log->warning("Bearer %s already configured. Reconfiguration not supported\n", get_rb_name(lcid).c_str());
|
||||
}
|
||||
}else{
|
||||
rlc_log->error("Radio bearer %s does not support default RLC configuration.",
|
||||
rb_id_text[lcid]);
|
||||
rlc_log->error("Radio bearer %s does not support default RLC configuration.\n",
|
||||
get_rb_name(lcid).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void rlc::add_bearer(uint32_t lcid, LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg)
|
||||
void rlc::add_bearer(uint32_t lcid, srslte_rlc_config_t cnfg)
|
||||
{
|
||||
if(lcid < 0 || lcid >= SRSLTE_N_RADIO_BEARERS) {
|
||||
rlc_log->error("Radio bearer id must be in [0:%d] - %d\n", SRSLTE_N_RADIO_BEARERS, lcid);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!rlc_array[lcid].active()) {
|
||||
rlc_log->info("Adding radio bearer %s with mode %s\n",
|
||||
rb_id_text[lcid], liblte_rrc_rlc_mode_text[cnfg->rlc_mode]);
|
||||
switch(cnfg->rlc_mode)
|
||||
get_rb_name(lcid).c_str(), liblte_rrc_rlc_mode_text[cnfg.rlc_mode]);
|
||||
switch(cnfg.rlc_mode)
|
||||
{
|
||||
case LIBLTE_RRC_RLC_MODE_AM:
|
||||
rlc_array[lcid].init(RLC_MODE_AM, rlc_log, lcid, pdcp, rrc, mac_timers);
|
||||
|
@ -238,7 +251,7 @@ void rlc::add_bearer(uint32_t lcid, LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg)
|
|||
return;
|
||||
}
|
||||
} else {
|
||||
rlc_log->warning("Bearer %s already created.\n", rb_id_text[lcid]);
|
||||
rlc_log->warning("Bearer %s already created.\n", get_rb_name(lcid).c_str());
|
||||
}
|
||||
rlc_array[lcid].configure(cnfg);
|
||||
|
||||
|
|
|
@ -74,20 +74,13 @@ void rlc_am::init(srslte::log *log_,
|
|||
rrc = rrc_;
|
||||
}
|
||||
|
||||
void rlc_am::configure(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg)
|
||||
void rlc_am::configure(srslte_rlc_config_t cfg_)
|
||||
{
|
||||
t_poll_retx = liblte_rrc_t_poll_retransmit_num[cnfg->ul_am_rlc.t_poll_retx];
|
||||
poll_pdu = liblte_rrc_poll_pdu_num[cnfg->ul_am_rlc.poll_pdu];
|
||||
poll_byte = liblte_rrc_poll_byte_num[cnfg->ul_am_rlc.poll_byte]*1000; // KB
|
||||
max_retx_thresh = liblte_rrc_max_retx_threshold_num[cnfg->ul_am_rlc.max_retx_thresh];
|
||||
|
||||
t_reordering = liblte_rrc_t_reordering_num[cnfg->dl_am_rlc.t_reordering];
|
||||
t_status_prohibit = liblte_rrc_t_status_prohibit_num[cnfg->dl_am_rlc.t_status_prohibit];
|
||||
|
||||
cfg = cfg_.am;
|
||||
log->info("%s configured: t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, max_retx_thresh=%d, "
|
||||
"t_reordering=%d, t_status_prohibit=%d\n",
|
||||
rb_id_text[lcid], t_poll_retx, poll_pdu, poll_byte, max_retx_thresh,
|
||||
t_reordering, t_status_prohibit);
|
||||
rrc->get_rb_name(lcid).c_str(), cfg.t_poll_retx, cfg.poll_pdu, cfg.poll_byte, cfg.max_retx_thresh,
|
||||
cfg.t_reordering, cfg.t_status_prohibit);
|
||||
}
|
||||
|
||||
|
||||
|
@ -176,7 +169,7 @@ uint32_t rlc_am::get_bearer()
|
|||
|
||||
void rlc_am::write_sdu(byte_buffer_t *sdu)
|
||||
{
|
||||
log->info_hex(sdu->msg, sdu->N_bytes, "%s Tx SDU", rb_id_text[lcid]);
|
||||
log->info_hex(sdu->msg, sdu->N_bytes, "%s Tx SDU", rrc->get_rb_name(lcid).c_str());
|
||||
tx_sdu_queue.write(sdu);
|
||||
}
|
||||
|
||||
|
@ -208,12 +201,14 @@ uint32_t rlc_am::get_total_buffer_state()
|
|||
}
|
||||
|
||||
// Bytes needed for tx SDUs
|
||||
n_sdus = tx_sdu_queue.size();
|
||||
n_bytes += tx_sdu_queue.size_bytes();
|
||||
if(tx_sdu)
|
||||
{
|
||||
n_sdus++;
|
||||
n_bytes += tx_sdu->N_bytes;
|
||||
if(tx_window.size() < 1024) {
|
||||
n_sdus = tx_sdu_queue.size();
|
||||
n_bytes += tx_sdu_queue.size_bytes();
|
||||
if(tx_sdu)
|
||||
{
|
||||
n_sdus++;
|
||||
n_bytes += tx_sdu->N_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
// Room needed for header extensions? (integer rounding)
|
||||
|
@ -256,7 +251,7 @@ uint32_t rlc_am::get_buffer_state()
|
|||
}
|
||||
|
||||
// Bytes needed for tx SDUs
|
||||
if(tx_window.size() < RLC_AM_WINDOW_SIZE) {
|
||||
if(tx_window.size() < 1024) {
|
||||
n_sdus = tx_sdu_queue.size();
|
||||
n_bytes = tx_sdu_queue.size_bytes();
|
||||
if(tx_sdu)
|
||||
|
@ -286,6 +281,7 @@ int rlc_am::read_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
pthread_mutex_lock(&mutex);
|
||||
|
||||
log->debug("MAC opportunity - %d bytes\n", nof_bytes);
|
||||
log->debug("tx_window size - %d PDUs\n", tx_window.size());
|
||||
|
||||
// Tx STATUS if requested
|
||||
if(do_status && !status_prohibited()) {
|
||||
|
@ -298,10 +294,11 @@ int rlc_am::read_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
return build_retx_pdu(payload, nof_bytes);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
// Build a PDU from SDUs
|
||||
return build_data_pdu(payload, nof_bytes);
|
||||
int ret = build_data_pdu(payload, nof_bytes);
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void rlc_am::write_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||
|
@ -343,7 +340,7 @@ void rlc_am::check_reordering_timeout()
|
|||
if(reordering_timeout.is_running() && reordering_timeout.expired())
|
||||
{
|
||||
reordering_timeout.reset();
|
||||
log->debug("%s reordering timeout expiry - updating vr_ms\n", rb_id_text[lcid]);
|
||||
log->debug("%s reordering timeout expiry - updating vr_ms\n", rrc->get_rb_name(lcid).c_str());
|
||||
|
||||
// 36.322 v10 Section 5.1.3.2.4
|
||||
vr_ms = vr_x;
|
||||
|
@ -358,7 +355,7 @@ void rlc_am::check_reordering_timeout()
|
|||
|
||||
if(RX_MOD_BASE(vr_h) > RX_MOD_BASE(vr_ms))
|
||||
{
|
||||
reordering_timeout.start(t_reordering);
|
||||
reordering_timeout.start(cfg.t_reordering);
|
||||
vr_x = vr_h;
|
||||
}
|
||||
|
||||
|
@ -372,9 +369,9 @@ void rlc_am::check_reordering_timeout()
|
|||
|
||||
bool rlc_am::poll_required()
|
||||
{
|
||||
if(poll_pdu > 0 && pdu_without_poll > (uint32_t)poll_pdu)
|
||||
if(cfg.poll_pdu > 0 && pdu_without_poll > (uint32_t)cfg.poll_pdu)
|
||||
return true;
|
||||
if(poll_byte > 0 && byte_without_poll > (uint32_t)poll_byte)
|
||||
if(cfg.poll_byte > 0 && byte_without_poll > (uint32_t)cfg.poll_byte)
|
||||
return true;
|
||||
if(poll_retx())
|
||||
return true;
|
||||
|
@ -405,18 +402,18 @@ int rlc_am::build_status_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
if(pdu_len > 0 && nof_bytes >= (uint32_t)pdu_len)
|
||||
{
|
||||
log->info("%s Tx status PDU - %s\n",
|
||||
rb_id_text[lcid], rlc_am_to_string(&status).c_str());
|
||||
rrc->get_rb_name(lcid).c_str(), rlc_am_to_string(&status).c_str());
|
||||
|
||||
do_status = false;
|
||||
poll_received = false;
|
||||
|
||||
if(t_status_prohibit > 0)
|
||||
status_prohibit_timeout.start(t_status_prohibit);
|
||||
if(cfg.t_status_prohibit > 0)
|
||||
status_prohibit_timeout.start(cfg.t_status_prohibit);
|
||||
debug_state();
|
||||
return rlc_am_write_status_pdu(&status, payload);
|
||||
}else{
|
||||
log->warning("%s Cannot tx status PDU - %d bytes available, %d bytes required\n",
|
||||
rb_id_text[lcid], nof_bytes, pdu_len);
|
||||
rrc->get_rb_name(lcid).c_str(), nof_bytes, pdu_len);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -433,7 +430,7 @@ int rlc_am::build_retx_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
|
||||
// Is resegmentation needed?
|
||||
if(retx.is_segment || required_buffer_size(retx) > (int)nof_bytes) {
|
||||
log->debug("%s build_retx_pdu - resegmentation required\n", rb_id_text[lcid]);
|
||||
log->debug("%s build_retx_pdu - resegmentation required\n", rrc->get_rb_name(lcid).c_str());
|
||||
return build_segment(payload, nof_bytes, retx);
|
||||
}
|
||||
|
||||
|
@ -446,7 +443,7 @@ int rlc_am::build_retx_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
poll_sn = vt_s;
|
||||
pdu_without_poll = 0;
|
||||
byte_without_poll = 0;
|
||||
poll_retx_timeout.start(t_poll_retx);
|
||||
poll_retx_timeout.start(cfg.t_poll_retx);
|
||||
}
|
||||
|
||||
uint8_t *ptr = payload;
|
||||
|
@ -455,10 +452,10 @@ int rlc_am::build_retx_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
|
||||
retx_queue.pop_front();
|
||||
tx_window[retx.sn].retx_count++;
|
||||
if(tx_window[retx.sn].retx_count >= max_retx_thresh)
|
||||
if(tx_window[retx.sn].retx_count >= cfg.max_retx_thresh)
|
||||
rrc->max_retx_attempted();
|
||||
log->info("%s Retx PDU scheduled for tx. SN: %d, retx count: %d\n",
|
||||
rb_id_text[lcid], retx.sn, tx_window[retx.sn].retx_count);
|
||||
rrc->get_rb_name(lcid).c_str(), retx.sn, tx_window[retx.sn].retx_count);
|
||||
|
||||
debug_state();
|
||||
return (ptr-payload) + tx_window[retx.sn].buf->N_bytes;
|
||||
|
@ -491,7 +488,7 @@ int rlc_am::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_amd_retx_t r
|
|||
if(nof_bytes <= head_len)
|
||||
{
|
||||
log->warning("%s Cannot build a PDU segment - %d bytes available, %d bytes required for header\n",
|
||||
rb_id_text[lcid], nof_bytes, head_len);
|
||||
rrc->get_rb_name(lcid).c_str(), nof_bytes, head_len);
|
||||
return 0;
|
||||
}
|
||||
pdu_space = nof_bytes-head_len;
|
||||
|
@ -557,15 +554,15 @@ int rlc_am::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_amd_retx_t r
|
|||
memcpy(ptr, data, len);
|
||||
|
||||
log->info("%s Retx PDU segment scheduled for tx. SN: %d, SO: %d\n",
|
||||
rb_id_text[lcid], retx.sn, retx.so_start);
|
||||
rrc->get_rb_name(lcid).c_str(), retx.sn, retx.so_start);
|
||||
|
||||
debug_state();
|
||||
int pdu_len = (ptr-payload) + len;
|
||||
if(pdu_len > (int)nof_bytes) {
|
||||
log->error("%s Retx PDU segment length error. Available: %d, Used: %d\n",
|
||||
rb_id_text[lcid], nof_bytes, pdu_len);
|
||||
rrc->get_rb_name(lcid).c_str(), nof_bytes, pdu_len);
|
||||
log->debug("%s Retx PDU segment length error. Header len: %d, Payload len: %d, N_li: %d\n",
|
||||
rb_id_text[lcid], (ptr-payload), len, new_header.N_li);
|
||||
rrc->get_rb_name(lcid).c_str(), (ptr-payload), len, new_header.N_li);
|
||||
}
|
||||
return pdu_len;
|
||||
|
||||
|
@ -582,6 +579,16 @@ int rlc_am::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
byte_buffer_t *pdu = pool_allocate;
|
||||
if (!pdu) {
|
||||
log->console("Fatal Error: Could not allocate PDU in build_data_pdu()\n");
|
||||
log->console("tx_window size: %d PDUs\n", tx_window.size());
|
||||
log->console("vt_a = %d, vt_ms = %d, vt_s = %d, poll_sn = %d "
|
||||
"vr_r = %d, vr_mr = %d, vr_x = %d, vr_ms = %d, vr_h = %d\n",
|
||||
vt_a, vt_ms, vt_s, poll_sn,
|
||||
vr_r, vr_mr, vr_x, vr_ms, vr_h);
|
||||
log->console("retx_queue size: %d PDUs\n", retx_queue.size());
|
||||
std::map<uint32_t, rlc_amd_tx_pdu_t>::iterator txit;
|
||||
for(txit = tx_window.begin(); txit != tx_window.end(); txit++) {
|
||||
log->console("tx_window - SN: %d\n", txit->first);
|
||||
}
|
||||
exit(-1);
|
||||
}
|
||||
rlc_amd_pdu_header_t header;
|
||||
|
@ -603,13 +610,13 @@ int rlc_am::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
if(pdu_space <= head_len)
|
||||
{
|
||||
log->warning("%s Cannot build a PDU - %d bytes available, %d bytes required for header\n",
|
||||
rb_id_text[lcid], nof_bytes, head_len);
|
||||
rrc->get_rb_name(lcid).c_str(), nof_bytes, head_len);
|
||||
pool->deallocate(pdu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log->debug("%s Building PDU - pdu_space: %d, head_len: %d \n",
|
||||
rb_id_text[lcid], pdu_space, head_len);
|
||||
rrc->get_rb_name(lcid).c_str(), pdu_space, head_len);
|
||||
|
||||
// Check for SDU segment
|
||||
if(tx_sdu)
|
||||
|
@ -624,7 +631,7 @@ int rlc_am::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
if(tx_sdu->N_bytes == 0)
|
||||
{
|
||||
log->info("%s Complete SDU scheduled for tx. Stack latency: %ld us\n",
|
||||
rb_id_text[lcid], tx_sdu->get_latency_us());
|
||||
rrc->get_rb_name(lcid).c_str(), tx_sdu->get_latency_us());
|
||||
pool->deallocate(tx_sdu);
|
||||
tx_sdu = NULL;
|
||||
}
|
||||
|
@ -635,7 +642,7 @@ int rlc_am::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
header.fi |= RLC_FI_FIELD_NOT_START_ALIGNED; // First byte does not correspond to first byte of SDU
|
||||
|
||||
log->debug("%s Building PDU - added SDU segment (len:%d) - pdu_space: %d, head_len: %d \n",
|
||||
rb_id_text[lcid], to_move, pdu_space, head_len);
|
||||
rrc->get_rb_name(lcid).c_str(), to_move, pdu_space, head_len);
|
||||
}
|
||||
|
||||
// Pull SDUs from queue
|
||||
|
@ -659,7 +666,7 @@ int rlc_am::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
if(tx_sdu->N_bytes == 0)
|
||||
{
|
||||
log->info("%s Complete SDU scheduled for tx. Stack latency: %ld us\n",
|
||||
rb_id_text[lcid], tx_sdu->get_latency_us());
|
||||
rrc->get_rb_name(lcid).c_str(), tx_sdu->get_latency_us());
|
||||
pool->deallocate(tx_sdu);
|
||||
tx_sdu = NULL;
|
||||
}
|
||||
|
@ -669,7 +676,7 @@ int rlc_am::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
pdu_space = 0;
|
||||
|
||||
log->debug("%s Building PDU - added SDU segment (len:%d) - pdu_space: %d, head_len: %d \n",
|
||||
rb_id_text[lcid], to_move, pdu_space, head_len);
|
||||
rrc->get_rb_name(lcid).c_str(), to_move, pdu_space, head_len);
|
||||
}
|
||||
|
||||
if(tx_sdu)
|
||||
|
@ -678,22 +685,22 @@ int rlc_am::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
// Set Poll bit
|
||||
pdu_without_poll++;
|
||||
byte_without_poll += (pdu->N_bytes + head_len);
|
||||
log->debug("%s pdu_without_poll: %d\n", rb_id_text[lcid], pdu_without_poll);
|
||||
log->debug("%s byte_without_poll: %d\n", rb_id_text[lcid], byte_without_poll);
|
||||
log->debug("%s pdu_without_poll: %d\n", rrc->get_rb_name(lcid).c_str(), pdu_without_poll);
|
||||
log->debug("%s byte_without_poll: %d\n", rrc->get_rb_name(lcid).c_str(), byte_without_poll);
|
||||
if(poll_required())
|
||||
{
|
||||
log->debug("%s setting poll bit to request status\n", rb_id_text[lcid]);
|
||||
log->debug("%s setting poll bit to request status\n", rrc->get_rb_name(lcid).c_str());
|
||||
header.p = 1;
|
||||
poll_sn = vt_s;
|
||||
pdu_without_poll = 0;
|
||||
byte_without_poll = 0;
|
||||
poll_retx_timeout.start(t_poll_retx);
|
||||
poll_retx_timeout.start(cfg.t_poll_retx);
|
||||
}
|
||||
|
||||
// Set SN
|
||||
header.sn = vt_s;
|
||||
vt_s = (vt_s + 1)%MOD;
|
||||
log->info("%s PDU scheduled for tx. SN: %d\n", rb_id_text[lcid], header.sn);
|
||||
log->info("%s PDU scheduled for tx. SN: %d\n", rrc->get_rb_name(lcid).c_str(), header.sn);
|
||||
|
||||
// Place PDU in tx_window, write header and TX
|
||||
tx_window[header.sn].buf = pdu;
|
||||
|
@ -714,26 +721,26 @@ void rlc_am::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes, rlc_amd_pdu_h
|
|||
std::map<uint32_t, rlc_amd_rx_pdu_t>::iterator it;
|
||||
|
||||
log->info_hex(payload, nof_bytes, "%s Rx data PDU SN: %d",
|
||||
rb_id_text[lcid], header.sn);
|
||||
rrc->get_rb_name(lcid).c_str(), header.sn);
|
||||
|
||||
if(!inside_rx_window(header.sn)) {
|
||||
if(header.p) {
|
||||
log->info("%s Status packet requested through polling bit\n", rb_id_text[lcid]);
|
||||
log->info("%s Status packet requested through polling bit\n", rrc->get_rb_name(lcid).c_str());
|
||||
do_status = true;
|
||||
}
|
||||
log->info("%s SN: %d outside rx window [%d:%d] - discarding\n",
|
||||
rb_id_text[lcid], header.sn, vr_r, vr_mr);
|
||||
rrc->get_rb_name(lcid).c_str(), header.sn, vr_r, vr_mr);
|
||||
return;
|
||||
}
|
||||
|
||||
it = rx_window.find(header.sn);
|
||||
if(rx_window.end() != it) {
|
||||
if(header.p) {
|
||||
log->info("%s Status packet requested through polling bit\n", rb_id_text[lcid]);
|
||||
log->info("%s Status packet requested through polling bit\n", rrc->get_rb_name(lcid).c_str());
|
||||
do_status = true;
|
||||
}
|
||||
log->info("%s Discarding duplicate SN: %d\n",
|
||||
rb_id_text[lcid], header.sn);
|
||||
rrc->get_rb_name(lcid).c_str(), header.sn);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -766,7 +773,7 @@ void rlc_am::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes, rlc_amd_pdu_h
|
|||
// Check poll bit
|
||||
if(header.p)
|
||||
{
|
||||
log->info("%s Status packet requested through polling bit\n", rb_id_text[lcid]);
|
||||
log->info("%s Status packet requested through polling bit\n", rrc->get_rb_name(lcid).c_str());
|
||||
poll_received = true;
|
||||
|
||||
// 36.322 v10 Section 5.2.3
|
||||
|
@ -798,7 +805,7 @@ void rlc_am::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes, rlc_amd_pdu_h
|
|||
{
|
||||
if(RX_MOD_BASE(vr_h) > RX_MOD_BASE(vr_r))
|
||||
{
|
||||
reordering_timeout.start(t_reordering);
|
||||
reordering_timeout.start(cfg.t_reordering);
|
||||
vr_x = vr_h;
|
||||
}
|
||||
}
|
||||
|
@ -811,16 +818,16 @@ void rlc_am::handle_data_pdu_segment(uint8_t *payload, uint32_t nof_bytes, rlc_a
|
|||
std::map<uint32_t, rlc_amd_rx_pdu_segments_t>::iterator it;
|
||||
|
||||
log->info_hex(payload, nof_bytes, "%s Rx data PDU segment. SN: %d, SO: %d",
|
||||
rb_id_text[lcid], header.sn, header.so);
|
||||
rrc->get_rb_name(lcid).c_str(), header.sn, header.so);
|
||||
|
||||
// Check inside rx window
|
||||
if(!inside_rx_window(header.sn)) {
|
||||
if(header.p) {
|
||||
log->info("%s Status packet requested through polling bit\n", rb_id_text[lcid]);
|
||||
log->info("%s Status packet requested through polling bit\n", rrc->get_rb_name(lcid).c_str());
|
||||
do_status = true;
|
||||
}
|
||||
log->info("%s SN: %d outside rx window [%d:%d] - discarding\n",
|
||||
rb_id_text[lcid], header.sn, vr_r, vr_mr);
|
||||
rrc->get_rb_name(lcid).c_str(), header.sn, vr_r, vr_mr);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -839,7 +846,7 @@ void rlc_am::handle_data_pdu_segment(uint8_t *payload, uint32_t nof_bytes, rlc_a
|
|||
if(rx_segments.end() != it) {
|
||||
|
||||
if(header.p) {
|
||||
log->info("%s Status packet requested through polling bit\n", rb_id_text[lcid]);
|
||||
log->info("%s Status packet requested through polling bit\n", rrc->get_rb_name(lcid).c_str());
|
||||
do_status = true;
|
||||
}
|
||||
|
||||
|
@ -869,7 +876,7 @@ void rlc_am::handle_data_pdu_segment(uint8_t *payload, uint32_t nof_bytes, rlc_a
|
|||
// Check poll bit
|
||||
if(header.p)
|
||||
{
|
||||
log->info("%s Status packet requested through polling bit\n", rb_id_text[lcid]);
|
||||
log->info("%s Status packet requested through polling bit\n", rrc->get_rb_name(lcid).c_str());
|
||||
poll_received = true;
|
||||
|
||||
// 36.322 v10 Section 5.2.3
|
||||
|
@ -887,23 +894,23 @@ void rlc_am::handle_data_pdu_segment(uint8_t *payload, uint32_t nof_bytes, rlc_a
|
|||
|
||||
void rlc_am::handle_control_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||
{
|
||||
log->info_hex(payload, nof_bytes, "%s Rx control PDU", rb_id_text[lcid]);
|
||||
log->info_hex(payload, nof_bytes, "%s Rx control PDU", rrc->get_rb_name(lcid).c_str());
|
||||
|
||||
rlc_status_pdu_t status;
|
||||
rlc_am_read_status_pdu(payload, nof_bytes, &status);
|
||||
|
||||
log->info("%s Rx Status PDU: %s\n", rb_id_text[lcid], rlc_am_to_string(&status).c_str());
|
||||
log->info("%s Rx Status PDU: %s\n", rrc->get_rb_name(lcid).c_str(), rlc_am_to_string(&status).c_str());
|
||||
|
||||
poll_retx_timeout.reset();
|
||||
|
||||
// Handle ACKs and NACKs
|
||||
std::map<uint32_t, rlc_amd_tx_pdu_t>::iterator it;
|
||||
bool update_vt_a = true;
|
||||
uint32_t i = vt_a;
|
||||
uint32_t i = vt_a;
|
||||
|
||||
while(TX_MOD_BASE(i) < TX_MOD_BASE(status.ack_sn) &&
|
||||
TX_MOD_BASE(i) < TX_MOD_BASE(vt_s))
|
||||
{
|
||||
std::map<uint32_t, rlc_amd_tx_pdu_t>::iterator it;
|
||||
|
||||
bool nack = false;
|
||||
for(uint32_t j=0;j<status.N_nack;j++) {
|
||||
if(status.nacks[j].nack_sn == i) {
|
||||
|
@ -930,7 +937,7 @@ void rlc_am::handle_control_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
}
|
||||
} else {
|
||||
log->warning("%s invalid segment NACK received for SN %d. so_start: %d, so_end: %d, N_bytes: %d\n",
|
||||
rb_id_text[lcid], i, status.nacks[j].so_start, status.nacks[j].so_end, it->second.buf->N_bytes);
|
||||
rrc->get_rb_name(lcid).c_str(), i, status.nacks[j].so_start, status.nacks[j].so_end, it->second.buf->N_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -943,14 +950,16 @@ void rlc_am::handle_control_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
|
||||
if(!nack) {
|
||||
//ACKed SNs get marked and removed from tx_window if possible
|
||||
it = tx_window.find(i);
|
||||
if(tx_window.end() != it)
|
||||
{
|
||||
tx_window[i].is_acked = true;
|
||||
if(tx_window.count(i) > 0) {
|
||||
it = tx_window.find(i);
|
||||
it->second.is_acked = true;
|
||||
if(it->second.buf) {
|
||||
pool->deallocate(it->second.buf);
|
||||
it->second.buf = 0;
|
||||
}
|
||||
if(update_vt_a)
|
||||
{
|
||||
pool->deallocate(tx_window[i].buf);
|
||||
tx_window.erase(i);
|
||||
tx_window.erase(it);
|
||||
vt_a = (vt_a + 1)%MOD;
|
||||
vt_ms = (vt_ms + 1)%MOD;
|
||||
}
|
||||
|
@ -982,7 +991,7 @@ void rlc_am::reassemble_rx_sdus()
|
|||
rx_sdu->N_bytes += len;
|
||||
rx_window[vr_r].buf->msg += len;
|
||||
rx_window[vr_r].buf->N_bytes -= len;
|
||||
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU", rb_id_text[lcid]);
|
||||
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU", rrc->get_rb_name(lcid).c_str());
|
||||
rx_sdu->set_timestamp();
|
||||
pdcp->write_pdu(lcid, rx_sdu);
|
||||
rx_sdu = pool_allocate;
|
||||
|
@ -998,7 +1007,7 @@ void rlc_am::reassemble_rx_sdus()
|
|||
rx_sdu->N_bytes += rx_window[vr_r].buf->N_bytes;
|
||||
if(rlc_am_end_aligned(rx_window[vr_r].header.fi))
|
||||
{
|
||||
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU", rb_id_text[lcid]);
|
||||
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU", rrc->get_rb_name(lcid).c_str());
|
||||
rx_sdu->set_timestamp();
|
||||
pdcp->write_pdu(lcid, rx_sdu);
|
||||
rx_sdu = pool_allocate;
|
||||
|
@ -1042,7 +1051,7 @@ void rlc_am::debug_state()
|
|||
{
|
||||
log->debug("%s vt_a = %d, vt_ms = %d, vt_s = %d, poll_sn = %d "
|
||||
"vr_r = %d, vr_mr = %d, vr_x = %d, vr_ms = %d, vr_h = %d\n",
|
||||
rb_id_text[lcid], vt_a, vt_ms, vt_s, poll_sn,
|
||||
rrc->get_rb_name(lcid).c_str(), vt_a, vt_ms, vt_s, poll_sn,
|
||||
vr_r, vr_mr, vr_x, vr_ms, vr_h);
|
||||
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ void rlc_entity::init(rlc_mode_t mode,
|
|||
rlc->init(rlc_entity_log_, lcid_, pdcp_, rrc_, mac_timers_);
|
||||
}
|
||||
|
||||
void rlc_entity::configure(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg)
|
||||
void rlc_entity::configure(srslte_rlc_config_t cnfg)
|
||||
{
|
||||
if(rlc)
|
||||
rlc->configure(cnfg);
|
||||
|
@ -76,6 +76,12 @@ void rlc_entity::reset()
|
|||
rlc = NULL;
|
||||
}
|
||||
|
||||
|
||||
void rlc_entity::empty_queue()
|
||||
{
|
||||
rlc->empty_queue();
|
||||
}
|
||||
|
||||
bool rlc_entity::active()
|
||||
{
|
||||
return (rlc != NULL);
|
||||
|
|
|
@ -46,7 +46,7 @@ void rlc_tm::init(srslte::log *log_,
|
|||
rrc = rrc_;
|
||||
}
|
||||
|
||||
void rlc_tm::configure(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg)
|
||||
void rlc_tm::configure(srslte_rlc_config_t cnfg)
|
||||
{
|
||||
log->error("Attempted to configure TM RLC entity");
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ uint32_t rlc_tm::get_bearer()
|
|||
// PDCP interface
|
||||
void rlc_tm::write_sdu(byte_buffer_t *sdu)
|
||||
{
|
||||
log->info_hex(sdu->msg, sdu->N_bytes, "%s Tx SDU", rb_id_text[lcid]);
|
||||
log->info_hex(sdu->msg, sdu->N_bytes, "%s Tx SDU", rrc->get_rb_name(lcid).c_str());
|
||||
ul_queue.write(sdu);
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ int rlc_tm::read_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
uint32_t pdu_size = ul_queue.size_tail_bytes();
|
||||
if(pdu_size > nof_bytes)
|
||||
{
|
||||
log->error("TX %s PDU size larger than MAC opportunity\n", rb_id_text[lcid]);
|
||||
log->error("TX %s PDU size larger than MAC opportunity\n", rrc->get_rb_name(lcid).c_str());
|
||||
return 0;
|
||||
}
|
||||
byte_buffer_t *buf;
|
||||
|
@ -107,13 +107,13 @@ int rlc_tm::read_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
pdu_size = buf->N_bytes;
|
||||
memcpy(payload, buf->msg, buf->N_bytes);
|
||||
log->info("%s Complete SDU scheduled for tx. Stack latency: %ld us\n",
|
||||
rb_id_text[lcid], buf->get_latency_us());
|
||||
rrc->get_rb_name(lcid).c_str(), buf->get_latency_us());
|
||||
pool->deallocate(buf);
|
||||
log->info_hex(payload, pdu_size, "TX %s, %s PDU", rb_id_text[lcid], rlc_mode_text[RLC_MODE_TM]);
|
||||
log->info_hex(payload, pdu_size, "TX %s, %s PDU", rrc->get_rb_name(lcid).c_str(), rlc_mode_text[RLC_MODE_TM]);
|
||||
return pdu_size;
|
||||
}
|
||||
|
||||
void rlc_tm:: write_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||
void rlc_tm::write_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||
{
|
||||
byte_buffer_t *buf = pool_allocate;
|
||||
memcpy(buf->msg, payload, nof_bytes);
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
#include "srslte/upper/rlc_um.h"
|
||||
|
||||
#define RX_MOD_BASE(x) (x-vr_uh-rx_window_size)%rx_mod
|
||||
#define RX_MOD_BASE(x) (x-vr_uh-cfg.rx_window_size)%cfg.rx_mod
|
||||
|
||||
namespace srslte {
|
||||
|
||||
|
@ -65,41 +65,28 @@ void rlc_um::init(srslte::log *log_,
|
|||
reordering_timeout_id = mac_timers->get_unique_id();
|
||||
}
|
||||
|
||||
void rlc_um::configure(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg)
|
||||
void rlc_um::configure(srslte_rlc_config_t cnfg_)
|
||||
{
|
||||
switch(cnfg->rlc_mode)
|
||||
cfg = cnfg_.um;
|
||||
|
||||
switch(cnfg_.rlc_mode)
|
||||
{
|
||||
case LIBLTE_RRC_RLC_MODE_UM_BI:
|
||||
t_reordering = liblte_rrc_t_reordering_num[cnfg->dl_um_bi_rlc.t_reordering];
|
||||
rx_sn_field_length = (rlc_umd_sn_size_t)cnfg->dl_um_bi_rlc.sn_field_len;
|
||||
rx_window_size = (RLC_UMD_SN_SIZE_5_BITS == rx_sn_field_length) ? 16 : 512;
|
||||
rx_mod = (RLC_UMD_SN_SIZE_5_BITS == rx_sn_field_length) ? 32 : 1024;
|
||||
tx_sn_field_length = (rlc_umd_sn_size_t)cnfg->ul_um_bi_rlc.sn_field_len;
|
||||
tx_mod = (RLC_UMD_SN_SIZE_5_BITS == tx_sn_field_length) ? 32 : 1024;
|
||||
log->info("%s configured in %s mode: "
|
||||
"t_reordering=%d ms, rx_sn_field_length=%u bits, tx_sn_field_length=%u bits\n",
|
||||
rb_id_text[lcid], liblte_rrc_rlc_mode_text[cnfg->rlc_mode],
|
||||
t_reordering,
|
||||
rlc_umd_sn_size_num[rx_sn_field_length],
|
||||
rlc_umd_sn_size_num[tx_sn_field_length]);
|
||||
rrc->get_rb_name(lcid).c_str(), liblte_rrc_rlc_mode_text[cnfg_.rlc_mode],
|
||||
cfg.t_reordering, rlc_umd_sn_size_num[cfg.rx_sn_field_length], rlc_umd_sn_size_num[cfg.rx_sn_field_length]);
|
||||
break;
|
||||
case LIBLTE_RRC_RLC_MODE_UM_UNI_UL:
|
||||
tx_sn_field_length = (rlc_umd_sn_size_t)cnfg->ul_um_uni_rlc.sn_field_len;
|
||||
tx_mod = (RLC_UMD_SN_SIZE_5_BITS == tx_sn_field_length) ? 32 : 1024;
|
||||
log->info("%s configured in %s mode: tx_sn_field_length=%u bits\n",
|
||||
rb_id_text[lcid], liblte_rrc_rlc_mode_text[cnfg->rlc_mode],
|
||||
rlc_umd_sn_size_num[tx_sn_field_length]);
|
||||
rrc->get_rb_name(lcid).c_str(), liblte_rrc_rlc_mode_text[cnfg_.rlc_mode],
|
||||
rlc_umd_sn_size_num[cfg.rx_sn_field_length]);
|
||||
break;
|
||||
case LIBLTE_RRC_RLC_MODE_UM_UNI_DL:
|
||||
t_reordering = liblte_rrc_t_reordering_num[cnfg->dl_um_uni_rlc.t_reordering];
|
||||
rx_sn_field_length = (rlc_umd_sn_size_t)cnfg->dl_um_uni_rlc.sn_field_len;
|
||||
rx_window_size = (RLC_UMD_SN_SIZE_5_BITS == rx_sn_field_length) ? 16 : 512;
|
||||
rx_mod = (RLC_UMD_SN_SIZE_5_BITS == rx_sn_field_length) ? 32 : 1024;
|
||||
log->info("%s configured in %s mode: "
|
||||
"t_reordering=%d ms, rx_sn_field_length=%u bits\n",
|
||||
rb_id_text[lcid], liblte_rrc_rlc_mode_text[cnfg->rlc_mode],
|
||||
liblte_rrc_t_reordering_num[t_reordering],
|
||||
rlc_umd_sn_size_num[rx_sn_field_length]);
|
||||
rrc->get_rb_name(lcid).c_str(), liblte_rrc_rlc_mode_text[cnfg_.rlc_mode],
|
||||
cfg.t_reordering, rlc_umd_sn_size_num[cfg.rx_sn_field_length]);
|
||||
break;
|
||||
default:
|
||||
log->error("RLC configuration mode not recognized\n");
|
||||
|
@ -159,7 +146,7 @@ uint32_t rlc_um::get_bearer()
|
|||
|
||||
void rlc_um::write_sdu(byte_buffer_t *sdu)
|
||||
{
|
||||
log->info_hex(sdu->msg, sdu->N_bytes, "%s Tx SDU", rb_id_text[lcid]);
|
||||
log->info_hex(sdu->msg, sdu->N_bytes, "%s Tx SDU", rrc->get_rb_name(lcid).c_str());
|
||||
tx_sdu_queue.write(sdu);
|
||||
}
|
||||
|
||||
|
@ -222,14 +209,14 @@ void rlc_um::timer_expired(uint32_t timeout_id)
|
|||
|
||||
// 36.322 v10 Section 5.1.2.2.4
|
||||
log->info("%s reordering timeout expiry - updating vr_ur and reassembling\n",
|
||||
rb_id_text[lcid]);
|
||||
rrc->get_rb_name(lcid).c_str());
|
||||
|
||||
log->warning("Lost PDU SN: %d\n", vr_ur);
|
||||
pdu_lost = true;
|
||||
rx_sdu->reset();
|
||||
while(RX_MOD_BASE(vr_ur) < RX_MOD_BASE(vr_ux))
|
||||
{
|
||||
vr_ur = (vr_ur + 1)%rx_mod;
|
||||
vr_ur = (vr_ur + 1)%cfg.rx_mod;
|
||||
log->debug("Entering Reassemble from timeout id=%d\n", timeout_id);
|
||||
reassemble_rx_sdus();
|
||||
log->debug("Finished reassemble from timeout id=%d\n", timeout_id);
|
||||
|
@ -237,7 +224,7 @@ void rlc_um::timer_expired(uint32_t timeout_id)
|
|||
mac_timers->get(reordering_timeout_id)->stop();
|
||||
if(RX_MOD_BASE(vr_uh) > RX_MOD_BASE(vr_ur))
|
||||
{
|
||||
mac_timers->get(reordering_timeout_id)->set(this, t_reordering);
|
||||
mac_timers->get(reordering_timeout_id)->set(this, cfg.t_reordering);
|
||||
mac_timers->get(reordering_timeout_id)->run();
|
||||
vr_ux = vr_uh;
|
||||
}
|
||||
|
@ -274,7 +261,7 @@ int rlc_um::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
header.fi = RLC_FI_FIELD_START_AND_END_ALIGNED;
|
||||
header.sn = vt_us;
|
||||
header.N_li = 0;
|
||||
header.sn_size = tx_sn_field_length;
|
||||
header.sn_size = cfg.tx_sn_field_length;
|
||||
|
||||
uint32_t to_move = 0;
|
||||
uint32_t last_li = 0;
|
||||
|
@ -286,7 +273,7 @@ int rlc_um::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
if(pdu_space <= head_len)
|
||||
{
|
||||
log->warning("%s Cannot build a PDU - %d bytes available, %d bytes required for header\n",
|
||||
rb_id_text[lcid], nof_bytes, head_len);
|
||||
rrc->get_rb_name(lcid).c_str(), nof_bytes, head_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -296,7 +283,7 @@ int rlc_um::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
uint32_t space = pdu_space-head_len;
|
||||
to_move = space >= tx_sdu->N_bytes ? tx_sdu->N_bytes : space;
|
||||
log->debug("%s adding remainder of SDU segment - %d bytes of %d remaining\n",
|
||||
rb_id_text[lcid], to_move, tx_sdu->N_bytes);
|
||||
rrc->get_rb_name(lcid).c_str(), to_move, tx_sdu->N_bytes);
|
||||
memcpy(pdu_ptr, tx_sdu->msg, to_move);
|
||||
last_li = to_move;
|
||||
pdu_ptr += to_move;
|
||||
|
@ -306,7 +293,7 @@ int rlc_um::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
if(tx_sdu->N_bytes == 0)
|
||||
{
|
||||
log->info("%s Complete SDU scheduled for tx. Stack latency: %ld us\n",
|
||||
rb_id_text[lcid], tx_sdu->get_latency_us());
|
||||
rrc->get_rb_name(lcid).c_str(), tx_sdu->get_latency_us());
|
||||
pool->deallocate(tx_sdu);
|
||||
tx_sdu = NULL;
|
||||
}
|
||||
|
@ -325,7 +312,7 @@ int rlc_um::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
uint32_t space = pdu_space-head_len;
|
||||
to_move = space >= tx_sdu->N_bytes ? tx_sdu->N_bytes : space;
|
||||
log->debug("%s adding new SDU segment - %d bytes of %d remaining\n",
|
||||
rb_id_text[lcid], to_move, tx_sdu->N_bytes);
|
||||
rrc->get_rb_name(lcid).c_str(), to_move, tx_sdu->N_bytes);
|
||||
memcpy(pdu_ptr, tx_sdu->msg, to_move);
|
||||
last_li = to_move;
|
||||
pdu_ptr += to_move;
|
||||
|
@ -335,7 +322,7 @@ int rlc_um::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
if(tx_sdu->N_bytes == 0)
|
||||
{
|
||||
log->info("%s Complete SDU scheduled for tx. Stack latency: %ld us\n",
|
||||
rb_id_text[lcid], tx_sdu->get_latency_us());
|
||||
rrc->get_rb_name(lcid).c_str(), tx_sdu->get_latency_us());
|
||||
pool->deallocate(tx_sdu);
|
||||
tx_sdu = NULL;
|
||||
}
|
||||
|
@ -347,14 +334,14 @@ int rlc_um::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
|
||||
// Set SN
|
||||
header.sn = vt_us;
|
||||
vt_us = (vt_us + 1)%tx_mod;
|
||||
vt_us = (vt_us + 1)%cfg.tx_mod;
|
||||
|
||||
// Add header and TX
|
||||
log->debug("%s packing PDU with length %d\n", rb_id_text[lcid], pdu->N_bytes);
|
||||
log->debug("%s packing PDU with length %d\n", rrc->get_rb_name(lcid).c_str(), pdu->N_bytes);
|
||||
rlc_um_write_data_pdu_header(&header, pdu);
|
||||
memcpy(payload, pdu->msg, pdu->N_bytes);
|
||||
uint32_t ret = pdu->N_bytes;
|
||||
log->debug("%sreturning length %d\n", rb_id_text[lcid], pdu->N_bytes);
|
||||
log->debug("%sreturning length %d\n", rrc->get_rb_name(lcid).c_str(), pdu->N_bytes);
|
||||
pool->deallocate(pdu);
|
||||
|
||||
debug_state();
|
||||
|
@ -365,23 +352,23 @@ void rlc_um::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
{
|
||||
std::map<uint32_t, rlc_umd_pdu_t>::iterator it;
|
||||
rlc_umd_pdu_header_t header;
|
||||
rlc_um_read_data_pdu_header(payload, nof_bytes, rx_sn_field_length, &header);
|
||||
rlc_um_read_data_pdu_header(payload, nof_bytes, cfg.rx_sn_field_length, &header);
|
||||
|
||||
log->info_hex(payload, nof_bytes, "RX %s Rx data PDU SN: %d",
|
||||
rb_id_text[lcid], header.sn);
|
||||
rrc->get_rb_name(lcid).c_str(), header.sn);
|
||||
|
||||
if(RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_uh-rx_window_size) &&
|
||||
if(RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_uh-cfg.rx_window_size) &&
|
||||
RX_MOD_BASE(header.sn) < RX_MOD_BASE(vr_ur))
|
||||
{
|
||||
log->info("%s SN: %d outside rx window [%d:%d] - discarding\n",
|
||||
rb_id_text[lcid], header.sn, vr_ur, vr_uh);
|
||||
rrc->get_rb_name(lcid).c_str(), header.sn, vr_ur, vr_uh);
|
||||
return;
|
||||
}
|
||||
it = rx_window.find(header.sn);
|
||||
if(rx_window.end() != it)
|
||||
{
|
||||
log->info("%s Discarding duplicate SN: %d\n",
|
||||
rb_id_text[lcid], header.sn);
|
||||
rrc->get_rb_name(lcid).c_str(), header.sn);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -403,7 +390,7 @@ void rlc_um::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
|
||||
// Update vr_uh
|
||||
if(!inside_reordering_window(header.sn))
|
||||
vr_uh = (header.sn + 1)%rx_mod;
|
||||
vr_uh = (header.sn + 1)%cfg.rx_mod;
|
||||
|
||||
// Reassemble and deliver SDUs, while updating vr_ur
|
||||
log->debug("Entering Reassemble from received PDU\n");
|
||||
|
@ -423,7 +410,7 @@ void rlc_um::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
{
|
||||
if(RX_MOD_BASE(vr_uh) > RX_MOD_BASE(vr_ur))
|
||||
{
|
||||
mac_timers->get(reordering_timeout_id)->set(this, t_reordering);
|
||||
mac_timers->get(reordering_timeout_id)->set(this, cfg.t_reordering);
|
||||
mac_timers->get(reordering_timeout_id)->run();
|
||||
vr_ux = vr_uh;
|
||||
}
|
||||
|
@ -452,11 +439,11 @@ void rlc_um::reassemble_rx_sdus()
|
|||
rx_sdu->N_bytes += len;
|
||||
rx_window[vr_ur].buf->msg += len;
|
||||
rx_window[vr_ur].buf->N_bytes -= len;
|
||||
if((pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) || (vr_ur != ((vr_ur_in_rx_sdu+1)%rx_mod))) {
|
||||
if((pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) || (vr_ur != ((vr_ur_in_rx_sdu+1)%cfg.rx_mod))) {
|
||||
log->warning("Dropping remainder of lost PDU (lower edge middle segments, vr_ur=%d, vr_ur_in_rx_sdu=%d)\n", vr_ur, vr_ur_in_rx_sdu);
|
||||
rx_sdu->reset();
|
||||
} else {
|
||||
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d, i=%d (lower edge middle segments)", rb_id_text[lcid], vr_ur, i);
|
||||
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d, i=%d (lower edge middle segments)", rrc->get_rb_name(lcid).c_str(), vr_ur, i);
|
||||
rx_sdu->set_timestamp();
|
||||
pdcp->write_pdu(lcid, rx_sdu);
|
||||
rx_sdu = pool_allocate;
|
||||
|
@ -476,7 +463,7 @@ void rlc_um::reassemble_rx_sdus()
|
|||
log->warning("Dropping remainder of lost PDU (lower edge last segments)\n");
|
||||
rx_sdu->reset();
|
||||
} else {
|
||||
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d (lower edge last segments)", rb_id_text[lcid], vr_ur);
|
||||
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d (lower edge last segments)", rrc->get_rb_name(lcid).c_str(), vr_ur);
|
||||
rx_sdu->set_timestamp();
|
||||
pdcp->write_pdu(lcid, rx_sdu);
|
||||
rx_sdu = pool_allocate;
|
||||
|
@ -489,7 +476,7 @@ void rlc_um::reassemble_rx_sdus()
|
|||
rx_window.erase(vr_ur);
|
||||
}
|
||||
|
||||
vr_ur = (vr_ur + 1)%rx_mod;
|
||||
vr_ur = (vr_ur + 1)%cfg.rx_mod;
|
||||
}
|
||||
|
||||
|
||||
|
@ -502,15 +489,15 @@ void rlc_um::reassemble_rx_sdus()
|
|||
int len = rx_window[vr_ur].header.li[i];
|
||||
memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, len);
|
||||
log->debug("Concatenating %d bytes in to current length %d. rx_window remaining bytes=%d, vr_ur_in_rx_sdu=%d, vr_ur=%d, rx_mod=%d, last_mod=%d\n",
|
||||
len, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes, vr_ur_in_rx_sdu, vr_ur, rx_mod, (vr_ur_in_rx_sdu+1)%rx_mod);
|
||||
len, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes, vr_ur_in_rx_sdu, vr_ur, cfg.rx_mod, (vr_ur_in_rx_sdu+1)%cfg.rx_mod);
|
||||
rx_sdu->N_bytes += len;
|
||||
rx_window[vr_ur].buf->msg += len;
|
||||
rx_window[vr_ur].buf->N_bytes -= len;
|
||||
if((pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) || (vr_ur != ((vr_ur_in_rx_sdu+1)%rx_mod))) {
|
||||
if((pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) || (vr_ur != ((vr_ur_in_rx_sdu+1)%cfg.rx_mod))) {
|
||||
log->warning("Dropping remainder of lost PDU (update vr_ur middle segments, vr_ur=%d, vr_ur_in_rx_sdu=%d)\n", vr_ur, vr_ur_in_rx_sdu);
|
||||
rx_sdu->reset();
|
||||
} else {
|
||||
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d, i=%d, (update vr_ur middle segments)", rb_id_text[lcid], vr_ur, i);
|
||||
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d, i=%d, (update vr_ur middle segments)", rrc->get_rb_name(lcid).c_str(), vr_ur, i);
|
||||
rx_sdu->set_timestamp();
|
||||
pdcp->write_pdu(lcid, rx_sdu);
|
||||
rx_sdu = pool_allocate;
|
||||
|
@ -530,7 +517,7 @@ void rlc_um::reassemble_rx_sdus()
|
|||
log->warning("Dropping remainder of lost PDU (update vr_ur last segments)\n");
|
||||
rx_sdu->reset();
|
||||
} else {
|
||||
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d (update vr_ur last segments)", rb_id_text[lcid], vr_ur);
|
||||
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d (update vr_ur last segments)", rrc->get_rb_name(lcid).c_str(), vr_ur);
|
||||
rx_sdu->set_timestamp();
|
||||
pdcp->write_pdu(lcid, rx_sdu);
|
||||
rx_sdu = pool_allocate;
|
||||
|
@ -542,13 +529,13 @@ void rlc_um::reassemble_rx_sdus()
|
|||
pool->deallocate(rx_window[vr_ur].buf);
|
||||
rx_window.erase(vr_ur);
|
||||
|
||||
vr_ur = (vr_ur + 1)%rx_mod;
|
||||
vr_ur = (vr_ur + 1)%cfg.rx_mod;
|
||||
}
|
||||
}
|
||||
|
||||
bool rlc_um::inside_reordering_window(uint16_t sn)
|
||||
{
|
||||
if(RX_MOD_BASE(sn) >= RX_MOD_BASE(vr_uh-rx_window_size) &&
|
||||
if(RX_MOD_BASE(sn) >= RX_MOD_BASE(vr_uh-cfg.rx_window_size) &&
|
||||
RX_MOD_BASE(sn) < RX_MOD_BASE(vr_uh))
|
||||
{
|
||||
return true;
|
||||
|
@ -560,7 +547,7 @@ bool rlc_um::inside_reordering_window(uint16_t sn)
|
|||
void rlc_um::debug_state()
|
||||
{
|
||||
log->debug("%s vt_us = %d, vr_ur = %d, vr_ux = %d, vr_uh = %d \n",
|
||||
rb_id_text[lcid], vt_us, vr_ur, vr_ux, vr_uh);
|
||||
rrc->get_rb_name(lcid).c_str(), vt_us, vr_ur, vr_ux, vr_uh);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ public:
|
|||
|
||||
// RRC interface
|
||||
void max_retx_attempted(){}
|
||||
std::string get_rb_name(uint32_t lcid) { return std::string(""); }
|
||||
|
||||
byte_buffer_t *sdus[10];
|
||||
int n_sdus;
|
||||
|
@ -101,6 +102,7 @@ void basic_test()
|
|||
cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4;
|
||||
cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25;
|
||||
cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4;
|
||||
cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5;
|
||||
|
||||
rlc1.configure(&cnfg);
|
||||
rlc2.configure(&cnfg);
|
||||
|
@ -180,6 +182,7 @@ void concat_test()
|
|||
cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4;
|
||||
cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25;
|
||||
cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4;
|
||||
cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5;
|
||||
|
||||
rlc1.configure(&cnfg);
|
||||
rlc2.configure(&cnfg);
|
||||
|
@ -244,6 +247,7 @@ void segment_test()
|
|||
cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4;
|
||||
cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25;
|
||||
cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4;
|
||||
cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5;
|
||||
|
||||
rlc1.configure(&cnfg);
|
||||
rlc2.configure(&cnfg);
|
||||
|
@ -326,6 +330,7 @@ void retx_test()
|
|||
cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4;
|
||||
cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25;
|
||||
cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4;
|
||||
cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5;
|
||||
|
||||
rlc1.configure(&cnfg);
|
||||
rlc2.configure(&cnfg);
|
||||
|
@ -422,6 +427,7 @@ void resegment_test_1()
|
|||
cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4;
|
||||
cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25;
|
||||
cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4;
|
||||
cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5;
|
||||
|
||||
rlc1.configure(&cnfg);
|
||||
rlc2.configure(&cnfg);
|
||||
|
@ -531,6 +537,7 @@ void resegment_test_2()
|
|||
cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4;
|
||||
cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25;
|
||||
cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4;
|
||||
cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5;
|
||||
|
||||
rlc1.configure(&cnfg);
|
||||
rlc2.configure(&cnfg);
|
||||
|
@ -637,6 +644,7 @@ void resegment_test_3()
|
|||
cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4;
|
||||
cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25;
|
||||
cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4;
|
||||
cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5;
|
||||
|
||||
rlc1.configure(&cnfg);
|
||||
rlc2.configure(&cnfg);
|
||||
|
@ -739,6 +747,7 @@ void resegment_test_4()
|
|||
cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4;
|
||||
cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25;
|
||||
cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4;
|
||||
cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5;
|
||||
|
||||
rlc1.configure(&cnfg);
|
||||
rlc2.configure(&cnfg);
|
||||
|
@ -841,6 +850,7 @@ void resegment_test_5()
|
|||
cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4;
|
||||
cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25;
|
||||
cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4;
|
||||
cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5;
|
||||
|
||||
rlc1.configure(&cnfg);
|
||||
rlc2.configure(&cnfg);
|
||||
|
@ -942,6 +952,7 @@ void resegment_test_6()
|
|||
cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4;
|
||||
cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25;
|
||||
cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4;
|
||||
cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5;
|
||||
|
||||
rlc1.configure(&cnfg);
|
||||
rlc2.configure(&cnfg);
|
||||
|
|
|
@ -71,6 +71,7 @@ public:
|
|||
|
||||
// RRC interface
|
||||
void max_retx_attempted(){}
|
||||
std::string get_rb_name(uint32_t lcid) { return std::string(""); }
|
||||
|
||||
byte_buffer_t *sdus[5];
|
||||
int n_sdus;
|
||||
|
|
|
@ -15,14 +15,14 @@ qci_config = (
|
|||
};
|
||||
dl_um = {
|
||||
sn_field_length = 10;
|
||||
t_reordering = 80;
|
||||
t_reordering = 45;
|
||||
};
|
||||
};
|
||||
logical_channel_config = {
|
||||
priority = 11;
|
||||
priority = 13;
|
||||
prioritized_bit_rate = -1;
|
||||
bucket_size_duration = 100;
|
||||
log_chan_group = 3;
|
||||
log_chan_group = 2;
|
||||
};
|
||||
},
|
||||
{
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
# mnc: Mobile Network Code
|
||||
# mme_addr: IP address of MME for S1 connnection
|
||||
# gtp_bind_addr: Local IP address to bind for GTP connection
|
||||
# n_prb: Number of Physical Resource Blocks (6,15,25,50,75,100)
|
||||
#
|
||||
#####################################################################
|
||||
[enb]
|
||||
|
@ -57,8 +58,8 @@ drb_config = drb.conf
|
|||
#####################################################################
|
||||
[rf]
|
||||
dl_earfcn = 3400
|
||||
tx_gain = 70
|
||||
rx_gain = 50
|
||||
tx_gain = 80
|
||||
rx_gain = 60
|
||||
|
||||
#device_name = auto
|
||||
#device_args = auto
|
||||
|
@ -121,7 +122,7 @@ enable = false
|
|||
#pdsch_mcs = -1
|
||||
#pdsch_max_mcs = -1
|
||||
#pusch_mcs = -1
|
||||
pusch_max_mcs = 16
|
||||
#pusch_max_mcs = -1
|
||||
nof_ctrl_symbols = 2
|
||||
|
||||
#####################################################################
|
||||
|
@ -141,9 +142,9 @@ nof_ctrl_symbols = 2
|
|||
#pdsch_max_its = 4
|
||||
#nof_phy_threads = 2
|
||||
#pregenerate_signals = false
|
||||
#tx_amplitude = 0.8
|
||||
#tx_amplitude = 0.6
|
||||
#link_failure_nof_err = 50
|
||||
#rrc_inactivity_timer = 30000
|
||||
#rrc_inactivity_timer = 10000
|
||||
#max_prach_offset_us = 30
|
||||
|
||||
#####################################################################
|
||||
|
|
|
@ -108,7 +108,7 @@ public:
|
|||
|
||||
int ul_crc_info(uint32_t tti, uint16_t rnti, bool crc);
|
||||
int ul_sr_info(uint32_t tti, uint16_t rnti);
|
||||
int ul_bsr(uint16_t rnti, uint32_t lcid, uint32_t bsr);
|
||||
int ul_bsr(uint16_t rnti, uint32_t lcid, uint32_t bsr, bool set_value = true);
|
||||
int ul_recv_len(uint16_t rnti, uint32_t lcid, uint32_t len);
|
||||
int ul_phr(uint16_t rnti, int phr);
|
||||
int ul_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi, uint32_t ul_ch_code);
|
||||
|
|
|
@ -104,7 +104,8 @@ public:
|
|||
void set_alloc(ul_alloc_t alloc);
|
||||
void same_alloc();
|
||||
bool is_adaptive_retx();
|
||||
|
||||
|
||||
void reset_pending_data();
|
||||
bool has_pending_ack();
|
||||
uint32_t get_pending_data();
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ public:
|
|||
void rem_bearer(uint32_t lc_id);
|
||||
|
||||
void dl_buffer_state(uint8_t lc_id, uint32_t tx_queue, uint32_t retx_queue);
|
||||
void ul_buffer_state(uint8_t lc_id, uint32_t bsr);
|
||||
void ul_buffer_state(uint8_t lc_id, uint32_t bsr, bool set_value = true);
|
||||
void ul_phr(int phr);
|
||||
void mac_buffer_state(uint32_t ce_code);
|
||||
void ul_recv_len(uint32_t lcid, uint32_t len);
|
||||
|
@ -114,8 +114,10 @@ public:
|
|||
uint32_t get_max_retx();
|
||||
|
||||
bool get_pucch_sched(uint32_t current_tti, uint32_t prb_idx[2], uint32_t *L);
|
||||
bool pucch_sr_collision(uint32_t current_tti, uint32_t n_cce);
|
||||
|
||||
bool pucch_sr_collision(uint32_t current_tti, uint32_t n_cce);
|
||||
|
||||
uint32_t get_pending_ul_old_data();
|
||||
|
||||
private:
|
||||
|
||||
typedef struct {
|
||||
|
@ -126,12 +128,13 @@ private:
|
|||
} ue_bearer_t;
|
||||
|
||||
bool is_sr_triggered();
|
||||
uint32_t get_pending_ul_old_data();
|
||||
int alloc_pdu(int tbs, sched_interface::dl_sched_pdu_t* pdu);
|
||||
int alloc_pdu(int tbs, sched_interface::dl_sched_pdu_t* pdu);
|
||||
|
||||
static uint32_t format1_count_prb(uint32_t bitmask, uint32_t cell_nof_prb);
|
||||
static int cqi_to_tbs(uint32_t cqi, uint32_t nof_prb, uint32_t nof_re, uint32_t max_mcs, uint32_t *mcs);
|
||||
static int alloc_tbs(uint32_t cqi, uint32_t nof_prb, uint32_t nof_re, uint32_t req_bytes, uint32_t max_mcs, int *mcs);
|
||||
static int cqi_to_tbs(uint32_t cqi, uint32_t nof_prb, uint32_t nof_re, uint32_t max_mcs, uint32_t max_Qm, uint32_t *mcs);
|
||||
int alloc_tbs_dl(uint32_t nof_prb, uint32_t nof_re, uint32_t req_bytes, int *mcs);
|
||||
int alloc_tbs_ul(uint32_t nof_prb, uint32_t nof_re, uint32_t req_bytes, int *mcs);
|
||||
int alloc_tbs(uint32_t nof_prb, uint32_t nof_re, uint32_t req_bytes, bool is_ul, int *mcs);
|
||||
|
||||
static bool bearer_is_ul(ue_bearer_t *lch);
|
||||
static bool bearer_is_dl(ue_bearer_t *lch);
|
||||
|
|
|
@ -58,6 +58,10 @@ public:
|
|||
}
|
||||
|
||||
virtual ~ue() {
|
||||
for (int i=0;i<NOF_HARQ_PROCESSES;i++) {
|
||||
srslte_softbuffer_rx_free(&softbuffer_rx[i]);
|
||||
srslte_softbuffer_tx_free(&softbuffer_tx[i]);
|
||||
}
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
|
||||
|
@ -81,7 +85,9 @@ public:
|
|||
|
||||
uint32_t rl_failure();
|
||||
void rl_failure_reset();
|
||||
|
||||
|
||||
void set_lcg(uint32_t lcid, uint32_t lcg);
|
||||
|
||||
void metrics_read(srsenb::mac_metrics_t* metrics);
|
||||
void metrics_rx(bool crc, uint32_t tbs);
|
||||
void metrics_tx(bool crc, uint32_t tbs);
|
||||
|
@ -96,7 +102,9 @@ private:
|
|||
void allocate_ce(srslte::sch_pdu *pdu, uint32_t lcid);
|
||||
|
||||
void metrics_phr(float phr);
|
||||
uint32_t phr_counter;
|
||||
uint32_t phr_counter;
|
||||
|
||||
std::vector<uint32_t> lc_groups[4];
|
||||
|
||||
mac_metrics_t metrics;
|
||||
|
||||
|
|
|
@ -43,6 +43,32 @@ namespace srsenb {
|
|||
#define SRSENB_N_DRB 8
|
||||
#define SRSENB_N_RADIO_BEARERS 11
|
||||
|
||||
typedef enum{
|
||||
RB_ID_SRB0 = 0,
|
||||
RB_ID_SRB1,
|
||||
RB_ID_SRB2,
|
||||
RB_ID_DRB1,
|
||||
RB_ID_DRB2,
|
||||
RB_ID_DRB3,
|
||||
RB_ID_DRB4,
|
||||
RB_ID_DRB5,
|
||||
RB_ID_DRB6,
|
||||
RB_ID_DRB7,
|
||||
RB_ID_DRB8,
|
||||
RB_ID_N_ITEMS,
|
||||
}rb_id_t;
|
||||
static const char rb_id_text[RB_ID_N_ITEMS][20] = { "SRB0",
|
||||
"SRB1",
|
||||
"SRB2",
|
||||
"DRB1",
|
||||
"DRB2",
|
||||
"DRB3",
|
||||
"DRB4",
|
||||
"DRB5",
|
||||
"DRB6",
|
||||
"DRB7",
|
||||
"DRB8"};
|
||||
|
||||
// Cat 3 UE - Max number of DL-SCH transport block bits received within a TTI
|
||||
// 3GPP 36.306 Table 4.1.1
|
||||
#define SRSENB_MAX_BUFFER_SIZE_BITS 102048
|
||||
|
|
|
@ -51,7 +51,7 @@ public:
|
|||
void add_user(uint16_t rnti);
|
||||
void rem_user(uint16_t rnti);
|
||||
void write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *sdu);
|
||||
void add_bearer(uint16_t rnti, uint32_t lcid, LIBLTE_RRC_PDCP_CONFIG_STRUCT *cnfg=NULL);
|
||||
void add_bearer(uint16_t rnti, uint32_t lcid, srslte::srslte_pdcp_config_t cnfg);
|
||||
void config_security(uint16_t rnti,
|
||||
uint32_t lcid,
|
||||
uint8_t *k_rrc_enc_,
|
||||
|
@ -89,6 +89,7 @@ private:
|
|||
void write_pdu_bcch_bch(srslte::byte_buffer_t *pdu);
|
||||
void write_pdu_bcch_dlsch(srslte::byte_buffer_t *pdu);
|
||||
void write_pdu_pcch(srslte::byte_buffer_t *pdu);
|
||||
std::string get_rb_name(uint32_t lcid);
|
||||
};
|
||||
|
||||
class user_interface
|
||||
|
|
|
@ -50,10 +50,11 @@ public:
|
|||
void add_user(uint16_t rnti);
|
||||
void rem_user(uint16_t rnti);
|
||||
void add_bearer(uint16_t rnti, uint32_t lcid);
|
||||
void add_bearer(uint16_t rnti, uint32_t lcid, LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg);
|
||||
void add_bearer(uint16_t rnti, uint32_t lcid, srslte::srslte_rlc_config_t cnfg);
|
||||
|
||||
// rlc_interface_pdcp
|
||||
void write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *sdu);
|
||||
std::string get_rb_name(uint32_t lcid);
|
||||
|
||||
// rlc_interface_mac
|
||||
int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t *payload, uint32_t nof_bytes);
|
||||
|
@ -73,6 +74,7 @@ private:
|
|||
void write_pdu_bcch_dlsch(srslte::byte_buffer_t *sdu);
|
||||
void write_pdu_pcch(srslte::byte_buffer_t *sdu);
|
||||
void max_retx_attempted();
|
||||
std::string get_rb_name(uint32_t lcid);
|
||||
uint16_t rnti;
|
||||
|
||||
srsenb::pdcp_interface_rlc *pdcp;
|
||||
|
|
|
@ -9,7 +9,7 @@ mac_cnfg =
|
|||
ulsch_cnfg =
|
||||
{
|
||||
max_harq_tx = 4;
|
||||
periodic_bsr_timer = 5; // in ms
|
||||
periodic_bsr_timer = 40; // in ms
|
||||
retx_bsr_timer = 320; // in ms
|
||||
};
|
||||
|
||||
|
@ -28,7 +28,7 @@ phy_cnfg =
|
|||
{
|
||||
beta_offset_ack_idx = 10;
|
||||
beta_offset_ri_idx = 5;
|
||||
beta_offset_cqi_idx = 5;
|
||||
beta_offset_cqi_idx = 10;
|
||||
};
|
||||
|
||||
// PUCCH-SR resources are scheduled on time-frequeny domain first, then multiplexed in the same resource.
|
||||
|
|
|
@ -23,9 +23,9 @@ sib2 =
|
|||
rach_cnfg =
|
||||
{
|
||||
num_ra_preambles = 52;
|
||||
preamble_init_rx_target_pwr = -108;
|
||||
preamble_init_rx_target_pwr = -104;
|
||||
pwr_ramping_step = 6; // in dB
|
||||
preamble_trans_max = 7;
|
||||
preamble_trans_max = 10;
|
||||
ra_resp_win_size = 10; // in ms
|
||||
mac_con_res_timer = 64; // in ms
|
||||
max_harq_msg3_tx = 4;
|
||||
|
@ -53,14 +53,14 @@ sib2 =
|
|||
pdsch_cnfg =
|
||||
{
|
||||
p_b = 0;
|
||||
rs_power = -4;
|
||||
rs_power = 20;
|
||||
};
|
||||
pusch_cnfg =
|
||||
{
|
||||
n_sb = 1;
|
||||
hopping_mode = "inter-subframe";
|
||||
pusch_hopping_offset = 2;
|
||||
enable_64_qam = false;
|
||||
enable_64_qam = false; // 64QAM PUSCH is not currently enabled
|
||||
ul_rs =
|
||||
{
|
||||
cyclic_shift = 0;
|
||||
|
@ -71,25 +71,25 @@ sib2 =
|
|||
};
|
||||
pucch_cnfg =
|
||||
{
|
||||
delta_pucch_shift = 1;
|
||||
n_rb_cqi = 1;
|
||||
delta_pucch_shift = 2;
|
||||
n_rb_cqi = 2;
|
||||
n_cs_an = 0;
|
||||
n1_pucch_an = 2;
|
||||
n1_pucch_an = 12;
|
||||
};
|
||||
ul_pwr_ctrl =
|
||||
{
|
||||
p0_nominal_pusch = -108;
|
||||
alpha = 1.0;
|
||||
p0_nominal_pucch = -88;
|
||||
p0_nominal_pusch = -85;
|
||||
alpha = 0.7;
|
||||
p0_nominal_pucch = -107;
|
||||
delta_flist_pucch =
|
||||
{
|
||||
format_1 = 2;
|
||||
format_1 = 0;
|
||||
format_1b = 3;
|
||||
format_2 = 0;
|
||||
format_2a = 0;
|
||||
format_2b = 0;
|
||||
format_2 = 1;
|
||||
format_2a = 2;
|
||||
format_2b = 2;
|
||||
};
|
||||
delta_preamble_msg3 = 4;
|
||||
delta_preamble_msg3 = 8;
|
||||
};
|
||||
ul_cp_length = "Normal";
|
||||
};
|
||||
|
|
|
@ -31,7 +31,9 @@ target_link_libraries(srsenb srsenb_upper
|
|||
|
||||
if (RPATH)
|
||||
set_target_properties(srsenb PROPERTIES INSTALL_RPATH ".")
|
||||
endif (RPATH)
|
||||
endif (RPATH)
|
||||
|
||||
install(TARGETS srsenb DESTINATION ${RUNTIME_DIR})
|
||||
|
||||
########################################################################
|
||||
# Option to run command after build (useful for remote builds)
|
||||
|
@ -42,3 +44,5 @@ if (NOT ${BUILDENB_CMD} STREQUAL "")
|
|||
else(NOT ${BUILDENB_CMD} STREQUAL "")
|
||||
message(STATUS "No post-build-ENB command defined")
|
||||
endif (NOT ${BUILDENB_CMD} STREQUAL "")
|
||||
|
||||
install(TARGETS srsenb DESTINATION ${RUNTIME_DIR})
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <strings.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <srslte/interfaces/sched_interface.h>
|
||||
|
||||
#include "srslte/common/log.h"
|
||||
#include "mac/mac.h"
|
||||
|
@ -163,7 +164,9 @@ int mac::rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint
|
|||
|
||||
int mac::bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t* cfg)
|
||||
{
|
||||
if (ue_db.count(rnti)) {
|
||||
if (ue_db.count(rnti)) {
|
||||
// configure BSR group in UE
|
||||
ue_db[rnti]->set_lcg(lc_id, (uint32_t) cfg->group);
|
||||
return scheduler.bearer_ue_cfg(rnti, lc_id, cfg);
|
||||
} else {
|
||||
Error("User rnti=0x%x not found\n", rnti);
|
||||
|
@ -259,7 +262,7 @@ void mac::rl_failure(uint16_t rnti)
|
|||
if (ue_db.count(rnti)) {
|
||||
uint32_t nof_fails = ue_db[rnti]->rl_failure();
|
||||
if (nof_fails >= (uint32_t) args.link_failure_nof_err && args.link_failure_nof_err > 0) {
|
||||
Info("Detected PUSCH failure for rnti=0x%x\n", rnti);
|
||||
Info("Detected Uplink failure for rnti=0x%x\n", rnti);
|
||||
rrc_h->rl_failure(rnti);
|
||||
ue_db[rnti]->rl_failure_reset();
|
||||
}
|
||||
|
@ -384,7 +387,7 @@ int mac::rach_detected(uint32_t tti, uint32_t preamble_idx, uint32_t time_adv)
|
|||
}
|
||||
// Save RA info
|
||||
pending_rars[ra_id].preamble_idx = preamble_idx;
|
||||
pending_rars[ra_id].ta_cmd = time_adv;
|
||||
pending_rars[ra_id].ta_cmd = 2*time_adv;
|
||||
pending_rars[ra_id].temp_crnti = last_rnti;
|
||||
|
||||
// Add new user to the scheduler so that it can RX/TX SRB0
|
||||
|
|
|
@ -302,12 +302,12 @@ int sched::ul_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi, uint32_t ul_ch
|
|||
return ret;
|
||||
}
|
||||
|
||||
int sched::ul_bsr(uint16_t rnti, uint32_t lcid, uint32_t bsr)
|
||||
int sched::ul_bsr(uint16_t rnti, uint32_t lcid, uint32_t bsr, bool set_value)
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
int ret = 0;
|
||||
if (ue_db.count(rnti)) {
|
||||
ue_db[rnti].ul_buffer_state(lcid, bsr);
|
||||
ue_db[rnti].ul_buffer_state(lcid, bsr, set_value);
|
||||
} else {
|
||||
Error("User rnti=0x%x not found\n", rnti);
|
||||
ret = -1;
|
||||
|
@ -792,13 +792,13 @@ int sched::ul_sched(uint32_t tti, srsenb::sched_interface::ul_sched_res_t* sched
|
|||
user->unset_sr();
|
||||
}
|
||||
|
||||
log_h->info("SCHED: %s %s rnti=0x%x, pid=%d, dci=%d,%d, grant=%d,%d, n_rtx=%d, tbs=%d, bsr=%d (%d)\n",
|
||||
log_h->info("SCHED: %s %s rnti=0x%x, pid=%d, dci=%d,%d, grant=%d,%d, n_rtx=%d, tbs=%d, bsr=%d (%d-%d)\n",
|
||||
is_rar?"RAR":"UL",
|
||||
is_newtx?"tx":"retx",
|
||||
rnti, h->get_id(),
|
||||
sched_result->pusch[nof_dci_elems].dci_location.L, sched_result->pusch[nof_dci_elems].dci_location.ncce,
|
||||
alloc.RB_start, alloc.L, h->nof_retx(), sched_result->pusch[nof_dci_elems].tbs,
|
||||
user->get_pending_ul_new_data(current_tti),pending_data_before);
|
||||
user->get_pending_ul_new_data(current_tti),pending_data_before, user->get_pending_ul_old_data());
|
||||
|
||||
nof_dci_elems++;
|
||||
} else {
|
||||
|
@ -812,8 +812,16 @@ int sched::ul_sched(uint32_t tti, srsenb::sched_interface::ul_sched_res_t* sched
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Update pending data counters after this TTI
|
||||
for(std::map<uint16_t, sched_ue>::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) {
|
||||
sched_ue *user = (sched_ue *) &iter->second;
|
||||
uint16_t rnti = (uint16_t) iter->first;
|
||||
|
||||
user->get_ul_harq(current_tti)->reset_pending_data();
|
||||
}
|
||||
|
||||
sched_result->nof_dci_elems = nof_dci_elems;
|
||||
sched_result->nof_phich_elems = nof_phich_elems;
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ bool harq_proc::get_ack()
|
|||
void harq_proc::set_ack(bool ack_)
|
||||
{
|
||||
ack = ack_;
|
||||
ack_received = true;
|
||||
ack_received = true;
|
||||
log_h->debug("ACK=%d received pid=%d, n_rtx=%d, max_retx=%d\n", ack_, id, n_rtx, max_retx);
|
||||
if (n_rtx >= max_retx) {
|
||||
Warning("SCHED: discarting TB pid=%d, tti=%d, maximum number of retx exceeded (%d)\n", id, tti, max_retx);
|
||||
|
@ -229,13 +229,22 @@ bool ul_harq_proc::has_pending_ack()
|
|||
active = false;
|
||||
}
|
||||
if (!active) {
|
||||
pending_data = 0;
|
||||
need_ack = false;
|
||||
need_ack = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t ul_harq_proc::get_pending_data()
|
||||
|
||||
|
||||
void ul_harq_proc::reset_pending_data()
|
||||
{
|
||||
if (!active) {
|
||||
pending_data = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t ul_harq_proc::get_pending_data()
|
||||
{
|
||||
return pending_data;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
|
||||
#include <string.h>
|
||||
#include <boost/concept_check.hpp>
|
||||
#include <srslte/interfaces/sched_interface.h>
|
||||
#include <srslte/phy/phch/pucch.h>
|
||||
|
||||
#include "srslte/srslte.h"
|
||||
#include "srslte/common/pdu.h"
|
||||
|
@ -163,12 +165,17 @@ void sched_ue::phy_config_enabled(uint32_t tti, bool enabled)
|
|||
phy_config_dedicated_enabled = enabled;
|
||||
}
|
||||
|
||||
void sched_ue::ul_buffer_state(uint8_t lc_id, uint32_t bsr)
|
||||
void sched_ue::ul_buffer_state(uint8_t lc_id, uint32_t bsr, bool set_value)
|
||||
{
|
||||
if (lc_id < sched_interface::MAX_LC) {
|
||||
lch[lc_id].bsr = bsr;
|
||||
Debug("SCHED: UL lcid=%d buffer_state=%d\n", lc_id, bsr);
|
||||
}
|
||||
if (set_value) {
|
||||
lch[lc_id].bsr = bsr;
|
||||
} else {
|
||||
lch[lc_id].bsr += bsr;
|
||||
}
|
||||
}
|
||||
Debug("SCHED: bsr=%d, lcid=%d, bsr={%d,%d,%d,%d}\n", bsr, lc_id,
|
||||
lch[0].bsr, lch[1].bsr, lch[2].bsr, lch[3].bsr);
|
||||
}
|
||||
|
||||
void sched_ue::ul_phr(int phr)
|
||||
|
@ -230,7 +237,8 @@ bool sched_ue::get_pucch_sched(uint32_t current_tti, uint32_t prb_idx[2], uint32
|
|||
if (!phy_config_dedicated_enabled) {
|
||||
return false;
|
||||
}
|
||||
srslte_pucch_sched_t pucch_sched;
|
||||
srslte_pucch_sched_t pucch_sched;
|
||||
pucch_sched.sps_enabled = false;
|
||||
pucch_sched.n_pucch_sr = cfg.sr_N_pucch;
|
||||
pucch_sched.n_pucch_2 = cfg.n_pucch_cqi;
|
||||
pucch_sched.N_pucch_1 = cfg.pucch_cfg.n1_pucch_an;
|
||||
|
@ -239,7 +247,7 @@ bool sched_ue::get_pucch_sched(uint32_t current_tti, uint32_t prb_idx[2], uint32
|
|||
|
||||
// First check if it has pending ACKs
|
||||
for (int i=0;i<SCHED_MAX_HARQ_PROC;i++) {
|
||||
if (((dl_harq[i].get_tti()+8)%10240) == current_tti) {
|
||||
if (((dl_harq[i].get_tti()+4)%10240) == current_tti) {
|
||||
uint32_t n_pucch = srslte_pucch_get_npucch(dl_harq[i].get_n_cce(), SRSLTE_PUCCH_FORMAT_1A, has_sr, &pucch_sched);
|
||||
if (prb_idx) {
|
||||
for (int i=0;i<2;i++) {
|
||||
|
@ -249,7 +257,7 @@ bool sched_ue::get_pucch_sched(uint32_t current_tti, uint32_t prb_idx[2], uint32
|
|||
if (L) {
|
||||
*L = 1;
|
||||
}
|
||||
Debug("SCHED: Reserved Format1A PUCCH for rnti=0x%x, n_prb=%d,%d, n_pucch=%d\n", rnti, prb_idx[0], prb_idx[1], n_pucch);
|
||||
Info("SCHED: Reserved Format1A PUCCH for rnti=0x%x, n_prb=%d,%d, n_pucch=%d\n", rnti, prb_idx[0], prb_idx[1], n_pucch);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -263,9 +271,24 @@ bool sched_ue::get_pucch_sched(uint32_t current_tti, uint32_t prb_idx[2], uint32
|
|||
if (L) {
|
||||
*L = 1;
|
||||
}
|
||||
Debug("SCHED: Reserved Format1 PUCCH for rnti=0x%x, n_prb=%d,%d, n_pucch=%d\n", rnti, prb_idx[0], prb_idx[1], cfg.sr_N_pucch);
|
||||
Info("SCHED: Reserved Format1 PUCCH for rnti=0x%x, n_prb=%d,%d, n_pucch=%d\n", rnti, prb_idx[0], prb_idx[1], cfg.sr_N_pucch);
|
||||
return true;
|
||||
}
|
||||
// Finally check Format2 (periodic CQI)
|
||||
if (cfg.cqi_enabled && srslte_cqi_send(cfg.cqi_idx, current_tti)) {
|
||||
if (prb_idx) {
|
||||
for (int i=0;i<2;i++) {
|
||||
prb_idx[i] = srslte_pucch_n_prb(&cfg.pucch_cfg, SRSLTE_PUCCH_FORMAT_2, cfg.cqi_pucch, cell.nof_prb, cell.cp, i);
|
||||
}
|
||||
}
|
||||
if(L) {
|
||||
*L = 2;
|
||||
}
|
||||
Info("SCHED: Reserved Format2 PUCCH for rnti=0x%x, n_prb=%d,%d, n_pucch=%d, pmi_idx=%d\n",
|
||||
rnti, prb_idx[0], prb_idx[1], cfg.cqi_pucch, cfg.cqi_idx);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -297,6 +320,8 @@ void sched_ue::ul_recv_len(uint32_t lcid, uint32_t len)
|
|||
}
|
||||
}
|
||||
}
|
||||
Debug("SCHED: recv_len=%d, lcid=%d, bsr={%d,%d,%d,%d}\n", len, lcid,
|
||||
lch[0].bsr, lch[1].bsr, lch[2].bsr, lch[3].bsr);
|
||||
}
|
||||
|
||||
void sched_ue::set_ul_crc(uint32_t tti, bool crc_res)
|
||||
|
@ -370,7 +395,7 @@ int sched_ue::generate_format1(dl_harq_proc *h,
|
|||
uint32_t nof_ctrl_symbols = cfi+(cell.nof_prb<10?1:0);
|
||||
uint32_t nof_re = srslte_ra_dl_grant_nof_re(&grant, cell, sf_idx, nof_ctrl_symbols);
|
||||
if (fixed_mcs_dl < 0) {
|
||||
tbs = alloc_tbs(dl_cqi, nof_prb, nof_re, req_bytes, max_mcs_dl, &mcs);
|
||||
tbs = alloc_tbs_dl(nof_prb, nof_re, req_bytes, &mcs);
|
||||
} else {
|
||||
tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(fixed_mcs_dl), nof_prb);
|
||||
mcs = fixed_mcs_dl;
|
||||
|
@ -441,7 +466,7 @@ int sched_ue::generate_format0(ul_harq_proc *h,
|
|||
uint32_t N_srs = 0;
|
||||
uint32_t nof_re = (2*(SRSLTE_CP_NSYMB(cell.cp)-1) - N_srs)*allocation.L*SRSLTE_NRE;
|
||||
if (fixed_mcs_ul < 0) {
|
||||
tbs = alloc_tbs(ul_cqi, allocation.L, nof_re, req_bytes, max_mcs_ul, &mcs);
|
||||
tbs = alloc_tbs_ul(allocation.L, nof_re, req_bytes, &mcs);
|
||||
} else {
|
||||
tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(fixed_mcs_ul), allocation.L);
|
||||
mcs = fixed_mcs_ul;
|
||||
|
@ -554,6 +579,10 @@ uint32_t sched_ue::get_pending_ul_new_data(uint32_t tti)
|
|||
} else {
|
||||
pending_data = 0;
|
||||
}
|
||||
if (pending_data) {
|
||||
Debug("SCHED: pending_data=%d, pending_ul_data=%d, bsr={%d,%d,%d,%d}\n", pending_data,pending_ul_data,
|
||||
lch[0].bsr, lch[1].bsr, lch[2].bsr, lch[3].bsr);
|
||||
}
|
||||
return pending_data;
|
||||
}
|
||||
|
||||
|
@ -581,7 +610,7 @@ uint32_t sched_ue::get_required_prb_dl(uint32_t req_bytes, uint32_t nof_ctrl_sym
|
|||
for (n=1;n<cell.nof_prb && nbytes < req_bytes;n++) {
|
||||
nof_re = srslte_ra_dl_approx_nof_re(cell, n, nof_ctrl_symbols);
|
||||
if (fixed_mcs_dl < 0) {
|
||||
tbs = alloc_tbs(dl_cqi, n, nof_re, 0, max_mcs_dl, &mcs);
|
||||
tbs = alloc_tbs_dl(n, nof_re, 0, &mcs);
|
||||
} else {
|
||||
tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(fixed_mcs_dl), n);
|
||||
}
|
||||
|
@ -610,7 +639,7 @@ uint32_t sched_ue::get_required_prb_ul(uint32_t req_bytes)
|
|||
uint32_t nof_re = (2*(SRSLTE_CP_NSYMB(cell.cp)-1) - N_srs)*n*SRSLTE_NRE;
|
||||
int tbs = 0;
|
||||
if (fixed_mcs_ul < 0) {
|
||||
tbs = alloc_tbs(ul_cqi, n, nof_re, 0, max_mcs_ul, &mcs);
|
||||
tbs = alloc_tbs_ul(n, nof_re, 0, &mcs);
|
||||
} else {
|
||||
tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(fixed_mcs_ul), n);
|
||||
}
|
||||
|
@ -731,36 +760,60 @@ uint32_t sched_ue::format1_count_prb(uint32_t bitmask, uint32_t cell_nof_prb) {
|
|||
return nof_prb;
|
||||
}
|
||||
|
||||
int sched_ue::cqi_to_tbs(uint32_t cqi, uint32_t nof_prb, uint32_t nof_re, uint32_t max_mcs, uint32_t *mcs) {
|
||||
int sched_ue::cqi_to_tbs(uint32_t cqi, uint32_t nof_prb, uint32_t nof_re, uint32_t max_mcs, uint32_t max_Qm, uint32_t *mcs) {
|
||||
float max_coderate = srslte_cqi_to_coderate(cqi);
|
||||
int sel_mcs = max_mcs+1;
|
||||
float coderate = 99;
|
||||
float coderate = 99;
|
||||
float eff_coderate = 99;
|
||||
uint32_t Qm = 1;
|
||||
int tbs = 0;
|
||||
|
||||
do {
|
||||
sel_mcs--;
|
||||
uint32_t tbs_idx = srslte_ra_tbs_idx_from_mcs(sel_mcs);
|
||||
tbs = srslte_ra_tbs_from_idx(tbs_idx, nof_prb);
|
||||
coderate = srslte_pdsch_coderate(tbs, nof_re);
|
||||
} while(sel_mcs > 0 && coderate >= max_coderate);
|
||||
coderate = srslte_coderate(tbs, nof_re);
|
||||
Qm = SRSLTE_MIN(max_Qm, srslte_mod_bits_x_symbol(srslte_ra_mod_from_mcs(sel_mcs)));
|
||||
eff_coderate = coderate/Qm;
|
||||
} while((sel_mcs > 0 && coderate > max_coderate) || eff_coderate > 0.930);
|
||||
if (mcs) {
|
||||
*mcs = (uint32_t) sel_mcs;
|
||||
}
|
||||
return tbs;
|
||||
}
|
||||
|
||||
/* In this scheduler we tend to use all the available bandwidth and select the MCS
|
||||
int sched_ue::alloc_tbs_dl(uint32_t nof_prb,
|
||||
uint32_t nof_re,
|
||||
uint32_t req_bytes,
|
||||
int *mcs)
|
||||
{
|
||||
return alloc_tbs(nof_prb, nof_re, req_bytes, false, mcs);
|
||||
}
|
||||
|
||||
int sched_ue::alloc_tbs_ul(uint32_t nof_prb,
|
||||
uint32_t nof_re,
|
||||
uint32_t req_bytes,
|
||||
int *mcs)
|
||||
{
|
||||
return alloc_tbs(nof_prb, nof_re, req_bytes, true, mcs);
|
||||
}
|
||||
|
||||
/* In this scheduler we tend to use all the available bandwidth and select the MCS
|
||||
* that approximates the minimum between the capacity and the requested rate
|
||||
*/
|
||||
int sched_ue::alloc_tbs(uint32_t cqi,
|
||||
uint32_t nof_prb,
|
||||
uint32_t nof_re,
|
||||
uint32_t req_bytes,
|
||||
uint32_t max_mcs,
|
||||
int *mcs)
|
||||
int sched_ue::alloc_tbs(uint32_t nof_prb,
|
||||
uint32_t nof_re,
|
||||
uint32_t req_bytes,
|
||||
bool is_ul,
|
||||
int *mcs)
|
||||
{
|
||||
uint32_t sel_mcs = 0;
|
||||
int tbs = cqi_to_tbs(cqi, nof_prb, nof_re, max_mcs, &sel_mcs)/8;
|
||||
uint32_t sel_mcs = 0;
|
||||
|
||||
uint32_t cqi = is_ul?ul_cqi:dl_cqi;
|
||||
uint32_t max_mcs = is_ul?max_mcs_ul:max_mcs_dl;
|
||||
uint32_t max_Qm = is_ul?4:6; // Allow 16-QAM in PUSCH Only
|
||||
|
||||
int tbs = cqi_to_tbs(cqi, nof_prb, nof_re, max_mcs, max_Qm, &sel_mcs)/8;
|
||||
|
||||
/* If less bytes are requested, lower the MCS */
|
||||
if (tbs > (int) req_bytes && req_bytes > 0) {
|
||||
|
|
|
@ -58,8 +58,14 @@ void ue::config(uint16_t rnti_, uint32_t nof_prb, sched_interface *sched_, rrc_i
|
|||
for(int i=0;i<NOF_HARQ_PROCESSES;i++) {
|
||||
pending_buffers[i] = NULL;
|
||||
}
|
||||
|
||||
// Set LCID group for SRB0 and SRB1
|
||||
set_lcg(0, 0);
|
||||
set_lcg(1, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ue::reset()
|
||||
{
|
||||
bzero(&metrics, sizeof(mac_metrics_t));
|
||||
|
@ -86,7 +92,16 @@ void ue::rl_failure_reset()
|
|||
{
|
||||
nof_failures = 0;
|
||||
}
|
||||
|
||||
|
||||
void ue::set_lcg(uint32_t lcid, uint32_t lcg)
|
||||
{
|
||||
// find and remove if already exists
|
||||
for (int i=0;i<4;i++) {
|
||||
lc_groups[lcg].erase(std::remove(lc_groups[lcg].begin(), lc_groups[lcg].end(), lcid), lc_groups[lcg].end());
|
||||
}
|
||||
lc_groups[lcg].push_back(lcid);
|
||||
}
|
||||
|
||||
srslte_softbuffer_rx_t* ue::get_rx_softbuffer(uint32_t tti)
|
||||
{
|
||||
return &softbuffer_rx[tti%NOF_HARQ_PROCESSES];
|
||||
|
@ -136,63 +151,83 @@ void ue::process_pdu(uint8_t* pdu, uint32_t nof_bytes, uint32_t tstamp)
|
|||
if (pcap) {
|
||||
pcap->write_ul_crnti(pdu, nof_bytes, rnti, true, last_tti);
|
||||
}
|
||||
|
||||
uint32_t lcid_most_data = 0;
|
||||
int most_data = -99;
|
||||
|
||||
while(mac_msg_ul.next()) {
|
||||
assert(mac_msg_ul.get());
|
||||
if (mac_msg_ul.get()->is_sdu())
|
||||
{
|
||||
if (mac_msg_ul.get()->is_sdu()) {
|
||||
// Route logical channel
|
||||
log_h->debug_hex(mac_msg_ul.get()->get_sdu_ptr(), mac_msg_ul.get()->get_payload_size(),
|
||||
"PDU: rnti=0x%x, lcid=%d, %d bytes\n",
|
||||
rnti, mac_msg_ul.get()->get_sdu_lcid(), mac_msg_ul.get()->get_payload_size());
|
||||
|
||||
|
||||
log_h->debug_hex(mac_msg_ul.get()->get_sdu_ptr(), mac_msg_ul.get()->get_payload_size(),
|
||||
"PDU: rnti=0x%x, lcid=%d, %d bytes\n",
|
||||
rnti, mac_msg_ul.get()->get_sdu_lcid(), mac_msg_ul.get()->get_payload_size());
|
||||
|
||||
|
||||
/* In some cases, an uplink transmission with only CQI has all zeros and gets routed to RRC
|
||||
* Compute the checksum if lcid=0 and avoid routing in that case
|
||||
*/
|
||||
bool route_pdu = true;
|
||||
bool route_pdu = true;
|
||||
if (mac_msg_ul.get()->get_sdu_lcid() == 0) {
|
||||
uint8_t *x = mac_msg_ul.get()->get_sdu_ptr();
|
||||
uint32_t sum = 0;
|
||||
for (uint32_t i=0;i<mac_msg_ul.get()->get_payload_size();i++) {
|
||||
uint32_t sum = 0;
|
||||
for (uint32_t i = 0; i < mac_msg_ul.get()->get_payload_size(); i++) {
|
||||
sum += x[i];
|
||||
}
|
||||
if (sum == 0) {
|
||||
route_pdu = false;
|
||||
route_pdu = false;
|
||||
Warning("Received all zero PDU\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (route_pdu) {
|
||||
rlc->write_pdu(rnti,
|
||||
mac_msg_ul.get()->get_sdu_lcid(),
|
||||
mac_msg_ul.get()->get_sdu_ptr(),
|
||||
mac_msg_ul.get()->get_payload_size());
|
||||
rlc->write_pdu(rnti,
|
||||
mac_msg_ul.get()->get_sdu_lcid(),
|
||||
mac_msg_ul.get()->get_sdu_ptr(),
|
||||
mac_msg_ul.get()->get_payload_size());
|
||||
}
|
||||
|
||||
|
||||
// Indicate scheduler to update BSR counters
|
||||
sched->ul_recv_len(rnti, mac_msg_ul.get()->get_sdu_lcid(), mac_msg_ul.get()->get_payload_size());
|
||||
|
||||
|
||||
if ((int) mac_msg_ul.get()->get_payload_size() > most_data) {
|
||||
most_data = (int) mac_msg_ul.get()->get_payload_size();
|
||||
lcid_most_data = mac_msg_ul.get()->get_sdu_lcid();
|
||||
}
|
||||
|
||||
// Save contention resolution if lcid == 0
|
||||
if (mac_msg_ul.get()->get_sdu_lcid() == 0 && route_pdu) {
|
||||
uint32_t nbytes = srslte::sch_subh::MAC_CE_CONTRES_LEN;
|
||||
uint32_t nbytes = srslte::sch_subh::MAC_CE_CONTRES_LEN;
|
||||
if (mac_msg_ul.get()->get_payload_size() >= nbytes) {
|
||||
uint8_t *ue_cri_ptr = (uint8_t*) &conres_id;
|
||||
uint8_t *pkt_ptr = mac_msg_ul.get()->get_sdu_ptr(); // Warning here: we want to include the
|
||||
for (uint32_t i=0;i<nbytes;i++) {
|
||||
ue_cri_ptr[nbytes-i-1] = pkt_ptr[i];
|
||||
uint8_t *ue_cri_ptr = (uint8_t *) &conres_id;
|
||||
uint8_t *pkt_ptr = mac_msg_ul.get()->get_sdu_ptr(); // Warning here: we want to include the
|
||||
for (uint32_t i = 0; i < nbytes; i++) {
|
||||
ue_cri_ptr[nbytes - i - 1] = pkt_ptr[i];
|
||||
}
|
||||
} else {
|
||||
Error("Received CCCH UL message of invalid size=%d bytes\n", mac_msg_ul.get()->get_payload_size());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Process MAC Control Element
|
||||
if (!process_ce(mac_msg_ul.get())) {
|
||||
Warning("Received Subheader with invalid or unkonwn LCID\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mac_msg_ul.reset();
|
||||
|
||||
/* Process CE after all SDUs because we need to update BSR after */
|
||||
bool bsr_received = false;
|
||||
while(mac_msg_ul.next()) {
|
||||
assert(mac_msg_ul.get());
|
||||
if (!mac_msg_ul.get()->is_sdu()) {
|
||||
// Process MAC Control Element
|
||||
bsr_received |= process_ce(mac_msg_ul.get());
|
||||
}
|
||||
}
|
||||
|
||||
// If BSR is not received means that new data has arrived and there is no space for BSR transmission
|
||||
if (!bsr_received && lcid_most_data > 2) {
|
||||
// Add BSR to the LCID for which most data was received
|
||||
sched->ul_bsr(rnti, lcid_most_data, 256, false); // false adds BSR instead of setting
|
||||
Debug("BSR not received. Giving extra grant\n");
|
||||
}
|
||||
|
||||
Debug("MAC PDU processed\n");
|
||||
|
||||
|
@ -220,9 +255,10 @@ void ue::push_pdu(uint32_t tti, uint32_t len)
|
|||
|
||||
bool ue::process_ce(srslte::sch_subh *subh) {
|
||||
uint32_t buff_size[4] = {0, 0, 0, 0};
|
||||
uint32_t idx = 0;
|
||||
float phr = 0;
|
||||
uint16_t old_rnti = 0;
|
||||
float phr = 0;
|
||||
int idx = 0;
|
||||
uint16_t old_rnti = 0;
|
||||
bool is_bsr = false;
|
||||
switch(subh->ce_type()) {
|
||||
case srslte::sch_subh::PHR_REPORT:
|
||||
phr = subh->get_phr();
|
||||
|
@ -241,23 +277,26 @@ bool ue::process_ce(srslte::sch_subh *subh) {
|
|||
}
|
||||
break;
|
||||
case srslte::sch_subh::TRUNC_BSR:
|
||||
case srslte::sch_subh::SHORT_BSR:
|
||||
case srslte::sch_subh::LONG_BSR:
|
||||
case srslte::sch_subh::SHORT_BSR:
|
||||
idx = subh->get_bsr(buff_size);
|
||||
if (idx > 0) {
|
||||
// Indicate BSR to scheduler
|
||||
sched->ul_bsr(rnti, idx, buff_size[idx]);
|
||||
Info("CE: Received BSR rnti=0x%x, lcid=%d, value=%d\n", rnti, idx, buff_size[idx]);
|
||||
} else if (idx == 0) {
|
||||
// TODO: map lcid group to lcid
|
||||
for (int i=0;i<4;i++) {
|
||||
sched->ul_bsr(rnti, i, buff_size[i]);
|
||||
}
|
||||
Info("CE: Received Long BSR rnti=0x%x, value=%d,%d,%d,%d\n", rnti,
|
||||
buff_size[0], buff_size[1], buff_size[2], buff_size[3]);
|
||||
} else {
|
||||
printf("Error!\n");
|
||||
for (uint32_t i=0;i<lc_groups[idx].size();i++) {
|
||||
// Indicate BSR to scheduler
|
||||
sched->ul_bsr(rnti, lc_groups[idx][i], buff_size[idx]);
|
||||
}
|
||||
Info("CE: Received %s BSR rnti=0x%x, lcg=%d, value=%d\n",
|
||||
subh->ce_type()==srslte::sch_subh::SHORT_BSR?"Short":"Trunc", rnti, idx, buff_size[idx]);
|
||||
is_bsr = true;
|
||||
break;
|
||||
case srslte::sch_subh::LONG_BSR:
|
||||
subh->get_bsr(buff_size);
|
||||
for (int idx=0;idx<4;idx++) {
|
||||
for (uint32_t i=0;i<lc_groups[idx].size();i++) {
|
||||
sched->ul_bsr(rnti, lc_groups[idx][i], buff_size[idx]);
|
||||
}
|
||||
}
|
||||
is_bsr = true;
|
||||
Info("CE: Received Long BSR rnti=0x%x, value=%d,%d,%d,%d\n", rnti,
|
||||
buff_size[0], buff_size[1], buff_size[2], buff_size[3]);
|
||||
break;
|
||||
case srslte::sch_subh::PADDING:
|
||||
Debug("CE: Received padding for rnti=0x%x\n", rnti);
|
||||
|
@ -266,7 +305,7 @@ bool ue::process_ce(srslte::sch_subh *subh) {
|
|||
Error("CE: Invalid lcid=0x%x\n", subh->ce_type());
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
return is_bsr;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ void parse_args(all_args_t *args, int argc, char* argv[]) {
|
|||
bpo::value<int>(&args->expert.mac.sched.pusch_mcs)->default_value(-1),
|
||||
"Optional fixed PUSCH MCS (ignores reported CQIs if specified)")
|
||||
("scheduler.pusch_max_mcs",
|
||||
bpo::value<int>(&args->expert.mac.sched.pusch_max_mcs)->default_value(16),
|
||||
bpo::value<int>(&args->expert.mac.sched.pusch_max_mcs)->default_value(-1),
|
||||
"Optional PUSCH MCS limit")
|
||||
("scheduler.nof_ctrl_symbols",
|
||||
bpo::value<int>(&args->expert.mac.sched.nof_ctrl_symbols)->default_value(3),
|
||||
|
@ -152,7 +152,7 @@ void parse_args(all_args_t *args, int argc, char* argv[]) {
|
|||
"Maximum number of turbo decoder iterations")
|
||||
|
||||
("expert.tx_amplitude",
|
||||
bpo::value<float>(&args->expert.phy.tx_amplitude)->default_value(0.8),
|
||||
bpo::value<float>(&args->expert.phy.tx_amplitude)->default_value(0.6),
|
||||
"Transmit amplitude factor")
|
||||
|
||||
("expert.nof_phy_threads",
|
||||
|
@ -176,7 +176,7 @@ void parse_args(all_args_t *args, int argc, char* argv[]) {
|
|||
"Chooses the coefficients for the 3-tap channel estimator centered filter.")
|
||||
|
||||
("expert.rrc_inactivity_timer",
|
||||
bpo::value<uint32_t>(&args->expert.rrc_inactivity_timer)->default_value(30000),
|
||||
bpo::value<uint32_t>(&args->expert.rrc_inactivity_timer)->default_value(10000),
|
||||
"Inactivity timer in ms")
|
||||
|
||||
|
||||
|
|
|
@ -49,6 +49,8 @@ using namespace std;
|
|||
#ifdef ENABLE_GUI
|
||||
#include "srsgui/srsgui.h"
|
||||
#include <semaphore.h>
|
||||
#include <srslte/phy/phch/ra.h>
|
||||
|
||||
void init_plots(srsenb::phch_worker *worker);
|
||||
pthread_t plot_thread;
|
||||
sem_t plot_sem;
|
||||
|
@ -114,7 +116,7 @@ void phch_worker::init(phch_common* phy_, srslte::log *log_h_)
|
|||
return;
|
||||
}
|
||||
|
||||
srslte_pucch_set_threshold(&enb_ul.pucch, 0.8, 0.5);
|
||||
srslte_pucch_set_threshold(&enb_ul.pucch, 0.8);
|
||||
srslte_sch_set_max_noi(&enb_ul.pusch.ul_sch, phy->params.pusch_max_its);
|
||||
srslte_enb_dl_set_amp(&enb_dl, phy->params.tx_amplitude);
|
||||
|
||||
|
@ -183,8 +185,8 @@ void phch_worker::set_config_dedicated(uint16_t rnti,
|
|||
srslte_enb_ul_cfg_ue(&enb_ul, rnti, uci_cfg, pucch_sched, srs_cfg);
|
||||
|
||||
ue_db[rnti].I_sr = I_sr;
|
||||
ue_db[rnti].I_sr_en = true;
|
||||
|
||||
ue_db[rnti].I_sr_en = true;
|
||||
|
||||
if (pucch_cqi) {
|
||||
ue_db[rnti].pmi_idx = pmi_idx;
|
||||
ue_db[rnti].cqi_en = true;
|
||||
|
@ -360,7 +362,6 @@ int phch_worker::decode_pusch(srslte_enb_ul_pusch_t *grants, uint32_t nof_pusch,
|
|||
}
|
||||
if (cqi_enabled) {
|
||||
uci_data.uci_cqi_len = srslte_cqi_size(&cqi_value);
|
||||
Info("cqi enabled len=%d\n", uci_data.uci_cqi_len);
|
||||
}
|
||||
|
||||
// mark this tti as having an ul grant to avoid pucch
|
||||
|
@ -369,7 +370,11 @@ int phch_worker::decode_pusch(srslte_enb_ul_pusch_t *grants, uint32_t nof_pusch,
|
|||
srslte_ra_ul_grant_t phy_grant;
|
||||
int res = -1;
|
||||
if (!srslte_ra_ul_dci_to_grant(&grants[i].grant, enb_ul.cell.nof_prb, n_rb_ho, &phy_grant, tti%8)) {
|
||||
res = srslte_enb_ul_get_pusch(&enb_ul, &phy_grant, grants[i].softbuffer,
|
||||
if (phy_grant.mcs.mod == SRSLTE_MOD_64QAM) {
|
||||
phy_grant.mcs.mod = SRSLTE_MOD_16QAM;
|
||||
}
|
||||
phy_grant.Qm = SRSLTE_MIN(phy_grant.Qm, 4);
|
||||
res = srslte_enb_ul_get_pusch(&enb_ul, &phy_grant, grants[i].softbuffer,
|
||||
rnti, grants[i].rv_idx,
|
||||
grants[i].current_tx_nb,
|
||||
grants[i].data,
|
||||
|
@ -420,7 +425,7 @@ int phch_worker::decode_pusch(srslte_enb_ul_pusch_t *grants, uint32_t nof_pusch,
|
|||
}
|
||||
*/
|
||||
log_h->info_hex(grants[i].data, phy_grant.mcs.tbs/8,
|
||||
"PUSCH: rnti=0x%x, prb=(%d,%d), tbs=%d, mcs=%d, rv=%d, snr=%.1f dB, n_iter=%d, crc=%s%s%s%s\n",
|
||||
"PUSCH: rnti=0x%x, prb=(%d,%d), tbs=%d, mcs=%d, rv=%d, snr=%.1f dB, n_iter=%d, crc=%s%s%s%s\n",
|
||||
rnti, phy_grant.n_prb[0], phy_grant.n_prb[0]+phy_grant.L_prb,
|
||||
phy_grant.mcs.tbs/8, phy_grant.mcs.idx, grants[i].grant.rv_idx,
|
||||
snr_db,
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
*/
|
||||
|
||||
#include "upper/pdcp.h"
|
||||
#include "upper/common_enb.h"
|
||||
|
||||
namespace srsenb {
|
||||
|
||||
|
@ -50,7 +51,7 @@ void pdcp::add_user(uint16_t rnti)
|
|||
{
|
||||
if (users.count(rnti) == 0) {
|
||||
srslte::pdcp *obj = new srslte::pdcp;
|
||||
obj->init(&users[rnti].rlc_itf, &users[rnti].rrc_itf, &users[rnti].gtpu_itf, log_h, SECURITY_DIRECTION_DOWNLINK);
|
||||
obj->init(&users[rnti].rlc_itf, &users[rnti].rrc_itf, &users[rnti].gtpu_itf, log_h, RB_ID_SRB0, SECURITY_DIRECTION_DOWNLINK);
|
||||
users[rnti].rlc_itf.rnti = rnti;
|
||||
users[rnti].gtpu_itf.rnti = rnti;
|
||||
users[rnti].rrc_itf.rnti = rnti;
|
||||
|
@ -72,14 +73,13 @@ void pdcp::rem_user(uint16_t rnti)
|
|||
}
|
||||
}
|
||||
|
||||
void pdcp::add_bearer(uint16_t rnti, uint32_t lcid, LIBLTE_RRC_PDCP_CONFIG_STRUCT* cnfg)
|
||||
void pdcp::add_bearer(uint16_t rnti, uint32_t lcid, srslte::srslte_pdcp_config_t cfg)
|
||||
{
|
||||
if (users.count(rnti)) {
|
||||
users[rnti].pdcp->add_bearer(lcid, cnfg);
|
||||
users[rnti].pdcp->add_bearer(lcid, cfg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void pdcp::reset(uint16_t rnti)
|
||||
{
|
||||
if (users.count(rnti)) {
|
||||
|
@ -143,6 +143,10 @@ void pdcp::user_interface_rrc::write_pdu_pcch(srslte::byte_buffer_t* pdu)
|
|||
{
|
||||
fprintf(stderr, "Error: Received PCCH from ue=%d\n", rnti);
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string pdcp::user_interface_rrc::get_rb_name(uint32_t lcid)
|
||||
{
|
||||
return std::string(rb_id_text[lcid]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
*/
|
||||
|
||||
#include "upper/rlc.h"
|
||||
#include "upper/common_enb.h"
|
||||
|
||||
namespace srsenb {
|
||||
|
||||
|
@ -53,7 +54,7 @@ void rlc::add_user(uint16_t rnti)
|
|||
{
|
||||
if (users.count(rnti) == 0) {
|
||||
srslte::rlc *obj = new srslte::rlc;
|
||||
obj->init(&users[rnti], &users[rnti], &users[rnti], log_h, mac_timers);
|
||||
obj->init(&users[rnti], &users[rnti], &users[rnti], log_h, mac_timers, RB_ID_SRB0);
|
||||
users[rnti].rnti = rnti;
|
||||
users[rnti].pdcp = pdcp;
|
||||
users[rnti].rrc = rrc;
|
||||
|
@ -82,11 +83,11 @@ void rlc::reset(uint16_t rnti)
|
|||
void rlc::clear_buffer(uint16_t rnti)
|
||||
{
|
||||
if (users.count(rnti)) {
|
||||
log_h->info("Clearing buffer rnti=0x%x\n", rnti);
|
||||
users[rnti].rlc->reset();
|
||||
users[rnti].rlc->empty_queue();
|
||||
for (int i=0;i<SRSLTE_N_RADIO_BEARERS;i++) {
|
||||
mac->rlc_buffer_state(rnti, i, 0, 0);
|
||||
}
|
||||
log_h->info("Cleared buffer rnti=0x%x\n", rnti);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,7 +98,7 @@ void rlc::add_bearer(uint16_t rnti, uint32_t lcid)
|
|||
}
|
||||
}
|
||||
|
||||
void rlc::add_bearer(uint16_t rnti, uint32_t lcid, LIBLTE_RRC_RLC_CONFIG_STRUCT* cnfg)
|
||||
void rlc::add_bearer(uint16_t rnti, uint32_t lcid, srslte::srslte_rlc_config_t cnfg)
|
||||
{
|
||||
if (users.count(rnti)) {
|
||||
users[rnti].rlc->add_bearer(lcid, cnfg);
|
||||
|
@ -184,4 +185,9 @@ void rlc::user_interface::write_pdu_pcch(srslte::byte_buffer_t* sdu)
|
|||
fprintf(stderr, "Error: Received PCCH from ue=%d\n", rnti);
|
||||
}
|
||||
|
||||
std::string rlc::user_interface::get_rb_name(uint32_t lcid)
|
||||
{
|
||||
return std::string(rb_id_text[lcid]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,13 +24,13 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <srslte/interfaces/sched_interface.h>
|
||||
#include <srslte/asn1/liblte_rrc.h>
|
||||
#include "srslte/asn1/liblte_mme.h"
|
||||
#include "upper/rrc.h"
|
||||
|
||||
using srslte::rb_id_text;
|
||||
using srslte::byte_buffer_t;
|
||||
using srslte::bit_buffer_t;
|
||||
using srslte::rb_id_t;
|
||||
|
||||
namespace srsenb {
|
||||
|
||||
|
@ -314,12 +314,11 @@ void rrc::release_complete(uint16_t rnti)
|
|||
if (!users[rnti].is_idle()) {
|
||||
rlc->clear_buffer(rnti);
|
||||
users[rnti].send_connection_release();
|
||||
// There is no RRCReleaseComplete message from UE thus sleep to enable all retx in PHY +50%
|
||||
usleep(1.5*8*1e3*cfg.mac_cnfg.ulsch_cnfg.max_harq_tx);
|
||||
// There is no RRCReleaseComplete message from UE thus wait ~100 subframes for tx
|
||||
usleep(100000);
|
||||
}
|
||||
rem_user(rnti);
|
||||
} else {
|
||||
|
||||
rrc_log->error("Received ReleaseComplete for unknown rnti=0x%x\n", rnti);
|
||||
}
|
||||
}
|
||||
|
@ -439,7 +438,7 @@ void rrc::add_paging_id(uint32_t ueid, LIBLTE_S1AP_UEPAGINGID_STRUCT UEPagingID)
|
|||
// Described in Section 7 of 36.304
|
||||
bool rrc::is_paging_opportunity(uint32_t tti, uint32_t *payload_len)
|
||||
{
|
||||
int sf_pattern[4][3] = {{9, 4, 0}, {-1, 9, 4}, {-1, -1, 5}, {-1, -1, 9}};
|
||||
int sf_pattern[4][4] = {{9, 4, -1, 0}, {-1, 9, -1, 4}, {-1, -1, -1, 5}, {-1, -1, -1, 9}};
|
||||
|
||||
if (pending_paging.empty()) {
|
||||
return false;
|
||||
|
@ -468,7 +467,7 @@ bool rrc::is_paging_opportunity(uint32_t tti, uint32_t *payload_len)
|
|||
|
||||
if ((sfn % T) == (T/N) * (ueid % N)) {
|
||||
|
||||
int sf_idx = sf_pattern[i_s%4][(Ns-1)%3];
|
||||
int sf_idx = sf_pattern[i_s%4][(Ns-1)%4];
|
||||
if (sf_idx < 0) {
|
||||
rrc_log->error("SF pattern is N/A for Ns=%d, i_s=%d, imsi_decimal=%d\n", Ns, i_s, ueid);
|
||||
} else if ((uint32_t) sf_idx == (tti%10)) {
|
||||
|
@ -607,11 +606,11 @@ void rrc::run_thread()
|
|||
}
|
||||
switch(p.lcid)
|
||||
{
|
||||
case srslte::RB_ID_SRB0:
|
||||
case RB_ID_SRB0:
|
||||
parse_ul_ccch(p.rnti, p.pdu);
|
||||
break;
|
||||
case srslte::RB_ID_SRB1:
|
||||
case srslte::RB_ID_SRB2:
|
||||
case RB_ID_SRB1:
|
||||
case RB_ID_SRB2:
|
||||
parse_ul_dcch(p.rnti, p.lcid, p.pdu);
|
||||
break;
|
||||
case LCID_REM_USER:
|
||||
|
@ -919,7 +918,7 @@ void rrc::ue::set_security_key(uint8_t* key, uint32_t length)
|
|||
k_up_enc,
|
||||
k_up_int);
|
||||
|
||||
parent->configure_security(rnti, srslte::RB_ID_SRB1,
|
||||
parent->configure_security(rnti, RB_ID_SRB1,
|
||||
k_rrc_enc, k_rrc_int,
|
||||
k_up_enc, k_up_int,
|
||||
cipher_algo, integ_algo);
|
||||
|
@ -1156,9 +1155,14 @@ void rrc::ue::send_connection_setup(bool is_setup)
|
|||
// Configure MAC
|
||||
parent->mac->ue_cfg(rnti, &sched_cfg);
|
||||
|
||||
// Configure SRB1 in RLC and PDCP
|
||||
// Configure SRB1 in RLC
|
||||
parent->rlc->add_bearer(rnti, 1);
|
||||
parent->pdcp->add_bearer(rnti, 1);
|
||||
|
||||
// Configure SRB1 in PDCP
|
||||
srslte::srslte_pdcp_config_t pdcp_cnfg;
|
||||
pdcp_cnfg.is_control = true;
|
||||
pdcp_cnfg.direction = SECURITY_DIRECTION_DOWNLINK;
|
||||
parent->pdcp->add_bearer(rnti, 1, pdcp_cnfg);
|
||||
|
||||
// Configure PHY layer
|
||||
parent->phy->set_config_dedicated(rnti, phy_cfg);
|
||||
|
@ -1170,7 +1174,6 @@ void rrc::ue::send_connection_setup(bool is_setup)
|
|||
rr_cfg->sps_cnfg_present = false;
|
||||
|
||||
send_dl_ccch(&dl_ccch_msg);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -1309,17 +1312,34 @@ void rrc::ue::send_connection_reconf(srslte::byte_buffer_t *pdu)
|
|||
// Add SRB2 and DRB1 to the scheduler
|
||||
srsenb::sched_interface::ue_bearer_cfg_t bearer_cfg;
|
||||
bearer_cfg.direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH;
|
||||
bearer_cfg.group = 0;
|
||||
parent->mac->bearer_ue_cfg(rnti, 2, &bearer_cfg);
|
||||
bearer_cfg.group = conn_reconf->rr_cnfg_ded.drb_to_add_mod_list[0].lc_cnfg.ul_specific_params.log_chan_group;
|
||||
parent->mac->bearer_ue_cfg(rnti, 3, &bearer_cfg);
|
||||
|
||||
// Configure SRB2 in RLC and PDCP
|
||||
parent->rlc->add_bearer(rnti, 2);
|
||||
parent->pdcp->add_bearer(rnti, 2);
|
||||
|
||||
|
||||
// Configure SRB2 in PDCP
|
||||
srslte::srslte_pdcp_config_t pdcp_cnfg;
|
||||
pdcp_cnfg.direction = SECURITY_DIRECTION_DOWNLINK;
|
||||
pdcp_cnfg.is_control = true;
|
||||
pdcp_cnfg.is_data = false;
|
||||
parent->pdcp->add_bearer(rnti, 2, pdcp_cnfg);
|
||||
|
||||
// Configure DRB1 in RLC
|
||||
parent->rlc->add_bearer(rnti, 3, &conn_reconf->rr_cnfg_ded.drb_to_add_mod_list[0].rlc_cnfg);
|
||||
|
||||
// Configure DRB1 in PDCP
|
||||
parent->pdcp->add_bearer(rnti, 3, &conn_reconf->rr_cnfg_ded.drb_to_add_mod_list[0].pdcp_cnfg);
|
||||
pdcp_cnfg.is_control = false;
|
||||
pdcp_cnfg.is_data = true;
|
||||
if (conn_reconf->rr_cnfg_ded.drb_to_add_mod_list[0].pdcp_cnfg.rlc_um_pdcp_sn_size_present) {
|
||||
if(LIBLTE_RRC_PDCP_SN_SIZE_7_BITS == conn_reconf->rr_cnfg_ded.drb_to_add_mod_list[0].pdcp_cnfg.rlc_um_pdcp_sn_size) {
|
||||
pdcp_cnfg.sn_len = 7;
|
||||
}
|
||||
}
|
||||
parent->pdcp->add_bearer(rnti, 3, pdcp_cnfg);
|
||||
|
||||
// DRB1 has already been configured in GTPU through bearer setup
|
||||
|
||||
// Add NAS Attach accept
|
||||
|
@ -1432,7 +1452,7 @@ void rrc::ue::send_dl_ccch(LIBLTE_RRC_DL_CCCH_MSG_STRUCT *dl_ccch_msg)
|
|||
rnti,
|
||||
liblte_rrc_dl_ccch_msg_type_text[dl_ccch_msg->msg_type]);
|
||||
|
||||
parent->pdcp->write_sdu(rnti, srslte::RB_ID_SRB0, pdu);
|
||||
parent->pdcp->write_sdu(rnti, RB_ID_SRB0, pdu);
|
||||
|
||||
} else {
|
||||
parent->rrc_log->error("Allocating pdu\n");
|
||||
|
@ -1453,7 +1473,7 @@ void rrc::ue::send_dl_dcch(LIBLTE_RRC_DL_DCCH_MSG_STRUCT *dl_dcch_msg, byte_buff
|
|||
rnti,
|
||||
liblte_rrc_dl_dcch_msg_type_text[dl_dcch_msg->msg_type]);
|
||||
|
||||
parent->pdcp->write_sdu(rnti, srslte::RB_ID_SRB1, pdu);
|
||||
parent->pdcp->write_sdu(rnti, RB_ID_SRB1, pdu);
|
||||
|
||||
} else {
|
||||
parent->rrc_log->error("Allocating pdu\n");
|
||||
|
|
|
@ -173,7 +173,7 @@ void s1ap::build_tai_cgi()
|
|||
void s1ap::initial_ue(uint16_t rnti, srslte::byte_buffer_t *pdu)
|
||||
{
|
||||
ue_ctxt_map[rnti].eNB_UE_S1AP_ID = next_eNB_UE_S1AP_ID++;
|
||||
ue_ctxt_map[rnti].stream_id = next_ue_stream_id++;
|
||||
ue_ctxt_map[rnti].stream_id = 1;
|
||||
ue_ctxt_map[rnti].release_requested = false;
|
||||
enbid_to_rnti_map[ue_ctxt_map[rnti].eNB_UE_S1AP_ID] = rnti;
|
||||
send_initialuemessage(rnti, pdu, false);
|
||||
|
@ -182,7 +182,7 @@ void s1ap::initial_ue(uint16_t rnti, srslte::byte_buffer_t *pdu)
|
|||
void s1ap::initial_ue(uint16_t rnti, srslte::byte_buffer_t *pdu, uint32_t m_tmsi, uint8_t mmec)
|
||||
{
|
||||
ue_ctxt_map[rnti].eNB_UE_S1AP_ID = next_eNB_UE_S1AP_ID++;
|
||||
ue_ctxt_map[rnti].stream_id = next_ue_stream_id++;
|
||||
ue_ctxt_map[rnti].stream_id = 1;
|
||||
ue_ctxt_map[rnti].release_requested = false;
|
||||
enbid_to_rnti_map[ue_ctxt_map[rnti].eNB_UE_S1AP_ID] = rnti;
|
||||
send_initialuemessage(rnti, pdu, true, m_tmsi, mmec);
|
||||
|
@ -594,7 +594,7 @@ bool s1ap::handle_uectxtreleasecommand(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMA
|
|||
s1ap_log->warning("Not handling S1AP message extension\n");
|
||||
}
|
||||
|
||||
uint16_t rnti;
|
||||
uint16_t rnti = 0;
|
||||
if(msg->UE_S1AP_IDs.choice_type == LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_UE_S1AP_ID_PAIR) {
|
||||
|
||||
if(msg->UE_S1AP_IDs.choice.uE_S1AP_ID_pair.ext) {
|
||||
|
|
|
@ -155,6 +155,7 @@ public:
|
|||
void set_activity_user(uint16_t rnti) {}
|
||||
bool is_paging_opportunity(uint32_t tti, uint32_t *payload_len) {return false;}
|
||||
void read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) {}
|
||||
std::string get_rb_name(uint32_t lcid) { return std::string("lcid"); }
|
||||
|
||||
void write_pdu(uint32_t lcid, srslte::byte_buffer_t *sdu)
|
||||
{
|
||||
|
@ -566,7 +567,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
my_phy.init(&phy_args, &phy_cfg, &my_radio, &my_mac, &log_phy);
|
||||
my_mac.init(&mac_args, &mac_cfg.cell, &my_phy, &my_tester, &my_tester, &log_mac);
|
||||
my_rlc.init(&my_tester, &my_tester, &my_tester, &log_rlc, &my_mac);
|
||||
my_rlc.init(&my_tester, &my_tester, &my_tester, &log_rlc, &my_mac, 0 /* SRB0 */);
|
||||
my_tester.init(&my_rlc, &my_mac, &my_phy, &log_tester, prog_args.ip_address);
|
||||
|
||||
if (prog_args.enable_gui) {
|
||||
|
|
|
@ -41,8 +41,8 @@ namespace srsue {
|
|||
class demux : public srslte::pdu_queue::process_callback
|
||||
{
|
||||
public:
|
||||
demux();
|
||||
void init(phy_interface_mac* phy_h_, rlc_interface_mac *rlc, srslte::log* log_h_, srslte::timers* timers_db_);
|
||||
demux(uint8_t nof_harq_proc_);
|
||||
void init(phy_interface_mac_common* phy_h_, rlc_interface_mac *rlc, srslte::log* log_h_, srslte::timers* timers_db_);
|
||||
|
||||
bool process_pdus();
|
||||
uint8_t* request_buffer(uint32_t pid, uint32_t len);
|
||||
|
@ -57,7 +57,6 @@ public:
|
|||
void process_pdu(uint8_t *pdu, uint32_t nof_bytes, uint32_t tstamp);
|
||||
|
||||
private:
|
||||
const static int NOF_HARQ_PID = 8;
|
||||
const static int MAX_PDU_LEN = 150*1024/8; // ~ 150 Mbps
|
||||
const static int NOF_BUFFER_PDUS = 64; // Number of PDU buffers per HARQ pid
|
||||
uint8_t bcch_buffer[1024]; // BCCH PID has a dedicated buffer
|
||||
|
@ -73,10 +72,11 @@ private:
|
|||
|
||||
bool is_uecrid_successful;
|
||||
|
||||
phy_interface_mac *phy_h;
|
||||
srslte::log *log_h;
|
||||
srslte::timers *timers_db;
|
||||
rlc_interface_mac *rlc;
|
||||
phy_interface_mac_common *phy_h;
|
||||
srslte::log *log_h;
|
||||
srslte::timers *timers_db;
|
||||
rlc_interface_mac *rlc;
|
||||
uint8_t nof_harq_proc;
|
||||
|
||||
// Buffer of PDUs
|
||||
srslte::pdu_queue pdus;
|
||||
|
|
|
@ -27,9 +27,15 @@
|
|||
#ifndef DL_HARQ_H
|
||||
#define DL_HARQ_H
|
||||
|
||||
#define Error(fmt, ...) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define Warning(fmt, ...) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define Info(fmt, ...) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define Debug(fmt, ...) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
|
||||
#include "srslte/common/log.h"
|
||||
#include "srslte/common/timers.h"
|
||||
#include "mac/demux.h"
|
||||
#include "mac/mac_common.h"
|
||||
#include "mac/dl_sps.h"
|
||||
#include "srslte/common/mac_pcap.h"
|
||||
|
||||
|
@ -39,79 +45,314 @@
|
|||
|
||||
|
||||
namespace srsue {
|
||||
|
||||
|
||||
template <std::size_t N, typename Tgrant, typename Taction, typename Tphygrant>
|
||||
class dl_harq_entity
|
||||
{
|
||||
public:
|
||||
|
||||
const static uint32_t NOF_HARQ_PROC = 8;
|
||||
const static uint32_t HARQ_BCCH_PID = NOF_HARQ_PROC;
|
||||
const static uint32_t HARQ_BCCH_PID = N;
|
||||
|
||||
dl_harq_entity();
|
||||
bool init(srslte::log *log_h_, mac_interface_rrc::mac_cfg_t *mac_cfg, srslte::timers *timers_, demux *demux_unit);
|
||||
|
||||
|
||||
/***************** PHY->MAC interface for DL processes **************************/
|
||||
void new_grant_dl(mac_interface_phy::mac_grant_t grant, mac_interface_phy::tb_action_dl_t *action);
|
||||
void tb_decoded(bool ack, srslte_rnti_type_t rnti_type, uint32_t harq_pid);
|
||||
|
||||
|
||||
void reset();
|
||||
void start_pcap(srslte::mac_pcap* pcap);
|
||||
int get_current_tbs(uint32_t harq_pid);
|
||||
dl_harq_entity() : proc(N+1)
|
||||
{
|
||||
pcap = NULL;
|
||||
}
|
||||
|
||||
bool init(srslte::log *log_h_, srslte::timers *timers_, demux *demux_unit_)
|
||||
{
|
||||
timers_db = timers_;
|
||||
demux_unit = demux_unit_;
|
||||
si_window_start = 0;
|
||||
log_h = log_h_;
|
||||
for (uint32_t i=0;i<N+1;i++) {
|
||||
if (!proc[i].init(i, this)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/***************** PHY->MAC interface for DL processes **************************/
|
||||
void new_grant_dl(Tgrant grant, Taction *action)
|
||||
{
|
||||
if (grant.rnti_type != SRSLTE_RNTI_SPS) {
|
||||
uint32_t harq_pid;
|
||||
// Set BCCH PID for SI RNTI
|
||||
if (grant.rnti_type == SRSLTE_RNTI_SI) {
|
||||
harq_pid = HARQ_BCCH_PID;
|
||||
} else {
|
||||
harq_pid = grant.pid%N;
|
||||
}
|
||||
if (grant.rnti_type == SRSLTE_RNTI_TEMP && last_temporal_crnti != grant.rnti) {
|
||||
grant.ndi = true;
|
||||
Info("Set NDI=1 for Temp-RNTI DL grant\n");
|
||||
last_temporal_crnti = grant.rnti;
|
||||
}
|
||||
if (grant.rnti_type == SRSLTE_RNTI_USER && proc[harq_pid].is_sps()) {
|
||||
grant.ndi = true;
|
||||
Info("Set NDI=1 for C-RNTI DL grant\n");
|
||||
}
|
||||
proc[harq_pid].new_grant_dl(grant, action);
|
||||
} else {
|
||||
/* This is for SPS scheduling */
|
||||
uint32_t harq_pid = get_harq_sps_pid(grant.tti)%N;
|
||||
if (grant.ndi) {
|
||||
grant.ndi = false;
|
||||
proc[harq_pid].new_grant_dl(grant, action);
|
||||
} else {
|
||||
if (grant.is_sps_release) {
|
||||
dl_sps_assig.clear();
|
||||
if (timers_db->get(TIME_ALIGNMENT)->is_running()) {
|
||||
//phy_h->send_sps_ack();
|
||||
Warning("PHY Send SPS ACK not implemented\n");
|
||||
}
|
||||
} else {
|
||||
Error("SPS not implemented\n");
|
||||
//dl_sps_assig.reset(grant.tti, grant);
|
||||
//grant.ndi = true;
|
||||
//procs[harq_pid].save_grant();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void tb_decoded(bool ack, srslte_rnti_type_t rnti_type, uint32_t harq_pid)
|
||||
{
|
||||
if (rnti_type == SRSLTE_RNTI_SI) {
|
||||
proc[N].tb_decoded(ack);
|
||||
} else {
|
||||
proc[harq_pid%N].tb_decoded(ack);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void reset()
|
||||
{
|
||||
for (uint32_t i=0;i<N+1;i++) {
|
||||
proc[i].reset();
|
||||
}
|
||||
dl_sps_assig.clear();
|
||||
}
|
||||
|
||||
void start_pcap(srslte::mac_pcap* pcap_) { pcap = pcap_; }
|
||||
|
||||
int get_current_tbs(uint32_t harq_pid) { return proc[harq_pid%N].get_current_tbs(); }
|
||||
|
||||
void set_si_window_start(int si_window_start_) { si_window_start = si_window_start_; }
|
||||
|
||||
float get_average_retx() { return average_retx; }
|
||||
|
||||
void set_si_window_start(int si_window_start);
|
||||
|
||||
float get_average_retx();
|
||||
|
||||
private:
|
||||
|
||||
|
||||
class dl_harq_process {
|
||||
public:
|
||||
dl_harq_process();
|
||||
bool init(uint32_t pid, dl_harq_entity *parent);
|
||||
void reset();
|
||||
bool is_sps();
|
||||
void new_grant_dl(mac_interface_phy::mac_grant_t grant, mac_interface_phy::tb_action_dl_t *action);
|
||||
void tb_decoded(bool ack);
|
||||
int get_current_tbs();
|
||||
dl_harq_process()
|
||||
{
|
||||
is_initiated = false;
|
||||
ack = false;
|
||||
bzero(&cur_grant, sizeof(Tgrant));
|
||||
}
|
||||
|
||||
bool init(uint32_t pid_, dl_harq_entity *parent)
|
||||
{
|
||||
if (srslte_softbuffer_rx_init(&softbuffer, 110)) {
|
||||
Error("Error initiating soft buffer\n");
|
||||
return false;
|
||||
} else {
|
||||
pid = pid_;
|
||||
is_initiated = true;
|
||||
harq_entity = parent;
|
||||
log_h = harq_entity->log_h;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
ack = false;
|
||||
payload_buffer_ptr = NULL;
|
||||
bzero(&cur_grant, sizeof(Tgrant));
|
||||
if (is_initiated) {
|
||||
srslte_softbuffer_rx_reset(&softbuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void new_grant_dl(Tgrant grant, Taction *action)
|
||||
{
|
||||
// Compute RV for BCCH when not specified in PDCCH format
|
||||
if (pid == HARQ_BCCH_PID && grant.rv == -1) {
|
||||
uint32_t k;
|
||||
if ((grant.tti/10)%2 == 0 && grant.tti%10 == 5) { // This is SIB1, k is different
|
||||
k = (grant.tti/20)%4;
|
||||
grant.rv = ((uint32_t) ceilf((float)1.5*k))%4;
|
||||
} else if (grant.rv == -1) {
|
||||
k = (grant.tti-harq_entity->si_window_start)%4;
|
||||
grant.rv = ((uint32_t) ceilf((float)1.5*k))%4;
|
||||
}
|
||||
}
|
||||
calc_is_new_transmission(grant);
|
||||
if (is_new_transmission) {
|
||||
ack = false;
|
||||
srslte_softbuffer_rx_reset_tbs(&softbuffer, cur_grant.n_bytes*8);
|
||||
n_retx = 0;
|
||||
}
|
||||
|
||||
// Save grant
|
||||
grant.last_ndi = cur_grant.ndi;
|
||||
grant.last_tti = cur_grant.tti;
|
||||
memcpy(&cur_grant, &grant, sizeof(Tgrant));
|
||||
|
||||
// Fill action structure
|
||||
bzero(action, sizeof(Taction));
|
||||
action->default_ack = ack;
|
||||
action->generate_ack = true;
|
||||
action->decode_enabled = false;
|
||||
|
||||
// If data has not yet been successfully decoded
|
||||
if (ack == false) {
|
||||
|
||||
// Instruct the PHY To combine the received data and attempt to decode it
|
||||
payload_buffer_ptr = harq_entity->demux_unit->request_buffer(pid, cur_grant.n_bytes);
|
||||
action->payload_ptr = payload_buffer_ptr;
|
||||
if (!action->payload_ptr) {
|
||||
action->decode_enabled = false;
|
||||
Error("Can't get a buffer for TBS=%d\n", cur_grant.n_bytes);
|
||||
return;
|
||||
}
|
||||
action->decode_enabled = true;
|
||||
action->rv = cur_grant.rv;
|
||||
action->rnti = cur_grant.rnti;
|
||||
action->softbuffer = &softbuffer;
|
||||
memcpy(&action->phy_grant, &cur_grant.phy_grant, sizeof(Tphygrant));
|
||||
n_retx++;
|
||||
|
||||
} else {
|
||||
Warning("DL PID %d: Received duplicate TB. Discarting and retransmitting ACK\n", pid);
|
||||
}
|
||||
|
||||
if (pid == HARQ_BCCH_PID || harq_entity->timers_db->get(TIME_ALIGNMENT)->is_expired()) {
|
||||
// Do not generate ACK
|
||||
Debug("Not generating ACK\n");
|
||||
action->generate_ack = false;
|
||||
} else {
|
||||
if (cur_grant.rnti_type == SRSLTE_RNTI_TEMP && ack == false) {
|
||||
// Postpone ACK after contention resolution is resolved
|
||||
action->generate_ack_callback = harq_entity->generate_ack_callback;
|
||||
action->generate_ack_callback_arg = harq_entity->demux_unit;
|
||||
Debug("ACK pending contention resolution\n");
|
||||
} else {
|
||||
Debug("Generating ACK\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tb_decoded(bool ack_)
|
||||
{
|
||||
ack = ack_;
|
||||
if (ack == true) {
|
||||
if (pid == HARQ_BCCH_PID) {
|
||||
if (harq_entity->pcap) {
|
||||
harq_entity->pcap->write_dl_sirnti(payload_buffer_ptr, cur_grant.n_bytes, ack, cur_grant.tti);
|
||||
}
|
||||
Debug("Delivering PDU=%d bytes to Dissassemble and Demux unit (BCCH)\n", cur_grant.n_bytes);
|
||||
harq_entity->demux_unit->push_pdu(pid, payload_buffer_ptr, cur_grant.n_bytes, cur_grant.tti);
|
||||
} else {
|
||||
if (harq_entity->pcap) {
|
||||
harq_entity->pcap->write_dl_crnti(payload_buffer_ptr, cur_grant.n_bytes, cur_grant.rnti, ack, cur_grant.tti);
|
||||
}
|
||||
if (ack) {
|
||||
if (cur_grant.rnti_type == SRSLTE_RNTI_TEMP) {
|
||||
Debug("Delivering PDU=%d bytes to Dissassemble and Demux unit (Temporal C-RNTI)\n", cur_grant.n_bytes);
|
||||
harq_entity->demux_unit->push_pdu_temp_crnti(payload_buffer_ptr, cur_grant.n_bytes);
|
||||
} else {
|
||||
Debug("Delivering PDU=%d bytes to Dissassemble and Demux unit\n", cur_grant.n_bytes);
|
||||
harq_entity->demux_unit->push_pdu(pid, payload_buffer_ptr, cur_grant.n_bytes, cur_grant.tti);
|
||||
|
||||
// Compute average number of retransmissions per packet
|
||||
harq_entity->average_retx = SRSLTE_VEC_CMA((float) n_retx, harq_entity->average_retx, harq_entity->nof_pkts++);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
harq_entity->demux_unit->deallocate(payload_buffer_ptr);
|
||||
}
|
||||
|
||||
Info("DL %d: %s tbs=%d, rv=%d, ack=%s, ndi=%d (%d), tti=%d (%d)\n",
|
||||
pid, is_new_transmission?"newTX":"reTX ",
|
||||
cur_grant.n_bytes, cur_grant.rv, ack?"OK":"KO",
|
||||
cur_grant.ndi, cur_grant.last_ndi, cur_grant.tti, cur_grant.last_tti);
|
||||
|
||||
if (ack && pid == HARQ_BCCH_PID) {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool is_sps() { return false; }
|
||||
|
||||
int get_current_tbs() { return cur_grant.n_bytes*8; }
|
||||
|
||||
private:
|
||||
bool calc_is_new_transmission(mac_interface_phy::mac_grant_t grant);
|
||||
|
||||
bool calc_is_new_transmission(Tgrant grant)
|
||||
{
|
||||
bool is_new_tb = true;
|
||||
if ((srslte_tti_interval(grant.tti, cur_grant.tti) <= 8 && (grant.n_bytes == cur_grant.n_bytes)) ||
|
||||
pid == HARQ_BCCH_PID)
|
||||
{
|
||||
is_new_tb = false;
|
||||
}
|
||||
|
||||
if ((grant.ndi != cur_grant.ndi && !is_new_tb) || // NDI toggled for same TB
|
||||
is_new_tb || // is new TB
|
||||
(pid == HARQ_BCCH_PID && grant.rv == 0)) // Broadcast PID and 1st TX (RV=0)
|
||||
{
|
||||
is_new_transmission = true;
|
||||
Debug("Set HARQ for new transmission\n");
|
||||
} else {
|
||||
is_new_transmission = false;
|
||||
Debug("Set HARQ for retransmission\n");
|
||||
}
|
||||
|
||||
return is_new_transmission;
|
||||
}
|
||||
|
||||
bool is_initiated;
|
||||
dl_harq_entity *harq_entity;
|
||||
srslte::log *log_h;
|
||||
|
||||
bool is_new_transmission;
|
||||
bool is_new_transmission;
|
||||
|
||||
uint32_t pid;
|
||||
uint8_t *payload_buffer_ptr;
|
||||
bool ack;
|
||||
|
||||
uint32_t n_retx;
|
||||
uint32_t n_retx;
|
||||
|
||||
mac_interface_phy::mac_grant_t cur_grant;
|
||||
Tgrant cur_grant;
|
||||
srslte_softbuffer_rx_t softbuffer;
|
||||
|
||||
};
|
||||
static bool generate_ack_callback(void *arg);
|
||||
|
||||
// Private members of dl_harq_entity
|
||||
|
||||
static bool generate_ack_callback(void *arg)
|
||||
{
|
||||
demux *demux_unit = (demux*) arg;
|
||||
return demux_unit->get_uecrid_successful();
|
||||
}
|
||||
|
||||
uint32_t get_harq_sps_pid(uint32_t tti);
|
||||
uint32_t get_harq_sps_pid(uint32_t tti) { return 0; }
|
||||
|
||||
dl_sps dl_sps_assig;
|
||||
|
||||
dl_harq_process proc[NOF_HARQ_PROC+1];
|
||||
|
||||
std::vector<dl_harq_process> proc;
|
||||
srslte::timers *timers_db;
|
||||
mac_interface_rrc::mac_cfg_t *mac_cfg;
|
||||
demux *demux_unit;
|
||||
srslte::log *log_h;
|
||||
srslte::mac_pcap *pcap;
|
||||
uint16_t last_temporal_crnti;
|
||||
int si_window_start;
|
||||
int si_window_start;
|
||||
|
||||
float average_retx;
|
||||
float average_retx;
|
||||
uint64_t nof_pkts;
|
||||
};
|
||||
|
||||
|
|
|
@ -98,17 +98,6 @@ public:
|
|||
u_int32_t get_unique_id();
|
||||
|
||||
uint32_t get_current_tti();
|
||||
|
||||
enum {
|
||||
HARQ_RTT,
|
||||
TIME_ALIGNMENT,
|
||||
CONTENTION_TIMER,
|
||||
BSR_TIMER_PERIODIC,
|
||||
BSR_TIMER_RETX,
|
||||
PHR_TIMER_PERIODIC,
|
||||
PHR_TIMER_PROHIBIT,
|
||||
NOF_MAC_TIMERS
|
||||
} mac_timers_t;
|
||||
|
||||
static const int MAC_NOF_UPPER_TIMERS = 20;
|
||||
|
||||
|
@ -117,6 +106,7 @@ private:
|
|||
|
||||
static const int MAC_MAIN_THREAD_PRIO = 5;
|
||||
static const int MAC_PDU_THREAD_PRIO = 6;
|
||||
static const int MAC_NOF_HARQ_PROC = 8;
|
||||
|
||||
// Interaction with PHY
|
||||
srslte::tti_sync_cv ttisync;
|
||||
|
@ -141,9 +131,9 @@ private:
|
|||
mux mux_unit;
|
||||
demux demux_unit;
|
||||
|
||||
/* DL/UL HARQ */
|
||||
dl_harq_entity dl_harq;
|
||||
ul_harq_entity ul_harq;
|
||||
/* DL/UL HARQ */
|
||||
dl_harq_entity<MAC_NOF_HARQ_PROC, mac_grant_t, tb_action_dl_t, srslte_phy_grant_t> dl_harq;
|
||||
ul_harq_entity<MAC_NOF_HARQ_PROC, mac_grant_t, tb_action_ul_t, srslte_phy_grant_t> ul_harq;
|
||||
|
||||
/* MAC Uplink-related Procedures */
|
||||
ra_proc ra_procedure;
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
*
|
||||
* \section COPYRIGHT
|
||||
*
|
||||
* Copyright 2013-2015 Software Radio Systems Limited
|
||||
*
|
||||
* \section LICENSE
|
||||
*
|
||||
* This file is part of the srsUE library.
|
||||
*
|
||||
* srsUE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* srsUE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* A copy of the GNU Affero General Public License can be found in
|
||||
* the LICENSE file in the top-level directory of this distribution
|
||||
* and at http://www.gnu.org/licenses/.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MAC_COMMON_H
|
||||
#define MAC_COMMON_H
|
||||
|
||||
namespace srsue {
|
||||
|
||||
typedef enum {
|
||||
HARQ_RTT,
|
||||
TIME_ALIGNMENT,
|
||||
CONTENTION_TIMER,
|
||||
BSR_TIMER_PERIODIC,
|
||||
BSR_TIMER_RETX,
|
||||
PHR_TIMER_PERIODIC,
|
||||
PHR_TIMER_PROHIBIT,
|
||||
NOF_MAC_TIMERS
|
||||
} mac_timers_t;
|
||||
|
||||
} // namespace srsue
|
||||
|
||||
#endif // MAC_COMMON_H
|
|
@ -55,9 +55,9 @@ namespace srsue {
|
|||
class mux
|
||||
{
|
||||
public:
|
||||
mux();
|
||||
mux(uint8_t nof_harq_proc_);
|
||||
void reset();
|
||||
void init(rlc_interface_mac *rlc, srslte::log *log_h, bsr_proc *bsr_procedure, phr_proc *phr_procedure_);
|
||||
void init(rlc_interface_mac *rlc, srslte::log *log_h, bsr_interface_mux *bsr_procedure, phr_proc *phr_procedure_);
|
||||
|
||||
bool is_pending_any_sdu();
|
||||
bool is_pending_sdu(uint32_t lcid);
|
||||
|
@ -87,16 +87,17 @@ private:
|
|||
std::vector<lchid_t> lch;
|
||||
|
||||
// Keep track of the PIDs that transmitted BSR reports
|
||||
bool pid_has_bsr[MAX_HARQ_PROC];
|
||||
std::vector<bool> pid_has_bsr;
|
||||
|
||||
// Mutex for exclusive access
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
srslte::log *log_h;
|
||||
rlc_interface_mac *rlc;
|
||||
bsr_proc *bsr_procedure;
|
||||
bsr_interface_mux *bsr_procedure;
|
||||
phr_proc *phr_procedure;
|
||||
uint16_t pending_crnti_ce;
|
||||
uint8_t nof_harq_proc;
|
||||
|
||||
/* Msg3 Buffer */
|
||||
static const uint32_t MSG3_BUFF_SZ = 128;
|
||||
|
@ -105,9 +106,6 @@ private:
|
|||
/* PDU Buffer */
|
||||
srslte::sch_pdu pdu_msg;
|
||||
bool msg3_has_been_transmitted;
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace srsue
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
namespace srsue {
|
||||
|
||||
class bsr_proc : public srslte::timer_callback
|
||||
class bsr_proc : public srslte::timer_callback, public bsr_interface_mux
|
||||
{
|
||||
public:
|
||||
bsr_proc();
|
||||
|
@ -48,18 +48,6 @@ public:
|
|||
void set_priority(uint32_t lcid, uint32_t priority);
|
||||
void timer_expired(uint32_t timer_id);
|
||||
uint32_t get_buffer_state();
|
||||
|
||||
typedef enum {
|
||||
LONG_BSR,
|
||||
SHORT_BSR,
|
||||
TRUNC_BSR
|
||||
} bsr_format_t;
|
||||
|
||||
typedef struct {
|
||||
bsr_format_t format;
|
||||
uint32_t buff_size[4];
|
||||
} bsr_t;
|
||||
|
||||
bool need_to_send_bsr_on_ul_grant(uint32_t grant_size, bsr_t *bsr);
|
||||
bool generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t *bsr);
|
||||
bool need_to_send_sr(uint32_t tti);
|
||||
|
|
|
@ -27,78 +27,268 @@
|
|||
#ifndef ULHARQ_H
|
||||
#define ULHARQ_H
|
||||
|
||||
#define Error(fmt, ...) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define Warning(fmt, ...) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define Info(fmt, ...) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define Debug(fmt, ...) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
|
||||
#include "srslte/interfaces/ue_interfaces.h"
|
||||
#include "srslte/common/log.h"
|
||||
#include "mac/mux.h"
|
||||
#include "mac/mac_common.h"
|
||||
#include "mac/ul_sps.h"
|
||||
#include "srslte/common/mac_pcap.h"
|
||||
#include "srslte/common/timers.h"
|
||||
#include "srslte/common/interfaces_common.h"
|
||||
|
||||
/* Uplink HARQ entity as defined in 5.4.2 of 36.321 */
|
||||
|
||||
|
||||
namespace srsue {
|
||||
|
||||
|
||||
template <std::size_t N, typename Tgrant, typename Taction, typename Tphygrant>
|
||||
class ul_harq_entity
|
||||
{
|
||||
public:
|
||||
|
||||
const static uint32_t NOF_HARQ_PROC = 8;
|
||||
static uint32_t pidof(uint32_t tti);
|
||||
static uint32_t pidof(uint32_t tti)
|
||||
{
|
||||
return (uint32_t) tti%N;
|
||||
}
|
||||
|
||||
ul_harq_entity() {
|
||||
ul_harq_entity() : proc(N)
|
||||
{
|
||||
pcap = NULL;
|
||||
timers_db = NULL;
|
||||
mux_unit = NULL;
|
||||
log_h = NULL;
|
||||
mac_cfg = NULL;
|
||||
params = NULL;
|
||||
rntis = NULL;
|
||||
average_retx = 0;
|
||||
nof_pkts = 0;
|
||||
nof_pkts = 0;
|
||||
}
|
||||
bool init(srslte::log *log_h,
|
||||
mac_interface_rrc::ue_rnti_t *rntis,
|
||||
mac_interface_rrc::mac_cfg_t *mac_cfg,
|
||||
srslte::timers* timers_,
|
||||
mux *mux_unit);
|
||||
void reset();
|
||||
void reset_ndi();
|
||||
|
||||
void start_pcap(srslte::mac_pcap* pcap);
|
||||
bool init(srslte::log *log_h_,
|
||||
mac_interface_rrc_common::ue_rnti_t *rntis_,
|
||||
mac_interface_rrc_common::ul_harq_params_t *params_,
|
||||
srslte::timers* timers_db_,
|
||||
mux *mux_unit_)
|
||||
{
|
||||
log_h = log_h_;
|
||||
mux_unit = mux_unit_;
|
||||
params = params_;
|
||||
rntis = rntis_;
|
||||
timers_db = timers_db_;
|
||||
for (uint32_t i=0;i<N;i++) {
|
||||
if (!proc[i].init(i, this)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
for (uint32_t i=0;i<N;i++) {
|
||||
proc[i].reset();
|
||||
}
|
||||
ul_sps_assig.clear();
|
||||
}
|
||||
|
||||
void reset_ndi()
|
||||
{
|
||||
for (uint32_t i=0;i<N;i++) {
|
||||
proc[i].reset_ndi();
|
||||
}
|
||||
}
|
||||
|
||||
void start_pcap(srslte::mac_pcap* pcap_)
|
||||
{
|
||||
pcap = pcap_;
|
||||
}
|
||||
|
||||
|
||||
/***************** PHY->MAC interface for UL processes **************************/
|
||||
void new_grant_ul(mac_interface_phy::mac_grant_t grant, mac_interface_phy::tb_action_ul_t *action);
|
||||
void new_grant_ul_ack(mac_interface_phy::mac_grant_t grant, bool ack, mac_interface_phy::tb_action_ul_t *action);
|
||||
void harq_recv(uint32_t tti, bool ack, mac_interface_phy::tb_action_ul_t *action);
|
||||
void new_grant_ul(Tgrant grant, Taction *action)
|
||||
{
|
||||
if (grant.rnti_type == SRSLTE_RNTI_USER ||
|
||||
grant.rnti_type == SRSLTE_RNTI_TEMP ||
|
||||
grant.rnti_type == SRSLTE_RNTI_RAR)
|
||||
{
|
||||
if (grant.rnti_type == SRSLTE_RNTI_USER && proc[pidof(grant.tti)].is_sps()) {
|
||||
grant.ndi = true;
|
||||
}
|
||||
run_tti(grant.tti, &grant, action);
|
||||
} else if (grant.rnti_type == SRSLTE_RNTI_SPS) {
|
||||
if (grant.ndi) {
|
||||
grant.ndi = proc[pidof(grant.tti)].get_ndi();
|
||||
run_tti(grant.tti, &grant, action);
|
||||
} else {
|
||||
Info("Not implemented\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int get_current_tbs(uint32_t tti);
|
||||
void new_grant_ul_ack(Tgrant grant, bool ack, Taction *action)
|
||||
{
|
||||
set_ack(grant.tti, ack, action);
|
||||
new_grant_ul(grant, action);
|
||||
}
|
||||
|
||||
void harq_recv(uint32_t tti, bool ack, Taction *action)
|
||||
{
|
||||
set_ack(tti, ack, action);
|
||||
run_tti(tti, NULL, action);
|
||||
}
|
||||
|
||||
int get_current_tbs(uint32_t tti)
|
||||
{
|
||||
int tti_harq = (int) tti-4;
|
||||
if (tti_harq < 0) {
|
||||
tti_harq += 10240;
|
||||
}
|
||||
uint32_t pid_harq = pidof(tti_harq);
|
||||
return proc[pid_harq].get_current_tbs();
|
||||
}
|
||||
|
||||
float get_average_retx();
|
||||
float get_average_retx()
|
||||
{
|
||||
return average_retx;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
private:
|
||||
class ul_harq_process {
|
||||
public:
|
||||
ul_harq_process();
|
||||
bool init(uint32_t pid, ul_harq_entity *parent);
|
||||
void reset();
|
||||
void reset_ndi();
|
||||
ul_harq_process()
|
||||
{
|
||||
current_tx_nb = 0;
|
||||
current_irv = 0;
|
||||
is_initiated = false;
|
||||
is_grant_configured = false;
|
||||
tti_last_tx = 0;
|
||||
bzero(&cur_grant, sizeof(Tgrant));
|
||||
}
|
||||
|
||||
bool init(uint32_t pid_, ul_harq_entity *parent)
|
||||
{
|
||||
if (srslte_softbuffer_tx_init(&softbuffer, 110)) {
|
||||
fprintf(stderr, "Error initiating soft buffer\n");
|
||||
return false;
|
||||
} else {
|
||||
is_initiated = true;
|
||||
harq_entity = parent;
|
||||
log_h = harq_entity->log_h;
|
||||
pid = pid_;
|
||||
payload_buffer = (uint8_t*) srslte_vec_malloc(payload_buffer_len*sizeof(uint8_t));
|
||||
if (!payload_buffer) {
|
||||
Error("Allocating memory\n");
|
||||
return false;
|
||||
}
|
||||
pdu_ptr = payload_buffer;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
current_tx_nb = 0;
|
||||
current_irv = 0;
|
||||
tti_last_tx = 0;
|
||||
is_grant_configured = false;
|
||||
bzero(&cur_grant, sizeof(Tgrant));
|
||||
}
|
||||
|
||||
void reset_ndi() { ndi = false; }
|
||||
|
||||
void run_tti(uint32_t tti, mac_interface_phy::mac_grant_t *grant, mac_interface_phy::tb_action_ul_t* action);
|
||||
void run_tti(uint32_t tti_tx, Tgrant *grant, Taction* action)
|
||||
{
|
||||
uint32_t max_retx;
|
||||
if (is_msg3) {
|
||||
max_retx = harq_entity->params->max_harq_msg3_tx;
|
||||
} else {
|
||||
max_retx = harq_entity->params->max_harq_tx;
|
||||
}
|
||||
|
||||
uint32_t get_rv();
|
||||
bool has_grant();
|
||||
// Receive and route HARQ feedbacks
|
||||
if (grant) {
|
||||
if ((!(grant->rnti_type == SRSLTE_RNTI_TEMP) && grant->ndi != get_ndi()) ||
|
||||
(grant->rnti_type == SRSLTE_RNTI_USER && !has_grant()) ||
|
||||
grant->is_from_rar)
|
||||
{
|
||||
// New transmission
|
||||
|
||||
void set_harq_feedback(bool ack);
|
||||
bool get_ndi();
|
||||
bool is_sps();
|
||||
uint32_t last_tx_tti();
|
||||
uint32_t get_nof_retx();
|
||||
int get_current_tbs();
|
||||
// Uplink grant in a RAR
|
||||
if (grant->is_from_rar) {
|
||||
Debug("Getting Msg3 buffer payload, grant size=%d bytes\n", grant->n_bytes);
|
||||
pdu_ptr = harq_entity->mux_unit->msg3_get(payload_buffer, grant->n_bytes);
|
||||
if (pdu_ptr) {
|
||||
generate_new_tx(tti_tx, true, grant, action);
|
||||
} else {
|
||||
Warning("UL RAR grant available but no Msg3 on buffer\n");
|
||||
}
|
||||
|
||||
// Normal UL grant
|
||||
} else {
|
||||
// Request a MAC PDU from the Multiplexing & Assemble Unit
|
||||
pdu_ptr = harq_entity->mux_unit->pdu_get(payload_buffer, grant->n_bytes, tti_tx, pid);
|
||||
if (pdu_ptr) {
|
||||
generate_new_tx(tti_tx, false, grant, action);
|
||||
} else {
|
||||
Warning("Uplink grant but no MAC PDU in Multiplex Unit buffer\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Adaptive Re-TX
|
||||
if (current_tx_nb >= max_retx) {
|
||||
Info("UL %d: Maximum number of ReTX reached (%d). Discarting TB.\n", pid, max_retx);
|
||||
reset();
|
||||
action->expect_ack = false;
|
||||
} else {
|
||||
generate_retx(tti_tx, grant, action);
|
||||
}
|
||||
}
|
||||
} else if (has_grant()) {
|
||||
// Non-Adaptive Re-Tx
|
||||
if (current_tx_nb >= max_retx) {
|
||||
Info("UL %d: Maximum number of ReTX reached (%d). Discarting TB.\n", pid, max_retx);
|
||||
reset();
|
||||
action->expect_ack = false;
|
||||
} else {
|
||||
generate_retx(tti_tx, action);
|
||||
}
|
||||
}
|
||||
if (harq_entity->pcap && grant) {
|
||||
if (grant->is_from_rar) {
|
||||
grant->rnti = harq_entity->rntis->temp_rnti;
|
||||
}
|
||||
harq_entity->pcap->write_ul_crnti(pdu_ptr, grant->n_bytes, grant->rnti, get_nof_retx(), tti_tx);
|
||||
}
|
||||
}
|
||||
|
||||
void set_harq_feedback(bool ack)
|
||||
{
|
||||
harq_feedback = ack;
|
||||
// UL packet successfully delivered
|
||||
if (ack) {
|
||||
Info("UL %d: HARQ = ACK for UL transmission. Discarting TB.\n", pid);
|
||||
reset();
|
||||
} else {
|
||||
Info("UL %d: HARQ = NACK for UL transmission\n", pid);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t get_rv()
|
||||
{
|
||||
int rv_of_irv[4] = {0, 2, 3, 1};
|
||||
return rv_of_irv[current_irv%4];
|
||||
}
|
||||
|
||||
bool has_grant() { return is_grant_configured; }
|
||||
bool get_ndi() { return ndi; }
|
||||
bool is_sps() { return false; }
|
||||
uint32_t last_tx_tti() { return tti_last_tx; }
|
||||
uint32_t get_nof_retx() { return current_tx_nb; }
|
||||
int get_current_tbs() { return cur_grant.n_bytes*8; }
|
||||
|
||||
private:
|
||||
mac_interface_phy::mac_grant_t cur_grant;
|
||||
Tgrant cur_grant;
|
||||
|
||||
uint32_t pid;
|
||||
uint32_t current_tx_nb;
|
||||
|
@ -118,28 +308,109 @@ private:
|
|||
uint8_t *payload_buffer;
|
||||
uint8_t *pdu_ptr;
|
||||
|
||||
void generate_retx(uint32_t tti_tx, mac_interface_phy::tb_action_ul_t *action);
|
||||
void generate_retx(uint32_t tti_tx, mac_interface_phy::mac_grant_t *grant,
|
||||
mac_interface_phy::tb_action_ul_t *action);
|
||||
void generate_new_tx(uint32_t tti_tx, bool is_msg3, mac_interface_phy::mac_grant_t *grant,
|
||||
mac_interface_phy::tb_action_ul_t *action);
|
||||
void generate_tx(uint32_t tti_tx, mac_interface_phy::tb_action_ul_t *action);
|
||||
void generate_retx(uint32_t tti_tx, Taction *action)
|
||||
{
|
||||
generate_retx(tti_tx, NULL, action);
|
||||
}
|
||||
|
||||
// Retransmission with or w/o grant (Section 5.4.2.2)
|
||||
void generate_retx(uint32_t tti_tx, Tgrant *grant,
|
||||
Taction *action)
|
||||
{
|
||||
int irv_of_rv[4] = {0, 3, 1, 2};
|
||||
if (grant) {
|
||||
// HARQ entity requests an adaptive transmission
|
||||
if (grant->rv) {
|
||||
current_irv = irv_of_rv[grant->rv%4];
|
||||
}
|
||||
memcpy(&cur_grant, grant, sizeof(Tgrant));
|
||||
harq_feedback = false;
|
||||
Info("UL %d: Adaptive retx=%d, RV=%d, TBS=%d\n",
|
||||
pid, current_tx_nb, get_rv(), grant->n_bytes);
|
||||
generate_tx(tti_tx, action);
|
||||
} else {
|
||||
Info("UL %d: Non-Adaptive retx=%d, RV=%d, TBS=%d\n",
|
||||
pid, current_tx_nb, get_rv(), cur_grant.n_bytes);
|
||||
// HARQ entity requests a non-adaptive transmission
|
||||
if (!harq_feedback) {
|
||||
generate_tx(tti_tx, action);
|
||||
}
|
||||
}
|
||||
|
||||
// On every Msg3 retransmission, restart mac-ContentionResolutionTimer as defined in Section 5.1.5
|
||||
if (is_msg3) {
|
||||
harq_entity->timers_db->get(CONTENTION_TIMER)->reset();
|
||||
}
|
||||
|
||||
harq_entity->mux_unit->pusch_retx(tti_tx, pid);
|
||||
}
|
||||
|
||||
// New transmission (Section 5.4.2.2)
|
||||
void generate_new_tx(uint32_t tti_tx, bool is_msg3_, Tgrant *grant, Taction *action)
|
||||
{
|
||||
if (grant) {
|
||||
// Compute average number of retransmissions per packet considering previous packet
|
||||
harq_entity->average_retx = SRSLTE_VEC_CMA((float) current_tx_nb, harq_entity->average_retx, harq_entity->nof_pkts++);
|
||||
memcpy(&cur_grant, grant, sizeof(Tgrant));
|
||||
harq_feedback = false;
|
||||
is_grant_configured = true;
|
||||
current_tx_nb = 0;
|
||||
current_irv = 0;
|
||||
is_msg3 = is_msg3_;
|
||||
Info("UL %d: New TX%s, RV=%d, TBS=%d, RNTI=%d\n",
|
||||
pid, is_msg3?" for Msg3":"", get_rv(), cur_grant.n_bytes, cur_grant.rnti);
|
||||
generate_tx(tti_tx, action);
|
||||
}
|
||||
}
|
||||
|
||||
// Transmission of pending frame (Section 5.4.2.2)
|
||||
void generate_tx(uint32_t tti_tx, Taction *action)
|
||||
{
|
||||
action->current_tx_nb = current_tx_nb;
|
||||
current_tx_nb++;
|
||||
action->expect_ack = true;
|
||||
action->rnti = is_msg3?harq_entity->rntis->temp_rnti:cur_grant.rnti;
|
||||
action->rv = cur_grant.rv>0?cur_grant.rv:get_rv();
|
||||
action->softbuffer = &softbuffer;
|
||||
action->tx_enabled = true;
|
||||
action->payload_ptr = pdu_ptr;
|
||||
memcpy(&action->phy_grant, &cur_grant.phy_grant, sizeof(Tphygrant));
|
||||
|
||||
current_irv = (current_irv+1)%4;
|
||||
tti_last_tx = tti_tx;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void run_tti(uint32_t tti, mac_interface_phy::mac_grant_t *grant, mac_interface_phy::tb_action_ul_t* action);
|
||||
void set_ack(uint32_t tti, bool ack);
|
||||
// Implements Section 5.4.2.1
|
||||
// Called with UL grant
|
||||
void run_tti(uint32_t tti, Tgrant *grant, Taction* action)
|
||||
{
|
||||
uint32_t tti_tx = (tti+action->tti_offset)%10240;
|
||||
proc[pidof(tti_tx)].run_tti(tti_tx, grant, action);
|
||||
}
|
||||
|
||||
void set_ack(uint32_t tti, bool ack, Taction *action)
|
||||
{
|
||||
int tti_harq = (int) tti - action->tti_offset;
|
||||
if (tti_harq < 0) {
|
||||
tti_harq += 10240;
|
||||
}
|
||||
uint32_t pid_harq = pidof(tti_harq);
|
||||
if (proc[pid_harq].has_grant() && (proc[pid_harq].last_tx_tti() <= (uint32_t)tti_harq)) {
|
||||
proc[pid_harq].set_harq_feedback(ack);
|
||||
}
|
||||
}
|
||||
|
||||
ul_sps ul_sps_assig;
|
||||
|
||||
srslte::timers *timers_db;
|
||||
mux *mux_unit;
|
||||
ul_harq_process proc[NOF_HARQ_PROC];
|
||||
std::vector<ul_harq_process> proc;
|
||||
srslte::log *log_h;
|
||||
srslte::mac_pcap *pcap;
|
||||
|
||||
mac_interface_rrc::ue_rnti_t *rntis;
|
||||
mac_interface_rrc::mac_cfg_t *mac_cfg;
|
||||
mac_interface_rrc_common::ue_rnti_t *rntis;
|
||||
mac_interface_rrc_common::ul_harq_params_t *params;
|
||||
|
||||
float average_retx;
|
||||
uint64_t nof_pkts;
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <string>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "ue_base.h"
|
||||
#include "srslte/radio/radio_multi.h"
|
||||
#include "phy/phy.h"
|
||||
#include "mac/mac.h"
|
||||
|
@ -56,90 +57,16 @@
|
|||
|
||||
namespace srsue {
|
||||
|
||||
/*******************************************************************************
|
||||
UE Parameters
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
uint32_t dl_earfcn;
|
||||
float dl_freq;
|
||||
float ul_freq;
|
||||
float rx_gain;
|
||||
float tx_gain;
|
||||
uint32_t nof_rx_ant;
|
||||
std::string device_name;
|
||||
std::string device_args;
|
||||
std::string time_adv_nsamples;
|
||||
std::string burst_preamble;
|
||||
}rf_args_t;
|
||||
|
||||
typedef struct {
|
||||
bool enable;
|
||||
std::string filename;
|
||||
}pcap_args_t;
|
||||
|
||||
typedef struct {
|
||||
bool enable;
|
||||
std::string phy_filename;
|
||||
std::string radio_filename;
|
||||
}trace_args_t;
|
||||
|
||||
typedef struct {
|
||||
std::string phy_level;
|
||||
std::string mac_level;
|
||||
std::string rlc_level;
|
||||
std::string pdcp_level;
|
||||
std::string rrc_level;
|
||||
std::string gw_level;
|
||||
std::string nas_level;
|
||||
std::string usim_level;
|
||||
std::string all_level;
|
||||
int phy_hex_limit;
|
||||
int mac_hex_limit;
|
||||
int rlc_hex_limit;
|
||||
int pdcp_hex_limit;
|
||||
int rrc_hex_limit;
|
||||
int gw_hex_limit;
|
||||
int nas_hex_limit;
|
||||
int usim_hex_limit;
|
||||
int all_hex_limit;
|
||||
std::string filename;
|
||||
}log_args_t;
|
||||
|
||||
typedef struct {
|
||||
bool enable;
|
||||
}gui_args_t;
|
||||
|
||||
typedef struct {
|
||||
phy_args_t phy;
|
||||
float metrics_period_secs;
|
||||
bool pregenerate_signals;
|
||||
int ue_cateogry;
|
||||
|
||||
}expert_args_t;
|
||||
|
||||
typedef struct {
|
||||
rf_args_t rf;
|
||||
rf_cal_t rf_cal;
|
||||
pcap_args_t pcap;
|
||||
trace_args_t trace;
|
||||
log_args_t log;
|
||||
gui_args_t gui;
|
||||
usim_args_t usim;
|
||||
expert_args_t expert;
|
||||
}all_args_t;
|
||||
|
||||
/*******************************************************************************
|
||||
Main UE class
|
||||
*******************************************************************************/
|
||||
|
||||
class ue
|
||||
:public ue_interface
|
||||
,public ue_metrics_interface
|
||||
:public ue_base
|
||||
{
|
||||
public:
|
||||
static ue* get_instance(void);
|
||||
static void cleanup(void);
|
||||
ue();
|
||||
|
||||
bool init(all_args_t *args_);
|
||||
void stop();
|
||||
|
@ -159,8 +86,6 @@ public:
|
|||
|
||||
|
||||
private:
|
||||
static ue *instance;
|
||||
ue();
|
||||
virtual ~ue();
|
||||
|
||||
srslte::radio_multi radio;
|
||||
|
@ -191,8 +116,6 @@ private:
|
|||
all_args_t *args;
|
||||
bool started;
|
||||
rf_metrics_t rf_metrics;
|
||||
|
||||
srslte::LOG_LEVEL_ENUM level(std::string l);
|
||||
|
||||
bool check_srslte_version();
|
||||
};
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
/**
|
||||
*
|
||||
* \section COPYRIGHT
|
||||
*
|
||||
* Copyright 2013-2015 Software Radio Systems Limited
|
||||
*
|
||||
* \section LICENSE
|
||||
*
|
||||
* This file is part of the srsUE library.
|
||||
*
|
||||
* srsUE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* srsUE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* A copy of the GNU Affero General Public License can be found in
|
||||
* the LICENSE file in the top-level directory of this distribution
|
||||
* and at http://www.gnu.org/licenses/.
|
||||
*
|
||||
*/
|
||||
|
||||
/******************************************************************************
|
||||
* File: ue_base.h
|
||||
* Description: Base class for UEs.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef UE_BASE_H
|
||||
#define UE_BASE_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <string>
|
||||
#include <pthread.h>
|
||||
#include "srslte/radio/radio_multi.h"
|
||||
#include "phy/phy.h"
|
||||
#include "upper/usim.h"
|
||||
#include "srslte/interfaces/ue_interfaces.h"
|
||||
|
||||
#include "srslte/common/logger.h"
|
||||
#include "srslte/common/log_filter.h"
|
||||
|
||||
#include "ue_metrics_interface.h"
|
||||
|
||||
namespace srsue {
|
||||
|
||||
/*******************************************************************************
|
||||
UE Parameters
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
uint32_t dl_earfcn;
|
||||
float dl_freq;
|
||||
float ul_freq;
|
||||
float rx_gain;
|
||||
float tx_gain;
|
||||
uint32_t nof_rx_ant;
|
||||
std::string device_name;
|
||||
std::string device_args;
|
||||
std::string time_adv_nsamples;
|
||||
std::string burst_preamble;
|
||||
}rf_args_t;
|
||||
|
||||
typedef struct {
|
||||
bool enable;
|
||||
std::string filename;
|
||||
}pcap_args_t;
|
||||
|
||||
typedef struct {
|
||||
bool enable;
|
||||
std::string phy_filename;
|
||||
std::string radio_filename;
|
||||
}trace_args_t;
|
||||
|
||||
typedef struct {
|
||||
std::string phy_level;
|
||||
std::string mac_level;
|
||||
std::string rlc_level;
|
||||
std::string pdcp_level;
|
||||
std::string rrc_level;
|
||||
std::string gw_level;
|
||||
std::string nas_level;
|
||||
std::string usim_level;
|
||||
std::string all_level;
|
||||
int phy_hex_limit;
|
||||
int mac_hex_limit;
|
||||
int rlc_hex_limit;
|
||||
int pdcp_hex_limit;
|
||||
int rrc_hex_limit;
|
||||
int gw_hex_limit;
|
||||
int nas_hex_limit;
|
||||
int usim_hex_limit;
|
||||
int all_hex_limit;
|
||||
std::string filename;
|
||||
}log_args_t;
|
||||
|
||||
typedef struct {
|
||||
bool enable;
|
||||
}gui_args_t;
|
||||
|
||||
typedef struct {
|
||||
phy_args_t phy;
|
||||
float metrics_period_secs;
|
||||
bool pregenerate_signals;
|
||||
std::string ue_cateogry;
|
||||
}expert_args_t;
|
||||
|
||||
typedef struct {
|
||||
rf_args_t rf;
|
||||
rf_cal_t rf_cal;
|
||||
pcap_args_t pcap;
|
||||
trace_args_t trace;
|
||||
log_args_t log;
|
||||
gui_args_t gui;
|
||||
usim_args_t usim;
|
||||
expert_args_t expert;
|
||||
}all_args_t;
|
||||
|
||||
typedef enum {
|
||||
LTE = 0,
|
||||
SRSUE_INSTANCE_TYPE_NITEMS
|
||||
} srsue_instance_type_t;
|
||||
static const char srsue_instance_type_text[SRSUE_INSTANCE_TYPE_NITEMS][10] = { "LTE" };
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
Main UE class
|
||||
*******************************************************************************/
|
||||
|
||||
class ue_base
|
||||
:public ue_interface
|
||||
,public ue_metrics_interface
|
||||
{
|
||||
public:
|
||||
ue_base() {}
|
||||
virtual ~ue_base() {}
|
||||
|
||||
static ue_base* get_instance(srsue_instance_type_t type);
|
||||
|
||||
void cleanup(void);
|
||||
|
||||
virtual bool init(all_args_t *args_) = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual bool is_attached() = 0;
|
||||
virtual void start_plot() = 0;
|
||||
|
||||
void handle_rf_msg(srslte_rf_error_t error);
|
||||
|
||||
// UE metrics interface
|
||||
virtual bool get_metrics(ue_metrics_t &m) = 0;
|
||||
|
||||
virtual void pregenerate_signals(bool enable) = 0;
|
||||
|
||||
srslte::log_filter rf_log;
|
||||
rf_metrics_t rf_metrics;
|
||||
srslte::LOG_LEVEL_ENUM level(std::string l);
|
||||
};
|
||||
|
||||
} // namespace srsue
|
||||
|
||||
#endif // UE_BASE_H
|
||||
|
|
@ -66,12 +66,11 @@ class nas
|
|||
: public nas_interface_rrc, public nas_interface_ue {
|
||||
public:
|
||||
nas();
|
||||
|
||||
void init(usim_interface_nas *usim_,
|
||||
rrc_interface_nas *rrc_,
|
||||
gw_interface_nas *gw_,
|
||||
srslte::log *nas_log_);
|
||||
|
||||
void init(usim_interface_nas *usim_,
|
||||
rrc_interface_nas *rrc_,
|
||||
gw_interface_nas *gw_,
|
||||
srslte::log *nas_log_,
|
||||
srslte::srslte_nas_config_t cfg_);
|
||||
void stop();
|
||||
|
||||
emm_state_t get_state();
|
||||
|
@ -101,6 +100,8 @@ private:
|
|||
usim_interface_nas *usim;
|
||||
gw_interface_nas *gw;
|
||||
|
||||
srslte::srslte_nas_config_t cfg;
|
||||
|
||||
emm_state_t state;
|
||||
|
||||
plmn_selection_state_t plmn_selection;
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "pthread.h"
|
||||
|
||||
#include "rrc_common.h"
|
||||
#include "srslte/common/buffer_pool.h"
|
||||
#include "srslte/common/log.h"
|
||||
#include "srslte/common/common.h"
|
||||
|
@ -42,29 +43,12 @@ using srslte::byte_buffer_t;
|
|||
|
||||
namespace srsue {
|
||||
|
||||
// RRC states (3GPP 36.331 v10.0.0)
|
||||
typedef enum {
|
||||
RRC_STATE_IDLE = 0,
|
||||
RRC_STATE_PLMN_SELECTION,
|
||||
RRC_STATE_CELL_SELECTING,
|
||||
RRC_STATE_CELL_SELECTED,
|
||||
RRC_STATE_CONNECTING,
|
||||
RRC_STATE_CONNECTED,
|
||||
RRC_STATE_N_ITEMS,
|
||||
} rrc_state_t;
|
||||
static const char rrc_state_text[RRC_STATE_N_ITEMS][100] = {"IDLE",
|
||||
"PLMN SELECTION",
|
||||
"CELL SELECTION",
|
||||
"CONNECTING",
|
||||
"CONNECTED",
|
||||
"RRC CONNECTED"};
|
||||
typedef enum {
|
||||
SI_ACQUIRE_IDLE = 0,
|
||||
SI_ACQUIRE_SIB1,
|
||||
SI_ACQUIRE_SIB2
|
||||
} si_acquire_state_t;
|
||||
|
||||
|
||||
class rrc
|
||||
: public rrc_interface_nas,
|
||||
public rrc_interface_phy,
|
||||
|
@ -206,72 +190,68 @@ private:
|
|||
|
||||
void write_pdu_pcch(byte_buffer_t *pdu);
|
||||
|
||||
// Radio bearers
|
||||
typedef enum{
|
||||
RB_ID_SRB0 = 0,
|
||||
RB_ID_SRB1,
|
||||
RB_ID_SRB2,
|
||||
RB_ID_DRB1,
|
||||
RB_ID_DRB2,
|
||||
RB_ID_DRB3,
|
||||
RB_ID_DRB4,
|
||||
RB_ID_DRB5,
|
||||
RB_ID_DRB6,
|
||||
RB_ID_DRB7,
|
||||
RB_ID_DRB8,
|
||||
RB_ID_MAX
|
||||
} rb_id_t;
|
||||
|
||||
std::map<uint8_t, std::string> bearers;
|
||||
std::string get_rb_name(uint32_t lcid) { return bearers.at(lcid); }
|
||||
|
||||
// RLC interface
|
||||
void max_retx_attempted();
|
||||
void max_retx_attempted();
|
||||
|
||||
// Senders
|
||||
void send_con_request();
|
||||
|
||||
void send_con_restablish_request();
|
||||
|
||||
void send_con_restablish_complete();
|
||||
|
||||
void send_con_setup_complete(byte_buffer_t *nas_msg);
|
||||
|
||||
void send_ul_info_transfer(uint32_t lcid, byte_buffer_t *sdu);
|
||||
|
||||
void send_security_mode_complete(uint32_t lcid, byte_buffer_t *pdu);
|
||||
|
||||
void send_rrc_con_reconfig_complete(uint32_t lcid, byte_buffer_t *pdu);
|
||||
|
||||
void send_rrc_ue_cap_info(uint32_t lcid, byte_buffer_t *pdu);
|
||||
void send_con_request();
|
||||
void send_con_restablish_request();
|
||||
void send_con_restablish_complete();
|
||||
void send_con_setup_complete(byte_buffer_t *nas_msg);
|
||||
void send_ul_info_transfer(uint32_t lcid, byte_buffer_t *sdu);
|
||||
void send_security_mode_complete(uint32_t lcid, byte_buffer_t *pdu);
|
||||
void send_rrc_con_reconfig_complete(uint32_t lcid, byte_buffer_t *pdu);
|
||||
void send_rrc_ue_cap_info(uint32_t lcid, byte_buffer_t *pdu);
|
||||
|
||||
// Parsers
|
||||
void parse_dl_ccch(byte_buffer_t *pdu);
|
||||
|
||||
void parse_dl_dcch(uint32_t lcid, byte_buffer_t *pdu);
|
||||
|
||||
void parse_dl_info_transfer(uint32_t lcid, byte_buffer_t *pdu);
|
||||
void parse_dl_ccch(byte_buffer_t *pdu);
|
||||
void parse_dl_dcch(uint32_t lcid, byte_buffer_t *pdu);
|
||||
void parse_dl_info_transfer(uint32_t lcid, byte_buffer_t *pdu);
|
||||
|
||||
// Helpers
|
||||
void reset_ue();
|
||||
|
||||
void rrc_connection_release();
|
||||
|
||||
void radio_link_failure();
|
||||
|
||||
uint32_t sib_start_tti(uint32_t tti, uint32_t period, uint32_t x);
|
||||
|
||||
void apply_sib2_configs(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2);
|
||||
|
||||
void handle_con_setup(LIBLTE_RRC_CONNECTION_SETUP_STRUCT *setup);
|
||||
|
||||
void handle_con_reest(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_STRUCT *setup);
|
||||
|
||||
void
|
||||
handle_rrc_con_reconfig(uint32_t lcid, LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT *reconfig, byte_buffer_t *pdu);
|
||||
|
||||
void add_srb(LIBLTE_RRC_SRB_TO_ADD_MOD_STRUCT *srb_cnfg);
|
||||
|
||||
void add_drb(LIBLTE_RRC_DRB_TO_ADD_MOD_STRUCT *drb_cnfg);
|
||||
|
||||
void release_drb(uint8_t lcid);
|
||||
|
||||
void apply_rr_config_dedicated(LIBLTE_RRC_RR_CONFIG_DEDICATED_STRUCT *cnfg);
|
||||
|
||||
void apply_phy_config_dedicated(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *phy_cnfg, bool apply_defaults);
|
||||
|
||||
void apply_mac_config_dedicated(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT *mac_cfg, bool apply_defaults);
|
||||
|
||||
// Helpers for setting default values
|
||||
void set_phy_default_pucch_srs();
|
||||
|
||||
void set_phy_default();
|
||||
|
||||
void set_mac_default();
|
||||
|
||||
void set_rrc_default();
|
||||
|
||||
void reset_ue();
|
||||
void rrc_connection_release();
|
||||
void radio_link_failure();
|
||||
static void* start_sib_thread(void *rrc_);
|
||||
void sib_search();
|
||||
uint32_t sib_start_tti(uint32_t tti, uint32_t period, uint32_t x);
|
||||
void apply_sib2_configs(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2);
|
||||
void handle_con_setup(LIBLTE_RRC_CONNECTION_SETUP_STRUCT *setup);
|
||||
void handle_con_reest(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_STRUCT *setup);
|
||||
void handle_rrc_con_reconfig(uint32_t lcid, LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT *reconfig, byte_buffer_t *pdu);
|
||||
void add_srb(LIBLTE_RRC_SRB_TO_ADD_MOD_STRUCT *srb_cnfg);
|
||||
void add_drb(LIBLTE_RRC_DRB_TO_ADD_MOD_STRUCT *drb_cnfg);
|
||||
void release_drb(uint8_t lcid);
|
||||
void apply_rr_config_dedicated(LIBLTE_RRC_RR_CONFIG_DEDICATED_STRUCT *cnfg);
|
||||
void apply_phy_config_dedicated(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *phy_cnfg, bool apply_defaults);
|
||||
void apply_mac_config_dedicated(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT *mac_cfg, bool apply_defaults);
|
||||
|
||||
// Helpers for setting default values
|
||||
void set_phy_default_pucch_srs();
|
||||
void set_phy_default();
|
||||
void set_mac_default();
|
||||
void set_rrc_default();
|
||||
void set_bearers();
|
||||
|
||||
};
|
||||
|
||||
} // namespace srsue
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
*
|
||||
* \section COPYRIGHT
|
||||
*
|
||||
* Copyright 2013-2015 Software Radio Systems Limited
|
||||
*
|
||||
* \section LICENSE
|
||||
*
|
||||
* This file is part of the srsUE library.
|
||||
*
|
||||
* srsUE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* srsUE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* A copy of the GNU Affero General Public License can be found in
|
||||
* the LICENSE file in the top-level directory of this distribution
|
||||
* and at http://www.gnu.org/licenses/.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef RRC_COMMON_H
|
||||
#define RRC_COMMON_H
|
||||
|
||||
namespace srsue {
|
||||
|
||||
|
||||
// RRC states (3GPP 36.331 v10.0.0)
|
||||
typedef enum {
|
||||
RRC_STATE_IDLE = 0,
|
||||
RRC_STATE_PLMN_SELECTION,
|
||||
RRC_STATE_CELL_SELECTING,
|
||||
RRC_STATE_CELL_SELECTED,
|
||||
RRC_STATE_CONNECTING,
|
||||
RRC_STATE_CONNECTED,
|
||||
RRC_STATE_N_ITEMS,
|
||||
} rrc_state_t;
|
||||
static const char rrc_state_text[RRC_STATE_N_ITEMS][100] = {"IDLE",
|
||||
"PLMN SELECTION",
|
||||
"CELL SELECTION",
|
||||
"CONNECTING",
|
||||
"CONNECTED",
|
||||
"RRC CONNECTED"};
|
||||
|
||||
} // namespace srsue
|
||||
|
||||
|
||||
#endif // RRC_COMMON_H
|
|
@ -31,7 +31,7 @@ if (RPATH)
|
|||
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
|
||||
endif (RPATH)
|
||||
|
||||
add_executable(srsue main.cc ue.cc metrics_stdout.cc)
|
||||
add_executable(srsue main.cc ue_base.cc ue.cc metrics_stdout.cc)
|
||||
target_link_libraries(srsue srsue_mac
|
||||
srsue_phy
|
||||
srsue_upper
|
||||
|
@ -46,6 +46,8 @@ if (RPATH)
|
|||
set_target_properties(srsue PROPERTIES INSTALL_RPATH ".")
|
||||
endif (RPATH)
|
||||
|
||||
install(TARGETS srsue DESTINATION ${RUNTIME_DIR})
|
||||
|
||||
########################################################################
|
||||
# Option to run command after build (useful for remote builds)
|
||||
########################################################################
|
||||
|
@ -55,3 +57,5 @@ if (NOT ${BUILDUE_CMD} STREQUAL "")
|
|||
else(NOT ${BUILDUE_CMD} STREQUAL "")
|
||||
message(STATUS "No post-build-UE command defined")
|
||||
endif (NOT ${BUILDUE_CMD} STREQUAL "")
|
||||
|
||||
install(TARGETS srsue DESTINATION ${RUNTIME_DIR})
|
||||
|
|
|
@ -36,11 +36,11 @@
|
|||
|
||||
namespace srsue {
|
||||
|
||||
demux::demux() : mac_msg(20), pending_mac_msg(20)
|
||||
demux::demux(uint8_t nof_harq_proc_) : mac_msg(20), pending_mac_msg(20), nof_harq_proc(nof_harq_proc_)
|
||||
{
|
||||
}
|
||||
|
||||
void demux::init(phy_interface_mac* phy_h_, rlc_interface_mac *rlc_, srslte::log* log_h_, srslte::timers* timers_db_)
|
||||
void demux::init(phy_interface_mac_common* phy_h_, rlc_interface_mac *rlc_, srslte::log* log_h_, srslte::timers* timers_db_)
|
||||
{
|
||||
phy_h = phy_h_;
|
||||
log_h = log_h_;
|
||||
|
@ -68,9 +68,9 @@ void demux::deallocate(uint8_t* payload_buffer_ptr)
|
|||
uint8_t* demux::request_buffer(uint32_t pid, uint32_t len)
|
||||
{
|
||||
uint8_t *buff = NULL;
|
||||
if (pid < NOF_HARQ_PID) {
|
||||
if (pid < nof_harq_proc) {
|
||||
return pdus.request(len);
|
||||
} else if (pid == NOF_HARQ_PID) {
|
||||
} else if (pid == nof_harq_proc) {
|
||||
buff = bcch_buffer;
|
||||
} else {
|
||||
Error("Requested buffer for invalid PID=%d\n", pid);
|
||||
|
@ -119,9 +119,9 @@ void demux::push_pdu_temp_crnti(uint8_t *buff, uint32_t nof_bytes)
|
|||
*/
|
||||
void demux::push_pdu(uint32_t pid, uint8_t *buff, uint32_t nof_bytes, uint32_t tstamp)
|
||||
{
|
||||
if (pid < NOF_HARQ_PID) {
|
||||
if (pid < nof_harq_proc) {
|
||||
return pdus.push(buff, nof_bytes, tstamp);
|
||||
} else if (pid == NOF_HARQ_PID) {
|
||||
} else if (pid == nof_harq_proc) {
|
||||
/* Demultiplexing of MAC PDU associated with SI-RNTI. The PDU passes through
|
||||
* the MAC in transparent mode.
|
||||
* Warning: In this case function sends the message to RLC now, since SI blocks do not
|
||||
|
@ -190,8 +190,8 @@ bool demux::process_ce(srslte::sch_subh *subh) {
|
|||
Info("Received TA=%d\n", subh->get_ta_cmd());
|
||||
|
||||
// Start or restart timeAlignmentTimer
|
||||
timers_db->get(mac::TIME_ALIGNMENT)->reset();
|
||||
timers_db->get(mac::TIME_ALIGNMENT)->run();
|
||||
timers_db->get(TIME_ALIGNMENT)->reset();
|
||||
timers_db->get(TIME_ALIGNMENT)->run();
|
||||
break;
|
||||
case srslte::sch_subh::PADDING:
|
||||
break;
|
||||
|
|
|
@ -1,337 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* \section COPYRIGHT
|
||||
*
|
||||
* Copyright 2013-2015 Software Radio Systems Limited
|
||||
*
|
||||
* \section LICENSE
|
||||
*
|
||||
* This file is part of the srsUE library.
|
||||
*
|
||||
* srsUE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* srsUE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* A copy of the GNU Affero General Public License can be found in
|
||||
* the LICENSE file in the top-level directory of this distribution
|
||||
* and at http://www.gnu.org/licenses/.
|
||||
*
|
||||
*/
|
||||
|
||||
#define Error(fmt, ...) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define Warning(fmt, ...) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define Info(fmt, ...) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define Debug(fmt, ...) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
|
||||
#include "mac/mac.h"
|
||||
#include "mac/dl_harq.h"
|
||||
|
||||
|
||||
namespace srsue {
|
||||
|
||||
|
||||
/***********************************************************
|
||||
*
|
||||
* HARQ ENTITY
|
||||
*
|
||||
*********************************************************/
|
||||
|
||||
dl_harq_entity::dl_harq_entity()
|
||||
{
|
||||
pcap = NULL;
|
||||
}
|
||||
|
||||
bool dl_harq_entity::init(srslte::log* log_h_, mac_interface_rrc::mac_cfg_t *mac_cfg_, srslte::timers* timers_, demux *demux_unit_)
|
||||
{
|
||||
timers_db = timers_;
|
||||
demux_unit = demux_unit_;
|
||||
mac_cfg = mac_cfg_;
|
||||
si_window_start = 0;
|
||||
log_h = log_h_;
|
||||
for (uint32_t i=0;i<NOF_HARQ_PROC+1;i++) {
|
||||
if (!proc[i].init(i, this)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void dl_harq_entity::start_pcap(srslte::mac_pcap* pcap_)
|
||||
{
|
||||
pcap = pcap_;
|
||||
}
|
||||
|
||||
void dl_harq_entity::reset()
|
||||
{
|
||||
for (uint32_t i=0;i<NOF_HARQ_PROC+1;i++) {
|
||||
proc[i].reset();
|
||||
}
|
||||
dl_sps_assig.clear();
|
||||
}
|
||||
|
||||
uint32_t dl_harq_entity::get_harq_sps_pid(uint32_t tti) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dl_harq_entity::new_grant_dl(mac_interface_phy::mac_grant_t grant, mac_interface_phy::tb_action_dl_t* action)
|
||||
{
|
||||
|
||||
if (grant.rnti_type != SRSLTE_RNTI_SPS) {
|
||||
uint32_t harq_pid;
|
||||
// Set BCCH PID for SI RNTI
|
||||
if (grant.rnti_type == SRSLTE_RNTI_SI) {
|
||||
harq_pid = HARQ_BCCH_PID;
|
||||
} else {
|
||||
harq_pid = grant.pid%NOF_HARQ_PROC;
|
||||
}
|
||||
if (grant.rnti_type == SRSLTE_RNTI_TEMP && last_temporal_crnti != grant.rnti) {
|
||||
grant.ndi = true;
|
||||
Info("Set NDI=1 for Temp-RNTI DL grant\n");
|
||||
last_temporal_crnti = grant.rnti;
|
||||
}
|
||||
if (grant.rnti_type == SRSLTE_RNTI_USER && proc[harq_pid].is_sps()) {
|
||||
grant.ndi = true;
|
||||
Info("Set NDI=1 for C-RNTI DL grant\n");
|
||||
}
|
||||
proc[harq_pid].new_grant_dl(grant, action);
|
||||
} else {
|
||||
/* This is for SPS scheduling */
|
||||
uint32_t harq_pid = get_harq_sps_pid(grant.tti)%NOF_HARQ_PROC;
|
||||
if (grant.ndi) {
|
||||
grant.ndi = false;
|
||||
proc[harq_pid].new_grant_dl(grant, action);
|
||||
} else {
|
||||
if (grant.is_sps_release) {
|
||||
dl_sps_assig.clear();
|
||||
if (timers_db->get(mac::TIME_ALIGNMENT)->is_running()) {
|
||||
//phy_h->send_sps_ack();
|
||||
Warning("PHY Send SPS ACK not implemented\n");
|
||||
}
|
||||
} else {
|
||||
Error("SPS not implemented\n");
|
||||
//dl_sps_assig.reset(grant.tti, grant);
|
||||
//grant.ndi = true;
|
||||
//procs[harq_pid].save_grant();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dl_harq_entity::tb_decoded(bool ack, srslte_rnti_type_t rnti_type, uint32_t harq_pid)
|
||||
{
|
||||
if (rnti_type == SRSLTE_RNTI_SI) {
|
||||
proc[NOF_HARQ_PROC].tb_decoded(ack);
|
||||
} else {
|
||||
proc[harq_pid%NOF_HARQ_PROC].tb_decoded(ack);
|
||||
}
|
||||
}
|
||||
|
||||
int dl_harq_entity::get_current_tbs(uint32_t harq_pid)
|
||||
{
|
||||
return proc[harq_pid%NOF_HARQ_PROC].get_current_tbs();
|
||||
}
|
||||
|
||||
|
||||
bool dl_harq_entity::generate_ack_callback(void *arg)
|
||||
{
|
||||
demux *demux_unit = (demux*) arg;
|
||||
return demux_unit->get_uecrid_successful();
|
||||
}
|
||||
|
||||
void dl_harq_entity::set_si_window_start(int si_window_start_)
|
||||
{
|
||||
si_window_start = si_window_start_;
|
||||
}
|
||||
|
||||
float dl_harq_entity::get_average_retx()
|
||||
{
|
||||
return average_retx;
|
||||
}
|
||||
|
||||
/***********************************************************
|
||||
*
|
||||
* HARQ PROCESS
|
||||
*
|
||||
*********************************************************/
|
||||
|
||||
dl_harq_entity::dl_harq_process::dl_harq_process() {
|
||||
is_initiated = false;
|
||||
ack = false;
|
||||
bzero(&cur_grant, sizeof(mac_interface_phy::mac_grant_t));
|
||||
}
|
||||
|
||||
void dl_harq_entity::dl_harq_process::reset() {
|
||||
ack = false;
|
||||
payload_buffer_ptr = NULL;
|
||||
bzero(&cur_grant, sizeof(mac_interface_phy::mac_grant_t));
|
||||
if (is_initiated) {
|
||||
srslte_softbuffer_rx_reset(&softbuffer);
|
||||
}
|
||||
}
|
||||
|
||||
bool dl_harq_entity::dl_harq_process::init(uint32_t pid_, dl_harq_entity *parent) {
|
||||
if (srslte_softbuffer_rx_init(&softbuffer, SRSLTE_MAX_PRB)) {
|
||||
fprintf(stderr, "Error initiating soft buffer\n");
|
||||
return false;
|
||||
} else {
|
||||
pid = pid_;
|
||||
is_initiated = true;
|
||||
harq_entity = parent;
|
||||
log_h = harq_entity->log_h;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool dl_harq_entity::dl_harq_process::is_sps()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dl_harq_entity::dl_harq_process::calc_is_new_transmission(mac_interface_phy::mac_grant_t grant) {
|
||||
|
||||
bool is_new_tb = true;
|
||||
if ((srslte_tti_interval(grant.tti, cur_grant.tti) <= 8 && (grant.n_bytes == cur_grant.n_bytes)) ||
|
||||
pid == HARQ_BCCH_PID)
|
||||
{
|
||||
is_new_tb = false;
|
||||
}
|
||||
|
||||
if ((grant.ndi != cur_grant.ndi && !is_new_tb) || // NDI toggled for same TB
|
||||
is_new_tb || // is new TB
|
||||
(pid == HARQ_BCCH_PID && grant.rv == 0)) // Broadcast PID and 1st TX (RV=0)
|
||||
{
|
||||
is_new_transmission = true;
|
||||
Debug("Set HARQ for new transmission\n");
|
||||
} else {
|
||||
is_new_transmission = false;
|
||||
Debug("Set HARQ for retransmission\n");
|
||||
}
|
||||
|
||||
return is_new_transmission;
|
||||
}
|
||||
|
||||
void dl_harq_entity::dl_harq_process::new_grant_dl(mac_interface_phy::mac_grant_t grant, mac_interface_phy::tb_action_dl_t* action)
|
||||
{
|
||||
// Compute RV for BCCH when not specified in PDCCH format
|
||||
if (pid == HARQ_BCCH_PID && grant.rv == -1) {
|
||||
uint32_t k;
|
||||
if ((grant.tti/10)%2 == 0 && grant.tti%10 == 5) { // This is SIB1, k is different
|
||||
k = (grant.tti/20)%4;
|
||||
grant.rv = ((uint32_t) ceilf((float)1.5*k))%4;
|
||||
} else if (grant.rv == -1) {
|
||||
k = (grant.tti-harq_entity->si_window_start)%4;
|
||||
grant.rv = ((uint32_t) ceilf((float)1.5*k))%4;
|
||||
}
|
||||
}
|
||||
calc_is_new_transmission(grant);
|
||||
if (is_new_transmission) {
|
||||
ack = false;
|
||||
srslte_softbuffer_rx_reset_tbs(&softbuffer, cur_grant.n_bytes*8);
|
||||
n_retx = 0;
|
||||
}
|
||||
|
||||
// Save grant
|
||||
grant.last_ndi = cur_grant.ndi;
|
||||
grant.last_tti = cur_grant.tti;
|
||||
memcpy(&cur_grant, &grant, sizeof(mac_interface_phy::mac_grant_t));
|
||||
|
||||
// Fill action structure
|
||||
bzero(action, sizeof(mac_interface_phy::tb_action_dl_t));
|
||||
action->default_ack = ack;
|
||||
action->generate_ack = true;
|
||||
action->decode_enabled = false;
|
||||
|
||||
// If data has not yet been successfully decoded
|
||||
if (ack == false) {
|
||||
|
||||
// Instruct the PHY To combine the received data and attempt to decode it
|
||||
payload_buffer_ptr = harq_entity->demux_unit->request_buffer(pid, cur_grant.n_bytes);
|
||||
action->payload_ptr = payload_buffer_ptr;
|
||||
if (!action->payload_ptr) {
|
||||
action->decode_enabled = false;
|
||||
Error("Can't get a buffer for TBS=%d\n", cur_grant.n_bytes);
|
||||
return;
|
||||
}
|
||||
action->decode_enabled = true;
|
||||
action->rv = cur_grant.rv;
|
||||
action->rnti = cur_grant.rnti;
|
||||
action->softbuffer = &softbuffer;
|
||||
memcpy(&action->phy_grant, &cur_grant.phy_grant, sizeof(srslte_phy_grant_t));
|
||||
n_retx++;
|
||||
|
||||
} else {
|
||||
Warning("DL PID %d: Received duplicate TB. Discarting and retransmitting ACK\n", pid);
|
||||
}
|
||||
|
||||
if (pid == HARQ_BCCH_PID || harq_entity->timers_db->get(mac::TIME_ALIGNMENT)->is_expired()) {
|
||||
// Do not generate ACK
|
||||
Debug("Not generating ACK\n");
|
||||
action->generate_ack = false;
|
||||
} else {
|
||||
if (cur_grant.rnti_type == SRSLTE_RNTI_TEMP && ack == false) {
|
||||
// Postpone ACK after contention resolution is resolved
|
||||
action->generate_ack_callback = harq_entity->generate_ack_callback;
|
||||
action->generate_ack_callback_arg = harq_entity->demux_unit;
|
||||
Debug("ACK pending contention resolution\n");
|
||||
} else {
|
||||
Debug("Generating ACK\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int dl_harq_entity::dl_harq_process::get_current_tbs()
|
||||
{
|
||||
return cur_grant.n_bytes*8;
|
||||
}
|
||||
|
||||
void dl_harq_entity::dl_harq_process::tb_decoded(bool ack_)
|
||||
{
|
||||
ack = ack_;
|
||||
if (ack == true) {
|
||||
if (pid == HARQ_BCCH_PID) {
|
||||
if (harq_entity->pcap) {
|
||||
harq_entity->pcap->write_dl_sirnti(payload_buffer_ptr, cur_grant.n_bytes, ack, cur_grant.tti);
|
||||
}
|
||||
Debug("Delivering PDU=%d bytes to Dissassemble and Demux unit (BCCH)\n", cur_grant.n_bytes);
|
||||
harq_entity->demux_unit->push_pdu(pid, payload_buffer_ptr, cur_grant.n_bytes, cur_grant.tti);
|
||||
} else {
|
||||
if (harq_entity->pcap) {
|
||||
harq_entity->pcap->write_dl_crnti(payload_buffer_ptr, cur_grant.n_bytes, cur_grant.rnti, ack, cur_grant.tti);
|
||||
}
|
||||
if (ack) {
|
||||
if (cur_grant.rnti_type == SRSLTE_RNTI_TEMP) {
|
||||
Debug("Delivering PDU=%d bytes to Dissassemble and Demux unit (Temporal C-RNTI)\n", cur_grant.n_bytes);
|
||||
harq_entity->demux_unit->push_pdu_temp_crnti(payload_buffer_ptr, cur_grant.n_bytes);
|
||||
} else {
|
||||
Debug("Delivering PDU=%d bytes to Dissassemble and Demux unit\n", cur_grant.n_bytes);
|
||||
harq_entity->demux_unit->push_pdu(pid, payload_buffer_ptr, cur_grant.n_bytes, cur_grant.tti);
|
||||
|
||||
// Compute average number of retransmissions per packet
|
||||
harq_entity->average_retx = SRSLTE_VEC_CMA((float) n_retx, harq_entity->average_retx, harq_entity->nof_pkts++);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
harq_entity->demux_unit->deallocate(payload_buffer_ptr);
|
||||
}
|
||||
|
||||
Info("DL %d: %s tbs=%d, rv=%d, ack=%s, ndi=%d (%d), tti=%d (%d)\n",
|
||||
pid, is_new_transmission?"newTX":"reTX ",
|
||||
cur_grant.n_bytes, cur_grant.rv, ack?"OK":"KO",
|
||||
cur_grant.ndi, cur_grant.last_ndi, cur_grant.tti, cur_grant.last_tti);
|
||||
|
||||
if (ack && pid == HARQ_BCCH_PID) {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -42,7 +42,9 @@
|
|||
namespace srsue {
|
||||
|
||||
mac::mac() : ttisync(10240),
|
||||
timers_db((uint32_t) NOF_MAC_TIMERS),
|
||||
timers_db((uint32_t) NOF_MAC_TIMERS),
|
||||
mux_unit(MAC_NOF_HARQ_PROC),
|
||||
demux_unit(MAC_NOF_HARQ_PROC),
|
||||
pdu_process_thread(&demux_unit)
|
||||
{
|
||||
started = false;
|
||||
|
@ -65,13 +67,13 @@ bool mac::init(phy_interface_mac *phy, rlc_interface_mac *rlc, rrc_interface_mac
|
|||
srslte_softbuffer_rx_init(&pch_softbuffer, 100);
|
||||
|
||||
bsr_procedure.init( rlc_h, log_h, &config, &timers_db);
|
||||
phr_procedure.init(phy_h, log_h, &config, &timers_db);
|
||||
mux_unit.init ( rlc_h, log_h, &bsr_procedure, &phr_procedure);
|
||||
demux_unit.init (phy_h, rlc_h, log_h, &timers_db);
|
||||
ra_procedure.init (phy_h, rrc, log_h, &uernti, &config, &timers_db, &mux_unit, &demux_unit);
|
||||
phr_procedure.init(phy_h, log_h, &config, &timers_db);
|
||||
mux_unit.init ( rlc_h, log_h, &bsr_procedure, &phr_procedure);
|
||||
demux_unit.init (phy_h, rlc_h, log_h, &timers_db);
|
||||
ra_procedure.init (phy_h, rrc, log_h, &uernti, &config, &timers_db, &mux_unit, &demux_unit);
|
||||
sr_procedure.init (phy_h, rrc, log_h, &config);
|
||||
ul_harq.init ( log_h, &uernti, &config, &timers_db, &mux_unit);
|
||||
dl_harq.init ( log_h, &config, &timers_db, &demux_unit);
|
||||
ul_harq.init ( log_h, &uernti, &config.ul_harq_params, &timers_db, &mux_unit);
|
||||
dl_harq.init ( log_h, &timers_db, &demux_unit);
|
||||
|
||||
reset();
|
||||
|
||||
|
@ -293,7 +295,7 @@ void mac::new_grant_ul(mac_interface_phy::mac_grant_t grant, mac_interface_phy::
|
|||
/* Start PHR Periodic timer on first UL grant */
|
||||
if (is_first_ul_grant) {
|
||||
is_first_ul_grant = false;
|
||||
timers_db.get(mac::PHR_TIMER_PERIODIC)->run();
|
||||
timers_db.get(PHR_TIMER_PERIODIC)->run();
|
||||
}
|
||||
if (grant.rnti_type == SRSLTE_RNTI_USER && ra_procedure.is_contention_resolution()) {
|
||||
ra_procedure.pdcch_to_crnti(true);
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
namespace srsue {
|
||||
|
||||
mux::mux() : pdu_msg(MAX_NOF_SUBHEADERS)
|
||||
mux::mux(uint8_t nof_harq_proc_) : pdu_msg(MAX_NOF_SUBHEADERS), pid_has_bsr(nof_harq_proc_), nof_harq_proc(nof_harq_proc_)
|
||||
{
|
||||
pthread_mutex_init(&mutex, NULL);
|
||||
|
||||
|
@ -51,7 +51,7 @@ mux::mux() : pdu_msg(MAX_NOF_SUBHEADERS)
|
|||
msg3_flush();
|
||||
}
|
||||
|
||||
void mux::init(rlc_interface_mac *rlc_, srslte::log *log_h_, bsr_proc *bsr_procedure_, phr_proc *phr_procedure_)
|
||||
void mux::init(rlc_interface_mac *rlc_, srslte::log *log_h_, bsr_interface_mux *bsr_procedure_, phr_proc *phr_procedure_)
|
||||
{
|
||||
log_h = log_h_;
|
||||
rlc = rlc_;
|
||||
|
@ -141,7 +141,7 @@ srslte::sch_subh::cetype bsr_format_convert(bsr_proc::bsr_format_t format) {
|
|||
|
||||
void mux::pusch_retx(uint32_t tx_tti, uint32_t pid)
|
||||
{
|
||||
if (pid_has_bsr[pid%MAX_HARQ_PROC]) {
|
||||
if (pid_has_bsr[pid%nof_harq_proc]) {
|
||||
bsr_procedure->set_tx_tti(tx_tti);
|
||||
}
|
||||
}
|
||||
|
@ -189,13 +189,17 @@ uint8_t* mux::pdu_get(uint8_t *payload, uint32_t pdu_sz, uint32_t tx_tti, uint32
|
|||
bsr_is_inserted = true;
|
||||
}
|
||||
}
|
||||
|
||||
// MAC control element for PHR
|
||||
float phr_value;
|
||||
if (phr_procedure->generate_phr_on_ul_grant(&phr_value)) {
|
||||
if (pdu_msg.new_subh()) {
|
||||
pdu_msg.get()->set_phr(phr_value);
|
||||
if (phr_procedure) {
|
||||
float phr_value;
|
||||
if (phr_procedure->generate_phr_on_ul_grant(&phr_value)) {
|
||||
if (pdu_msg.new_subh()) {
|
||||
pdu_msg.get()->set_phr(phr_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update buffer states for all logical channels
|
||||
int sdu_space = pdu_msg.get_sdu_space();
|
||||
for (uint32_t i=0;i<lch.size();i++) {
|
||||
|
@ -252,7 +256,7 @@ uint8_t* mux::pdu_get(uint8_t *payload, uint32_t pdu_sz, uint32_t tx_tti, uint32
|
|||
/* Generate MAC PDU and save to buffer */
|
||||
uint8_t *ret = pdu_msg.write_packet(log_h);
|
||||
|
||||
pid_has_bsr[pid%MAX_HARQ_PROC] = bsr_is_inserted;
|
||||
pid_has_bsr[pid%nof_harq_proc] = bsr_is_inserted;
|
||||
if (bsr_is_inserted) {
|
||||
bsr_procedure->set_tx_tti(tx_tti);
|
||||
}
|
||||
|
|
|
@ -57,10 +57,10 @@ void bsr_proc::init(rlc_interface_mac *rlc_, srslte::log* log_h_, mac_interface_
|
|||
|
||||
void bsr_proc::reset()
|
||||
{
|
||||
timers_db->get(mac::BSR_TIMER_PERIODIC)->stop();
|
||||
timers_db->get(mac::BSR_TIMER_PERIODIC)->reset();
|
||||
timers_db->get(mac::BSR_TIMER_RETX)->stop();
|
||||
timers_db->get(mac::BSR_TIMER_RETX)->reset();
|
||||
timers_db->get(BSR_TIMER_PERIODIC)->stop();
|
||||
timers_db->get(BSR_TIMER_PERIODIC)->reset();
|
||||
timers_db->get(BSR_TIMER_RETX)->stop();
|
||||
timers_db->get(BSR_TIMER_RETX)->reset();
|
||||
|
||||
reset_sr = false;
|
||||
sr_is_sent = false;
|
||||
|
@ -78,14 +78,14 @@ void bsr_proc::reset()
|
|||
/* Process Periodic BSR */
|
||||
void bsr_proc::timer_expired(uint32_t timer_id) {
|
||||
switch(timer_id) {
|
||||
case mac::BSR_TIMER_PERIODIC:
|
||||
case BSR_TIMER_PERIODIC:
|
||||
if (triggered_bsr_type == NONE) {
|
||||
// Check condition 4 in Sec 5.4.5
|
||||
triggered_bsr_type = PERIODIC;
|
||||
Debug("BSR: Triggering Periodic BSR\n");
|
||||
}
|
||||
break;
|
||||
case mac::BSR_TIMER_RETX:
|
||||
case BSR_TIMER_RETX:
|
||||
// Enable reTx of SR only if periodic timer is not infinity
|
||||
int periodic = liblte_rrc_periodic_bsr_timer_num[mac_cfg->main.ulsch_cnfg.periodic_bsr_timer];
|
||||
if (periodic >= 0) {
|
||||
|
@ -222,17 +222,17 @@ void bsr_proc::step(uint32_t tti)
|
|||
}
|
||||
|
||||
int periodic = liblte_rrc_periodic_bsr_timer_num[mac_cfg->main.ulsch_cnfg.periodic_bsr_timer];
|
||||
if (periodic > 0 && (uint32_t)periodic != timers_db->get(mac::BSR_TIMER_PERIODIC)->get_timeout())
|
||||
if (periodic > 0 && (uint32_t)periodic != timers_db->get(BSR_TIMER_PERIODIC)->get_timeout())
|
||||
{
|
||||
timers_db->get(mac::BSR_TIMER_PERIODIC)->set(this, periodic);
|
||||
timers_db->get(mac::BSR_TIMER_PERIODIC)->run();
|
||||
timers_db->get(BSR_TIMER_PERIODIC)->set(this, periodic);
|
||||
timers_db->get(BSR_TIMER_PERIODIC)->run();
|
||||
Info("BSR: Configured timer periodic %d ms\n", periodic);
|
||||
}
|
||||
int retx = liblte_rrc_retransmission_bsr_timer_num[mac_cfg->main.ulsch_cnfg.retx_bsr_timer];
|
||||
if (retx > 0 && (uint32_t)retx != timers_db->get(mac::BSR_TIMER_RETX)->get_timeout())
|
||||
if (retx > 0 && (uint32_t)retx != timers_db->get(BSR_TIMER_RETX)->get_timeout())
|
||||
{
|
||||
timers_db->get(mac::BSR_TIMER_RETX)->set(this, retx);
|
||||
timers_db->get(mac::BSR_TIMER_RETX)->run();
|
||||
timers_db->get(BSR_TIMER_RETX)->set(this, retx);
|
||||
timers_db->get(BSR_TIMER_RETX)->run();
|
||||
Info("BSR: Configured timer reTX %d ms\n", retx);
|
||||
}
|
||||
|
||||
|
@ -309,18 +309,18 @@ bool bsr_proc::need_to_send_bsr_on_ul_grant(uint32_t grant_size, bsr_t *bsr)
|
|||
grant_size, total_data, bsr_sz);
|
||||
ret = true;
|
||||
}
|
||||
if (timers_db->get(mac::BSR_TIMER_PERIODIC)->get_timeout() && bsr->format != TRUNC_BSR) {
|
||||
timers_db->get(mac::BSR_TIMER_PERIODIC)->reset();
|
||||
timers_db->get(mac::BSR_TIMER_PERIODIC)->run();
|
||||
if (timers_db->get(BSR_TIMER_PERIODIC)->get_timeout() && bsr->format != TRUNC_BSR) {
|
||||
timers_db->get(BSR_TIMER_PERIODIC)->reset();
|
||||
timers_db->get(BSR_TIMER_PERIODIC)->run();
|
||||
}
|
||||
}
|
||||
// Cancel all triggered BSR and SR
|
||||
triggered_bsr_type = NONE;
|
||||
reset_sr = true;
|
||||
// Restart or Start ReTX timer
|
||||
if (timers_db->get(mac::BSR_TIMER_RETX)->get_timeout()) {
|
||||
timers_db->get(mac::BSR_TIMER_RETX)->reset();
|
||||
timers_db->get(mac::BSR_TIMER_RETX)->run();
|
||||
if (timers_db->get(BSR_TIMER_RETX)->get_timeout()) {
|
||||
timers_db->get(BSR_TIMER_RETX)->reset();
|
||||
timers_db->get(BSR_TIMER_RETX)->run();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -340,9 +340,9 @@ bool bsr_proc::generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t *bsr)
|
|||
bsr_type_tostring(triggered_bsr_type), bsr_format_tostring(bsr->format),
|
||||
bsr->buff_size[0], bsr->buff_size[1], bsr->buff_size[2], bsr->buff_size[3]);
|
||||
|
||||
if (timers_db->get(mac::BSR_TIMER_PERIODIC)->get_timeout() && bsr->format != TRUNC_BSR) {
|
||||
timers_db->get(mac::BSR_TIMER_PERIODIC)->reset();
|
||||
timers_db->get(mac::BSR_TIMER_PERIODIC)->run();
|
||||
if (timers_db->get(BSR_TIMER_PERIODIC)->get_timeout() && bsr->format != TRUNC_BSR) {
|
||||
timers_db->get(BSR_TIMER_PERIODIC)->reset();
|
||||
timers_db->get(BSR_TIMER_PERIODIC)->run();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -76,13 +76,13 @@ bool phr_proc::pathloss_changed() {
|
|||
/* Trigger PHR when timers exire */
|
||||
void phr_proc::timer_expired(uint32_t timer_id) {
|
||||
switch(timer_id) {
|
||||
case mac::PHR_TIMER_PERIODIC:
|
||||
timers_db->get(mac::PHR_TIMER_PERIODIC)->reset();
|
||||
timers_db->get(mac::PHR_TIMER_PERIODIC)->run();
|
||||
case PHR_TIMER_PERIODIC:
|
||||
timers_db->get(PHR_TIMER_PERIODIC)->reset();
|
||||
timers_db->get(PHR_TIMER_PERIODIC)->run();
|
||||
Debug("PHR: Triggered by timer periodic (timer expired).\n");
|
||||
phr_is_triggered = true;
|
||||
break;
|
||||
case mac::PHR_TIMER_PROHIBIT:
|
||||
case PHR_TIMER_PROHIBIT:
|
||||
int pathloss_db = liblte_rrc_dl_pathloss_change_num[mac_cfg->main.phr_cnfg.dl_pathloss_change];
|
||||
if (pathloss_changed()) {
|
||||
Info("PHR: Triggered by pathloss difference. cur_pathloss_db=%f (timer expired)\n", last_pathloss_db);
|
||||
|
@ -105,8 +105,8 @@ void phr_proc::step(uint32_t tti)
|
|||
if (timer_periodic != cfg_timer_periodic && cfg_timer_periodic > 0)
|
||||
{
|
||||
timer_periodic = cfg_timer_periodic;
|
||||
timers_db->get(mac::PHR_TIMER_PERIODIC)->set(this, timer_periodic);
|
||||
timers_db->get(mac::PHR_TIMER_PERIODIC)->run();
|
||||
timers_db->get(PHR_TIMER_PERIODIC)->set(this, timer_periodic);
|
||||
timers_db->get(PHR_TIMER_PERIODIC)->run();
|
||||
phr_is_triggered = true;
|
||||
Info("PHR: Configured timer periodic %d ms\n", timer_periodic);
|
||||
}
|
||||
|
@ -118,12 +118,12 @@ void phr_proc::step(uint32_t tti)
|
|||
if (timer_prohibit != cfg_timer_prohibit && cfg_timer_prohibit > 0)
|
||||
{
|
||||
timer_prohibit = cfg_timer_prohibit;
|
||||
timers_db->get(mac::PHR_TIMER_PROHIBIT)->set(this, timer_prohibit);
|
||||
timers_db->get(mac::PHR_TIMER_PROHIBIT)->run();
|
||||
timers_db->get(PHR_TIMER_PROHIBIT)->set(this, timer_prohibit);
|
||||
timers_db->get(PHR_TIMER_PROHIBIT)->run();
|
||||
Info("PHR: Configured timer prohibit %d ms\n", timer_prohibit);
|
||||
phr_is_triggered = true;
|
||||
}
|
||||
if (pathloss_changed() && timers_db->get(mac::PHR_TIMER_PROHIBIT)->is_expired())
|
||||
if (pathloss_changed() && timers_db->get(PHR_TIMER_PROHIBIT)->is_expired())
|
||||
{
|
||||
Info("PHR: Triggered by pathloss difference. cur_pathloss_db=%f\n", last_pathloss_db);
|
||||
phr_is_triggered = true;
|
||||
|
@ -140,10 +140,10 @@ bool phr_proc::generate_phr_on_ul_grant(float *phr)
|
|||
|
||||
Debug("PHR: Generating PHR=%f\n", phr?*phr:0.0);
|
||||
|
||||
timers_db->get(mac::PHR_TIMER_PERIODIC)->reset();
|
||||
timers_db->get(mac::PHR_TIMER_PROHIBIT)->reset();
|
||||
timers_db->get(mac::PHR_TIMER_PERIODIC)->run();
|
||||
timers_db->get(mac::PHR_TIMER_PROHIBIT)->run();
|
||||
timers_db->get(PHR_TIMER_PERIODIC)->reset();
|
||||
timers_db->get(PHR_TIMER_PROHIBIT)->reset();
|
||||
timers_db->get(PHR_TIMER_PERIODIC)->run();
|
||||
timers_db->get(PHR_TIMER_PROHIBIT)->run();
|
||||
|
||||
phr_is_triggered = false;
|
||||
|
||||
|
|
|
@ -115,7 +115,7 @@ void ra_proc::read_params() {
|
|||
delta_preamble_db = delta_preamble_db_table[configIndex%5];
|
||||
|
||||
if (contentionResolutionTimer > 0) {
|
||||
timers_db->get(mac::CONTENTION_TIMER)->set(this, contentionResolutionTimer);
|
||||
timers_db->get(CONTENTION_TIMER)->set(this, contentionResolutionTimer);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -165,14 +165,14 @@ void ra_proc::process_timeadv_cmd(uint32_t ta) {
|
|||
if (preambleIndex == 0) {
|
||||
// Preamble not selected by UE MAC
|
||||
phy_h->set_timeadv_rar(ta);
|
||||
timers_db->get(mac::TIME_ALIGNMENT)->reset();
|
||||
timers_db->get(mac::TIME_ALIGNMENT)->run();
|
||||
timers_db->get(TIME_ALIGNMENT)->reset();
|
||||
timers_db->get(TIME_ALIGNMENT)->run();
|
||||
Debug("Applying RAR TA CMD %d\n", ta);
|
||||
} else {
|
||||
// Preamble selected by UE MAC
|
||||
if (!timers_db->get(mac::TIME_ALIGNMENT)->is_running()) {
|
||||
if (!timers_db->get(TIME_ALIGNMENT)->is_running()) {
|
||||
phy_h->set_timeadv_rar(ta);
|
||||
timers_db->get(mac::TIME_ALIGNMENT)->run();
|
||||
timers_db->get(TIME_ALIGNMENT)->run();
|
||||
Debug("Applying RAR TA CMD %d\n", ta);
|
||||
} else {
|
||||
// Ignore TA CMD
|
||||
|
@ -355,8 +355,8 @@ void ra_proc::tb_decoded_ok() {
|
|||
state = CONTENTION_RESOLUTION;
|
||||
|
||||
// Start contention resolution timer
|
||||
timers_db->get(mac::CONTENTION_TIMER)->reset();
|
||||
timers_db->get(mac::CONTENTION_TIMER)->run();
|
||||
timers_db->get(CONTENTION_TIMER)->reset();
|
||||
timers_db->get(CONTENTION_TIMER)->run();
|
||||
}
|
||||
} else {
|
||||
rDebug("Found RAR for preamble %d\n", rar_pdu_msg.get()->get_rapid());
|
||||
|
@ -417,7 +417,7 @@ bool ra_proc::contention_resolution_id_received(uint64_t rx_contention_id) {
|
|||
rDebug("MAC PDU Contains Contention Resolution ID CE\n");
|
||||
|
||||
// MAC PDU successfully decoded and contains MAC CE contention Id
|
||||
timers_db->get(mac::CONTENTION_TIMER)->stop();
|
||||
timers_db->get(CONTENTION_TIMER)->stop();
|
||||
|
||||
if (transmitted_contention_id == rx_contention_id)
|
||||
{
|
||||
|
@ -453,7 +453,7 @@ void ra_proc::step_contention_resolution() {
|
|||
(started_by_pdcch && pdcch_to_crnti_received != PDCCH_CRNTI_NOT_RECEIVED))
|
||||
{
|
||||
rDebug("PDCCH for C-RNTI received\n");
|
||||
timers_db->get(mac::CONTENTION_TIMER)->stop();
|
||||
timers_db->get(CONTENTION_TIMER)->stop();
|
||||
rntis->temp_rnti = 0;
|
||||
state = COMPLETION;
|
||||
}
|
||||
|
@ -565,7 +565,7 @@ void ra_proc::pdcch_to_crnti(bool contains_uplink_grant) {
|
|||
|
||||
void ra_proc::harq_retx()
|
||||
{
|
||||
timers_db->get(mac::CONTENTION_TIMER)->reset();
|
||||
timers_db->get(CONTENTION_TIMER)->reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,394 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* \section COPYRIGHT
|
||||
*
|
||||
* Copyright 2013-2015 Software Radio Systems Limited
|
||||
*
|
||||
* \section LICENSE
|
||||
*
|
||||
* This file is part of the srsUE library.
|
||||
*
|
||||
* srsUE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* srsUE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* A copy of the GNU Affero General Public License can be found in
|
||||
* the LICENSE file in the top-level directory of this distribution
|
||||
* and at http://www.gnu.org/licenses/.
|
||||
*
|
||||
*/
|
||||
|
||||
#define Error(fmt, ...) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define Warning(fmt, ...) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define Info(fmt, ...) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define Debug(fmt, ...) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
|
||||
#include "srslte/common/log.h"
|
||||
#include "mac/mac.h"
|
||||
#include "mac/ul_harq.h"
|
||||
|
||||
|
||||
namespace srsue {
|
||||
|
||||
/***********************************************************
|
||||
*
|
||||
* HARQ ENTITY
|
||||
*
|
||||
*********************************************************/
|
||||
|
||||
bool ul_harq_entity::init(srslte::log *log_h_,
|
||||
mac_interface_rrc::ue_rnti_t *rntis_,
|
||||
mac_interface_rrc::mac_cfg_t *mac_cfg_,
|
||||
srslte::timers *timers_db_,
|
||||
mux *mux_unit_) {
|
||||
log_h = log_h_;
|
||||
mux_unit = mux_unit_;
|
||||
mac_cfg = mac_cfg_;
|
||||
rntis = rntis_;
|
||||
timers_db = timers_db_;
|
||||
for (uint32_t i=0;i<NOF_HARQ_PROC;i++) {
|
||||
if (!proc[i].init(i, this)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
uint32_t ul_harq_entity::pidof(uint32_t tti) {
|
||||
return (uint32_t) tti%NOF_HARQ_PROC;
|
||||
}
|
||||
|
||||
void ul_harq_entity::start_pcap(srslte::mac_pcap* pcap_)
|
||||
{
|
||||
pcap = pcap_;
|
||||
}
|
||||
|
||||
void ul_harq_entity::reset() {
|
||||
for (uint32_t i=0;i<NOF_HARQ_PROC;i++) {
|
||||
proc[i].reset();
|
||||
}
|
||||
ul_sps_assig.clear();
|
||||
}
|
||||
void ul_harq_entity::reset_ndi() {
|
||||
for (uint32_t i=0;i<NOF_HARQ_PROC;i++) {
|
||||
proc[i].reset_ndi();
|
||||
}
|
||||
}
|
||||
|
||||
int ul_harq_entity::get_current_tbs(uint32_t tti) {
|
||||
int tti_harq = (int) tti-4;
|
||||
if (tti_harq < 0) {
|
||||
tti_harq += 10240;
|
||||
}
|
||||
uint32_t pid_harq = pidof(tti_harq);
|
||||
return proc[pid_harq].get_current_tbs();
|
||||
}
|
||||
|
||||
void ul_harq_entity::set_ack(uint32_t tti, bool ack) {
|
||||
int tti_harq = (int) tti - 4;
|
||||
if (tti_harq < 0) {
|
||||
tti_harq += 10240;
|
||||
}
|
||||
uint32_t pid_harq = pidof(tti_harq);
|
||||
if (proc[pid_harq].has_grant() && (proc[pid_harq].last_tx_tti() <= (uint32_t)tti_harq)) {
|
||||
proc[pid_harq].set_harq_feedback(ack);
|
||||
}
|
||||
}
|
||||
|
||||
void ul_harq_entity::harq_recv(uint32_t tti, bool ack, mac_interface_phy::tb_action_ul_t* action)
|
||||
{
|
||||
set_ack(tti, ack);
|
||||
run_tti(tti, NULL, action);
|
||||
}
|
||||
|
||||
// Implements Section 5.4.1
|
||||
void ul_harq_entity::new_grant_ul(mac_interface_phy::mac_grant_t grant, mac_interface_phy::tb_action_ul_t* action)
|
||||
{
|
||||
if (grant.rnti_type == SRSLTE_RNTI_USER ||
|
||||
grant.rnti_type == SRSLTE_RNTI_TEMP ||
|
||||
grant.rnti_type == SRSLTE_RNTI_RAR)
|
||||
{
|
||||
if (grant.rnti_type == SRSLTE_RNTI_USER && proc[pidof(grant.tti)].is_sps()) {
|
||||
grant.ndi = true;
|
||||
}
|
||||
run_tti(grant.tti, &grant, action);
|
||||
} else if (grant.rnti_type == SRSLTE_RNTI_SPS) {
|
||||
if (grant.ndi) {
|
||||
grant.ndi = proc[pidof(grant.tti)].get_ndi();
|
||||
run_tti(grant.tti, &grant, action);
|
||||
} else {
|
||||
Info("Not implemented\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ul_harq_entity::new_grant_ul_ack(mac_interface_phy::mac_grant_t grant, bool ack, mac_interface_phy::tb_action_ul_t* action)
|
||||
{
|
||||
set_ack(grant.tti, ack);
|
||||
new_grant_ul(grant, action);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Implements Section 5.4.2.1
|
||||
// Called with UL grant
|
||||
void ul_harq_entity::run_tti(uint32_t tti, mac_interface_phy::mac_grant_t *grant, mac_interface_phy::tb_action_ul_t* action)
|
||||
{
|
||||
uint32_t tti_tx = (tti+4)%10240;
|
||||
proc[pidof(tti_tx)].run_tti(tti_tx, grant, action);
|
||||
}
|
||||
|
||||
|
||||
float ul_harq_entity::get_average_retx()
|
||||
{
|
||||
return average_retx;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************
|
||||
*
|
||||
* HARQ PROCESS
|
||||
*
|
||||
*********************************************************/
|
||||
|
||||
static int rv_of_irv[4] = {0, 2, 3, 1};
|
||||
static int irv_of_rv[4] = {0, 3, 1, 2};
|
||||
|
||||
ul_harq_entity::ul_harq_process::ul_harq_process() {
|
||||
current_tx_nb = 0;
|
||||
current_irv = 0;
|
||||
is_initiated = false;
|
||||
is_grant_configured = false;
|
||||
tti_last_tx = 0;
|
||||
bzero(&cur_grant, sizeof(mac_interface_phy::mac_grant_t));
|
||||
}
|
||||
|
||||
void ul_harq_entity::ul_harq_process::reset() {
|
||||
current_tx_nb = 0;
|
||||
current_irv = 0;
|
||||
tti_last_tx = 0;
|
||||
is_grant_configured = false;
|
||||
bzero(&cur_grant, sizeof(mac_interface_phy::mac_grant_t));
|
||||
}
|
||||
|
||||
bool ul_harq_entity::ul_harq_process::has_grant() {
|
||||
return is_grant_configured;
|
||||
}
|
||||
|
||||
void ul_harq_entity::ul_harq_process::reset_ndi() {
|
||||
ndi = false;
|
||||
}
|
||||
|
||||
bool ul_harq_entity::ul_harq_process::get_ndi()
|
||||
{
|
||||
return ndi;
|
||||
}
|
||||
|
||||
uint32_t ul_harq_entity::ul_harq_process::get_rv()
|
||||
{
|
||||
return rv_of_irv[current_irv%4];
|
||||
}
|
||||
|
||||
void ul_harq_entity::ul_harq_process::set_harq_feedback(bool ack) {
|
||||
harq_feedback = ack;
|
||||
// UL packet successfully delivered
|
||||
if (ack) {
|
||||
Info("UL %d: HARQ = ACK for UL transmission. Discarting TB.\n", pid);
|
||||
reset();
|
||||
} else {
|
||||
Info("UL %d: HARQ = NACK for UL transmission\n", pid);
|
||||
}
|
||||
}
|
||||
|
||||
bool ul_harq_entity::ul_harq_process::init(uint32_t pid_, ul_harq_entity* parent) {
|
||||
if (srslte_softbuffer_tx_init(&softbuffer, SRSLTE_MAX_PRB)) {
|
||||
fprintf(stderr, "Error initiating soft buffer\n");
|
||||
return false;
|
||||
} else {
|
||||
is_initiated = true;
|
||||
harq_entity = parent;
|
||||
log_h = harq_entity->log_h;
|
||||
pid = pid_;
|
||||
payload_buffer = (uint8_t*) srslte_vec_malloc(payload_buffer_len*sizeof(uint8_t));
|
||||
if (!payload_buffer) {
|
||||
Error("Allocating memory\n");
|
||||
return false;
|
||||
}
|
||||
pdu_ptr = payload_buffer;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void ul_harq_entity::ul_harq_process::run_tti(uint32_t tti_tx, mac_interface_phy::mac_grant_t* grant, mac_interface_phy::tb_action_ul_t* action)
|
||||
{
|
||||
|
||||
|
||||
uint32_t max_retx;
|
||||
if (is_msg3) {
|
||||
max_retx = harq_entity->mac_cfg->rach.max_harq_msg3_tx;
|
||||
} else {
|
||||
max_retx = liblte_rrc_max_harq_tx_num[harq_entity->mac_cfg->main.ulsch_cnfg.max_harq_tx];
|
||||
}
|
||||
|
||||
|
||||
// Receive and route HARQ feedbacks
|
||||
if (grant) {
|
||||
if ((!(grant->rnti_type == SRSLTE_RNTI_TEMP) && grant->ndi != get_ndi()) ||
|
||||
(grant->rnti_type == SRSLTE_RNTI_USER && !has_grant()) ||
|
||||
grant->is_from_rar)
|
||||
{
|
||||
// New transmission
|
||||
|
||||
// Uplink grant in a RAR
|
||||
if (grant->is_from_rar) {
|
||||
Debug("Getting Msg3 buffer payload, grant size=%d bytes\n", grant->n_bytes);
|
||||
pdu_ptr = harq_entity->mux_unit->msg3_get(payload_buffer, grant->n_bytes);
|
||||
if (pdu_ptr) {
|
||||
generate_new_tx(tti_tx, true, grant, action);
|
||||
} else {
|
||||
Warning("UL RAR grant available but no Msg3 on buffer\n");
|
||||
}
|
||||
|
||||
// Normal UL grant
|
||||
} else {
|
||||
// Request a MAC PDU from the Multiplexing & Assemble Unit
|
||||
pdu_ptr = harq_entity->mux_unit->pdu_get(payload_buffer, grant->n_bytes, tti_tx, pid);
|
||||
if (pdu_ptr) {
|
||||
generate_new_tx(tti_tx, false, grant, action);
|
||||
} else {
|
||||
Warning("Uplink grant but no MAC PDU in Multiplex Unit buffer\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Adaptive Re-TX
|
||||
if (current_tx_nb >= max_retx) {
|
||||
Info("UL %d: Maximum number of ReTX reached (%d). Discarting TB.\n", pid, max_retx);
|
||||
reset();
|
||||
action->expect_ack = false;
|
||||
} else {
|
||||
generate_retx(tti_tx, grant, action);
|
||||
}
|
||||
}
|
||||
} else if (has_grant()) {
|
||||
// Non-Adaptive Re-Tx
|
||||
if (current_tx_nb >= max_retx) {
|
||||
Info("UL %d: Maximum number of ReTX reached (%d). Discarting TB.\n", pid, max_retx);
|
||||
reset();
|
||||
action->expect_ack = false;
|
||||
} else {
|
||||
generate_retx(tti_tx, action);
|
||||
}
|
||||
}
|
||||
if (harq_entity->pcap && grant) {
|
||||
if (grant->is_from_rar) {
|
||||
grant->rnti = harq_entity->rntis->temp_rnti;
|
||||
}
|
||||
harq_entity->pcap->write_ul_crnti(pdu_ptr, grant->n_bytes, grant->rnti, get_nof_retx(), tti_tx);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
int ul_harq_entity::ul_harq_process::get_current_tbs()
|
||||
{
|
||||
return cur_grant.n_bytes*8;
|
||||
}
|
||||
|
||||
void ul_harq_entity::ul_harq_process::generate_retx(uint32_t tti_tx, mac_interface_phy::tb_action_ul_t *action)
|
||||
{
|
||||
generate_retx(tti_tx, NULL, action);
|
||||
}
|
||||
|
||||
// Retransmission with or w/o grant (Section 5.4.2.2)
|
||||
void ul_harq_entity::ul_harq_process::generate_retx(uint32_t tti_tx, mac_interface_phy::mac_grant_t *grant,
|
||||
mac_interface_phy::tb_action_ul_t *action)
|
||||
{
|
||||
if (grant) {
|
||||
// HARQ entity requests an adaptive transmission
|
||||
if (grant->rv) {
|
||||
current_irv = irv_of_rv[grant->rv%4];
|
||||
}
|
||||
memcpy(&cur_grant, grant, sizeof(mac_interface_phy::mac_grant_t));
|
||||
harq_feedback = false;
|
||||
Info("UL %d: Adaptive retx=%d, RV=%d, TBS=%d\n",
|
||||
pid, current_tx_nb, get_rv(), grant->n_bytes);
|
||||
generate_tx(tti_tx, action);
|
||||
} else {
|
||||
Info("UL %d: Non-Adaptive retx=%d, RV=%d, TBS=%d\n",
|
||||
pid, current_tx_nb, get_rv(), cur_grant.n_bytes);
|
||||
// HARQ entity requests a non-adaptive transmission
|
||||
if (!harq_feedback) {
|
||||
generate_tx(tti_tx, action);
|
||||
}
|
||||
}
|
||||
|
||||
// On every Msg3 retransmission, restart mac-ContentionResolutionTimer as defined in Section 5.1.5
|
||||
if (is_msg3) {
|
||||
harq_entity->timers_db->get(mac::CONTENTION_TIMER)->reset();
|
||||
}
|
||||
|
||||
harq_entity->mux_unit->pusch_retx(tti_tx, pid);
|
||||
}
|
||||
|
||||
// New transmission (Section 5.4.2.2)
|
||||
void ul_harq_entity::ul_harq_process::generate_new_tx(uint32_t tti_tx, bool is_msg3_,
|
||||
mac_interface_phy::mac_grant_t *grant,
|
||||
mac_interface_phy::tb_action_ul_t *action)
|
||||
{
|
||||
if (grant) {
|
||||
|
||||
// Compute average number of retransmissions per packet considering previous packet
|
||||
harq_entity->average_retx = SRSLTE_VEC_CMA((float) current_tx_nb, harq_entity->average_retx, harq_entity->nof_pkts++);
|
||||
|
||||
|
||||
memcpy(&cur_grant, grant, sizeof(mac_interface_phy::mac_grant_t));
|
||||
harq_feedback = false;
|
||||
is_grant_configured = true;
|
||||
current_tx_nb = 0;
|
||||
current_irv = 0;
|
||||
is_msg3 = is_msg3_;
|
||||
Info("UL %d: New TX%s, RV=%d, TBS=%d, RNTI=%d\n",
|
||||
pid, is_msg3?" for Msg3":"", get_rv(), cur_grant.n_bytes, cur_grant.rnti);
|
||||
generate_tx(tti_tx, action);
|
||||
}
|
||||
}
|
||||
|
||||
// Transmission of pending frame (Section 5.4.2.2)
|
||||
void ul_harq_entity::ul_harq_process::generate_tx(uint32_t tti_tx, mac_interface_phy::tb_action_ul_t *action)
|
||||
{
|
||||
action->current_tx_nb = current_tx_nb;
|
||||
current_tx_nb++;
|
||||
action->expect_ack = true;
|
||||
action->rnti = is_msg3?harq_entity->rntis->temp_rnti:cur_grant.rnti;
|
||||
action->rv = cur_grant.rv>0?cur_grant.rv:get_rv();
|
||||
action->softbuffer = &softbuffer;
|
||||
action->tx_enabled = true;
|
||||
action->payload_ptr = pdu_ptr;
|
||||
memcpy(&action->phy_grant, &cur_grant.phy_grant, sizeof(srslte_phy_grant_t));
|
||||
|
||||
current_irv = (current_irv+1)%4;
|
||||
tti_last_tx = tti_tx;
|
||||
}
|
||||
|
||||
bool ul_harq_entity::ul_harq_process::is_sps()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t ul_harq_entity::ul_harq_process::last_tx_tti()
|
||||
{
|
||||
return tti_last_tx;
|
||||
}
|
||||
|
||||
uint32_t ul_harq_entity::ul_harq_process::get_nof_retx()
|
||||
{
|
||||
return current_tx_nb;
|
||||
}
|
||||
|
||||
}
|
|
@ -127,7 +127,7 @@ void parse_args(all_args_t *args, int argc, char *argv[]) {
|
|||
"index of the core used by the sync thread")
|
||||
|
||||
("expert.ue_category",
|
||||
bpo::value<int>(&args->expert.ue_cateogry)->default_value(4),
|
||||
bpo::value<string>(&args->expert.ue_cateogry)->default_value("4"),
|
||||
"UE Category (1 to 5)")
|
||||
|
||||
("expert.metrics_period_secs",
|
||||
|
@ -343,18 +343,26 @@ void *input_loop(void *m) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
signal(SIGINT, sig_int_handler);
|
||||
all_args_t args;
|
||||
metrics_stdout metrics;
|
||||
ue *ue = ue::get_instance();
|
||||
|
||||
cout << "--- Software Radio Systems LTE UE ---" << endl << endl;
|
||||
|
||||
parse_args(&args, argc, argv);
|
||||
|
||||
|
||||
srsue_instance_type_t type = LTE;
|
||||
ue_base *ue = ue_base::get_instance(type);
|
||||
if (!ue) {
|
||||
cout << "Error creating UE instance." << endl << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
cout << "--- Software Radio Systems " << srsue_instance_type_text[type] << " UE ---" << endl << endl;
|
||||
if (!ue->init(&args)) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
metrics_stdout metrics;
|
||||
metrics.init(ue, args.expert.metrics_period_secs);
|
||||
|
||||
pthread_t input;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue