added srsENB code

This commit is contained in:
Ismael Gomez 2017-06-01 12:25:57 +02:00
parent 8a367bf825
commit e5ae82aef1
66 changed files with 14359 additions and 2 deletions

View File

@ -204,8 +204,8 @@ endif(CMAKE_COMPILER_IS_GNUCXX)
if(CMAKE_COMPILER_IS_GNUCC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-write-strings -Wno-format-extra-args -Winline -Wno-unused-result -Wno-format -std=c99 -D_GNU_SOURCE -g")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-comment -Wno-write-strings -Wno-format-extra-args -Winline -Wno-unused-result -Wno-format -std=c99 -D_GNU_SOURCE -g")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-comment -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable")
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
find_package(SSE)
@ -321,3 +321,14 @@ if(NOT DISABLE_SRSUE)
else(NOT DISABLE_SRSUE)
message(STATUS "Building without srsUE")
endif(NOT DISABLE_SRSUE)
if(NOT DISABLE_SRSENB)
if(RF_FOUND)
message(STATUS "Building with srsENB")
add_subdirectory(srsenb)
else(RF_FOUND)
message(STATUS "Building without srsENB due to missing RF driver")
endif(RF_FOUND)
else(NOT DISABLE_SRSENB)
message(STATUS "Building without srsENB")
endif(NOT DISABLE_SRSENB)

View File

@ -0,0 +1,75 @@
# Find the CUnit includes and library
#
# This module defines
# LIBCONFIG_INCLUDE_DIR, where to find cppunit include files, etc.
# LIBCONFIG_LIBRARIES, the libraries to link against to use CppUnit.
# LIBCONFIG_STATIC_LIBRARIY_PATH
# LIBCONFIG_FOUND, If false, do not try to use CppUnit.
# also defined, but not for general use are
# LIBCONFIG_LIBRARY, where to find the CUnit library.
#MESSAGE("Searching for libconfig library")
FIND_PATH(LIBCONFIG_INCLUDE_DIR libconfig.h
/usr/local/include
/usr/include
)
FIND_PATH(LIBCONFIGPP_INCLUDE_DIR libconfig.h++
/usr/local/include
/usr/include
)
FIND_LIBRARY(LIBCONFIG_LIBRARY config
/usr/local/lib
/usr/lib
)
FIND_LIBRARY(LIBCONFIGPP_LIBRARY config++
/usr/local/lib
/usr/lib
)
FIND_LIBRARY(LIBCONFIG_STATIC_LIBRARY "libconfig${CMAKE_STATIC_LIBRARY_SUFFIX}"
/usr/local/lib
/usr/lib
)
FIND_LIBRARY(LIBCONFIGPP_STATIC_LIBRARY "libconfig++${CMAKE_STATIC_LIBRARY_SUFFIX}"
/usr/local/lib
/usr/lib
)
IF(LIBCONFIG_INCLUDE_DIR)
IF(LIBCONFIG_LIBRARY)
SET(LIBCONFIG_FOUND TRUE)
SET(LIBCONFIG_LIBRARIES ${LIBCONFIG_LIBRARY})
SET(LIBCONFIG_STATIC_LIBRARY_PATH ${LIBCONFIG_STATIC_LIBRARY})
ENDIF(LIBCONFIG_LIBRARY)
ENDIF(LIBCONFIG_INCLUDE_DIR)
IF(LIBCONFIGPP_INCLUDE_DIR)
IF(LIBCONFIGPP_LIBRARY)
SET(LIBCONFIGPP_FOUND TRUE)
SET(LIBCONFIGPP_LIBRARIES ${LIBCONFIGPP_LIBRARY})
SET(LIBCONFIGPP_STATIC_LIBRARY_PATH ${LIBCONFIGPP_STATIC_LIBRARY})
ENDIF(LIBCONFIGPP_LIBRARY)
ENDIF(LIBCONFIGPP_INCLUDE_DIR)
IF (LIBCONFIGPP_FOUND)
IF (NOT LibConfig_FIND_QUIETLY)
MESSAGE(STATUS "Found LibConfig++: ${LIBCONFIGPP_LIBRARIES}" )
MESSAGE(STATUS "static LibConfig++ path: ${LIBCONFIGPP_STATIC_LIBRARY_PATH}")
MESSAGE(STATUS "Found LibConfig: ${LIBCONFIG_LIBRARIES}")
MESSAGE(STATUS "static LibConfig path: ${LIBCONFIG_STATIC_LIBRARY_PATH}")
ENDIF (NOT LibConfig_FIND_QUIETLY)
ELSE (LIBCONFIGPP_FOUND)
IF (LibConfig_FIND_REQUIRED)
MESSAGE(SEND_ERROR "Could NOT find LibConfig")
ENDIF (LibConfig_FIND_REQUIRED)
ENDIF (LIBCONFIGPP_FOUND)
MARK_AS_ADVANCED(LIBCONFIG_INCLUDE_DIR LIBCONFIG_LIBRARIES)
MARK_AS_ADVANCED(LIBCONFIGPP_INCLUDE_DIR LIBCONFIGPP_LIBRARIES)

View File

@ -0,0 +1,38 @@
# - Try to find sctp
#
# Once done this will define
# SCTP_FOUND - System has mbedtls
# SCTP_INCLUDE_DIRS - The mbedtls include directories
# SCTP_LIBRARIES - The mbedtls library
INCLUDE(FindPkgConfig)
PKG_CHECK_MODULES(PC_SCTP sctp)
#find Mbedtls
FIND_PATH(
SCTP_INCLUDE_DIRS
NAMES netinet/sctp.h
HINTS ${PC_SCTP_INCLUDEDIR}
${CMAKE_INSTALL_PREFIX}/include
PATHS /usr/local/include
/usr/include
)
FIND_LIBRARY(
SCTP_LIBRARIES
NAMES sctp
HINTS ${PC_SCTP_LIBDIR}
${CMAKE_INSTALL_PREFIX}/lib
${CMAKE_INSTALL_PREFIX}/lib64
PATHS /usr/local/lib
/usr/local/lib64
/usr/lib
/usr/lib64
)
message(STATUS "SCTP LIBRARIES: " ${SCTP_LIBRARIES})
message(STATUS "SCTP INCLUDE DIRS: " ${SCTP_INCLUDE_DIRS})
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SCTP DEFAULT_MSG SCTP_LIBRARIES SCTP_INCLUDE_DIRS)
MARK_AS_ADVANCED(SCTP_LIBRARIES SCTP_INCLUDE_DIRS)

77
srsenb/CMakeLists.txt Normal file
View File

@ -0,0 +1,77 @@
#
# Copyright 2013-2017 Software Radio Systems Limited
#
# This file is part of srsLTE
#
# srsLTE 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.
#
# srsLTE 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/.
#
find_package(LibConfig REQUIRED)
find_package(SCTP REQUIRED)
if(STATIC_LIBCONFIGPP)
set(LIBCONFIGPP_LIBRARIES "${LIBCONFIGPP_STATIC_LIBRARY_PATH}")
endif(STATIC_LIBCONFIGPP)
########################################################################
# Find boost
########################################################################
set(BOOST_REQUIRED_COMPONENTS
program_options
system
)
if(UNIX AND EXISTS "/usr/lib64")
list(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix
endif(UNIX AND EXISTS "/usr/lib64")
set(Boost_ADDITIONAL_VERSIONS
"1.35.0" "1.35" "1.36.0" "1.36" "1.37.0" "1.37" "1.38.0" "1.38" "1.39.0" "1.39"
"1.40.0" "1.40" "1.41.0" "1.41" "1.42.0" "1.42" "1.43.0" "1.43" "1.44.0" "1.44"
"1.45.0" "1.45" "1.46.0" "1.46" "1.47.0" "1.47" "1.48.0" "1.48" "1.49.0" "1.49"
"1.50.0" "1.50" "1.51.0" "1.51" "1.52.0" "1.52" "1.53.0" "1.53" "1.54.0" "1.54"
"1.55.0" "1.55" "1.56.0" "1.56" "1.57.0" "1.57" "1.58.0" "1.58" "1.59.0" "1.59"
"1.60.0" "1.60" "1.61.0" "1.61" "1.62.0" "1.62" "1.63.0" "1.63" "1.64.0" "1.64"
"1.65.0" "1.65" "1.66.0" "1.66" "1.67.0" "1.67" "1.68.0" "1.68" "1.69.0" "1.69"
)
find_package(Boost "1.35" COMPONENTS ${BOOST_REQUIRED_COMPONENTS})
if(NOT Boost_FOUND)
message(FATAL_ERROR "Boost required to compile srsENB")
endif()
########################################################################
# Find dependencies
########################################################################
find_package(Threads REQUIRED)
########################################################################
# Setup the include and linker paths
########################################################################
include_directories(
${Boost_INCLUDE_DIRS}
${POLAR_INCLUDE_DIRS}
${PROJECT_SOURCE_DIR}/srsenb/hdr
)
link_directories(
${Boost_LIBRARY_DIRS}
${POLAR_LIBRARY_DIRS}
)
########################################################################
# Add subdirectories
########################################################################
add_subdirectory(src)
add_subdirectory(test)

56
srsenb/drb.conf.example Normal file
View File

@ -0,0 +1,56 @@
// All times are in ms. Use -1 for infinity, where available
qci_config = (
{
qci=7;
pdcp_config = {
discard_timer = 100;
pdcp_sn_size = 12;
}
rlc_config = {
ul_um = {
sn_field_length = 10;
};
dl_um = {
sn_field_length = 10;
t_reordering = 80;
};
};
logical_channel_config = {
priority = 11;
prioritized_bit_rate = -1;
bucket_size_duration = 100;
log_chan_group = 3;
};
},
{
qci=9;
pdcp_config = {
discard_timer = -1;
status_report_required = false;
}
rlc_config = {
ul_am = {
t_poll_retx = 200;
poll_pdu = 16;
poll_byte = -1;
max_retx_thresh = 8;
};
dl_am = {
t_reordering = 80;
t_status_prohibit = 35;
};
};
logical_channel_config = {
priority = 3;
prioritized_bit_rate = 8;
bucket_size_duration = 50;
log_chan_group = 3;
};
}
);

162
srsenb/enb.conf.example Normal file
View File

@ -0,0 +1,162 @@
#####################################################################
# srsENB configuration file
#####################################################################
#####################################################################
# eNB configuration
#
# enb_id: 20-bit eNB identifier.
# cell_id: 8-bit cell identifier.
# tac: 16-bit Tracking Area Code.
# mcc: Mobile Country Code
# mnc: Mobile Network Code
# mme_addr: IP address of MME for S1 connnection
# gtp_bind_addr: Local IP address to bind for GTP connection
#
#####################################################################
[enb]
enb_id = 0x19B
cell_id = 0x01
phy_cell_id = 1
tac = 0x0007
mcc = 001
mnc = 01
mme_addr = 127.0.1.100
gtp_bind_addr = 127.0.1.1
n_prb = 25
#####################################################################
# eNB configuration files
#
# sib_config: SIB1, SIB2 and SIB3 configuration file
# rr_config: Radio Resources configuration file
# drb_config: DRB configuration file
#####################################################################
[enb_files]
sib_config = sib.conf
rr_config = rr.conf
drb_config = drb.conf
#####################################################################
# RF configuration
#
# dl_earfcn: EARFCN code for DL
# tx_gain: Transmit gain (dB).
# rx_gain: Optional receive gain (dB). If disabled, AGC if enabled
#
# Optional parameters:
# device_name: Device driver family. Supported options: "auto" (uses first found), "UHD" or "bladeRF"
# device_args: Arguments for the device driver. Options are "auto" or any string.
# Default for UHD: "recv_frame_size=9232,send_frame_size=9232"
# Default for bladeRF: ""
# #time_adv_nsamples: Transmission time advance (in number of samples) to compensate for RF delay
# from antenna to timestamp insertion.
# Default "auto". B210 USRP: 100 samples, bladeRF: 27.
# burst_preamble_us: Preamble length to transmit before start of burst.
# Default "auto". B210 USRP: 400 us, bladeRF: 0 us.
#####################################################################
[rf]
dl_earfcn = 3400
tx_gain = 60
rx_gain = 50
#device_name = auto
#device_args = auto
#time_adv_nsamples = auto
#burst_preamble_us = auto
#####################################################################
# MAC-layer packet capture configuration
#
# Packets are captured to file in the compact format decoded by
# the Wireshark mac-lte-framed dissector and with DLT 147.
# To use the dissector, edit the preferences for DLT_USER to
# add an entry with DLT=147, Payload Protocol=mac-lte-framed.
# For more information see: https://wiki.wireshark.org/MAC-LTE
#
# enable: Enable MAC layer packet captures (true/false)
# filename: File path to use for packet captures
#####################################################################
[pcap]
enable = false
filename = /tmp/enb.pcap
#####################################################################
# Log configuration
#
# Log levels can be set for individual layers. "all_level" sets log
# level for all layers unless otherwise configured.
# Format: e.g. phy_level = info
#
# In the same way, packet hex dumps can be limited for each level.
# "all_hex_limit" sets the hex limit for all layers unless otherwise
# configured.
# Format: e.g. phy_hex_limit = 32
#
# Logging layers: phy, mac, rlc, pdcp, rrc, nas, gtpu, usim, all
# Logging levels: debug, info, warning, error, none
#
# filename: File path to use for log output
#####################################################################
[log]
all_level = info
all_hex_limit = 32
filename = /tmp/enb.log
[gui]
enable = false
#####################################################################
# Scheduler configuration options
#
# pdsch_mcs: Optional fixed PDSCH MCS (ignores reported CQIs if specified)
# pdsch_max_mcs: Optional PDSCH MCS limit
# pusch_mcs: Optional fixed PUSCH MCS (ignores reported CQIs if specified)
# pusch_max_mcs: Optional PUSCH MCS limit
# #nof_ctrl_symbols: Number of control symbols
#
#####################################################################
[scheduler]
#pdsch_mcs = -1
#pdsch_max_mcs = -1
#pusch_mcs = -1
#pusch_max_mcs = -1
#nof_ctrl_symbols = 3
#####################################################################
# Expert configuration options
#
# pdsch_max_its: Maximum number of turbo decoder iterations (Default 4)
# nof_phy_threads: Selects the number of PHY threads (maximum 4, minimum 1, default 2)
# metrics_period_secs: Sets the period at which metrics are requested from the UE.
# pregenerate_signals: Pregenerate uplink signals after attach. Improves CPU performance.
# tx_amplitude: Transmit amplitude factor (set 0-1 to reduce PAPR)
# link_failure_nof_err: Number of PUSCH failures after which a radio-link failure is triggered.
# a link failure is when SNR<0 and CRC=KO
#####################################################################
[expert]
#pdsch_max_its = 4
#nof_phy_threads = 2
#pregenerate_signals = false
#tx_amplitude = 0.8
#link_failure_nof_err = 10
#rrc_inactivity_timer = 5000
#####################################################################
# Manual RF calibration
#
# Applies DC offset and IQ imbalance to TX and RX modules.
# Currently this configuration is only used if the detected device is a bladeRF
#
# tx_corr_dc_gain: TX DC offset gain correction
# tx_corr_dc_phase: TX DC offset phase correction
# tx_corr_iq_i: TX IQ imbalance inphase correction
# tx_corr_iq_q: TX IQ imbalance quadrature correction
# same can be configured for rx_*
#####################################################################
[rf_calibration]
tx_corr_dc_gain = 20
tx_corr_dc_phase = 184
tx_corr_iq_i = 19
tx_corr_iq_q = 97

View File

@ -0,0 +1,5 @@
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/version.h.in
${PROJECT_BINARY_DIR}/version.h
)

17
srsenb/hdr/cfg_parser.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef CFG_PARSER_H
#define CFG_PARSER_H
#include "enb.h"
namespace srsenb {
class cfg_parser
{
public:
void parse_sibs(all_args_t *args, rrc_cfg_t *rrc_cfg, phy_cfg_t *phy_config_common);
};
}
#endif // CFG_PARSER_H

185
srsenb/hdr/enb.h Normal file
View File

@ -0,0 +1,185 @@
/******************************************************************************
* File: enb.h
* Description: Top-level eNodeB class. Creates and links all
* layers and helpers.
*****************************************************************************/
#ifndef ENB_H
#define ENB_H
#include <stdarg.h>
#include <string>
#include <pthread.h>
#include "phy/phy.h"
#include "mac/mac.h"
#include "upper/rrc.h"
#include "upper/gtpu.h"
#include "upper/s1ap.h"
#include "upper/rlc.h"
#include "upper/pdcp.h"
#include "srslte/radio/radio.h"
#include "srslte/common/bcd_helpers.h"
#include "srslte/common/buffer_pool.h"
#include "srslte/interfaces/ue_interfaces.h"
#include "srslte/common/logger.h"
#include "srslte/common/log_filter.h"
#include "srslte/common/mac_pcap.h"
#include "srslte/interfaces/sched_interface.h"
#include "srslte/interfaces/enb_metrics_interface.h"
namespace srsenb {
/*******************************************************************************
eNodeB Parameters
*******************************************************************************/
typedef struct {
s1ap_args_t s1ap;
uint32_t n_prb;
uint32_t pci;
}enb_args_t;
typedef struct {
std::string sib_config;
std::string rr_config;
std::string drb_config;
} enb_files_t;
typedef struct {
uint32_t dl_earfcn;
uint32_t ul_earfcn;
float dl_freq;
float ul_freq;
float rx_gain;
float tx_gain;
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 {
std::string phy_level;
std::string mac_level;
std::string rlc_level;
std::string pdcp_level;
std::string rrc_level;
std::string gtpu_level;
std::string s1ap_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 gtpu_hex_limit;
int s1ap_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;
mac_args_t mac;
uint32_t rrc_inactivity_timer;
float metrics_period_secs;
}expert_args_t;
typedef struct {
enb_args_t enb;
enb_files_t enb_files;
rf_args_t rf;
rf_cal_t rf_cal;
pcap_args_t pcap;
log_args_t log;
gui_args_t gui;
expert_args_t expert;
}all_args_t;
/*******************************************************************************
Main UE class
*******************************************************************************/
class enb
:public enb_metrics_interface
{
public:
static enb* get_instance(void);
static void cleanup(void);
bool init(all_args_t *args_);
void stop();
void start_plot();
static void rf_msg(srslte_rf_error_t error);
void handle_rf_msg(srslte_rf_error_t error);
// eNodeB metrics interface
bool get_metrics(enb_metrics_t &m);
void pregenerate_signals(bool enable);
private:
static enb *instance;
enb();
virtual ~enb();
srslte::radio radio;
srsenb::phy phy;
srsenb::mac mac;
srslte::mac_pcap mac_pcap;
srsenb::rlc rlc;
srsenb::pdcp pdcp;
srsenb::rrc rrc;
srsenb::gtpu gtpu;
srsenb::s1ap s1ap;
srslte::logger logger;
srslte::log_filter rf_log;
std::vector<void*> phy_log;
srslte::log_filter mac_log;
srslte::log_filter rlc_log;
srslte::log_filter pdcp_log;
srslte::log_filter rrc_log;
srslte::log_filter gtpu_log;
srslte::log_filter s1ap_log;
srslte::byte_buffer_pool *pool;
all_args_t *args;
bool started;
rf_metrics_t rf_metrics;
srslte::LOG_LEVEL_ENUM level(std::string l);
bool check_srslte_version();
int parse_sib1(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *data);
int parse_sib2(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *data);
int parse_sib3(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *data);
int parse_sib4(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *data);
int parse_sib9(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9_STRUCT *data);
int parse_sibs(all_args_t *args, rrc_cfg_t *rrc_cfg, phy_cfg_t *phy_config_common);
int parse_rr(all_args_t *args, rrc_cfg_t *rrc_cfg);
int parse_drb(all_args_t *args, rrc_cfg_t *rrc_cfg);
bool sib_is_present(LIBLTE_RRC_SCHEDULING_INFO_STRUCT *sched_info, uint32_t nof_sched_info, LIBLTE_RRC_SIB_TYPE_ENUM sib_num);
int parse_cell_cfg(all_args_t *args, srslte_cell_t *cell);
};
} // namespace srsenb
#endif // UE_H

206
srsenb/hdr/mac/mac.h Normal file
View File

@ -0,0 +1,206 @@
#ifndef MAC_H
#define MAC_H
#include <vector>
#include "srslte/common/log.h"
#include "srslte/common/timers.h"
#include "srslte/interfaces/enb_interfaces.h"
#include "srslte/interfaces/sched_interface.h"
#include "srslte/common/tti_sync_cv.h"
#include "srslte/common/threads.h"
#include "srslte/common/tti_sync_cv.h"
#include "srslte/common/mac_pcap.h"
#include "mac/scheduler.h"
#include "mac/scheduler_metric.h"
#include "srslte/interfaces/enb_metrics_interface.h"
#include "mac/ue.h"
namespace srsenb {
class pdu_process_handler
{
public:
virtual bool process_pdus() = 0;
};
typedef struct {
sched_interface::sched_args_t sched;
int link_failure_nof_err;
} mac_args_t;
class mac
:public mac_interface_phy,
public mac_interface_rlc,
public mac_interface_rrc,
public srslte::mac_interface_timers,
public pdu_process_handler
{
public:
mac();
bool init(mac_args_t *args, srslte_cell_t *cell, phy_interface_mac *phy, rlc_interface_mac *rlc, rrc_interface_mac *rrc, srslte::log *log_h);
void stop();
void start_pcap(srslte::mac_pcap* pcap_);
/******** Interface from PHY (PHY -> MAC) ****************/
int sr_detected(uint32_t tti, uint16_t rnti);
int rach_detected(uint32_t tti, uint32_t preamble_idx, uint32_t time_adv);
int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value);
int snr_info(uint32_t tti, uint16_t rnti, float snr);
int ack_info(uint32_t tti, uint16_t rnti, bool ack);
int crc_info(uint32_t tti, uint16_t rnti, uint32_t nof_bytes, bool crc_res);
int get_dl_sched(uint32_t tti, dl_sched_t *dl_sched_res);
int get_ul_sched(uint32_t tti, ul_sched_t *ul_sched_res);
void rl_failure(uint16_t rnti);
void rl_ok(uint16_t rnti);
void tti_clock();
/******** Interface from RRC (RRC -> MAC) ****************/
/* Provides cell configuration including SIB periodicity, etc. */
int cell_cfg(sched_interface::cell_cfg_t *cell_cfg);
void reset();
/* Manages UE scheduling context */
int ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t *cfg);
int ue_rem(uint16_t rnti);
// Indicates that the PHY config dedicated has been enabled or not
void phy_config_enabled(uint16_t rnti, bool enabled);
/* Manages UE bearers and associated configuration */
int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t *cfg);
int bearer_ue_rem(uint16_t rnti, uint32_t lc_id);
int rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue);
bool process_pdus();
void timer_expired(uint32_t timer_id);
srslte::timers::timer* get(uint32_t timer_id);
u_int32_t get_unique_id();
uint32_t get_current_tti();
void get_metrics(mac_metrics_t metrics[ENB_METRICS_MAX_USERS]);
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;
private:
void log_step_ul(uint32_t tti);
void log_step_dl(uint32_t tti);
static const int MAX_LOCATIONS = 20;
static const uint32_t cfi = 3;
srslte_dci_location_t locations[MAX_LOCATIONS];
static const int MAC_PDU_THREAD_PRIO = 3;
// Interaction with PHY
phy_interface_mac *phy_h;
rlc_interface_mac *rlc_h;
rrc_interface_mac *rrc_h;
srslte::log *log_h;
srslte_cell_t cell;
mac_args_t args;
uint32_t tti;
bool started;
/* Scheduler unit */
sched scheduler;
dl_metric_rr sched_metric_dl_rr;
ul_metric_rr sched_metric_ul_rr;
/* Map of active UEs */
std::map<uint16_t, ue*> ue_db;
uint16_t last_rnti;
uint8_t* assemble_rar(sched_interface::dl_sched_rar_grant_t *grants, uint32_t nof_grants, int rar_idx, uint32_t pdu_len);
uint8_t* assemble_si(uint32_t index);
const static int rar_payload_len = 128;
std::vector<srslte::rar_pdu> rar_pdu_msg;
uint8_t rar_payload[sched_interface::MAX_RAR_LIST][rar_payload_len];
typedef struct {
uint32_t preamble_idx;
uint32_t ta_cmd;
uint16_t temp_crnti;
} pending_rar_t;
const static int MAX_PENDING_RARS = 64;
pending_rar_t pending_rars[MAX_PENDING_RARS];
const static int NOF_BCCH_DLSCH_MSG=sched_interface::MAX_SIBS;
uint8_t bcch_dlsch_payload[sched_interface::MAX_SIB_PAYLOAD_LEN];
const static int pcch_payload_buffer_len = 1024;
uint8_t pcch_payload_buffer[pcch_payload_buffer_len];
srslte_softbuffer_tx_t bcch_softbuffer_tx[NOF_BCCH_DLSCH_MSG];
srslte_softbuffer_tx_t pcch_softbuffer_tx;
srslte_softbuffer_tx_t rar_softbuffer_tx;
/* Functions for MAC Timers */
srslte::timers timers_db;
void setup_timers();
// pointer to MAC PCAP object
srslte::mac_pcap* pcap;
/* Class to run upper-layer timers with normal priority */
class upper_timers : public thread {
public:
upper_timers() : timers_db(MAC_NOF_UPPER_TIMERS),ttisync(10240) {start();}
void tti_clock();
void stop();
void reset();
srslte::timers::timer* get(uint32_t timer_id);
uint32_t get_unique_id();
private:
void run_thread();
srslte::timers timers_db;
srslte::tti_sync_cv ttisync;
bool running;
};
upper_timers upper_timers_thread;
/* Class to process MAC PDUs from DEMUX unit */
class pdu_process : public thread {
public:
pdu_process(pdu_process_handler *h);
void notify();
void stop();
private:
void run_thread();
bool running;
bool have_data;
pthread_mutex_t mutex;
pthread_cond_t cvar;
pdu_process_handler *handler;
};
pdu_process pdu_process_thread;
};
} // namespace srsue
#endif // MAC_H

View File

@ -0,0 +1,27 @@
#ifndef ENB_MAC_METRICS_H
#define ENB_MAC_METRICS_H
namespace srsenb {
// MAC metrics per user
struct mac_metrics_t
{
uint16_t rnti;
int tx_pkts;
int tx_errors;
int tx_brate;
int rx_pkts;
int rx_errors;
int rx_brate;
int ul_buffer;
int dl_buffer;
float phr;
};
} // namespace srsenb
#endif // ENB_MAC_METRICS_H

202
srsenb/hdr/mac/scheduler.h Normal file
View File

@ -0,0 +1,202 @@
#ifndef SCHED_H
#define SCHED_H
#include <map>
#include "srslte/common/log.h"
#include "srslte/interfaces/enb_interfaces.h"
#include "srslte/interfaces/sched_interface.h"
#include "scheduler_ue.h"
#include "scheduler_harq.h"
#include <pthread.h>
namespace srsenb {
class sched : public sched_interface
{
public:
/*************************************************************
*
* Scheduling metric interface definition
*
************************************************************/
class metric_dl
{
public:
/* Virtual methods for user metric calculation */
virtual void new_tti(std::map<uint16_t,sched_ue> &ue_db, uint32_t start_rb, uint32_t nof_rb, uint32_t nof_ctrl_symbols, uint32_t tti) = 0;
virtual dl_harq_proc* get_user_allocation(sched_ue *user) = 0;
};
class metric_ul
{
public:
/* Virtual methods for user metric calculation */
virtual void new_tti(std::map<uint16_t,sched_ue> &ue_db, uint32_t nof_rb, uint32_t tti) = 0;
virtual ul_harq_proc* get_user_allocation(sched_ue *user) = 0;
virtual void update_allocation(ul_harq_proc::ul_alloc_t alloc) = 0;
};
/*************************************************************
*
* FAPI-like Interface
*
************************************************************/
sched();
void init(rrc_interface_mac *rrc, srslte::log *log);
void set_metric(metric_dl *dl_metric, metric_ul *ul_metric);
int cell_cfg(cell_cfg_t *cell_cfg);
void set_sched_cfg(sched_args_t *sched_cfg);
int reset();
int ue_cfg(uint16_t rnti, ue_cfg_t *ue_cfg);
int ue_rem(uint16_t rnti);
bool ue_exists(uint16_t rnti);
void phy_config_enabled(uint16_t rnti, bool enabled);
int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, ue_bearer_cfg_t *cfg);
int bearer_ue_rem(uint16_t rnti, uint32_t lc_id);
uint32_t get_ul_buffer(uint16_t rnti);
uint32_t get_dl_buffer(uint16_t rnti);
int dl_rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue);
int dl_mac_buffer_state(uint16_t rnti, uint32_t ce_code);
int dl_ack_info(uint32_t tti, uint16_t rnti, bool ack);
int dl_rach_info(uint32_t tti, uint32_t ra_id, uint16_t rnti, uint32_t estimated_size);
int dl_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value);
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_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);
int dl_sched(uint32_t tti, dl_sched_res_t *sched_result);
int ul_sched(uint32_t tti, ul_sched_res_t *sched_result);
/* Custom TPC functions
*/
void tpc_inc(uint16_t rnti);
void tpc_dec(uint16_t rnti);
static uint32_t get_rvidx(uint32_t retx_idx) {
const static int rv_idx[4] = {0, 2, 3, 1};
return rv_idx[retx_idx%4];
}
static void generate_cce_location(srslte_regs_t *regs, sched_ue::sched_dci_cce_t *location,
uint32_t cfi, uint32_t sf_idx = 0, uint16_t rnti = 0);
private:
metric_dl *dl_metric;
metric_ul *ul_metric;
srslte::log *log_h;
rrc_interface_mac *rrc;
cell_cfg_t cfg;
sched_args_t sched_cfg;
const static int MAX_PRB = 100;
const static int MAX_RBG = 25;
const static int MAX_CCE = 128;
// This is for computing DCI locations
srslte_regs_t regs;
bool used_cce[MAX_CCE];
typedef struct {
int buf_rar;
uint16_t rnti;
uint32_t ra_id;
uint32_t rar_tti;
} sched_rar_t;
typedef struct {
bool is_in_window;
uint32_t window_start;
uint32_t n_tx;
} sched_sib_t;
int dl_sched_bc(dl_sched_bc_t bc[MAX_BC_LIST]);
int dl_sched_rar(dl_sched_rar_t rar[MAX_RAR_LIST]);
int dl_sched_data(dl_sched_data_t data[MAX_DATA_LIST]);
int generate_format1a(uint32_t rb_start, uint32_t l_crb, uint32_t tbs, uint32_t rv, srslte_ra_dl_dci_t *dci);
bool generate_dci(srslte_dci_location_t *sched_location, sched_ue::sched_dci_cce_t *locations, uint32_t aggr_level, sched_ue *user = NULL);
std::map<uint16_t, sched_ue> ue_db;
sched_sib_t pending_sibs[MAX_SIBS];
typedef struct {
bool enabled;
uint16_t rnti;
uint32_t L;
uint32_t n_prb;
uint32_t mcs;
} pending_msg3_t;
const static int SCHED_MAX_PENDING_RAR = 8;
sched_rar_t pending_rar[SCHED_MAX_PENDING_RAR];
pending_msg3_t pending_msg3[10];
// Allowed DCI locations for SIB and RAR per CFI
sched_ue::sched_dci_cce_t common_locations[3];
sched_ue::sched_dci_cce_t rar_locations[3][10];
uint32_t bc_aggr_level;
uint32_t rar_aggr_level;
uint32_t pdsch_re[10];
uint32_t avail_rbg;
uint32_t P;
uint32_t start_rbg;
uint32_t si_n_rbg;
uint32_t rar_n_rb;
uint32_t nof_rbg;
uint32_t sf_idx;
uint32_t sfn;
uint32_t current_tti;
uint32_t current_cfi;
bool configured;
pthread_mutex_t mutex;
};
}
#endif

View File

@ -0,0 +1,102 @@
#ifndef SCHED_HARQ_H
#define SCHED_HARQ_H
#include <map>
#include "srslte/common/log.h"
#include "srslte/interfaces/sched_interface.h"
namespace srsenb {
class harq_proc
{
public:
void config(uint32_t id, uint32_t max_retx, srslte::log* log_h);
void set_max_retx(uint32_t max_retx);
void reset();
uint32_t get_id();
bool is_empty();
void new_retx(uint32_t tti, int *mcs, int *tbs);
bool get_ack();
void set_ack(bool ack);
uint32_t nof_tx();
uint32_t nof_retx();
uint32_t get_tti();
bool get_ndi();
protected:
void new_tx_common(uint32_t tti, int mcs, int tbs);
bool has_pending_retx_common();
bool ack;
bool active;
bool ndi;
uint32_t id;
uint32_t max_retx;
uint32_t n_rtx;
uint32_t tx_cnt;
int tti;
int last_mcs;
int last_tbs;
srslte::log* log_h;
private:
bool ack_received;
};
class dl_harq_proc : public harq_proc
{
public:
void new_tx(uint32_t tti, int mcs, int tbs, uint32_t n_cce);
uint32_t get_rbgmask();
void set_rbgmask(uint32_t new_mask);
bool has_pending_retx(uint32_t tti);
int get_tbs();
uint32_t get_n_cce();
private:
uint32_t rbgmask;
uint32_t nof_rbg;
uint32_t n_cce;
};
class ul_harq_proc : public harq_proc
{
public:
typedef struct {
uint32_t RB_start;
uint32_t L;
} ul_alloc_t;
void new_tx(uint32_t tti, int mcs, int tbs);
ul_alloc_t get_alloc();
void set_alloc(ul_alloc_t alloc);
void same_alloc();
bool is_adaptive_retx();
bool has_pending_ack();
uint32_t get_pending_data();
void set_rar_mcs(uint32_t mcs);
bool get_rar_mcs(int* mcs);
private:
ul_alloc_t allocation;
bool need_ack;
int pending_data;
uint32_t rar_mcs;
bool has_rar_mcs;
bool is_adaptive;
bool is_msg3;
};
}
#endif

View File

@ -0,0 +1,62 @@
#ifndef SCHED_METRIC_H
#define SCHED_METRIC_H
#include "mac/scheduler.h"
namespace srsenb {
class dl_metric_rr : public sched::metric_dl
{
public:
void new_tti(std::map<uint16_t,sched_ue> &ue_db, uint32_t start_rb, uint32_t nof_rb, uint32_t nof_ctrl_symbols, uint32_t tti);
dl_harq_proc* get_user_allocation(sched_ue *user);
private:
const static int MAX_RBG = 25;
bool new_allocation(uint32_t nof_rbg, uint32_t* rbgmask);
void update_allocation(uint32_t new_mask);
bool allocation_is_valid(uint32_t mask);
uint32_t get_required_rbg(sched_ue *user, uint32_t tti);
uint32_t count_rbg(uint32_t mask);
uint32_t calc_rbg_mask(bool mask[25]);
bool used_rb[MAX_RBG];
uint32_t nof_users_with_data;
uint32_t current_tti;
uint32_t total_rb;
uint32_t used_rb_mask;
uint32_t nof_ctrl_symbols;
uint32_t available_rb;
};
class ul_metric_rr : public sched::metric_ul
{
public:
void new_tti(std::map<uint16_t,sched_ue> &ue_db, uint32_t nof_rb, uint32_t tti);
ul_harq_proc* get_user_allocation(sched_ue *user);
void update_allocation(ul_harq_proc::ul_alloc_t alloc);
private:
const static int MAX_PRB = 100;
bool new_allocation(uint32_t L, ul_harq_proc::ul_alloc_t *alloc);
bool allocation_is_valid(ul_harq_proc::ul_alloc_t alloc);
uint32_t nof_users_with_data;
bool used_rb[MAX_PRB];
uint32_t current_tti;
uint32_t nof_rb;
uint32_t available_rb;
};
}
#endif

View File

@ -0,0 +1,156 @@
#ifndef SCHED_UE_H
#define SCHED_UE_H
#include <map>
#include "srslte/common/log.h"
#include "srslte/interfaces/sched_interface.h"
#include "scheduler_harq.h"
namespace srsenb {
class sched_ue {
public:
// used by sched_metric
uint32_t ue_idx;
typedef struct {
uint32_t cce_start[4][6];
uint32_t nof_loc[4];
} sched_dci_cce_t;
/*************************************************************
*
* FAPI-like Interface
*
************************************************************/
sched_ue();
void reset();
void phy_config_enabled(uint32_t tti, bool enabled);
void set_cfg(uint16_t rnti, sched_interface::ue_cfg_t* cfg, sched_interface::cell_cfg_t *cell_cfg,
srslte_regs_t *regs, srslte::log *log_h);
void set_bearer_cfg(uint32_t lc_id, srsenb::sched_interface::ue_bearer_cfg_t* cfg);
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_phr(int phr);
void mac_buffer_state(uint32_t ce_code);
void ul_recv_len(uint32_t lcid, uint32_t len);
void set_ul_cqi(uint32_t tti, uint32_t cqi, uint32_t ul_ch_code);
void set_dl_cqi(uint32_t tti, uint32_t cqi);
int set_ack_info(uint32_t tti, bool ack);
void set_ul_crc(uint32_t tti, bool crc_res);
/*******************************************************
* Custom functions
*******************************************************/
void tpc_inc();
void tpc_dec();
void set_max_mcs(int mcs_ul, int mcs_dl);
void set_fixed_mcs(int mcs_ul, int mcs_dl);
/*******************************************************
* Functions used by scheduler metric objects
*******************************************************/
uint32_t get_required_prb_dl(uint32_t req_bytes, uint32_t nof_ctrl_symbols);
uint32_t get_required_prb_ul(uint32_t req_bytes);
uint32_t get_pending_dl_new_data(uint32_t tti);
uint32_t get_pending_ul_new_data(uint32_t tti);
dl_harq_proc *get_pending_dl_harq(uint32_t tti);
dl_harq_proc *get_empty_dl_harq();
ul_harq_proc *get_ul_harq(uint32_t tti);
/*******************************************************
* Functions used by the scheduler object
*******************************************************/
void set_sr();
void unset_sr();
int generate_format1(dl_harq_proc *h, sched_interface::dl_sched_data_t *data, uint32_t tti, uint32_t cfi);
int generate_format0(ul_harq_proc *h, sched_interface::ul_sched_data_t *data, uint32_t tti, bool cqi_request);
uint32_t get_aggr_level(uint32_t nof_bits);
sched_dci_cce_t *get_locations(uint32_t current_cfi, uint32_t sf_idx);
bool needs_cqi(uint32_t tti, bool will_send = false);
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);
private:
typedef struct {
sched_interface::ue_bearer_cfg_t cfg;
int buf_tx;
int buf_retx;
int bsr;
} 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);
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 bool bearer_is_ul(ue_bearer_t *lch);
static bool bearer_is_dl(ue_bearer_t *lch);
bool is_first_dl_tx();
sched_interface::ue_cfg_t cfg;
srslte_cell_t cell;
srslte::log* log_h;
/* Buffer states */
bool sr;
int buf_mac;
int buf_ul;
ue_bearer_t lch[sched_interface::MAX_LC];
int power_headroom;
uint32_t dl_cqi;
uint32_t dl_cqi_tti;
uint32_t cqi_request_tti;
uint32_t ul_cqi;
uint32_t ul_cqi_tti;
uint16_t rnti;
uint32_t max_mcs_dl;
uint32_t max_mcs_ul;
int fixed_mcs_ul;
int fixed_mcs_dl;
int next_tpc_pusch;
int next_tpc_pucch;
// Allowed DCI locations per CFI and per subframe
sched_dci_cce_t dci_locations[3][10];
const static int SCHED_MAX_HARQ_PROC = 8;
dl_harq_proc dl_harq[SCHED_MAX_HARQ_PROC];
ul_harq_proc ul_harq[SCHED_MAX_HARQ_PROC];
bool phy_config_dedicated_enabled;
};
}
#endif

117
srsenb/hdr/mac/ue.h Normal file
View File

@ -0,0 +1,117 @@
#ifndef UE_H
#define UE_H
#include "srslte/common/log.h"
#include "srslte/common/pdu.h"
#include "srslte/common/mac_pcap.h"
#include "srslte/common/pdu_queue.h"
#include "srslte/interfaces/enb_interfaces.h"
#include "srslte/interfaces/sched_interface.h"
#include <pthread.h>
#include "mac/mac_metrics.h"
namespace srsenb {
class ue : public srslte::read_pdu_interface,
public srslte::pdu_queue::process_callback
{
public:
ue() : mac_msg_dl(20), mac_msg_ul(20), pdus(128) {
rlc = NULL;
log_h = NULL;
rnti = 0;
pcap = NULL;
nof_failures = 0;
phr_counter = 0;
is_phy_added = false;
for (int i=0;i<NOF_HARQ_PROCESSES;i++) {
pending_buffers[i] = NULL;
}
pthread_mutex_init(&mutex, NULL);
}
virtual ~ue() {
pthread_mutex_destroy(&mutex);
}
void reset();
void start_pcap(srslte::mac_pcap* pcap_);
void set_tti(uint32_t tti);
void config(uint16_t rnti, uint32_t nof_prb, sched_interface *sched, rrc_interface_mac *rrc_, rlc_interface_mac *rlc, srslte::log *log_h);
uint8_t* generate_pdu(sched_interface::dl_sched_pdu_t pdu[sched_interface::MAX_RLC_PDU_LIST],
uint32_t nof_pdu_elems, uint32_t grant_size);
srslte_softbuffer_tx_t* get_tx_softbuffer(uint32_t harq_process);
srslte_softbuffer_rx_t* get_rx_softbuffer(uint32_t tti);
bool process_pdus();
uint8_t *request_buffer(uint32_t tti, uint32_t len);
void process_pdu(uint8_t *pdu, uint32_t nof_bytes, uint32_t tstamp);
void push_pdu(uint32_t tti, uint32_t len);
void deallocate_pdu(uint32_t tti);
uint32_t rl_failure();
void rl_failure_reset();
void metrics_read(srsenb::mac_metrics_t* metrics);
void metrics_rx(bool crc, uint32_t tbs);
void metrics_tx(bool crc, uint32_t tbs);
bool is_phy_added;
private:
int read_pdu(uint32_t lcid, uint8_t *payload, uint32_t requested_bytes);
void allocate_sdu(srslte::sch_pdu *pdu, uint32_t lcid, uint32_t sdu_len);
bool process_ce(srslte::sch_subh *subh);
void allocate_ce(srslte::sch_pdu *pdu, uint32_t lcid);
void metrics_phr(float phr);
uint32_t phr_counter;
mac_metrics_t metrics;
srslte::mac_pcap* pcap;
uint64_t conres_id;
uint16_t rnti;
uint32_t last_tti;
uint32_t nof_failures;
const static int NOF_HARQ_PROCESSES = 8;
srslte_softbuffer_tx_t softbuffer_tx[NOF_HARQ_PROCESSES];
srslte_softbuffer_rx_t softbuffer_rx[NOF_HARQ_PROCESSES];
uint8_t *pending_buffers[NOF_HARQ_PROCESSES];
// For DL there is a single buffer
const static int payload_buffer_len = 128*1024;
uint8_t tx_payload_buffer[payload_buffer_len];
// For UL there are multiple buffers per PID and are managed by pdu_queue
srslte::pdu_queue pdus;
srslte::sch_pdu mac_msg_dl, mac_msg_ul;
rlc_interface_mac *rlc;
rrc_interface_mac* rrc;
srslte::log *log_h;
sched_interface* sched;
bool conres_id_available;
// Mutexes
pthread_mutex_t mutex;
};
}
#endif

View File

@ -0,0 +1,73 @@
/**
*
* \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: metrics_stdout.h
* Description: Metrics class printing to stdout.
*****************************************************************************/
#ifndef METRICS_STDOUT_H
#define METRICS_STDOUT_H
#include <pthread.h>
#include <stdint.h>
#include <string>
#include "srslte/interfaces/enb_metrics_interface.h"
namespace srsenb {
class metrics_stdout
{
public:
metrics_stdout();
bool init(enb_metrics_interface *u, float report_period_secs=1.0);
void stop();
void toggle_print(bool b);
static void* metrics_thread_start(void *m);
void metrics_thread_run();
private:
void print_metrics();
void print_disconnect();
std::string float_to_string(float f, int digits);
std::string float_to_eng_string(float f, int digits);
std::string int_to_eng_string(int f, int digits);
enb_metrics_interface *enb_;
bool started;
bool do_print;
pthread_t metrics_thread;
enb_metrics_t metrics;
float metrics_report_period; // seconds
uint8_t n_reports;
};
} // namespace srsenb
#endif // METRICS_STDOUT_H

285
srsenb/hdr/parser.h Normal file
View File

@ -0,0 +1,285 @@
#ifndef PARSER_H
#define PARSER_H
#include <stdarg.h>
#include <string>
#include <list>
#include <stdlib.h>
#include <stdint.h>
#include <typeinfo>
#include <libconfig.h++>
#include <string.h>
#include <iostream>
#include <fstream>
namespace srsenb {
using namespace libconfig;
class parser
{
public:
class field_itf
{
public:
virtual ~field_itf(){}
virtual int parse(Setting &root) = 0;
virtual const char* get_name() = 0;
};
template<class T>
class field_enum_str : public field_itf
{
public:
field_enum_str(const char* name_, T *store_ptr_, const char (*value_str_)[20], uint32_t nof_items_, bool *enabled_value_ = NULL)
{
name = name_;
store_ptr = store_ptr_;
value_str = value_str_;
nof_items = nof_items_;
enabled_value = enabled_value_;
}
const char* get_name() {
return name;
}
int parse(Setting &root)
{
std::string val;
if (root.exists(name)) {
if (enabled_value) {
*enabled_value = true;
}
if (root.lookupValue(name, val)) {
bool found = false;
// find value
for (uint32_t i=0;i<nof_items && !found;i++) {
if (!strcmp(value_str[i], val.c_str())) {
*store_ptr = (T) i;
found = true;
}
}
if (!found) {
fprintf(stderr, "Invalid option: %s for field %s\n", val.c_str(), name);
fprintf(stderr, "Valid options: %s", value_str[0]);
for (uint32_t i=1;i<nof_items;i++) {
fprintf(stderr, ", %s", value_str[i]);
}
fprintf(stderr, "\n");
return -1;
}
return 0;
} else {
return -1;
}
} else {
if (enabled_value) {
*enabled_value = false;
return 0;
} else {
return -1;
}
}
}
private:
const char* name;
T *store_ptr;
const char (*value_str)[20];
uint32_t nof_items;
bool *enabled_value;
};
template<class T, class S>
class field_enum_num : public field_itf
{
public:
field_enum_num(const char* name_, T *store_ptr_, const S *value_str_, uint32_t nof_items_, bool *enabled_value_ = NULL)
{
name = name_;
store_ptr = store_ptr_;
value_str = value_str_;
nof_items = nof_items_;
enabled_value = enabled_value_;
}
const char* get_name() {
return name;
}
int parse(Setting &root)
{
S val;
if (root.exists(name)) {
if (enabled_value) {
*enabled_value = true;
}
if (parser::lookupValue(root, name, &val)) {
bool found = false;
// find value
for (uint32_t i=0;i<nof_items && !found;i++) {
if (value_str[i] == val) {
*store_ptr = (T) i;
found = true;
}
}
if (!found) {
std::cout << "Invalid option: " << +val << " for field " << name << std::endl;
std::cout << "Valid options: ";
for (uint32_t i=0;i<nof_items;i++) {
std::cout << +value_str[i] << ", ";
}
std::cout << std::endl;
return -1;
}
return 0;
} else {
return -1;
}
} else {
if (enabled_value) {
*enabled_value = false;
return 0;
} else {
return -1;
}
}
}
private:
const char* name;
T *store_ptr;
const S *value_str;
uint32_t nof_items;
bool *enabled_value;
};
template<class T>
class field : public field_itf
{
public:
field(const char* name_, T *store_ptr_, bool *enabled_value_ = NULL)
{
name = name_;
store_ptr = store_ptr_;
enabled_value = enabled_value_;
}
const char* get_name() {
return name;
}
int parse(Setting &root)
{
if (root.exists(name)) {
if (enabled_value) {
*enabled_value = true;
}
if (!parser::lookupValue(root, name, store_ptr)) {
return -1;
} else {
return 0;
}
} else {
if (enabled_value) {
*enabled_value = false;
return 0;
} else {
return -1;
}
}
}
private:
const char* name;
T *store_ptr;
bool *enabled_value;
};
class section
{
public:
section(std::string name);
~section();
void set_optional(bool *enabled_value);
void add_subsection(section *s);
void add_field(field_itf *f);
int parse(Setting &root);
private:
std::string name;
bool *enabled_value;
std::list<section*> sub_sections;
std::list<field_itf*> fields;
};
parser(std::string filename);
int parse();
void add_section(section *s);
static int parse_section(std::string filename, section *s);
static bool lookupValue(Setting &root, const char *name, std::string *val) {
return root.lookupValue(name, *val);
}
static bool lookupValue(Setting &root, const char *name, uint8_t *val) {
uint32_t t;
bool r = root.lookupValue(name, t);
*val = (uint8_t) t;
return r;
}
static bool lookupValue(Setting &root, const char *name, uint16_t *val) {
uint32_t t;
bool r = root.lookupValue(name, t);
*val = (uint16_t) t;
return r;
}
static bool lookupValue(Setting &root, const char *name, uint32_t *val) {
uint32_t t;
bool r = root.lookupValue(name, t);
*val = t;
return r;
}
static bool lookupValue(Setting &root, const char *name, int8_t *val) {
int32_t t;
bool r = root.lookupValue(name, t);
*val = (int8_t) t;
return r;
}
static bool lookupValue(Setting &root, const char *name, int16_t *val) {
int32_t t;
bool r = root.lookupValue(name, t);
*val = (int16_t) t;
return r;
}
static bool lookupValue(Setting &root, const char *name, int32_t *val) {
int32_t t;
bool r = root.lookupValue(name, t);
*val = t;
return r;
}
static bool lookupValue(Setting &root, const char *name, double *val) {
double t;
bool r = root.lookupValue(name, t);
*val = t;
return r;
}
static bool lookupValue(Setting &root, const char *name, bool *val) {
bool t;
bool r = root.lookupValue(name, t);
*val = t;
return r;
}
private:
std::list<section*> sections;
std::string filename;
};
}
#endif // PARSER_H

View File

@ -0,0 +1,85 @@
#ifndef ENBPHCHCOMMON_H
#define ENBPHCHCOMMON_H
#include <map>
#include "srslte/interfaces/enb_interfaces.h"
#include "srslte/interfaces/enb_metrics_interface.h"
#include "srslte/common/log.h"
#include "srslte/common/threads.h"
#include "srslte/common/thread_pool.h"
#include "srslte/radio/radio.h"
namespace srsenb {
typedef struct {
float max_prach_offset_us;
int pusch_max_its;
float tx_amplitude;
int nof_phy_threads;
std::string equalizer_mode;
float estimator_fil_w;
bool pregenerate_signals;
} phy_args_t;
class phch_common
{
public:
phch_common(uint32_t max_mutex_) : tx_mutex(max_mutex_) {
max_mutex = max_mutex_;
params.max_prach_offset_us = 20;
}
bool init(srslte_cell_t *cell, srslte::radio *radio_handler, mac_interface_phy *mac);
void reset();
void stop();
void set_nof_mutex(uint32_t nof_mutex);
void worker_end(uint32_t tx_mutex_cnt, cf_t *buffer, uint32_t nof_samples, srslte_timestamp_t tx_time);
// Common objects
srslte_cell_t cell;
srslte_refsignal_dmrs_pusch_cfg_t pusch_cfg;
srslte_pusch_hopping_cfg_t hopping_cfg;
srslte_pucch_cfg_t pucch_cfg;
phy_args_t params;
srslte::radio *radio;
mac_interface_phy *mac;
// Common objects for schedulign grants
mac_interface_phy::ul_sched_t ul_grants[10];
mac_interface_phy::dl_sched_t dl_grants[10];
// Map of pending ACKs for each user
typedef struct {
bool is_pending[10];
uint16_t n_pdcch[10];
} pending_ack_t;
std::map<uint16_t,pending_ack_t> pending_ack;
void ack_add_rnti(uint16_t rnti);
void ack_rem_rnti(uint16_t rnti);
void ack_clear(uint32_t sf_idx);
void ack_set_pending(uint32_t sf_idx, uint16_t rnti, uint32_t n_pdcch);
bool ack_is_pending(uint32_t sf_idx, uint16_t rnti, uint32_t *last_n_pdcch = NULL);
private:
std::vector<pthread_mutex_t> tx_mutex;
bool is_first_tx;
bool is_first_of_burst;
uint32_t nof_workers;
uint32_t nof_mutex;
uint32_t max_mutex;
};
} // namespace srsenb
#endif // UEPHY_H

View File

@ -0,0 +1,123 @@
/**
*
* \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 ENBPHYWORKER_H
#define ENBPHYWORKER_H
#include <string.h>
#include "srslte/srslte.h"
#include "phy/phch_common.h"
#define LOG_EXECTIME
namespace srsenb {
class phch_worker : public srslte::thread_pool::worker
{
public:
phch_worker();
void init(phch_common *phy, srslte::log *log_h);
void reset();
cf_t *get_buffer_rx();
void set_time(uint32_t tti, uint32_t tx_mutex_cnt, srslte_timestamp_t tx_time);
int add_rnti(uint16_t rnti);
void rem_rnti(uint16_t rnti);
uint32_t get_nof_rnti();
/* These are used by the GUI plotting tools */
int read_ce_abs(float *ce_abs);
int read_pusch_d(cf_t *pusch_d);
void start_plot();
void set_config_dedicated(uint16_t rnti,
srslte_uci_cfg_t *uci_cfg,
srslte_pucch_sched_t *pucch_sched,
srslte_refsignal_srs_cfg_t *srs_cfg,
uint32_t I_sr, bool pucch_cqi, uint32_t pmi_idx, bool pucch_cqi_ack);
uint32_t get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS]);
private:
const static float PUSCH_RL_SNR_DB_TH = 1.0;
const static float PUCCH_RL_CORR_TH = 0.1;
void work_imp();
int encode_pdsch(srslte_enb_dl_pdsch_t *grants, uint32_t nof_grants, uint32_t sf_idx);
int decode_pusch(srslte_enb_ul_pusch_t *grants, uint32_t nof_pusch, uint32_t tti_rx);
int encode_phich(srslte_enb_dl_phich_t *acks, uint32_t nof_acks, uint32_t sf_idx);
int encode_pdcch_dl(srslte_enb_dl_pdsch_t *grants, uint32_t nof_grants, uint32_t sf_idx);
int encode_pdcch_ul(srslte_enb_ul_pusch_t *grants, uint32_t nof_grants, uint32_t sf_idx);
int decode_pucch(uint32_t tti_rx);
/* Common objects */
srslte::log *log_h;
phch_common *phy;
bool initiated;
cf_t *signal_buffer_rx;
cf_t *signal_buffer_tx;
uint32_t tti_rx, tti_tx, tti_sched_ul, sf_rx, sf_tx, sf_sched_ul, tx_mutex_cnt;
srslte_enb_dl_t enb_dl;
srslte_enb_ul_t enb_ul;
srslte_timestamp_t tx_time;
// Class to store user information
class ue {
public:
ue() : I_sr(0), I_sr_en(false), cqi_en(false), pucch_cqi_ack(false), pmi_idx(0), has_grant_tti(0) {bzero(&metrics, sizeof(phy_metrics_t));}
uint32_t I_sr;
uint32_t pmi_idx;
bool I_sr_en;
bool cqi_en;
bool pucch_cqi_ack;
int has_grant_tti;
uint32_t rnti;
srslte_enb_ul_phich_info_t phich_info;
void metrics_read(phy_metrics_t *metrics);
void metrics_dl(uint32_t mcs);
void metrics_ul(uint32_t mcs, float rssi, float sinr, uint32_t turbo_iters);
private:
phy_metrics_t metrics;
};
std::map<uint16_t,ue> ue_db;
// mutex to protect worker_imp() from configuration interface
pthread_mutex_t mutex;
};
} // namespace srsenb
#endif // ENBPHYWORKER_H

75
srsenb/hdr/phy/phy.h Normal file
View File

@ -0,0 +1,75 @@
#ifndef ENBPHY_H
#define ENBPHY_H
#include "srslte/common/log.h"
#include "phy/txrx.h"
#include "phy/phch_worker.h"
#include "phy/phch_common.h"
#include "srslte/radio/radio.h"
#include "srslte/interfaces/enb_interfaces.h"
#include "srslte/common/task_dispatcher.h"
#include "srslte/common/trace.h"
#include "srslte/interfaces/enb_metrics_interface.h"
namespace srsenb {
typedef struct {
srslte_cell_t cell;
LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT prach_cnfg;
LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT pdsch_cnfg;
LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT pusch_cnfg;
LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT pucch_cnfg;
LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT srs_ul_cnfg;
} phy_cfg_t;
class phy : public phy_interface_mac,
public phy_interface_rrc
{
public:
phy();
bool init(phy_args_t *args, phy_cfg_t *common_cfg, srslte::radio *radio_handler, mac_interface_phy *mac, srslte::log* log_h);
bool init(phy_args_t *args, phy_cfg_t *common_cfg, srslte::radio *radio_handler, mac_interface_phy *mac, std::vector<void*> log_vec);
void stop();
/* MAC->PHY interface */
int add_rnti(uint16_t rnti);
void rem_rnti(uint16_t rnti);
static uint32_t tti_to_SFN(uint32_t tti);
static uint32_t tti_to_subf(uint32_t tti);
void start_plot();
void set_config_dedicated(uint16_t rnti, LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT* dedicated);
void get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS]);
private:
uint32_t nof_workers;
const static int MAX_WORKERS = 4;
const static int DEFAULT_WORKERS = 2;
const static int PRACH_WORKER_THREAD_PRIO = 80;
const static int SF_RECV_THREAD_PRIO = 1;
const static int WORKERS_THREAD_PRIO = 0;
srslte::radio *radio_handler;
srslte::thread_pool workers_pool;
std::vector<phch_worker> workers;
phch_common workers_common;
prach_worker prach;
txrx tx_rx;
srslte_prach_cfg_t prach_cfg;
void parse_config(phy_cfg_t* cfg);
};
} // namespace srsenb
#endif // UEPHY_H

View File

@ -0,0 +1,34 @@
#ifndef ENB_PHY_METRICS_H
#define ENB_PHY_METRICS_H
namespace srsenb {
// PHY metrics per user
struct ul_metrics_t
{
float n;
float sinr;
float rssi;
float turbo_iters;
float mcs;
int n_samples;
};
struct dl_metrics_t
{
float mcs;
int n_samples;
};
struct phy_metrics_t
{
dl_metrics_t dl;
ul_metrics_t ul;
};
} // namespace srsenb
#endif // ENB_PHY_METRICS_H

View File

@ -0,0 +1,49 @@
#ifndef PRACH_WORKER_H
#define PRACH_WORKER_H
#include "srslte/interfaces/enb_interfaces.h"
#include "srslte/common/log.h"
#include "srslte/common/threads.h"
namespace srsenb {
class prach_worker : thread
{
public:
prach_worker() : initiated(false),max_prach_offset_us(0) {}
int init(srslte_cell_t *cell, srslte_prach_cfg_t *prach_cfg, mac_interface_phy *mac, srslte::log *log_h, int priority);
int new_tti(uint32_t tti, cf_t *buffer);
void set_max_prach_offset_us(float delay_us);
void stop();
private:
void run_thread();
int run_tti(uint32_t tti);
uint32_t prach_nof_det;
uint32_t prach_indices[165];
float prach_offsets[165];
float prach_p2avg[165];
srslte_cell_t cell;
srslte_prach_cfg_t prach_cfg;
srslte_prach_t prach;
pthread_mutex_t mutex;
pthread_cond_t cvar;
cf_t *signal_buffer_rx;
srslte::log* log_h;
mac_interface_phy *mac;
float max_prach_offset_us;
bool initiated;
uint32_t pending_tti;
int processed_tti;
bool running;
uint32_t nof_sf;
uint32_t sf_cnt;
};
}
#endif // PRACH_WORKER_H

52
srsenb/hdr/phy/txrx.h Normal file
View File

@ -0,0 +1,52 @@
#ifndef ENBTXRX_H
#define ENBTXRX_H
#include "srslte/common/log.h"
#include "srslte/common/threads.h"
#include "srslte/common/thread_pool.h"
#include "srslte/radio/radio.h"
#include "phy/phch_common.h"
#include "phy/prach_worker.h"
namespace srsenb {
typedef _Complex float cf_t;
class txrx : public thread
{
public:
txrx();
bool init(srslte::radio *radio_handler,
srslte::thread_pool *_workers_pool,
phch_common *worker_com,
prach_worker *prach,
srslte::log *log_h,
uint32_t prio);
void stop();
const static int MUTEX_X_WORKER = 4;
private:
void run_thread();
srslte::radio *radio_h;
srslte::log *log_h;
srslte::thread_pool *workers_pool;
prach_worker *prach;
phch_common *worker_com;
uint32_t tx_mutex_cnt;
uint32_t nof_tx_mutex;
// Main system TTI counter
uint32_t tti;
bool running;
};
} // namespace srsenb
#endif // UEPHY_H

View File

@ -0,0 +1,140 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
*/
#ifndef COMMON_ENB_H
#define COMMON_ENB_H
/*******************************************************************************
INCLUDES
*******************************************************************************/
#include <stdint.h>
namespace srsenb {
#define ENB_METRICS_MAX_USERS 64
#define SRSENB_RRC_MAX_N_PLMN_IDENTITIES 6
#define SRSENB_N_SRB 3
#define SRSENB_N_DRB 8
#define SRSENB_N_RADIO_BEARERS 11
// 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
#define SRSENB_MAX_BUFFER_SIZE_BYTES 12756
#define SRSENB_BUFFER_HEADER_OFFSET 1024
/******************************************************************************
* Convert PLMN to BCD-coded MCC and MNC.
* Digits are represented by 4-bit nibbles. Unused nibbles are filled with 0xF.
* MNC 001 represented as 0xF001
* MNC 01 represented as 0xFF01
* PLMN encoded as per TS 36.413 sec 9.2.3.8
*****************************************************************************/
inline void s1ap_plmn_to_mccmnc(uint32_t plmn, uint16_t *mcc, uint16_t *mnc)
{
uint8_t nibbles[6];
nibbles[0] = (plmn & 0xF00000) >> 20;
nibbles[1] = (plmn & 0x0F0000) >> 16;
nibbles[2] = (plmn & 0x00F000) >> 12;
nibbles[3] = (plmn & 0x000F00) >> 8;
nibbles[4] = (plmn & 0x0000F0) >> 4;
nibbles[5] = (plmn & 0x00000F);
*mcc = 0xF000;
*mnc = 0xF000;
*mcc |= nibbles[1] << 8; // MCC digit 1
*mcc |= nibbles[0] << 4; // MCC digit 2
*mcc |= nibbles[3]; // MCC digit 3
if(nibbles[2] == 0xF) {
// 2-digit MNC
*mnc |= 0x0F00; // MNC digit 1
*mnc |= nibbles[5] << 4; // MNC digit 2
*mnc |= nibbles[4]; // MNC digit 3
} else {
// 3-digit MNC
*mnc |= nibbles[5] << 8; // MNC digit 1
*mnc |= nibbles[4] << 4; // MNC digit 2
*mnc |= nibbles[2] ; // MNC digit 3
}
}
/******************************************************************************
* Convert BCD-coded MCC and MNC to PLMN.
* Digits are represented by 4-bit nibbles. Unused nibbles are filled with 0xF.
* MNC 001 represented as 0xF001
* MNC 01 represented as 0xFF01
* PLMN encoded as per TS 36.413 sec 9.2.3.8
*****************************************************************************/
inline void s1ap_mccmnc_to_plmn(uint16_t mcc, uint16_t mnc, uint32_t *plmn)
{
uint8_t nibbles[6];
nibbles[1] = (mcc & 0x0F00) >> 8; // MCC digit 1
nibbles[0] = (mcc & 0x00F0) >> 4; // MCC digit 2
nibbles[3] = (mcc & 0x000F); // MCC digit 3
if((mnc & 0xFF00) == 0xFF00) {
// 2-digit MNC
nibbles[2] = 0x0F; // MNC digit 1
nibbles[5] = (mnc & 0x00F0) >> 4; // MNC digit 2
nibbles[4] = (mnc & 0x000F); // MNC digit 3
} else {
// 3-digit MNC
nibbles[5] = (mnc & 0x0F00) >> 8; // MNC digit 1
nibbles[4] = (mnc & 0x00F0) >> 4; // MNC digit 2
nibbles[2] = (mnc & 0x000F); // MNC digit 3
}
*plmn = 0x000000;
*plmn |= nibbles[0] << 20;
*plmn |= nibbles[1] << 16;
*plmn |= nibbles[2] << 12;
*plmn |= nibbles[3] << 8;
*plmn |= nibbles[4] << 4;
*plmn |= nibbles[5];
}
/******************************************************************************
* Safe conversions between byte buffers and integer types.
* Note: these don't perform endian conversion - use e.g. htonl/ntohl if required
*****************************************************************************/
inline void uint8_to_uint32(uint8_t *buf, uint32_t *i)
{
*i = (uint32_t)buf[0] << 24 |
(uint32_t)buf[1] << 16 |
(uint32_t)buf[2] << 8 |
(uint32_t)buf[3];
}
inline void uint32_to_uint8(uint32_t i, uint8_t *buf)
{
buf[0] = (i >> 24) & 0xFF;
buf[1] = (i >> 16) & 0xFF;
buf[2] = (i >> 8) & 0xFF;
buf[3] = i & 0xFF;
}
inline void uint8_to_uint16(uint8_t *buf, uint16_t *i)
{
*i = (uint32_t)buf[0] << 8 |
(uint32_t)buf[1];
}
inline void uint16_to_uint8(uint16_t i, uint8_t *buf)
{
buf[0] = (i >> 8) & 0xFF;
buf[1] = i & 0xFF;
}
} // namespace srsenb
#endif // COMMON_ENB_H

103
srsenb/hdr/upper/gtpu.h Normal file
View File

@ -0,0 +1,103 @@
#include <string.h>
#include <map>
#include "srslte/common/buffer_pool.h"
#include "srslte/common/log.h"
#include "upper/common_enb.h"
#include "srslte/common/threads.h"
#include "srslte/srslte.h"
#include "srslte/interfaces/enb_interfaces.h"
#ifndef GTPU_H
#define GTPU_H
namespace srsenb {
/****************************************************************************
* GTPU Header
* Ref: 3GPP TS 29.281 v10.1.0 Section 5
*
* | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
*
* 1 | Version |PT | * | E | S |PN |
* 2 | Message Type |
* 3 | Length (1st Octet) |
* 4 | Length (2nd Octet) |
* 5 | TEID (1st Octet) |
* 6 | TEID (2nd Octet) |
* 7 | TEID (3rd Octet) |
* 8 | TEID (4th Octet) |
***************************************************************************/
#define GTPU_HEADER_LEN 8
typedef struct{
uint8_t flags; // Only support 0x30 - v1, PT1 (GTP), no other flags
uint8_t message_type; // Only support 0xFF - T-PDU type
uint16_t length;
uint32_t teid;
}gtpu_header_t;
class gtpu
:public gtpu_interface_rrc
,public gtpu_interface_pdcp
,public thread
{
public:
bool init(std::string gtp_bind_addr_, std::string mme_addr_, pdcp_interface_gtpu *pdcp_, srslte::log *gtpu_log_);
void stop();
// gtpu_interface_rrc
void add_bearer(uint16_t rnti, uint32_t lcid, uint32_t teid_out, uint32_t *teid_in);
void rem_bearer(uint16_t rnti, uint32_t lcid);
void rem_user(uint16_t rnti);
// gtpu_interface_pdcp
void write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *pdu);
private:
static const int THREAD_PRIO = 7;
static const int GTPU_PORT = 2152;
srslte::byte_buffer_pool *pool;
bool running;
bool run_enable;
std::string gtp_bind_addr;
std::string mme_addr;
srsenb::pdcp_interface_gtpu *pdcp;
srslte::log *gtpu_log;
typedef struct{
uint32_t teids_in[SRSENB_N_RADIO_BEARERS];
uint32_t teids_out[SRSENB_N_RADIO_BEARERS];
}bearer_map;
std::map<uint16_t, bearer_map> rnti_bearers;
srslte_netsink_t snk;
srslte_netsource_t src;
void run_thread();
pthread_mutex_t mutex;
/****************************************************************************
* Header pack/unpack helper functions
* Ref: 3GPP TS 29.281 v10.1.0 Section 5
***************************************************************************/
bool gtpu_write_header(gtpu_header_t *header, srslte::byte_buffer_t *pdu);
bool gtpu_read_header(srslte::byte_buffer_t *pdu, gtpu_header_t *header);
/****************************************************************************
* TEID to RNIT/LCID helper functions
***************************************************************************/
void teidin_to_rntilcid(uint32_t teidin, uint16_t *rnti, uint16_t *lcid);
void rntilcid_to_teidin(uint16_t rnti, uint16_t lcid, uint32_t *teidin);
};
} // namespace srsenb
#endif // GTPU_H

89
srsenb/hdr/upper/pdcp.h Normal file
View File

@ -0,0 +1,89 @@
#include <map>
#include "srslte/interfaces/ue_interfaces.h"
#include "srslte/interfaces/enb_interfaces.h"
#include "srslte/upper/pdcp.h"
#ifndef PDCP_ENB_H
#define PDCP_ENB_H
namespace srsenb {
class pdcp : public pdcp_interface_rlc,
public pdcp_interface_gtpu,
public pdcp_interface_rrc
{
public:
void init(rlc_interface_pdcp *rlc_, rrc_interface_pdcp *rrc_, gtpu_interface_pdcp *gtpu_, srslte::log *pdcp_log_);
void stop();
// pdcp_interface_rlc
void write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *sdu);
// pdcp_interface_rrc
void reset(uint16_t rnti);
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 config_security(uint16_t rnti,
uint32_t lcid,
uint8_t *k_rrc_enc_,
uint8_t *k_rrc_int_,
srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo_,
srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo_);
private:
class user_interface_rlc : public srsue::rlc_interface_pdcp
{
public:
uint16_t rnti;
srsenb::rlc_interface_pdcp *rlc;
// rlc_interface_pdcp
void write_sdu(uint32_t lcid, srslte::byte_buffer_t *sdu);
};
class user_interface_gtpu : public srsue::gw_interface_pdcp
{
public:
uint16_t rnti;
srsenb::gtpu_interface_pdcp *gtpu;
// gw_interface_pdcp
void write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu);
};
class user_interface_rrc : public srsue::rrc_interface_pdcp
{
public:
uint16_t rnti;
srsenb::rrc_interface_pdcp *rrc;
// rrc_interface_pdcp
void write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu);
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);
};
class user_interface
{
public:
user_interface_rlc rlc_itf;
user_interface_gtpu gtpu_itf;
user_interface_rrc rrc_itf;
srslte::pdcp *pdcp;
};
std::map<uint32_t,user_interface> users;
rlc_interface_pdcp *rlc;
rrc_interface_pdcp *rrc;
gtpu_interface_pdcp *gtpu;
srslte::log *log_h;
srslte::byte_buffer_pool *pool;
};
}
#endif // PDCP_ENB_H

71
srsenb/hdr/upper/rlc.h Normal file
View File

@ -0,0 +1,71 @@
#include <map>
#include "srslte/interfaces/ue_interfaces.h"
#include "srslte/interfaces/enb_interfaces.h"
#include "srslte/upper/rlc.h"
#ifndef RLC_ENB_H
#define RLC_ENB_H
namespace srsenb {
class rlc : public rlc_interface_mac,
public rlc_interface_rrc,
public rlc_interface_pdcp
{
public:
void init(pdcp_interface_rlc *pdcp_, rrc_interface_rlc *rrc_, mac_interface_rlc *mac_,
srslte::mac_interface_timers *mac_timers_, srslte::log *log_h);
void stop();
// rlc_interface_rrc
void reset(uint16_t rnti);
void clear_buffer(uint16_t rnti);
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);
// rlc_interface_pdcp
void write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *sdu);
// rlc_interface_mac
int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t *payload, uint32_t nof_bytes);
void read_pdu_bcch_dlsch(uint32_t sib_index, uint8_t *payload);
void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t *payload, uint32_t nof_bytes);
void read_pdu_pcch(uint8_t *payload, uint32_t buffer_size);
private:
class user_interface : public srsue::pdcp_interface_rlc,
public srsue::rrc_interface_rlc,
public srsue::ue_interface
{
public:
void write_pdu(uint32_t lcid, srslte::byte_buffer_t *sdu);
void write_pdu_bcch_bch(srslte::byte_buffer_t *sdu);
void write_pdu_bcch_dlsch(srslte::byte_buffer_t *sdu);
void write_pdu_pcch(srslte::byte_buffer_t *sdu);
void max_retx_attempted();
uint16_t rnti;
srsenb::pdcp_interface_rlc *pdcp;
srsenb::rrc_interface_rlc *rrc;
srslte::rlc *rlc;
srsenb::rlc *parent;
};
std::map<uint32_t,user_interface> users;
mac_interface_rlc *mac;
pdcp_interface_rlc *pdcp;
rrc_interface_rlc *rrc;
srslte::log *log_h;
srslte::byte_buffer_pool *pool;
srslte::mac_interface_timers *mac_timers;
};
}
#endif // RLC_H

315
srsenb/hdr/upper/rrc.h Normal file
View File

@ -0,0 +1,315 @@
#ifndef RRC_H
#define RRC_H
#include <map>
#include <queue>
#include "srslte/common/buffer_pool.h"
#include "srslte/common/common.h"
#include "srslte/common/block_queue.h"
#include "srslte/common/threads.h"
#include "srslte/common/timeout.h"
#include "srslte/common/log.h"
#include "srslte/interfaces/enb_interfaces.h"
#include "upper/common_enb.h"
#include "rrc_metrics.h"
namespace srsenb {
typedef struct {
uint32_t period;
LIBLTE_RRC_DSR_TRANS_MAX_ENUM dsr_max;
uint32_t nof_prb;
uint32_t sf_mapping[80];
uint32_t nof_subframes;
} rrc_cfg_sr_t;
typedef enum {
RRC_CFG_CQI_MODE_PERIODIC = 0,
RRC_CFG_CQI_MODE_APERIODIC,
RRC_CFG_CQI_MODE_N_ITEMS
} rrc_cfg_cqi_mode_t;
static const char rrc_cfg_cqi_mode_text[RRC_CFG_CQI_MODE_N_ITEMS][20] = {"periodic", "aperiodic"};
typedef struct {
uint32_t sf_mapping[80];
uint32_t nof_subframes;
uint32_t nof_prb;
uint32_t period;
bool simultaneousAckCQI;
rrc_cfg_cqi_mode_t mode;
} rrc_cfg_cqi_t;
typedef struct {
bool configured;
LIBLTE_RRC_UL_SPECIFIC_PARAMETERS_STRUCT lc_cfg;
LIBLTE_RRC_PDCP_CONFIG_STRUCT pdcp_cfg;
LIBLTE_RRC_RLC_CONFIG_STRUCT rlc_cfg;
} rrc_cfg_qci_t;
#define MAX_NOF_QCI 10
typedef struct {
LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_STRUCT sibs[LIBLTE_RRC_MAX_SIB];
LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT mac_cnfg;
LIBLTE_RRC_PUSCH_CONFIG_DEDICATED_STRUCT pusch_cfg;
rrc_cfg_sr_t sr_cfg;
rrc_cfg_cqi_t cqi_cfg;
rrc_cfg_qci_t qci_cfg[MAX_NOF_QCI];
srslte_cell_t cell;
uint32_t inactivity_timeout_ms;
}rrc_cfg_t;
static const char rrc_state_text[RRC_STATE_N_ITEMS][100] = {"IDLE",
"WAIT FOR CON SETUP COMPLETE",
"WAIT FOR SECURITY MODE COMPLETE",
"WAIT FOR UE CAPABILITIY INFORMATION",
"WAIT FOR CON RECONF COMPLETE",
"RRC CONNECTED"
"RELEASE REQUEST"};
class rrc : public rrc_interface_pdcp,
public rrc_interface_mac,
public rrc_interface_rlc,
public rrc_interface_s1ap,
public thread
{
public:
rrc() : act_monitor(this), cnotifier(NULL) {}
void init(rrc_cfg_t *cfg,
phy_interface_rrc *phy,
mac_interface_rrc *mac,
rlc_interface_rrc *rlc,
pdcp_interface_rrc *pdcp,
s1ap_interface_rrc *s1ap,
gtpu_interface_rrc *gtpu,
srslte::log *log_rrc);
void stop();
void get_metrics(rrc_metrics_t &m);
// rrc_interface_mac
void rl_failure(uint16_t rnti);
void add_user(uint16_t rnti);
void upd_user(uint16_t new_rnti, uint16_t old_rnti);
void set_activity_user(uint16_t rnti);
bool is_paging_opportunity(uint32_t tti, uint32_t *payload_len);
// rrc_interface_rlc
void read_pdu_bcch_dlsch(uint32_t sib_idx, uint8_t *payload);
void read_pdu_pcch(uint8_t *payload, uint32_t buffer_size);
void max_retx_attempted(uint16_t rnti);
// rrc_interface_s1ap
void write_dl_info(uint16_t rnti, srslte::byte_buffer_t *sdu);
void release_complete(uint16_t rnti);
bool setup_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *msg);
bool setup_ue_erabs(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *msg);
bool release_erabs(uint32_t rnti);
void add_paging_id(uint32_t ueid, LIBLTE_S1AP_UEPAGINGID_STRUCT UEPagingID);
// rrc_interface_pdcp
void write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *pdu);
void parse_sibs();
uint32_t get_nof_users();
// Notifier for user connect
class connect_notifier {
public:
virtual void user_connected(uint16_t rnti) = 0;
};
void set_connect_notifer(connect_notifier *cnotifier);
class activity_monitor : public thread
{
public:
activity_monitor(rrc* parent_);
void stop();
private:
rrc* parent;
bool running;
void run_thread();
};
class ue
{
public:
ue();
bool is_connected();
bool is_idle();
bool is_timeout();
void set_activity();
rrc_state_t get_state();
void send_connection_setup(bool is_setup = true);
void send_connection_reest();
void send_connection_release();
void send_connection_reest_rej();
void send_connection_reconf(srslte::byte_buffer_t *sdu);
void send_connection_reconf_new_bearer(LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT *e);
void send_connection_reconf_upd(srslte::byte_buffer_t *pdu);
void send_security_mode_command();
void send_ue_cap_enquiry();
void parse_ul_dcch(uint32_t lcid, srslte::byte_buffer_t* pdu);
void handle_rrc_con_req(LIBLTE_RRC_CONNECTION_REQUEST_STRUCT *msg);
void handle_rrc_con_reest_req(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REQUEST_STRUCT *msg);
void handle_rrc_con_setup_complete(LIBLTE_RRC_CONNECTION_SETUP_COMPLETE_STRUCT *msg, srslte::byte_buffer_t *pdu);
void handle_security_mode_complete(LIBLTE_RRC_SECURITY_MODE_COMPLETE_STRUCT *msg);
void handle_security_mode_failure(LIBLTE_RRC_SECURITY_MODE_FAILURE_STRUCT *msg);
void handle_ue_cap_info(LIBLTE_RRC_UE_CAPABILITY_INFORMATION_STRUCT *msg);
void set_bitrates(LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT *rates);
void set_security_capabilities(LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT *caps);
void set_security_key(uint8_t* key, uint32_t length);
bool setup_erabs(LIBLTE_S1AP_E_RABTOBESETUPLISTCTXTSUREQ_STRUCT *e);
bool setup_erabs(LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT *e);
bool release_erabs();
void notify_s1ap_ue_ctxt_setup_complete();
void notify_s1ap_ue_erab_setup_response(LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT *e);
int sr_allocate(uint32_t period, uint32_t *I_sr, uint32_t *N_pucch_sr);
void sr_get(uint32_t *I_sr, uint32_t *N_pucch_sr);
int sr_free();
int cqi_allocate(uint32_t period, uint32_t *pmi_idx, uint32_t *n_pucch);
void cqi_get(uint32_t *pmi_idx, uint32_t *n_pucch);
int cqi_free();
void send_dl_ccch(LIBLTE_RRC_DL_CCCH_MSG_STRUCT *dl_ccch_msg);
void send_dl_dcch(LIBLTE_RRC_DL_DCCH_MSG_STRUCT *dl_dcch_msg, srslte::byte_buffer_t *pdu = NULL);
uint16_t rnti;
rrc *parent;
bool connect_notified;
private:
struct timeval t_last_activity;
// S-TMSI for this UE
bool has_tmsi;
uint32_t m_tmsi;
uint8_t mmec;
uint8_t transaction_id;
rrc_state_t state;
std::map<uint32_t, LIBLTE_RRC_SRB_TO_ADD_MOD_STRUCT> srbs;
std::map<uint32_t, LIBLTE_RRC_DRB_TO_ADD_MOD_STRUCT> drbs;
uint8_t k_enb[32]; // Provided by MME
uint8_t k_rrc_enc[32];
uint8_t k_rrc_int[32];
uint8_t k_up_enc[32];
uint8_t k_up_int[32]; // Not used: only for relay nodes (3GPP 33.401 Annex A.7)
srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo;
srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo;
LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT bitrates;
LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT security_capabilities;
LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT eutra_capabilities;
typedef struct {
uint8_t id;
LIBLTE_S1AP_E_RABLEVELQOSPARAMETERS_STRUCT qos_params;
LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT address;
uint32_t teid_out;
uint32_t teid_in;
}erab_t;
std::map<uint8_t, erab_t> erabs;
int sr_sched_sf_idx;
int sr_sched_prb_idx;
bool sr_allocated;
uint32_t sr_N_pucch;
uint32_t sr_I;
uint32_t cqi_pucch;
uint32_t cqi_idx;
bool cqi_allocated;
int cqi_sched_sf_idx;
bool cqi_sched_prb_idx;
int get_drbid_config(LIBLTE_RRC_DRB_TO_ADD_MOD_STRUCT *drb, int drbid);
};
private:
std::map<uint16_t,ue> users;
std::map<uint32_t, LIBLTE_S1AP_UEPAGINGID_STRUCT > pending_paging;
activity_monitor act_monitor;
LIBLTE_BYTE_MSG_STRUCT sib_buffer[LIBLTE_RRC_MAX_SIB];
// user connect notifier
connect_notifier *cnotifier;
void rem_user(uint16_t rnti);
uint32_t generate_sibs();
void config_mac();
void parse_ul_dcch(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *pdu);
void parse_ul_ccch(uint16_t rnti, srslte::byte_buffer_t *pdu);
void configure_security(uint16_t rnti,
uint32_t lcid,
uint8_t *k_rrc_enc,
uint8_t *k_rrc_int,
uint8_t *k_up_enc,
uint8_t *k_up_int,
srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo,
srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo);
srslte::byte_buffer_pool *pool;
srslte::bit_buffer_t bit_buf;
srslte::bit_buffer_t bit_buf_paging;
srslte::byte_buffer_t erab_info;
phy_interface_rrc *phy;
mac_interface_rrc *mac;
rlc_interface_rrc *rlc;
pdcp_interface_rrc *pdcp;
gtpu_interface_rrc *gtpu;
s1ap_interface_rrc *s1ap;
srslte::log *rrc_log;
typedef struct{
uint16_t rnti;
uint32_t lcid;
srslte::byte_buffer_t* pdu;
}rrc_pdu;
const static uint32_t LCID_REM_USER = 0xffff0001;
bool running;
static const int RRC_THREAD_PRIO = 7;
srslte::block_queue<rrc_pdu> rx_pdu_queue;
typedef struct {
uint32_t nof_users[100][80];
} sr_sched_t;
sr_sched_t sr_sched;
sr_sched_t cqi_sched;
rrc_cfg_t cfg;
uint32_t nof_si_messages;
LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT sib2;
void run_thread();
void rem_user_thread(uint16_t rnti);
pthread_mutex_t user_mutex;
pthread_mutex_t paging_mutex;
};
} // namespace srsenb
#endif // RRC_H

View File

@ -0,0 +1,40 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2016 Software Radio Systems Limited
*
*/
#ifndef ENB_RRC_METRICS_H
#define ENB_RRC_METRICS_H
#include "upper/common_enb.h"
namespace srsenb {
typedef enum{
RRC_STATE_IDLE = 0,
RRC_STATE_WAIT_FOR_CON_SETUP_COMPLETE,
RRC_STATE_WAIT_FOR_SECURITY_MODE_COMPLETE,
RRC_STATE_WAIT_FOR_UE_CAP_INFO,
RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE,
RRC_STATE_REGISTERED,
RRC_STATE_RELEASE_REQUEST,
RRC_STATE_N_ITEMS,
}rrc_state_t;
struct rrc_ue_metrics_t
{
rrc_state_t state;
};
struct rrc_metrics_t
{
uint16_t n_ues;
rrc_ue_metrics_t ues[ENB_METRICS_MAX_USERS];
};
} // namespace srsenb
#endif // ENB_S1AP_METRICS_H

133
srsenb/hdr/upper/s1ap.h Normal file
View File

@ -0,0 +1,133 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2016 Software Radio Systems Limited
*
*/
#ifndef S1AP_H
#define S1AP_H
#include <map>
#include "srslte/common/buffer_pool.h"
#include "srslte/common/log.h"
#include "srslte/common/common.h"
#include "srslte/common/msg_queue.h"
#include "srslte/common/threads.h"
#include "srslte/interfaces/enb_interfaces.h"
#include "upper/common_enb.h"
#include "srslte/asn1/liblte_s1ap.h"
#include "s1ap_metrics.h"
namespace srsenb {
typedef struct {
uint32_t enb_id; // 20-bit id (lsb bits)
uint8_t cell_id; // 8-bit cell id
uint16_t tac; // 16-bit tac
uint16_t mcc; // BCD-coded with 0xF filler
uint16_t mnc; // BCD-coded with 0xF filler
std::string mme_addr;
std::string gtp_bind_addr;
std::string enb_name;
}s1ap_args_t;
typedef struct {
uint32_t rnti;
uint32_t eNB_UE_S1AP_ID;
uint32_t MME_UE_S1AP_ID;
bool release_requested;
uint16_t stream_id;
}ue_ctxt_t;
class s1ap
:public s1ap_interface_rrc
,public thread
{
public:
bool init(s1ap_args_t args_, rrc_interface_s1ap *rrc_, srslte::log *s1ap_log_);
void stop();
void get_metrics(s1ap_metrics_t &m);
void run_thread();
// RRC interface
void initial_ue(uint16_t rnti, srslte::byte_buffer_t *pdu);
void initial_ue(uint16_t rnti, srslte::byte_buffer_t *pdu, uint32_t m_tmsi, uint8_t mmec);
void write_pdu(uint16_t rnti, srslte::byte_buffer_t *pdu);
bool user_exists(uint16_t rnti);
void user_inactivity(uint16_t rnti);
bool user_link_lost(uint16_t rnti);
void release_eutran(uint16_t rnti);
void ue_ctxt_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res);
void ue_erab_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res);
//void ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps);
private:
static const int S1AP_THREAD_PRIO = 7;
static const int MME_PORT = 36412;
static const int ADDR_FAMILY = AF_INET;
static const int SOCK_TYPE = SOCK_STREAM;
static const int PROTO = IPPROTO_SCTP;
static const int PPID = 18;
static const int NONUE_STREAM_ID = 0;
rrc_interface_s1ap *rrc;
s1ap_args_t args;
srslte::log *s1ap_log;
srslte::byte_buffer_pool *pool;
bool mme_connected;
bool running;
int socket_fd; // SCTP socket file descriptor
struct sockaddr_in mme_addr; // MME address
uint32_t next_eNB_UE_S1AP_ID; // Next ENB-side UE identifier
uint16_t next_ue_stream_id; // Next UE SCTP stream identifier
// Protocol IEs sent with every UL S1AP message
LIBLTE_S1AP_TAI_STRUCT tai;
LIBLTE_S1AP_EUTRAN_CGI_STRUCT eutran_cgi;
LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT s1setupresponse;
std::map<uint16_t, ue_ctxt_t> ue_ctxt_map;
std::map<uint32_t, uint16_t> enbid_to_rnti_map;
void build_tai_cgi();
bool connect_mme();
bool setup_s1();
bool handle_s1ap_rx_pdu(srslte::byte_buffer_t *pdu);
bool handle_initiatingmessage(LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *msg);
bool handle_successfuloutcome(LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *msg);
bool handle_unsuccessfuloutcome(LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT *msg);
bool handle_paging(LIBLTE_S1AP_MESSAGE_PAGING_STRUCT *msg);
bool handle_s1setupresponse(LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT *msg);
bool handle_dlnastransport(LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *msg);
bool handle_initialctxtsetuprequest(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *msg);
bool handle_uectxtreleasecommand(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMAND_STRUCT *msg);
bool handle_s1setupfailure(LIBLTE_S1AP_MESSAGE_S1SETUPFAILURE_STRUCT *msg);
bool handle_erabsetuprequest(LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *msg);
bool send_initialuemessage(uint16_t rnti, srslte::byte_buffer_t *pdu, bool has_tmsi, uint32_t m_tmsi=0, uint8_t mmec=0);
bool send_ulnastransport(uint16_t rnti, srslte::byte_buffer_t *pdu);
bool send_uectxtreleaserequest(uint16_t rnti, LIBLTE_S1AP_CAUSE_STRUCT *cause);
bool send_uectxtreleasecomplete(uint16_t rnti, uint32_t mme_ue_id, uint32_t enb_ue_id);
bool send_initial_ctxt_setup_response(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res_);
bool send_initial_ctxt_setup_failure(uint16_t rnti);
bool send_erab_setup_response(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res_);
//bool send_ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps)
bool find_mme_ue_id(uint32_t mme_ue_id, uint16_t *rnti, uint32_t *enb_ue_id);
std::string get_cause(LIBLTE_S1AP_CAUSE_STRUCT *c);
};
} // namespace srsenb
#endif // S1AP_H

View File

@ -0,0 +1,28 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2016 Software Radio Systems Limited
*
*/
#ifndef ENB_S1AP_METRICS_H
#define ENB_S1AP_METRICS_H
namespace srsenb {
typedef enum{
S1AP_ATTACHING = 0, // Attempting to create S1 connection
S1AP_READY, // S1 connected
S1AP_ERROR // Failure
}S1AP_STATUS_ENUM;
struct s1ap_metrics_t
{
S1AP_STATUS_ENUM status;
};
} // namespace srsenb
#endif // ENB_S1AP_METRICS_H

50
srsenb/rr.conf.example Normal file
View File

@ -0,0 +1,50 @@
mac_cnfg =
{
phr_cnfg =
{
dl_pathloss_change = "3dB"; // Valid: 1, 3, 6 or INFINITY
periodic_phr_timer = 100;
prohibit_phr_timer = 0;
};
ulsch_cnfg =
{
max_harq_tx = 5;
periodic_bsr_timer = 20; // in ms
retx_bsr_timer = 320; // in ms
};
time_alignment_timer = -1; // -1 is infinity
};
phy_cnfg =
{
phich_cnfg =
{
duration = "Normal";
resources = "1/6";
};
pusch_cnfg_ded =
{
beta_offset_ack_idx = 5;
beta_offset_ri_idx = 12;
beta_offset_cqi_idx = 15;
};
// PUCCH-SR resources are scheduled on time-frequeny domain first, then multiplexed in the same resource.
sched_request_cnfg =
{
dsr_trans_max = 4;
period = 40; // in ms
subframe = [0]; // vector of subframe indices allowed for SR transmissions
nof_prb = 2; // number of PRBs on each extreme used for SR (total prb is twice this number)
};
cqi_report_cnfg =
{
mode = "periodic"; // periodic uses pucch2, aperiodic is 3-0
period = 40; // Both periodic and aperiodic in ms
subframe = [1]; // In periodic: vector of subframe indices allowed for SR transmissions
nof_prb = 2; // In periodic: number of PRBs on each extreme used for SR (total prb is twice this number)
simultaneousAckCQI = true; // In periodic: indicates if ACK and CQI shall be transmitted simultaneously (Format2A)
};
};

122
srsenb/sib.conf.example Normal file
View File

@ -0,0 +1,122 @@
sib1 =
{
intra_freq_reselection = "Allowed";
q_rx_lev_min = -130;
//p_max = 3;
cell_barred = "Not Barred"
si_window_length = 20;
sched_info =
(
{
si_periodicity = 32;
si_mapping_info = []; // comma-separated array of SIB-indexes (from 3 to 13).
// Leave empty or commented to just scheduler sib2
}
);
system_info_value_tag = 0;
};
sib2 =
{
rr_config_common_sib =
{
rach_cnfg =
{
num_ra_preambles = 4
preamble_init_rx_target_pwr = -108;
pwr_ramping_step = 6; // in dB
preamble_trans_max = 7;
ra_resp_win_size = 8; // in ms
mac_con_res_timer = 64; // in ms
max_harq_msg3_tx = 1;
preambles_group_a_cnfg =
{
size_of_ra = 4;
msg_size = 56;
msg_pwr_offset_group_b = -1;
};
};
bcch_cnfg =
{
modification_period_coeff = 2; // in ms
};
pcch_cnfg =
{
default_paging_cycle = 128; // in ms
nB = "1";
};
prach_cnfg =
{
root_sequence_index = 128;
prach_cnfg_info =
{
high_speed_flag = false;
prach_config_index = 53;
prach_freq_offset = 11;
zero_correlation_zone_config = 11;
};
};
pdsch_cnfg =
{
p_b = 0;
rs_power = -4;
};
pusch_cnfg =
{
n_sb = 1;
hopping_mode = "inter-subframe";
pusch_hopping_offset = 2;
enable_64_qam = false;
ul_rs =
{
cyclic_shift = 0;
group_assignment_pusch = 0;
group_hopping_enabled = false;
sequence_hopping_enabled = false;
};
};
pucch_cnfg =
{
delta_pucch_shift = 1;
n_rb_cqi = 1;
n_cs_an = 0;
n1_pucch_an = 2;
};
ul_pwr_ctrl =
{
p0_nominal_pusch = -86;
alpha = 1.0;
p0_nominal_pucch = -108;
delta_flist_pucch =
{
format_1 = 0;
format_1b = 3;
format_2 = 0;
format_2a = 0;
format_2b = 0;
};
delta_preamble_msg3 = 4;
};
ul_cp_length = "Normal";
};
ue_timers_and_constants =
{
t300 = 2000; // in ms
t301 = 100; // in ms
t310 = 1000; // in ms
n310 = 1;
t311 = 1000; // in ms
n311 = 1;
};
freqInfo =
{
ul_carrier_freq_present = false;
ul_bw_present = false;
additional_spectrum_emission = 1;
};
time_alignment_timer = "INFINITY"; // use "sf500", "sf750", etc.
};

45
srsenb/src/CMakeLists.txt Normal file
View File

@ -0,0 +1,45 @@
add_subdirectory(phy)
add_subdirectory(mac)
add_subdirectory(upper)
# Link libstdc++ and libgcc
if(STATIC_LIB)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++ -static-libgcc")
endif(STATIC_LIB)
if (RPATH)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
endif (RPATH)
add_executable(enb main.cc enb.cc parser.cc enb_cfg_parser.cc metrics_stdout.cc)
target_link_libraries(enb srsenb_upper
srsenb_mac
srsenb_phy
srslte_common
srslte_phy
srslte_upper
srslte_radio
${CMAKE_THREAD_LIBS_INIT}
${Boost_LIBRARIES}
${POLAR_LIBRARIES}
${LIBCONFIGPP_LIBRARIES}
${SCTP_LIBRARIES})
set_target_properties(enb PROPERTIES INSTALL_RPATH ".")
########################################################################
# Option to run command after build (useful for remote builds)
########################################################################
if (NOT ${BUILD_CMD} STREQUAL "")
message(STATUS "Added custom post-build command: ${BUILD_CMD}")
add_custom_command(TARGET enb POST_BUILD COMMAND ${BUILD_CMD})
else(NOT ${BUILD_CMD} STREQUAL "")
message(STATUS "No post-build command defined")
endif (NOT ${BUILD_CMD} STREQUAL "")

283
srsenb/src/enb.cc Normal file
View File

@ -0,0 +1,283 @@
#include <boost/algorithm/string.hpp>
#include <boost/thread/mutex.hpp>
#include "enb.h"
namespace srsenb {
enb* enb::instance = NULL;
boost::mutex enb_instance_mutex;
enb* enb::get_instance(void)
{
boost::mutex::scoped_lock lock(enb_instance_mutex);
if(NULL == instance) {
instance = new enb();
}
return(instance);
}
void enb::cleanup(void)
{
boost::mutex::scoped_lock lock(enb_instance_mutex);
if(NULL != instance) {
delete instance;
instance = NULL;
}
}
enb::enb()
:started(false)
{
pool = srslte::byte_buffer_pool::get_instance();
}
enb::~enb()
{
srslte::byte_buffer_pool::cleanup();
}
bool enb::init(all_args_t *args_)
{
args = args_;
logger.init(args->log.filename);
rf_log.init("RF ", &logger);
// Create array of pointers to phy_logs
for (int i=0;i<args->expert.phy.nof_phy_threads;i++) {
srslte::log_filter *mylog = new srslte::log_filter;
char tmp[16];
sprintf(tmp, "PHY%d",i);
mylog->init(tmp, &logger, true);
phy_log.push_back((void*) mylog);
}
mac_log.init("MAC ", &logger, true);
rlc_log.init("RLC ", &logger);
pdcp_log.init("PDCP", &logger);
rrc_log.init("RRC ", &logger);
gtpu_log.init("GTPU", &logger);
s1ap_log.init("S1AP", &logger);
// Init logs
logger.log("\n\n");
rf_log.set_level(srslte::LOG_LEVEL_INFO);
for (int i=0;i<args->expert.phy.nof_phy_threads;i++) {
((srslte::log_filter*) phy_log[i])->set_level(level(args->log.phy_level));
}
mac_log.set_level(level(args->log.mac_level));
rlc_log.set_level(level(args->log.rlc_level));
pdcp_log.set_level(level(args->log.pdcp_level));
rrc_log.set_level(level(args->log.rrc_level));
gtpu_log.set_level(level(args->log.gtpu_level));
s1ap_log.set_level(level(args->log.s1ap_level));
for (int i=0;i<args->expert.phy.nof_phy_threads;i++) {
((srslte::log_filter*) phy_log[i])->set_hex_limit(args->log.phy_hex_limit);
}
mac_log.set_hex_limit(args->log.mac_hex_limit);
rlc_log.set_hex_limit(args->log.rlc_hex_limit);
pdcp_log.set_hex_limit(args->log.pdcp_hex_limit);
rrc_log.set_hex_limit(args->log.rrc_hex_limit);
gtpu_log.set_hex_limit(args->log.gtpu_hex_limit);
s1ap_log.set_hex_limit(args->log.s1ap_hex_limit);
// Set up pcap and trace
if(args->pcap.enable)
{
mac_pcap.open(args->pcap.filename.c_str());
mac.start_pcap(&mac_pcap);
}
// Init layers
/* Start Radio */
char *dev_name = NULL;
if (args->rf.device_name.compare("auto")) {
dev_name = (char*) args->rf.device_name.c_str();
}
char *dev_args = NULL;
if (args->rf.device_args.compare("auto")) {
dev_args = (char*) args->rf.device_args.c_str();
}
if(!radio.init(dev_args, dev_name))
{
printf("Failed to find device %s with args %s\n",
args->rf.device_name.c_str(), args->rf.device_args.c_str());
return false;
}
// Set RF options
if (args->rf.time_adv_nsamples.compare("auto")) {
radio.set_tx_adv(atoi(args->rf.time_adv_nsamples.c_str()));
}
if (args->rf.burst_preamble.compare("auto")) {
radio.set_burst_preamble(atof(args->rf.burst_preamble.c_str()));
}
radio.set_manual_calibration(&args->rf_cal);
radio.set_rx_gain(args->rf.rx_gain);
radio.set_tx_gain(args->rf.tx_gain);
if (args->rf.dl_freq < 0) {
args->rf.dl_freq = 1e6*srslte_band_fd(args->rf.dl_earfcn);
if (args->rf.dl_freq < 0) {
fprintf(stderr, "Error getting DL frequency for EARFCN=%d\n", args->rf.dl_earfcn);
return false;
}
}
if (args->rf.ul_freq < 0) {
if (args->rf.ul_earfcn == 0) {
args->rf.ul_earfcn = srslte_band_ul_earfcn(args->rf.dl_earfcn);
}
args->rf.ul_freq = 1e6*srslte_band_fu(args->rf.ul_earfcn);
if (args->rf.ul_freq < 0) {
fprintf(stderr, "Error getting UL frequency for EARFCN=%d\n", args->rf.dl_earfcn);
return false;
}
}
((srslte::log_filter*) phy_log[0])->console("Setting frequency: DL=%.1f Mhz, UL=%.1f MHz\n", args->rf.dl_freq/1e6, args->rf.ul_freq/1e6);
radio.set_tx_freq(args->rf.dl_freq);
radio.set_rx_freq(args->rf.ul_freq);
radio.register_error_handler(rf_msg);
srslte_cell_t cell_cfg;
phy_cfg_t phy_cfg;
rrc_cfg_t rrc_cfg;
if (parse_cell_cfg(args, &cell_cfg)) {
fprintf(stderr, "Error parsing Cell configuration\n");
return false;
}
if (parse_sibs(args, &rrc_cfg, &phy_cfg)) {
fprintf(stderr, "Error parsing SIB configuration\n");
return false;
}
if (parse_rr(args, &rrc_cfg)) {
fprintf(stderr, "Error parsing Radio Resources configuration\n");
return false;
}
if (parse_drb(args, &rrc_cfg)) {
fprintf(stderr, "Error parsing DRB configuration\n");
return false;
}
rrc_cfg.inactivity_timeout_ms = args->expert.rrc_inactivity_timer;
// Copy cell struct to rrc and phy
memcpy(&rrc_cfg.cell, &cell_cfg, sizeof(srslte_cell_t));
memcpy(&phy_cfg.cell, &cell_cfg, sizeof(srslte_cell_t));
// Init all layers
phy.init(&args->expert.phy, &phy_cfg, &radio, &mac, phy_log);
mac.init(&args->expert.mac, &cell_cfg, &phy, &rlc, &rrc, &mac_log);
rlc.init(&pdcp, &rrc, &mac, &mac, &rlc_log);
pdcp.init(&rlc, &rrc, &gtpu, &pdcp_log);
rrc.init(&rrc_cfg, &phy, &mac, &rlc, &pdcp, &s1ap, &gtpu, &rrc_log);
s1ap.init(args->enb.s1ap, &rrc, &s1ap_log);
gtpu.init(args->enb.s1ap.gtp_bind_addr, args->enb.s1ap.mme_addr, &pdcp, &gtpu_log);
started = true;
return true;
}
void enb::pregenerate_signals(bool enable)
{
//phy.enable_pregen_signals(enable);
}
void enb::stop()
{
if(started)
{
mac.stop();
phy.stop();
usleep(1e5);
rlc.stop();
pdcp.stop();
gtpu.stop();
rrc.stop();
usleep(1e5);
if(args->pcap.enable)
{
mac_pcap.close();
}
radio.stop();
started = false;
}
}
void enb::start_plot() {
phy.start_plot();
}
bool enb::get_metrics(enb_metrics_t &m)
{
m.rf = rf_metrics;
bzero(&rf_metrics, sizeof(rf_metrics_t));
rf_metrics.rf_error = false; // Reset error flag
phy.get_metrics(m.phy);
mac.get_metrics(m.mac);
rrc.get_metrics(m.rrc);
s1ap.get_metrics(m.s1ap);
m.running = started;
return true;
}
void enb::rf_msg(srslte_rf_error_t error)
{
enb *u = enb::get_instance();
u->handle_rf_msg(error);
}
void enb::handle_rf_msg(srslte_rf_error_t error)
{
if(error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_OVERFLOW) {
rf_metrics.rf_o++;
rf_metrics.rf_error = true;
rf_log.warning("Overflow\n");
}else if(error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_UNDERFLOW) {
rf_metrics.rf_u++;
rf_metrics.rf_error = true;
rf_log.warning("Underflow\n");
} else if(error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_LATE) {
rf_metrics.rf_l++;
rf_metrics.rf_error = true;
rf_log.warning("Late\n");
} else if (error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_OTHER) {
std::string str(error.msg);
str.erase(std::remove(str.begin(), str.end(), '\n'), str.end());
str.erase(std::remove(str.begin(), str.end(), '\r'), str.end());
str.push_back('\n');
rf_log.info(str);
}
}
srslte::LOG_LEVEL_ENUM enb::level(std::string l)
{
boost::to_upper(l);
if("NONE" == l){
return srslte::LOG_LEVEL_NONE;
}else if("ERROR" == l){
return srslte::LOG_LEVEL_ERROR;
}else if("WARNING" == l){
return srslte::LOG_LEVEL_WARNING;
}else if("INFO" == l){
return srslte::LOG_LEVEL_INFO;
}else if("DEBUG" == l){
return srslte::LOG_LEVEL_DEBUG;
}else{
return srslte::LOG_LEVEL_NONE;
}
}
} // namespace srsenb

1100
srsenb/src/enb_cfg_parser.cc Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,94 @@
#ifndef ENB_CFG_PARSER_SIB1_H
#define ENB_CFG_PARSER_SIB1_H
#include <string>
#include <stdlib.h>
#include <stdint.h>
#include <libconfig.h++>
#include <string.h>
#include <iostream>
#include "parser.h"
#include "upper/rrc.h"
#include "srslte/asn1/liblte_rrc.h"
namespace srsenb {
using namespace libconfig;
class field_sched_info : public parser::field_itf
{
public:
field_sched_info(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *data_) { data = data_; }
~field_sched_info() {}
int parse(Setting &root);
const char* get_name() {
return "sched_info";
}
private:
LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *data;
};
class field_intra_neigh_cell_list : public parser::field_itf
{
public:
field_intra_neigh_cell_list(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *data_) { data = data_; }
~field_intra_neigh_cell_list(){}
int parse(Setting &root);
const char* get_name() {
return "intra_neigh_cell_list";
}
private:
LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *data;
};
class field_intra_black_cell_list : public parser::field_itf
{
public:
field_intra_black_cell_list(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *data_) { data = data_; }
~field_intra_black_cell_list(){}
int parse(Setting &root);
const char* get_name() {
return "intra_black_cell_list";
}
private:
LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *data;
};
class field_sf_mapping : public parser::field_itf
{
public:
field_sf_mapping(uint32_t *sf_mapping_, uint32_t *nof_subframes_) { sf_mapping = sf_mapping_; nof_subframes = nof_subframes_; }
~field_sf_mapping(){}
int parse(Setting &root);
const char* get_name() {
return "sf_mapping";
}
private:
uint32_t *sf_mapping;
uint32_t *nof_subframes;
};
class field_qci : public parser::field_itf
{
public:
field_qci(rrc_cfg_qci_t *cfg_) { cfg = cfg_; }
~field_qci(){}
const char* get_name() {
return "field_cqi";
}
int parse(Setting &root);
private:
rrc_cfg_qci_t *cfg;
};
}
#endif

View File

@ -0,0 +1,5 @@
file(GLOB SOURCES "*.cc")
add_library(srsenb_mac SHARED ${SOURCES})
target_link_libraries(srsenb_mac)

732
srsenb/src/mac/mac.cc Normal file
View File

@ -0,0 +1,732 @@
#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 <string.h>
#include <strings.h>
#include <pthread.h>
#include <unistd.h>
#include "srslte/common/log.h"
#include "mac/mac.h"
//#define WRITE_SIB_PCAP
namespace srsenb {
mac::mac() : timers_db((uint32_t) NOF_MAC_TIMERS),
rar_pdu_msg(sched_interface::MAX_RAR_LIST),
pdu_process_thread(this)
{
started = false;
pcap = NULL;
}
bool mac::init(mac_args_t *args_, srslte_cell_t *cell_, phy_interface_mac *phy, rlc_interface_mac *rlc, rrc_interface_mac *rrc, srslte::log *log_h_)
{
started = false;
if (cell_ && phy && rlc && log_h_ && args_) {
phy_h = phy;
rlc_h = rlc;
rrc_h = rrc;
log_h = log_h_;
memcpy(&args, args_, sizeof(mac_args_t));
memcpy(&cell, cell_, sizeof(srslte_cell_t));
scheduler.init(rrc, log_h);
// Set default scheduler (RR)
scheduler.set_metric(&sched_metric_dl_rr, &sched_metric_ul_rr);
// Set default scheduler configuration
scheduler.set_sched_cfg(&args.sched);
// Init softbuffer for SI messages
for (int i=0;i<NOF_BCCH_DLSCH_MSG;i++) {
srslte_softbuffer_tx_init(&bcch_softbuffer_tx[i], cell.nof_prb);
}
// Init softbuffer for PCCH
srslte_softbuffer_tx_init(&pcch_softbuffer_tx, cell.nof_prb);
// Init softbuffer for RAR
srslte_softbuffer_tx_init(&rar_softbuffer_tx, cell.nof_prb);
reset();
started = true;
}
return started;
}
void mac::stop()
{
for (int i=0;i<NOF_BCCH_DLSCH_MSG;i++) {
srslte_softbuffer_tx_free(&bcch_softbuffer_tx[i]);
}
srslte_softbuffer_tx_free(&pcch_softbuffer_tx);
srslte_softbuffer_tx_free(&rar_softbuffer_tx);
started = false;
upper_timers_thread.stop();
pdu_process_thread.stop();
}
// Implement Section 5.9
void mac::reset()
{
Info("Resetting MAC\n");
timers_db.stop_all();
upper_timers_thread.reset();
tti = 0;
last_rnti = 70;
/* Setup scheduler */
scheduler.reset();
/* Setup SI-RNTI in PHY */
phy_h->add_rnti(SRSLTE_SIRNTI);
/* Setup P-RNTI in PHY */
phy_h->add_rnti(SRSLTE_PRNTI);
/* Setup RA-RNTI in PHY */
for (int i=0;i<10;i++) {
phy_h->add_rnti(1+i);
}
}
uint32_t mac::get_unique_id()
{
return upper_timers_thread.get_unique_id();
}
/* Front-end to upper-layer timers */
srslte::timers::timer* mac::get(uint32_t timer_id)
{
return upper_timers_thread.get(timer_id);
}
void mac::start_pcap(srslte::mac_pcap* pcap_)
{
pcap = pcap_;
// Set pcap in all UEs for UL messages
for(std::map<uint16_t, ue*>::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) {
ue *u = iter->second;
u->start_pcap(pcap);
}
}
/********************************************************
*
* RLC interface
*
*******************************************************/
int mac::rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue)
{
if (ue_db.count(rnti)) {
return scheduler.dl_rlc_buffer_state(rnti, lc_id, tx_queue, retx_queue);
} else {
Error("User rnti=0x%x not found\n", rnti);
return -1;
}
}
int mac::bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t* cfg)
{
if (ue_db.count(rnti)) {
return scheduler.bearer_ue_cfg(rnti, lc_id, cfg);
} else {
Error("User rnti=0x%x not found\n", rnti);
return -1;
}
}
int mac::bearer_ue_rem(uint16_t rnti, uint32_t lc_id)
{
if (ue_db.count(rnti)) {
return scheduler.bearer_ue_rem(rnti, lc_id);
} else {
Error("User rnti=0x%x not found\n", rnti);
return -1;
}
}
void mac::phy_config_enabled(uint16_t rnti, bool enabled)
{
scheduler.phy_config_enabled(rnti, enabled);
}
// Update UE configuration
int mac::ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t* cfg)
{
if (ue_db.count(rnti)) {
// Add RNTI to the PHY (pregerate signals) now instead of after PRACH
if (!ue_db[rnti]->is_phy_added) {
ue_db[rnti]->is_phy_added = true;
Info("Registering rnti=0x%x to PHY...\n", rnti);
// Register new user in PHY
if (phy_h->add_rnti(rnti)) {
Error("Registering new ue rnti=0x%x to PHY\n", rnti);
}
Info("Done registering rnti=0x%x to PHY...\n", rnti);
}
// Update Scheduler configuration
if (scheduler.ue_cfg(rnti, cfg)) {
Error("Registering new UE rnti=0x%x to SCHED\n", rnti);
return -1;
}
return 0;
} else {
Error("User rnti=0x%x not found\n", rnti);
return -1;
}
}
// Removes UE from DB
int mac::ue_rem(uint16_t rnti)
{
if (ue_db.count(rnti)) {
scheduler.ue_rem(rnti);
phy_h->rem_rnti(rnti);
delete ue_db[rnti];
ue_db.erase(rnti);
Info("User rnti=0x%x removed from MAC/PHY\n", rnti);
return 0;
} else {
Error("User rnti=0x%x not found\n", rnti);
return -1;
}
}
int mac::cell_cfg(sched_interface::cell_cfg_t* cell_cfg)
{
return scheduler.cell_cfg(cell_cfg);
}
void mac::get_metrics(mac_metrics_t metrics[ENB_METRICS_MAX_USERS])
{
int cnt=0;
for(std::map<uint16_t, ue*>::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) {
ue *u = iter->second;
u->metrics_read(&metrics[cnt]);
cnt++;
}
}
/********************************************************
*
* PHY interface
*
*******************************************************/
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);
rrc_h->rl_failure(rnti);
ue_db[rnti]->rl_failure_reset();
}
} else {
Error("User rnti=0x%x not found\n", rnti);
}
}
void mac::rl_ok(uint16_t rnti)
{
if (ue_db.count(rnti)) {
ue_db[rnti]->rl_failure_reset();
} else {
Error("User rnti=0x%x not found\n", rnti);
}
}
int mac::ack_info(uint32_t tti, uint16_t rnti, bool ack)
{
log_h->step(tti);
uint32_t nof_bytes = scheduler.dl_ack_info(tti, rnti, ack);
ue_db[rnti]->metrics_tx(ack, nof_bytes);
if (ack) {
if (nof_bytes > 64) { // do not count RLC status messages only
rrc_h->set_activity_user(rnti);
log_h->debug("DL activity rnti=0x%x, n_bytes=%d\n", rnti, nof_bytes);
}
}
return 0;
}
int mac::crc_info(uint32_t tti, uint16_t rnti, uint32_t nof_bytes, bool crc)
{
log_h->step(tti);
if (ue_db.count(rnti)) {
ue_db[rnti]->set_tti(tti);
ue_db[rnti]->metrics_rx(crc, nof_bytes);
// push the pdu through the queue if received correctly
if (crc) {
ue_db[rnti]->push_pdu(tti, nof_bytes);
pdu_process_thread.notify();
if (nof_bytes > 64) { // do not count RLC status messages only
rrc_h->set_activity_user(rnti);
log_h->debug("UL activity rnti=0x%x, n_bytes=%d\n", rnti, nof_bytes);
}
} else {
ue_db[rnti]->deallocate_pdu(tti);
}
return scheduler.ul_crc_info(tti, rnti, crc);
} else {
Error("User rnti=0x%x not found\n", rnti);
return -1;
}
}
int mac::cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value)
{
log_h->step(tti);
if (ue_db.count(rnti)) {
scheduler.dl_cqi_info(tti, rnti, cqi_value);
} else {
Error("User rnti=0x%x not found\n", rnti);
return -1;
}
return 0;
}
int mac::snr_info(uint32_t tti, uint16_t rnti, float snr)
{
log_h->step(tti);
if (ue_db.count(rnti)) {
uint32_t cqi = srslte_cqi_from_snr(snr);
scheduler.ul_cqi_info(tti, rnti, cqi, 0);
} else {
Error("User rnti=0x%x not found\n", rnti);
return -1;
}
return 0;
}
int mac::sr_detected(uint32_t tti, uint16_t rnti)
{
log_h->step(tti);
if (ue_db.count(rnti)) {
scheduler.ul_sr_info(tti, rnti);
} else {
Error("User rnti=0x%x not found\n", rnti);
return -1;
}
return 0;
}
int mac::rach_detected(uint32_t tti, uint32_t preamble_idx, uint32_t time_adv)
{
log_h->step(tti);
// Find empty slot for pending rars
uint32_t ra_id=0;
while(pending_rars[ra_id].temp_crnti && ra_id<MAX_PENDING_RARS) {
ra_id++;
}
if (ra_id == MAX_PENDING_RARS) {
Error("Maximum number of pending RARs exceeded (%d)\n", MAX_PENDING_RARS);
return -1;
}
// Create new UE
ue_db[last_rnti] = new ue;
ue_db[last_rnti]->config(last_rnti, cell.nof_prb, &scheduler, rrc_h, rlc_h, log_h);
// Set PCAP if available
if (pcap) {
ue_db[last_rnti]->start_pcap(pcap);
}
// Save RA info
pending_rars[ra_id].preamble_idx = preamble_idx;
pending_rars[ra_id].ta_cmd = time_adv;
pending_rars[ra_id].temp_crnti = last_rnti;
// Add new user to the scheduler so that it can RX/TX SRB0
sched_interface::ue_cfg_t uecfg;
bzero(&uecfg, sizeof(sched_interface::ue_cfg_t));
uecfg.ue_bearers[0].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH;
if (scheduler.ue_cfg(last_rnti, &uecfg)) {
Error("Registering new user rnti=0x%x to SCHED\n", last_rnti);
return -1;
}
// Register new user in RRC
rrc_h->add_user(last_rnti);
// Trigger scheduler RACH
scheduler.dl_rach_info(tti, ra_id, last_rnti, 7);
log_h->info("RACH: tti=%d, preamble=%d, offset=%d, temp_crnti=0x%x\n",
tti, preamble_idx, time_adv, last_rnti);
log_h->console("RACH: tti=%d, preamble=%d, offset=%d, temp_crnti=0x%x\n",
tti, preamble_idx, time_adv, last_rnti);
// Increae RNTI counter
last_rnti++;
if (last_rnti >= 60000) {
last_rnti = 70;
}
return 0;
}
int mac::get_dl_sched(uint32_t tti, dl_sched_t *dl_sched_res)
{
log_step_dl(tti);
if (!started) {
return 0;
}
if (!dl_sched_res) {
return SRSLTE_ERROR_INVALID_INPUTS;
}
// Run scheduler with current info
sched_interface::dl_sched_res_t sched_result;
bzero(&sched_result, sizeof(sched_interface::dl_sched_res_t));
if (scheduler.dl_sched(tti, &sched_result) < 0) {
Error("Running scheduler\n");
return SRSLTE_ERROR;
}
int n = 0;
// Copy data grants
for (uint32_t i=0;i<sched_result.nof_data_elems;i++) {
// Get UE
uint16_t rnti = sched_result.data[i].rnti;
// Copy grant info
dl_sched_res->sched_grants[n].rnti = rnti;
memcpy(&dl_sched_res->sched_grants[n].grant, &sched_result.data[i].dci, sizeof(srslte_ra_dl_dci_t));
memcpy(&dl_sched_res->sched_grants[n].location, &sched_result.data[i].dci_location, sizeof(srslte_dci_location_t));
dl_sched_res->sched_grants[n].softbuffer = ue_db[rnti]->get_tx_softbuffer(sched_result.data[i].dci.harq_process);
// Get PDU if it's a new transmission
if (sched_result.data[i].nof_pdu_elems > 0) {
dl_sched_res->sched_grants[n].data = ue_db[rnti]->generate_pdu(sched_result.data[i].pdu,
sched_result.data[i].nof_pdu_elems,
sched_result.data[i].tbs);
srslte_softbuffer_tx_reset_tbs(dl_sched_res->sched_grants[n].softbuffer, sched_result.data[i].tbs);
if (pcap) {
pcap->write_dl_crnti(dl_sched_res->sched_grants[n].data, sched_result.data[i].tbs, rnti, true, tti);
}
} else {
dl_sched_res->sched_grants[n].data = NULL;
}
n++;
}
// Copy RAR grants
for (uint32_t i=0;i<sched_result.nof_rar_elems;i++) {
// Copy grant info
dl_sched_res->sched_grants[n].rnti = sched_result.rar[i].rarnti;
memcpy(&dl_sched_res->sched_grants[n].grant, &sched_result.rar[i].dci, sizeof(srslte_ra_dl_dci_t));
memcpy(&dl_sched_res->sched_grants[n].location, &sched_result.rar[i].dci_location, sizeof(srslte_dci_location_t));
// Set softbuffer (there are no retx in RAR but a softbuffer is required)
dl_sched_res->sched_grants[n].softbuffer = &rar_softbuffer_tx;
srslte_softbuffer_tx_reset_tbs(&rar_softbuffer_tx, sched_result.rar[i].tbs); // TBS is usually 54-bit
// Assemble PDU
dl_sched_res->sched_grants[n].data = assemble_rar(sched_result.rar[i].grants, sched_result.rar[i].nof_grants, i, sched_result.rar[i].tbs);
if (pcap) {
pcap->write_dl_ranti(dl_sched_res->sched_grants[n].data, sched_result.data[i].tbs, dl_sched_res->sched_grants[n].rnti, true, tti);
}
n++;
}
// Copy SI and Paging grants
for (uint32_t i=0;i<sched_result.nof_bc_elems;i++) {
// Copy grant info
dl_sched_res->sched_grants[n].rnti = (sched_result.bc[i].type == sched_interface::dl_sched_bc_t::BCCH ) ? SRSLTE_SIRNTI : SRSLTE_PRNTI;
memcpy(&dl_sched_res->sched_grants[n].grant, &sched_result.bc[i].dci, sizeof(srslte_ra_dl_dci_t));
memcpy(&dl_sched_res->sched_grants[n].location, &sched_result.bc[i].dci_location, sizeof(srslte_dci_location_t));
// Set softbuffer
if (sched_result.bc[i].type == sched_interface::dl_sched_bc_t::BCCH) {
dl_sched_res->sched_grants[n].softbuffer = &bcch_softbuffer_tx[sched_result.bc[i].index];
if (sched_result.bc[i].dci.rv_idx == 0) {
srslte_softbuffer_tx_reset_tbs(dl_sched_res->sched_grants[n].softbuffer, sched_result.bc[i].tbs*8);
}
dl_sched_res->sched_grants[n].data = assemble_si(sched_result.bc[i].index);
#ifdef WRITE_SIB_PCAP
if (pcap) {
pcap->write_dl_sirnti(dl_sched_res->sched_grants[n].data, sched_result.bc[i].tbs, true, tti);
}
#endif
} else {
dl_sched_res->sched_grants[n].softbuffer = &pcch_softbuffer_tx;
srslte_softbuffer_tx_reset_tbs(dl_sched_res->sched_grants[n].softbuffer, sched_result.bc[i].tbs*8);
dl_sched_res->sched_grants[n].data = pcch_payload_buffer;
rlc_h->read_pdu_pcch(pcch_payload_buffer, pcch_payload_buffer_len);
if (pcap) {
pcap->write_dl_pch(dl_sched_res->sched_grants[n].data, sched_result.bc[i].tbs, true, tti);
}
}
n++;
}
dl_sched_res->nof_grants = n;
// Number of CCH symbols
dl_sched_res->cfi = sched_result.cfi;
return SRSLTE_SUCCESS;
}
uint8_t* mac::assemble_rar(sched_interface::dl_sched_rar_grant_t* grants, uint32_t nof_grants, int rar_idx, uint32_t pdu_len)
{
uint8_t grant_buffer[64];
if (pdu_len < rar_payload_len) {
srslte::rar_pdu *pdu = &rar_pdu_msg[rar_idx];
pdu->init_tx(rar_payload[rar_idx], pdu_len);
for (uint32_t i=0;i<nof_grants;i++) {
srslte_dci_rar_grant_pack(&grants[i].grant, grant_buffer);
if (pdu->new_subh()) {
/* Search pending RAR */
int idx = grants[i].ra_id;
pdu->get()->set_rapid(pending_rars[idx].preamble_idx);
pdu->get()->set_ta_cmd(pending_rars[idx].ta_cmd);
pdu->get()->set_temp_crnti(pending_rars[idx].temp_crnti);
pdu->get()->set_sched_grant(grant_buffer);
bzero(&pending_rars[idx], sizeof(pending_rar_t));
}
}
pdu->write_packet(rar_payload[rar_idx]);
return rar_payload[rar_idx];
} else {
Error("Assembling RAR: pdu_len > rar_payload_len (%d>%d)\n", pdu_len, rar_payload_len);
return NULL;
}
}
uint8_t* mac::assemble_si(uint32_t index)
{
rlc_h->read_pdu_bcch_dlsch(index, bcch_dlsch_payload);
return bcch_dlsch_payload;
}
int mac::get_ul_sched(uint32_t tti, ul_sched_t *ul_sched_res)
{
log_step_ul(tti);
if (!started) {
return 0;
}
if (!ul_sched_res) {
return SRSLTE_ERROR_INVALID_INPUTS;
}
// Run scheduler with current info
sched_interface::ul_sched_res_t sched_result;
bzero(&sched_result, sizeof(sched_interface::ul_sched_res_t));
if (scheduler.ul_sched(tti, &sched_result)<0) {
Error("Running scheduler\n");
return SRSLTE_ERROR;
}
// Copy DCI grants
ul_sched_res->nof_grants = 0;
int n = 0;
for (uint32_t i=0;i<sched_result.nof_dci_elems;i++) {
if (sched_result.pusch[i].tbs > 0) {
// Get UE
uint16_t rnti = sched_result.pusch[i].rnti;
// Copy grant info
ul_sched_res->sched_grants[n].rnti = rnti;
ul_sched_res->sched_grants[n].current_tx_nb = sched_result.pusch[i].current_tx_nb;
ul_sched_res->sched_grants[n].needs_pdcch = sched_result.pusch[i].needs_pdcch;
memcpy(&ul_sched_res->sched_grants[n].grant, &sched_result.pusch[i].dci, sizeof(srslte_ra_ul_dci_t));
memcpy(&ul_sched_res->sched_grants[n].location, &sched_result.pusch[i].dci_location, sizeof(srslte_dci_location_t));
ul_sched_res->sched_grants[n].softbuffer = ue_db[rnti]->get_rx_softbuffer(tti);
if (sched_result.pusch[n].current_tx_nb == 0) {
srslte_softbuffer_rx_reset_tbs(ul_sched_res->sched_grants[n].softbuffer, sched_result.pusch[i].tbs*8);
}
ul_sched_res->sched_grants[n].data = ue_db[rnti]->request_buffer(tti, sched_result.pusch[i].tbs);
ul_sched_res->nof_grants++;
n++;
} else {
Warning("Grant %d for rnti=0x%x has zero TBS\n", i, sched_result.pusch[i].rnti);
}
}
// Copy PHICH actions
for (uint32_t i=0;i<sched_result.nof_phich_elems;i++) {
ul_sched_res->phich[i].ack = sched_result.phich[i].phich == sched_interface::ul_sched_phich_t::ACK;
ul_sched_res->phich[i].rnti = sched_result.phich[i].rnti;
}
ul_sched_res->nof_phich = sched_result.nof_phich_elems;
return SRSLTE_SUCCESS;
}
void mac::log_step_ul(uint32_t tti)
{
int tti_ul = tti-8;
if (tti_ul < 0) {
tti_ul += 10240;
}
log_h->step(tti_ul);
}
void mac::log_step_dl(uint32_t tti)
{
int tti_dl = tti-4;
if (tti_dl < 0) {
tti_dl += 10240;
}
log_h->step(tti_dl);
}
void mac::tti_clock()
{
upper_timers_thread.tti_clock();
}
/********************************************************
*
* Class to run upper-layer timers with normal priority
*
*******************************************************/
void mac::upper_timers::run_thread()
{
running=true;
ttisync.set_producer_cntr(0);
ttisync.resync();
while(running) {
ttisync.wait();
timers_db.step_all();
}
}
srslte::timers::timer* mac::upper_timers::get(uint32_t timer_id)
{
return timers_db.get(timer_id%MAC_NOF_UPPER_TIMERS);
}
uint32_t mac::upper_timers::get_unique_id()
{
return timers_db.get_unique_id();
}
void mac::upper_timers::stop()
{
running=false;
ttisync.increase();
wait_thread_finish();
}
void mac::upper_timers::reset()
{
timers_db.stop_all();
}
void mac::upper_timers::tti_clock()
{
ttisync.increase();
}
/********************************************************
*
* Class that runs a thread to process DL MAC PDUs from
* DEMU unit
*
*******************************************************/
mac::pdu_process::pdu_process(pdu_process_handler *h)
{
handler = h;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cvar, NULL);
have_data = false;
start(MAC_PDU_THREAD_PRIO);
}
void mac::pdu_process::stop()
{
pthread_mutex_lock(&mutex);
running = false;
pthread_cond_signal(&cvar);
pthread_mutex_unlock(&mutex);
wait_thread_finish();
}
void mac::pdu_process::notify()
{
pthread_mutex_lock(&mutex);
have_data = true;
pthread_cond_signal(&cvar);
pthread_mutex_unlock(&mutex);
}
void mac::pdu_process::run_thread()
{
running = true;
while(running) {
have_data = handler->process_pdus();
if (!have_data) {
pthread_mutex_lock(&mutex);
while(!have_data && running) {
pthread_cond_wait(&cvar, &mutex);
}
pthread_mutex_unlock(&mutex);
}
}
}
bool mac::process_pdus()
{
bool ret = false;
for(std::map<uint16_t, ue*>::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) {
ue *u = iter->second;
uint16_t rnti = iter->first;
ret = ret | u->process_pdus();
}
return ret;
}
}

938
srsenb/src/mac/scheduler.cc Normal file
View File

@ -0,0 +1,938 @@
#include <string.h>
#include "srslte/srslte.h"
#include "srslte/common/pdu.h"
#include "mac/scheduler.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__)
namespace srsenb {
/*******************************************************
*
* Initialization and sched configuration functions
*
*******************************************************/
sched::sched()
{
log_h = NULL;
pthread_mutex_init(&mutex, NULL);
reset();
}
void sched::init(rrc_interface_mac *rrc_, srslte::log* log)
{
sched_cfg.pdsch_max_mcs = 28;
sched_cfg.pdsch_mcs = -1;
sched_cfg.pusch_max_mcs = 28;
sched_cfg.pusch_mcs = -1;
sched_cfg.nof_ctrl_symbols = 3;
log_h = log;
rrc = rrc_;
reset();
}
int sched::reset()
{
bzero(pending_rar, sizeof(sched_rar_t)*SCHED_MAX_PENDING_RAR);
bzero(pending_sibs, sizeof(sched_sib_t)*MAX_SIBS);
ue_db.clear();
configured = false;
return 0;
}
void sched::set_sched_cfg(sched_interface::sched_args_t* sched_cfg_)
{
if (sched_cfg_) {
memcpy(&sched_cfg, sched_cfg_, sizeof(sched_args_t));
}
}
void sched::set_metric(sched::metric_dl* dl_metric_, sched::metric_ul* ul_metric_)
{
dl_metric = dl_metric_;
ul_metric = ul_metric_;
}
int sched::cell_cfg(sched_interface::cell_cfg_t* cell_cfg)
{
// Basic cell config checks
if (cell_cfg->si_window_ms == 0) {
Error("SCHED: Invalid si-window length 0 ms\n");
return -1;
}
pthread_mutex_lock(&mutex);
memcpy(&cfg, cell_cfg, sizeof(sched_interface::cell_cfg_t));
// Get DCI locations
srslte_regs_init(&regs, cfg.cell);
P = srslte_ra_type0_P(cfg.cell.nof_prb);
si_n_rbg = 4/P;
rar_n_rb = 3;
nof_rbg = (uint32_t) ceil((float) cfg.cell.nof_prb/P);
// Compute Common locations for DCI for each CFI
for (uint32_t cfi=0;cfi<3;cfi++) {
generate_cce_location(&regs, &common_locations[cfi], cfi+1);
}
// Compute UE locations for RA-RNTI
for (int cfi=0;cfi<3;cfi++) {
for (int sf_idx=0;sf_idx<10;sf_idx++) {
uint16_t ra_rnti = 1+sf_idx;
generate_cce_location(&regs, &rar_locations[cfi][sf_idx], cfi+1, sf_idx);
}
}
configured = true;
pthread_mutex_unlock(&mutex);
return 0;
}
/*******************************************************
*
* FAPI-like main sched interface. Wrappers to UE object
*
*******************************************************/
int sched::ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t *ue_cfg)
{
pthread_mutex_lock(&mutex);
// Add or config user
ue_db[rnti].set_cfg(rnti, ue_cfg, &cfg, &regs, log_h);
ue_db[rnti].set_max_mcs(sched_cfg.pusch_max_mcs, sched_cfg.pdsch_max_mcs);
ue_db[rnti].set_fixed_mcs(sched_cfg.pusch_mcs, sched_cfg.pdsch_mcs);
pthread_mutex_unlock(&mutex);
return 0;
}
int sched::ue_rem(uint16_t rnti)
{
pthread_mutex_lock(&mutex);
int ret = 0;
if (ue_db.count(rnti)) {
ue_db.erase(rnti);
} else {
Error("User rnti=0x%x not found\n", rnti);
ret = -1;
}
pthread_mutex_unlock(&mutex);
return ret;
}
bool sched::ue_exists(uint16_t rnti)
{
return (ue_db.count(rnti) == 1);
}
void sched::phy_config_enabled(uint16_t rnti, bool enabled)
{
pthread_mutex_lock(&mutex);
if (ue_db.count(rnti)) {
ue_db[rnti].phy_config_enabled(current_tti, enabled);
} else {
Error("User rnti=0x%x not found\n", rnti);
}
pthread_mutex_unlock(&mutex);
}
int sched::bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t *cfg)
{
pthread_mutex_lock(&mutex);
int ret = 0;
if (ue_db.count(rnti)) {
ue_db[rnti].set_bearer_cfg(lc_id, cfg);
} else {
Error("User rnti=0x%x not found\n", rnti);
ret = -1;
}
pthread_mutex_unlock(&mutex);
return ret;
}
int sched::bearer_ue_rem(uint16_t rnti, uint32_t lc_id)
{
pthread_mutex_lock(&mutex);
int ret = 0;
if (ue_db.count(rnti)) {
ue_db[rnti].rem_bearer(lc_id);
} else {
Error("User rnti=0x%x not found\n", rnti);
ret = -1;
}
pthread_mutex_unlock(&mutex);
return ret;
}
uint32_t sched::get_dl_buffer(uint16_t rnti)
{
pthread_mutex_lock(&mutex);
uint32_t ret = 0;
if (ue_db.count(rnti)) {
ret = ue_db[rnti].get_pending_dl_new_data(current_tti);
} else {
Error("User rnti=0x%x not found\n", rnti);
}
pthread_mutex_unlock(&mutex);
return ret;
}
uint32_t sched::get_ul_buffer(uint16_t rnti)
{
pthread_mutex_lock(&mutex);
uint32_t ret = 0;
if (ue_db.count(rnti)) {
ret = ue_db[rnti].get_pending_ul_new_data(current_tti);
} else {
Error("User rnti=0x%x not found\n", rnti);
}
pthread_mutex_unlock(&mutex);
return ret;
}
int sched::dl_rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue)
{
pthread_mutex_lock(&mutex);
int ret = 0;
if (ue_db.count(rnti)) {
ue_db[rnti].dl_buffer_state(lc_id, tx_queue, retx_queue);
} else {
Error("User rnti=0x%x not found\n", rnti);
ret = -1;
}
pthread_mutex_unlock(&mutex);
return ret;
}
int sched::dl_mac_buffer_state(uint16_t rnti, uint32_t ce_code)
{
pthread_mutex_lock(&mutex);
int ret = 0;
if (ue_db.count(rnti)) {
ue_db[rnti].mac_buffer_state(ce_code);
} else {
Error("User rnti=0x%x not found\n", rnti);
ret = -1;
}
pthread_mutex_unlock(&mutex);
return ret;
}
int sched::dl_ack_info(uint32_t tti, uint16_t rnti, bool ack)
{
pthread_mutex_lock(&mutex);
int ret = 0;
if (ue_db.count(rnti)) {
ret = ue_db[rnti].set_ack_info(tti, ack);
} else {
Error("User rnti=0x%x not found\n", rnti);
ret = -1;
}
pthread_mutex_unlock(&mutex);
return ret;
}
int sched::ul_crc_info(uint32_t tti, uint16_t rnti, bool crc)
{
pthread_mutex_lock(&mutex);
int ret = 0;
if (ue_db.count(rnti)) {
ue_db[rnti].set_ul_crc(tti, crc);
} else {
Error("User rnti=0x%x not found\n", rnti);
ret = -1;
}
pthread_mutex_unlock(&mutex);
return ret;
}
int sched::dl_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value)
{
pthread_mutex_lock(&mutex);
int ret = 0;
if (ue_db.count(rnti)) {
ue_db[rnti].set_dl_cqi(tti, cqi_value);
} else {
Error("User rnti=0x%x not found\n", rnti);
ret = -1;
}
pthread_mutex_unlock(&mutex);
return ret;
}
int sched::dl_rach_info(uint32_t tti, uint32_t ra_id, uint16_t rnti, uint32_t estimated_size)
{
for (int i=0;i<SCHED_MAX_PENDING_RAR;i++) {
if (!pending_rar[i].buf_rar) {
pending_rar[i].ra_id = ra_id;
pending_rar[i].rnti = rnti;
pending_rar[i].rar_tti = tti;
pending_rar[i].buf_rar = estimated_size;
return 0;
}
}
Warning("SCHED: New RACH discarted because maximum number of pending RAR exceeded (%d)\n",
SCHED_MAX_PENDING_RAR);
return -1;
}
int sched::ul_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi, uint32_t ul_ch_code)
{
pthread_mutex_lock(&mutex);
int ret = 0;
if (ue_db.count(rnti)) {
ue_db[rnti].set_ul_cqi(tti, cqi, ul_ch_code);
} else {
Error("User rnti=0x%x not found\n", rnti);
ret = -1;
}
pthread_mutex_unlock(&mutex);
return ret;
}
int sched::ul_bsr(uint16_t rnti, uint32_t lcid, uint32_t bsr)
{
pthread_mutex_lock(&mutex);
int ret = 0;
if (ue_db.count(rnti)) {
ue_db[rnti].ul_buffer_state(lcid, bsr);
} else {
Error("User rnti=0x%x not found\n", rnti);
ret = -1;
}
pthread_mutex_unlock(&mutex);
return ret;
}
int sched::ul_recv_len(uint16_t rnti, uint32_t lcid, uint32_t len)
{
pthread_mutex_lock(&mutex);
int ret = 0;
if (ue_db.count(rnti)) {
ue_db[rnti].ul_recv_len(lcid, len);
} else {
Error("User rnti=0x%x not found\n", rnti);
ret = -1;
}
pthread_mutex_unlock(&mutex);
return ret;
}
int sched::ul_phr(uint16_t rnti, int phr)
{
pthread_mutex_lock(&mutex);
int ret = 0;
if (ue_db.count(rnti)) {
ue_db[rnti].ul_phr(phr);
} else {
Error("User rnti=0x%x not found\n", rnti);
ret = -1;
}
pthread_mutex_unlock(&mutex);
return ret;
}
int sched::ul_sr_info(uint32_t tti, uint16_t rnti)
{
pthread_mutex_lock(&mutex);
int ret = 0;
if (ue_db.count(rnti)) {
ue_db[rnti].set_sr();;
} else {
Error("User rnti=0x%x not found\n", rnti);
ret = -1;
}
pthread_mutex_unlock(&mutex);
return ret;
}
void sched::tpc_inc(uint16_t rnti)
{
if (ue_db.count(rnti)) {
ue_db[rnti].tpc_inc();
} else {
Error("User rnti=0x%x not found\n", rnti);
}
}
void sched::tpc_dec(uint16_t rnti)
{
if (ue_db.count(rnti)) {
ue_db[rnti].tpc_dec();
} else {
Error("User rnti=0x%x not found\n", rnti);
}
}
/*******************************************************
*
* Main sched functions
*
*******************************************************/
// Schedules Broadcast messages (SIB)
int sched::dl_sched_bc(dl_sched_bc_t bc[MAX_BC_LIST])
{
// TODO: Enable multipe SIBs per SI
int nof_bc_elems = 0;
for (int i=0;i<MAX_SIBS;i++) {
if (cfg.sibs[i].len) {
if (!pending_sibs[i].is_in_window) {
uint32_t sf = 5;
uint32_t x = 0;
if (i > 0) {
x = (i-1)*cfg.si_window_ms;
sf = x%10;
}
if ((sfn%(cfg.sibs[i].period_rf)) == x/10 && sf_idx == sf) {
pending_sibs[i].is_in_window = true;
pending_sibs[i].window_start = current_tti;
pending_sibs[i].n_tx = 0;
}
} else {
if (i > 0) {
if (srslte_tti_interval(current_tti, pending_sibs[i].window_start) > cfg.si_window_ms) {
pending_sibs[i].is_in_window = false;
pending_sibs[i].window_start = 0;
}
} else {
// SIB1 is always in window
if (pending_sibs[0].n_tx == 4) {
pending_sibs[0].n_tx = 0;
}
}
}
}
}
for (int i=0;i<MAX_SIBS;i++) {
if (cfg.sibs[i].len &&
pending_sibs[i].is_in_window &&
pending_sibs[i].n_tx < 4 &&
avail_rbg > si_n_rbg)
{
uint32_t nof_tx = 4;
if (i > 0) {
if (cfg.si_window_ms <= 10) {
nof_tx = 1;
} else if (cfg.si_window_ms <= 20) {
nof_tx = 2;
} else if (cfg.si_window_ms <= 30) {
nof_tx = 3;
} else {
nof_tx = 4;
}
}
uint32_t n_sf = (current_tti-pending_sibs[i].window_start);
if ((i == 0 && (sfn%2) == 0 && sf_idx == 5) ||
(i > 0 && n_sf >= (cfg.si_window_ms/nof_tx)*pending_sibs[i].n_tx && sf_idx==1))
{
uint32_t rv = get_rvidx(pending_sibs[i].n_tx);
// Try to allocate DCI first
if (generate_dci(&bc[nof_bc_elems].dci_location, &common_locations[current_cfi-1], bc_aggr_level)) {
if (generate_format1a(start_rbg*P, si_n_rbg*P, cfg.sibs[i].len, rv, &bc[nof_bc_elems].dci) >= 0) {
bc[nof_bc_elems].index = i;
bc[nof_bc_elems].type = sched_interface::dl_sched_bc_t::BCCH;
bc[nof_bc_elems].tbs = cfg.sibs[i].len;
Debug("SCHED: SIB%d, start_rb=%d, n_rb=%d, rv=%d, len=%d, period=%d\n",
i+1, start_rbg*P, si_n_rbg*P, rv, cfg.sibs[i].len, cfg.sibs[i].period_rf);
pending_sibs[i].n_tx++;
nof_bc_elems++;
avail_rbg -= si_n_rbg;
start_rbg += si_n_rbg;
} else {
Error("Could not allocate DCI Format1A for SIB%d, len=%d\n", i+1, cfg.sibs[i].len);
}
} else {
Warning("SCHED: Could not schedule DCI for SIB=%d, L=%d\n", i+1, bc_aggr_level);
}
}
}
}
// Schedule Paging
if (rrc) {
uint32_t paging_payload = 0;
if (rrc->is_paging_opportunity(current_tti, &paging_payload)) {
if (avail_rbg > si_n_rbg && paging_payload)
{
if (generate_dci(&bc[nof_bc_elems].dci_location, &common_locations[current_cfi-1], bc_aggr_level)) {
int tbs = generate_format1a(start_rbg*P, si_n_rbg*P, paging_payload, 0, &bc[nof_bc_elems].dci);
if (tbs > 0) {
bc[nof_bc_elems].type = sched_interface::dl_sched_bc_t::PCCH;
bc[nof_bc_elems].tbs = tbs;
nof_bc_elems++;
Info("SCHED: PCH start_rb=%d, tbs=%d\n", start_rbg, tbs);
avail_rbg -= si_n_rbg;
start_rbg += si_n_rbg;
}
}
}
}
}
return nof_bc_elems;
}
// Schedules RAR
int sched::dl_sched_rar(dl_sched_rar_t rar[MAX_RAR_LIST])
{
int nof_rar_elems = 0;
for (uint32_t i=0;i<SCHED_MAX_PENDING_RAR;i++)
{
if (pending_rar[i].buf_rar > 0 && avail_rbg >= rar_n_rb)
{
/* Check if we are still within the RAR window, otherwise discard it */
if (current_tti <= (pending_rar[i].rar_tti + cfg.prach_rar_window + 3)%10240 && current_tti >= pending_rar[i].rar_tti + 3)
{
// Try to schedule DCI for this RAR
if (generate_dci(&rar[nof_rar_elems].dci_location, &rar_locations[current_cfi-1][sf_idx], rar_aggr_level)) {
/* Find all pending RARs with same transmission TTI */
uint32_t tti = pending_rar[i].rar_tti;
uint32_t rar_sfidx = (tti+1)%10;
uint32_t buf_rar = 0;
uint32_t nof_grants = 0;
for (int j=0;j<SCHED_MAX_PENDING_RAR;j++) {
if (pending_rar[j].rar_tti == tti) {
uint32_t L_prb = 3;
uint32_t n_prb = 2;
if (nof_grants == 0) {
bzero(&rar[nof_rar_elems].grants[nof_grants], sizeof(srslte_dci_rar_grant_t));
rar[nof_rar_elems].grants[nof_grants].grant.tpc_pusch = 3;
rar[nof_rar_elems].grants[nof_grants].grant.trunc_mcs = 0;
rar[nof_rar_elems].grants[nof_grants].grant.rba = srslte_ra_type2_to_riv(L_prb, n_prb, cfg.cell.nof_prb);
rar[nof_rar_elems].grants[nof_grants].ra_id = pending_rar[j].ra_id;
buf_rar += pending_rar[i].buf_rar;
pending_rar[j].buf_rar = 0;
pending_rar[j].rar_tti = 0;
// Save UL resources
uint32_t pending_tti=(current_tti+6)%10;
pending_msg3[pending_tti].enabled = true;
pending_msg3[pending_tti].rnti = pending_rar[j].rnti;
pending_msg3[pending_tti].L = L_prb;
pending_msg3[pending_tti].n_prb = n_prb;
pending_msg3[pending_tti].mcs = rar[nof_rar_elems].grants[nof_grants].grant.trunc_mcs;
log_h->info("SCHED: RAR, ra_id=%d, rnti=0x%x, rarnti_idx=%d, start_rb=%d, n_rb=%d, rar_grant_rba=%d, rar_grant_mcs=%d\n",
pending_rar[j].ra_id, pending_rar[j].rnti, rar_sfidx, start_rbg*P, rar_n_rb,
rar[nof_rar_elems].grants[nof_grants].grant.rba,
rar[nof_rar_elems].grants[nof_grants].grant.trunc_mcs);
} else {
log_h->warning("Only 1 RA is responded at a time. Found %d for TTI=%d\n", nof_grants+1, tti);
}
nof_grants++;
}
}
rar[nof_rar_elems].nof_grants = nof_grants;
rar[nof_rar_elems].rarnti = rar_sfidx;
if (generate_format1a(start_rbg*P, rar_n_rb, buf_rar, 0, &rar[nof_rar_elems].dci) >= 0) {
rar[nof_rar_elems].tbs = buf_rar;
nof_rar_elems++;
avail_rbg -= rar_n_rb;
start_rbg += rar_n_rb;
} else {
Error("SCHED: Allocating Format1A grant\n");
}
} else {
log_h->console("SCHED: Could not schedule DCI for RAR tti=%d, L=%d\n", pending_rar[i].rar_tti, rar_aggr_level);
}
} else {
log_h->console("SCHED: Could not transmit RAR within the window (RA TTI=%d, Window=%d, Now=%d)\n",
pending_rar[i].rar_tti, cfg.prach_rar_window, current_tti);
pending_rar[i].buf_rar = 0;
pending_rar[i].rar_tti = 0;
}
}
}
return nof_rar_elems;
}
// Schedules data to users
int sched::dl_sched_data(dl_sched_data_t data[MAX_DATA_LIST])
{
uint32_t nof_ctrl_symbols = (cfg.cell.nof_prb<10)?(current_cfi+1):current_cfi;
dl_metric->new_tti(ue_db, start_rbg, avail_rbg, nof_ctrl_symbols, current_tti);
int nof_data_elems = 0;
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;
dl_harq_proc *h = dl_metric->get_user_allocation(user);
if (h) {
// Try to schedule DCI first
if (generate_dci(&data[nof_data_elems].dci_location,
user->get_locations(current_cfi, sf_idx),
user->get_aggr_level(srslte_dci_format_sizeof(SRSLTE_DCI_FORMAT1, cfg.cell.nof_prb, cfg.cell.nof_ports)), user))
{
bool is_newtx = h->is_empty();
int tbs = user->generate_format1(h, &data[nof_data_elems], current_tti, current_cfi);
if (tbs >= 0) {
log_h->info("SCHED: DL %s rnti=0x%x, pid=%d, mask=0x%x, dci=%d,%d, n_rtx=%d, tbs=%d, buffer=%d\n",
!is_newtx?"retx":"tx", rnti, h->get_id(), h->get_rbgmask(),
data[nof_data_elems].dci_location.L, data[nof_data_elems].dci_location.ncce, h->nof_retx(),
tbs, user->get_pending_dl_new_data(current_tti));
nof_data_elems++;
} else {
log_h->warning("SCHED: Error DL %s rnti=0x%x, pid=%d, mask=0x%x, dci=%d,%d, tbs=%d, buffer=%d\n",
!is_newtx?"retx":"tx", rnti, h->get_id(), h->get_rbgmask(),
data[nof_data_elems].dci_location.L, data[nof_data_elems].dci_location.ncce,
tbs, user->get_pending_dl_new_data(current_tti));
}
} else {
Warning("SCHED: Could not schedule DL DCI for rnti=0x%x, pid=%d\n", rnti, h->get_id());
}
}
}
return nof_data_elems;
}
// Downlink Scheduler
int sched::dl_sched(uint32_t tti, sched_interface::dl_sched_res_t* sched_result)
{
if (!configured) {
return 0;
}
pthread_mutex_lock(&mutex);
/* If ul_sched() not yet called this tti, reset CCE state */
if (current_tti != tti) {
bzero(used_cce, MAX_CCE*sizeof(bool));
}
/* Initialize variables */
current_tti = tti;
sfn = tti/10;
sf_idx = tti%10;
avail_rbg = nof_rbg;
start_rbg = 0;
current_cfi = sched_cfg.nof_ctrl_symbols;
bc_aggr_level = 2;
rar_aggr_level = 2;
bzero(sched_result, sizeof(sched_interface::dl_sched_res_t));
/* Schedule Broadcast data */
sched_result->nof_bc_elems += dl_sched_bc(sched_result->bc);
/* Schedule RAR */
sched_result->nof_rar_elems += dl_sched_rar(sched_result->rar);
/* Schedule pending RLC data */
sched_result->nof_data_elems += dl_sched_data(sched_result->data);
/* Set CFI */
sched_result->cfi = current_cfi;
pthread_mutex_unlock(&mutex);
return 0;
}
// Uplink sched
int sched::ul_sched(uint32_t tti, srsenb::sched_interface::ul_sched_res_t* sched_result)
{
if (!configured) {
return 0;
}
pthread_mutex_lock(&mutex);
/* If dl_sched() not yet called this tti (this tti is +4ms advanced), reset CCE state */
if ((current_tti+4)%10240 != tti) {
bzero(used_cce, MAX_CCE*sizeof(bool));
}
/* Initialize variables */
current_tti = tti;
sfn = tti/10;
if (tti > 4) {
sf_idx = (tti-4)%10;
} else {
sf_idx = (tti+10240-4)%10;
}
int nof_dci_elems = 0;
int nof_phich_elems = 0;
// current_cfi is set in dl_sched()
bzero(sched_result, sizeof(sched_interface::ul_sched_res_t));
// Get HARQ process for 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;
ul_harq_proc *h = user->get_ul_harq(current_tti);
/* Indicate PHICH acknowledgment if needed */
if (h->has_pending_ack()) {
sched_result->phich[nof_phich_elems].phich = h->get_ack()?ul_sched_phich_t::ACK:ul_sched_phich_t::NACK;
sched_result->phich[nof_phich_elems].rnti = rnti;
nof_phich_elems++;
}
}
ul_metric->new_tti(ue_db, cfg.cell.nof_prb, current_tti);
// Update available allocation if there's a pending RAR
if (pending_msg3[tti%10].enabled) {
ul_harq_proc::ul_alloc_t msg3 = {pending_msg3[tti%10].n_prb, pending_msg3[tti%10].L};
ul_metric->update_allocation(msg3);
}
// Allocate PUCCH resources
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;
uint32_t prb_idx[2] = {0, 0};
uint32_t L = 0;
if (user->get_pucch_sched(current_tti, prb_idx, &L)) {
for (int i=0;i<2;i++) {
ul_harq_proc::ul_alloc_t pucch = {prb_idx[i], L};
ul_metric->update_allocation(pucch);
}
}
}
// Now allocate PUSCH
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;
ul_harq_proc *h = NULL;
// Check if there are pending Msg3 transmissions
bool is_rar = false;
if (pending_msg3[tti%10].enabled && pending_msg3[tti%10].rnti == rnti) {
h = user->get_ul_harq(tti);
if (h) {
ul_harq_proc::ul_alloc_t alloc;
alloc.L = pending_msg3[tti%10].L;
alloc.RB_start = pending_msg3[tti%10].n_prb;
h->set_alloc(alloc);
h->set_rar_mcs(pending_msg3[tti%10].mcs);
is_rar = true;
pending_msg3[tti%10].enabled = false;
} else {
Warning("No HARQ pid available for transmission of Msg3\n");
}
} else {
h = ul_metric->get_user_allocation(user);
}
if (h)
{
ul_harq_proc::ul_alloc_t alloc = h->get_alloc();
bool is_newtx = h->is_empty();
bool needs_pdcch = !h->is_adaptive_retx() && !is_rar;
// Set number of retx
if (is_newtx) {
if (is_rar) {
h->set_max_retx(cfg.maxharq_msg3tx);
} else {
h->set_max_retx(user->get_max_retx());
}
}
// Generate PDCCH except for RAR and non-adaptive retx
if (needs_pdcch) {
uint32_t aggr_level = user->get_aggr_level(srslte_dci_format_sizeof(SRSLTE_DCI_FORMAT0, cfg.cell.nof_prb, cfg.cell.nof_ports));
if (!generate_dci(&sched_result->pusch[nof_dci_elems].dci_location,
user->get_locations(current_cfi, sf_idx),
aggr_level))
{
log_h->warning("SCHED: Could not schedule UL DCI rnti=0x%x, pid=%d, L=%d\n",
rnti, h->get_id(), aggr_level);
sched_result->pusch[nof_dci_elems].needs_pdcch = false;
} else {
sched_result->pusch[nof_dci_elems].needs_pdcch = true;
}
} else {
sched_result->pusch[nof_dci_elems].needs_pdcch = false;
}
// Generate grant unless DCI could not be generated and was required
if (sched_result->pusch[nof_dci_elems].needs_pdcch == needs_pdcch) {
uint32_t pending_data_before = user->get_pending_ul_new_data(current_tti);
if (user->generate_format0(h, &sched_result->pusch[nof_dci_elems], current_tti, user->needs_cqi(tti, true)) > 0)
{
if (is_newtx) {
// Un-trigger SR
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",
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);
nof_dci_elems++;
} else {
log_h->warning("SCHED: Error %s %s rnti=0x%x, pid=%d, dci=%d,%d, grant=%d,%d, tbs=%d, bsr=%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, sched_result->pusch[nof_dci_elems].tbs,
user->get_pending_ul_new_data(current_tti));
}
}
}
}
sched_result->nof_dci_elems = nof_dci_elems;
sched_result->nof_phich_elems = nof_phich_elems;
pthread_mutex_unlock(&mutex);
return SRSLTE_SUCCESS;
}
/*******************************************************
*
* Helper functions
*
*******************************************************/
void sched::generate_cce_location(srslte_regs_t *regs_, sched_ue::sched_dci_cce_t* location,
uint32_t cfi, uint32_t sf_idx, uint16_t rnti)
{
srslte_dci_location_t loc[64];
uint32_t nloc = 0;
if (rnti == 0) {
nloc = srslte_pdcch_common_locations_ncce(srslte_regs_pdcch_ncce(regs_, cfi),
loc, 64);
} else {
nloc = srslte_pdcch_ue_locations_ncce(srslte_regs_pdcch_ncce(regs_, cfi),
loc, 64, sf_idx, rnti);
}
for (uint32_t l=0;l<=3;l++) {
int n=0;
for (uint32_t i=0;i<nloc;i++) {
if (loc[i].L == l) {
location->cce_start[l][n] = loc[i].ncce;
n++;
}
}
location->nof_loc[l] = n;
}
}
#define NCCE(L) (1<<L)
bool sched::generate_dci(srslte_dci_location_t *sched_location, sched_ue::sched_dci_cce_t *locations, uint32_t aggr_level, sched_ue *user)
{
uint32_t ncand=0;
bool allocated=false;
while(ncand<locations->nof_loc[aggr_level] && !allocated) {
uint32_t ncce = locations->cce_start[aggr_level][ncand];
bool used = false;
if (user) {
used = user->pucch_sr_collision(current_tti, ncce);
}
for (int j=0;j<NCCE(aggr_level) && !used;j++) {
if (used_cce[ncce+j]) {
used = true;
}
}
if (used) {
ncand++;
} else {
for (int j=0;j<NCCE(aggr_level) && !used;j++) {
used_cce[ncce+j] = true;
}
allocated = true;
Debug("SCHED: Allocated DCI L=%d, ncce=%d\n", aggr_level, ncce);
}
}
if (allocated && sched_location) {
sched_location->L = aggr_level;
sched_location->ncce = locations->cce_start[aggr_level][ncand];
}
return allocated;
}
int sched::generate_format1a(uint32_t rb_start, uint32_t l_crb, uint32_t tbs_bytes, uint32_t rv, srslte_ra_dl_dci_t *dci)
{
/* Calculate I_tbs for this TBS */
int tbs = tbs_bytes*8;
int i;
int mcs = -1;
for (i=0;i<27;i++) {
if (srslte_ra_tbs_from_idx(i, 2) >= tbs) {
dci->type2_alloc.n_prb1a = srslte_ra_type2_t::SRSLTE_RA_TYPE2_NPRB1A_2;
mcs = i;
tbs = srslte_ra_tbs_from_idx(i, 2);
break;
} else if (srslte_ra_tbs_from_idx(i, 3) >= tbs) {
dci->type2_alloc.n_prb1a = srslte_ra_type2_t::SRSLTE_RA_TYPE2_NPRB1A_3;
mcs = i;
tbs = srslte_ra_tbs_from_idx(i, 3);
break;
}
}
if (i == 28) {
Error("Can't allocate Format 1A for TBS=%d\n", tbs);
return -1;
}
dci->alloc_type = SRSLTE_RA_ALLOC_TYPE2;
dci->type2_alloc.mode = srslte_ra_type2_t::SRSLTE_RA_TYPE2_LOC;
dci->type2_alloc.L_crb = l_crb;
dci->type2_alloc.RB_start = rb_start;
dci->harq_process = 0;
dci->mcs_idx = mcs;
dci->rv_idx = rv;
dci->tb_en[0] = true;
dci->tb_en[1] = false;
return tbs;
}
}

View File

@ -0,0 +1,238 @@
#include <string.h>
#include <boost/concept_check.hpp>
#include "srslte/srslte.h"
#include "srslte/common/pdu.h"
#include "mac/scheduler.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__)
namespace srsenb {
/******************************************************
*
* These classes manage the HARQ Processes.
* There is a common class and two child classes for UL and DL.
*
******************************************************/
void harq_proc::config(uint32_t id_, uint32_t max_retx_, srslte::log* log_h_)
{
log_h = log_h_;
id = id_;
max_retx = max_retx_;
ndi = false;
}
void harq_proc::set_max_retx(uint32_t max_retx_) {
log_h->debug("Set max_retx=%d pid=%d\n", max_retx_, id);
max_retx = max_retx_;
}
uint32_t harq_proc::get_id()
{
return id;
}
void harq_proc::reset()
{
active = false;
ack = true;
ack_received = false;
n_rtx = 0;
tti = 0;
last_mcs = -1;
last_tbs = -1;
tx_cnt = 0;
}
bool harq_proc::is_empty()
{
return !active || (active && ack && ack_received);
}
bool harq_proc::has_pending_retx_common()
{
return !ack && n_rtx < max_retx;
}
uint32_t harq_proc::get_tti()
{
return tti;
}
bool harq_proc::get_ack()
{
return ack;
}
void harq_proc::set_ack(bool ack_)
{
ack = ack_;
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);
active = false;
}
}
void harq_proc::new_tx_common(uint32_t tti_, int mcs, int tbs)
{
reset();
ndi = !ndi;
tti = tti_;
tx_cnt++;
last_mcs = mcs;
last_tbs = tbs;
if (max_retx) {
active = true;
} else {
active = false; // Can reuse this process if no retx are allowed
}
}
void harq_proc::new_retx(uint32_t tti_, int *mcs, int *tbs)
{
ack_received = false;
tti = tti_;
n_rtx++;
if (mcs) {
*mcs = last_mcs;
}
if (tbs) {
*tbs = last_tbs;
}
}
uint32_t harq_proc::nof_tx()
{
return tx_cnt;
}
uint32_t harq_proc::nof_retx()
{
return n_rtx;
}
bool harq_proc::get_ndi()
{
return ndi;
}
/******************************************************
* UE::DL HARQ class *
******************************************************/
void dl_harq_proc::new_tx(uint32_t tti, int mcs, int tbs, uint32_t n_cce_)
{
n_cce = n_cce_;
new_tx_common(tti, mcs, tbs);
}
uint32_t dl_harq_proc::get_n_cce()
{
return n_cce;
}
uint32_t dl_harq_proc::get_rbgmask()
{
return rbgmask;
}
void dl_harq_proc::set_rbgmask(uint32_t new_mask)
{
rbgmask = new_mask;
}
bool dl_harq_proc::has_pending_retx(uint32_t current_tti)
{
return srslte_tti_interval(current_tti, tti) >= 8 && has_pending_retx_common();
}
int dl_harq_proc::get_tbs()
{
return last_tbs;
}
/******************************************************
* UE::UL HARQ class *
******************************************************/
ul_harq_proc::ul_alloc_t ul_harq_proc::get_alloc()
{
return allocation;
}
void ul_harq_proc::set_alloc(ul_harq_proc::ul_alloc_t alloc)
{
is_adaptive = false;
memcpy(&allocation, &alloc, sizeof(ul_alloc_t));
}
void ul_harq_proc::same_alloc()
{
is_adaptive = true;
}
bool ul_harq_proc::is_adaptive_retx()
{
return is_adaptive;
}
void ul_harq_proc::new_tx(uint32_t tti_, int mcs, int tbs)
{
need_ack = true;
new_tx_common(tti_, mcs, tbs);
pending_data = tbs;
}
bool ul_harq_proc::has_pending_ack()
{
bool ret = need_ack;
// Reset if already received a positive ACK
if (active && ack) {
active = false;
}
if (!active) {
pending_data = 0;
need_ack = false;
}
return ret;
}
uint32_t ul_harq_proc::get_pending_data()
{
return pending_data;
}
void ul_harq_proc::set_rar_mcs(uint32_t mcs)
{
rar_mcs = mcs;
has_rar_mcs = true;
}
bool ul_harq_proc::get_rar_mcs(int *mcs)
{
if (has_rar_mcs) {
if (mcs) {
*mcs = (int) rar_mcs;
}
has_rar_mcs = false;
return true;
}
return false;
}
}

View File

@ -0,0 +1,310 @@
#include <string.h>
#include "srslte/srslte.h"
#include "mac/scheduler_metric.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__)
namespace srsenb {
/*****************************************************************
*
* Downlink Metric
*
*****************************************************************/
uint32_t dl_metric_rr::calc_rbg_mask(bool mask[MAX_RBG])
{
// Build RBG bitmask
uint32_t rbg_bitmask = 0;
for (uint32_t n=0;n<total_rb;n++) {
if (mask[n]) {
rbg_bitmask |= (1<<(total_rb-1-n));
}
}
return rbg_bitmask;
}
uint32_t dl_metric_rr::count_rbg(uint32_t mask) {
uint32_t count = 0;
while(mask > 0) {
if ((mask & 1) == 1) {
count++;
}
mask >>= 1;
}
return count;
}
uint32_t dl_metric_rr::get_required_rbg(sched_ue *user, uint32_t tti)
{
dl_harq_proc *h = user->get_pending_dl_harq(tti);
if (h) {
return count_rbg(h->get_rbgmask());
}
uint32_t pending_data = user->get_pending_dl_new_data(current_tti);
return user->get_required_prb_dl(pending_data, nof_ctrl_symbols);
}
void dl_metric_rr::new_tti(std::map<uint16_t,sched_ue> &ue_db, uint32_t start_rb, uint32_t nof_rb, uint32_t nof_ctrl_symbols_, uint32_t tti)
{
total_rb = start_rb+nof_rb;
for (uint32_t i=0;i<total_rb;i++) {
if (i<start_rb) {
used_rb[i] = true;
} else {
used_rb[i] = false;
}
}
available_rb = nof_rb;
used_rb_mask = calc_rbg_mask(used_rb);
current_tti = tti;
nof_ctrl_symbols = nof_ctrl_symbols_;
nof_users_with_data = 0;
for(std::map<uint16_t, sched_ue>::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) {
sched_ue *user = (sched_ue*) &iter->second;
if (user->get_pending_dl_new_data(current_tti) || user->get_pending_dl_harq(current_tti)) {
user->ue_idx = nof_users_with_data;
nof_users_with_data++;
}
}
}
bool dl_metric_rr::new_allocation(uint32_t nof_rbg, uint32_t *rbgmask) {
bool mask_bit[MAX_RBG];
bzero(mask_bit, sizeof(bool)*MAX_RBG);
for (uint32_t i=0;i<total_rb && nof_rbg > 0;i++) {
if (used_rb[i]) {
mask_bit[i] = false;
} else {
mask_bit[i] = true;
nof_rbg--;
}
}
if (rbgmask) {
*rbgmask = calc_rbg_mask(mask_bit);
}
return (nof_rbg == 0);
}
void dl_metric_rr::update_allocation(uint32_t new_mask) {
used_rb_mask |= new_mask;
for (uint32_t n=0;n<total_rb;n++) {
if (used_rb_mask & (1<<(total_rb-1-n))) {
used_rb[n] = true;
} else {
used_rb[n] = false;
}
}
}
bool dl_metric_rr::allocation_is_valid(uint32_t mask)
{
return (mask & used_rb_mask);
}
dl_harq_proc* dl_metric_rr::get_user_allocation(sched_ue *user)
{
uint32_t pending_data = user->get_pending_dl_new_data(current_tti);
dl_harq_proc *h = user->get_pending_dl_harq(current_tti);
// Time-domain RR scheduling
if (pending_data || h) {
if (nof_users_with_data) {
if (nof_users_with_data == 2) {
}
if ((current_tti%nof_users_with_data) != user->ue_idx) {
return NULL;
}
}
}
// Schedule retx if we have space
if (h) {
uint32_t retx_mask = h->get_rbgmask();
// If can schedule the same mask, do it
if (!allocation_is_valid(retx_mask)) {
update_allocation(retx_mask);
return h;
}
// If not, try to find another mask in the current tti
uint32_t nof_rbg = count_rbg(retx_mask);
if (nof_rbg < available_rb) {
if (new_allocation(nof_rbg, &retx_mask)) {
update_allocation(retx_mask);
h->set_rbgmask(retx_mask);
return h;
}
}
}
// If could not schedule the reTx, or there wasn't any pending retx, find an empty PID
h = user->get_empty_dl_harq();
if (h) {
// Allocate resources based on pending data
if (pending_data) {
uint32_t pending_rb = user->get_required_prb_dl(pending_data, nof_ctrl_symbols);
uint32_t newtx_mask = 0;
new_allocation(pending_rb, &newtx_mask);
if (newtx_mask) {
update_allocation(newtx_mask);
h->set_rbgmask(newtx_mask);
return h;
}
}
}
return NULL;
}
/*****************************************************************
*
* Uplink Metric
*
*****************************************************************/
void ul_metric_rr::new_tti(std::map<uint16_t,sched_ue> &ue_db, uint32_t nof_rb_, uint32_t tti)
{
current_tti = tti;
nof_rb = nof_rb_;
available_rb = nof_rb_;
bzero(used_rb, nof_rb*sizeof(bool));
nof_users_with_data = 0;
for(std::map<uint16_t, sched_ue>::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) {
sched_ue *user = (sched_ue*) &iter->second;
if (user->get_pending_ul_new_data(current_tti) || !user->get_ul_harq(current_tti)->is_empty()) {
user->ue_idx = nof_users_with_data;
nof_users_with_data++;
}
}
}
bool ul_metric_rr::allocation_is_valid(ul_harq_proc::ul_alloc_t alloc)
{
if (alloc.RB_start+alloc.L > nof_rb) {
return false;
}
for (uint32_t n=alloc.RB_start;n<alloc.RB_start+alloc.L;n++) {
if (used_rb[n]) {
return false;
}
}
return true;
}
bool ul_metric_rr::new_allocation(uint32_t L, ul_harq_proc::ul_alloc_t* alloc)
{
bzero(alloc, sizeof(ul_harq_proc::ul_alloc_t));
for (uint32_t n=0;n<nof_rb && alloc->L < L;n++) {
if (!used_rb[n] && alloc->L == 0) {
alloc->RB_start = n;
}
if (!used_rb[n]) {
alloc->L++;
} else if (alloc->L > 0) {
// avoid edges
if (n < 3) {
alloc->RB_start = 0;
alloc->L = 0;
} else {
break;
}
}
}
if (!alloc->L) {
return 0;
}
// Make sure L is allowed by SC-FDMA modulation
while (!srslte_dft_precoding_valid_prb(alloc->L)) {
alloc->L--;
}
return alloc->L == L;
}
void ul_metric_rr::update_allocation(ul_harq_proc::ul_alloc_t alloc)
{
if (alloc.L > available_rb) {
return;
}
if (alloc.RB_start + alloc.L > nof_rb) {
return;
}
for (uint32_t n=alloc.RB_start;n<alloc.RB_start+alloc.L;n++) {
used_rb[n] = true;
}
available_rb -= alloc.L;
}
ul_harq_proc* ul_metric_rr::get_user_allocation(sched_ue *user)
{
// Time-domain RR scheduling
uint32_t pending_data = user->get_pending_ul_new_data(current_tti);
ul_harq_proc *h = user->get_ul_harq(current_tti);
if (pending_data || !h->is_empty()) {
if (nof_users_with_data) {
if ((current_tti%nof_users_with_data) != user->ue_idx) {
return NULL;
}
}
}
// Schedule retx if we have space
if (!h->is_empty()) {
ul_harq_proc::ul_alloc_t alloc = h->get_alloc();
// If can schedule the same mask, do it
if (allocation_is_valid(alloc)) {
update_allocation(alloc);
h->same_alloc();
return h;
}
// If not, try to find another mask in the current tti
if (new_allocation(alloc.L, &alloc)) {
update_allocation(alloc);
h->set_alloc(alloc);
return h;
}
}
// If could not schedule the reTx, or there wasn't any pending retx, find an empty PID
if (h->is_empty()) {
// Allocate resources based on pending data
if (pending_data) {
uint32_t pending_rb = user->get_required_prb_ul(pending_data);
ul_harq_proc::ul_alloc_t alloc;
new_allocation(pending_rb, &alloc);
if (alloc.L) {
update_allocation(alloc);
h->set_alloc(alloc);
return h;
}
}
}
return NULL;
}
}

View File

@ -0,0 +1,762 @@
#include <string.h>
#include <boost/concept_check.hpp>
#include "srslte/srslte.h"
#include "srslte/common/pdu.h"
#include "mac/scheduler_ue.h"
#include "mac/scheduler.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__)
/******************************************************
* UE class *
******************************************************/
namespace srsenb {
/*******************************************************
*
* Initialization and configuration functions
*
*******************************************************/
sched_ue::sched_ue()
{
reset();
}
void sched_ue::set_cfg(uint16_t rnti_, sched_interface::ue_cfg_t *cfg_, sched_interface::cell_cfg_t *cell_cfg,
srslte_regs_t *regs, srslte::log *log_h_)
{
reset();
rnti = rnti_;
log_h = log_h_;
memcpy(&cell, &cell_cfg->cell, sizeof(srslte_cell_t));
max_mcs_dl = 28;
max_mcs_ul = 28;
if (cfg_) {
memcpy(&cfg, cfg_, sizeof(sched_interface::ue_cfg_t));
}
Info("SCHED: Added user rnti=0x%x\n", rnti);
for (int i=0;i<sched_interface::MAX_LC;i++) {
set_bearer_cfg(i, &cfg.ue_bearers[i]);
}
// Config HARQ processes
for (int i=0;i<SCHED_MAX_HARQ_PROC;i++) {
dl_harq[i].config(i, cfg.maxharq_tx, log_h);
ul_harq[i].config(i, cfg.maxharq_tx, log_h);
}
// Generate allowed CCE locations
for (int cfi=0;cfi<3;cfi++) {
for (int sf_idx=0;sf_idx<10;sf_idx++) {
sched::generate_cce_location(regs, &dci_locations[cfi][sf_idx], cfi+1, sf_idx, rnti);
}
}
}
void sched_ue::reset()
{
bzero(&cfg, sizeof(sched_interface::ue_cfg_t));
sr = false;
next_tpc_pusch = 1;
next_tpc_pucch = 1;
buf_mac = 0;
buf_ul = 0;
phy_config_dedicated_enabled = false;
dl_cqi = 1;
ul_cqi = 1;
dl_cqi_tti = 0;
ul_cqi_tti = 0;
cqi_request_tti = 0;
for (int i=0;i<SCHED_MAX_HARQ_PROC;i++) {
dl_harq[i].reset();
ul_harq[i].reset();
}
for (int i=0;i<sched_interface::MAX_LC; i++) {
rem_bearer(i);
}
}
void sched_ue::set_fixed_mcs(int mcs_ul, int mcs_dl) {
fixed_mcs_ul = mcs_ul;
fixed_mcs_dl = mcs_dl;
}
void sched_ue::set_max_mcs(int mcs_ul, int mcs_dl) {
if (mcs_ul < 0) {
max_mcs_ul = 28;
} else {
max_mcs_ul = mcs_ul;
}
if (mcs_dl < 0) {
max_mcs_dl = 28;
} else {
max_mcs_dl = mcs_dl;
}
}
/*******************************************************
*
* FAPI-like main scheduler interface.
*
*******************************************************/
void sched_ue::set_bearer_cfg(uint32_t lc_id, sched_interface::ue_bearer_cfg_t* cfg)
{
if (lc_id < sched_interface::MAX_LC) {
memcpy(&lch[lc_id].cfg, cfg, sizeof(sched_interface::ue_bearer_cfg_t));
lch[lc_id].buf_tx = 0;
lch[lc_id].buf_retx = 0;
if (lch[lc_id].cfg.direction != sched_interface::ue_bearer_cfg_t::IDLE) {
Info("SCHED: Set bearer config lc_id=%d, direction=%d\n", lc_id, (int) lch[lc_id].cfg.direction);
}
}
}
void sched_ue::rem_bearer(uint32_t lc_id)
{
if (lc_id < sched_interface::MAX_LC) {
bzero(&lch[lc_id], sizeof(ue_bearer_t));
}
}
void sched_ue::phy_config_enabled(uint32_t tti, bool enabled)
{
dl_cqi_tti = tti;
phy_config_dedicated_enabled = enabled;
}
void sched_ue::ul_buffer_state(uint8_t lc_id, uint32_t bsr)
{
if (lc_id < sched_interface::MAX_LC) {
lch[lc_id].bsr = bsr;
Debug("SCHED: UL lcid=%d buffer_state=%d\n", lc_id, bsr);
}
}
void sched_ue::ul_phr(int phr)
{
power_headroom= phr;
}
void sched_ue::dl_buffer_state(uint8_t lc_id, uint32_t tx_queue, uint32_t retx_queue)
{
if (lc_id < sched_interface::MAX_LC) {
lch[lc_id].buf_retx = retx_queue;
lch[lc_id].buf_tx = tx_queue;
Debug("SCHED: DL lcid=%d buffer_state=%d,%d\n", lc_id, tx_queue, retx_queue);
}
}
void sched_ue::mac_buffer_state(uint32_t ce_code)
{
buf_mac++;
}
void sched_ue::set_sr()
{
sr = true;
}
void sched_ue::unset_sr()
{
sr = false;
}
bool sched_ue::pucch_sr_collision(uint32_t current_tti, uint32_t n_cce)
{
if (!phy_config_dedicated_enabled) {
return false;
}
srslte_pucch_sched_t pucch_sched;
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;
bool has_sr = cfg.sr_enabled && srslte_ue_ul_sr_send_tti(cfg.sr_I, current_tti);
if (!has_sr) {
return false;
}
uint32_t n_pucch_sr = srslte_pucch_get_npucch(n_cce, SRSLTE_PUCCH_FORMAT_1A, true, &pucch_sched);
uint32_t n_pucch_nosr = srslte_pucch_get_npucch(n_cce, SRSLTE_PUCCH_FORMAT_1A, false, &pucch_sched);
if (srslte_pucch_n_prb(&cfg.pucch_cfg, SRSLTE_PUCCH_FORMAT_1A, n_pucch_sr, cell.nof_prb, cell.cp, 0) ==
srslte_pucch_n_prb(&cfg.pucch_cfg, SRSLTE_PUCCH_FORMAT_1A, n_pucch_nosr, cell.nof_prb, cell.cp, 0))
{
return true;
} else {
return false;
}
}
bool sched_ue::get_pucch_sched(uint32_t current_tti, uint32_t prb_idx[2], uint32_t *L)
{
if (!phy_config_dedicated_enabled) {
return false;
}
srslte_pucch_sched_t pucch_sched;
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;
bool has_sr = cfg.sr_enabled && srslte_ue_ul_sr_send_tti(cfg.sr_I, current_tti);
// 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) {
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++) {
prb_idx[i] = srslte_pucch_n_prb(&cfg.pucch_cfg, SRSLTE_PUCCH_FORMAT_1A, n_pucch, cell.nof_prb, cell.cp, i);
}
}
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);
return true;
}
}
// If there is no Format1A/B, then check if it's expecting Format1
if (has_sr) {
if (prb_idx) {
for (int i=0;i<2;i++) {
prb_idx[i] = srslte_pucch_n_prb(&cfg.pucch_cfg, SRSLTE_PUCCH_FORMAT_1, cfg.sr_N_pucch, cell.nof_prb, cell.cp, i);
}
}
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);
return true;
}
return false;
}
int sched_ue::set_ack_info(uint32_t tti, bool ack)
{
for (int i=0;i<SCHED_MAX_HARQ_PROC;i++) {
if (((dl_harq[i].get_tti()+4)%10240) == tti) {
Debug("SCHED: Set ACK=%d for rnti=0x%x, pid=%d, tti=%d\n", ack, rnti, i, tti);
dl_harq[i].set_ack(ack);
return dl_harq[i].get_tbs();
}
}
Warning("SCHED: Received ACK info for unknown TTI=%d\n", tti);
return -1;
}
void sched_ue::ul_recv_len(uint32_t lcid, uint32_t len)
{
// Remove PDCP header??
if (len > 4) {
len -= 4;
}
if (lcid < sched_interface::MAX_LC) {
if (bearer_is_ul(&lch[lcid])) {
if (lch[lcid].bsr > (int) len) {
lch[lcid].bsr -= len;
} else {
lch[lcid].bsr = 0;
}
}
}
}
void sched_ue::set_ul_crc(uint32_t tti, bool crc_res)
{
get_ul_harq(tti)->set_ack(crc_res);
}
void sched_ue::set_dl_cqi(uint32_t tti, uint32_t cqi)
{
dl_cqi = cqi;
dl_cqi_tti = tti;
}
void sched_ue::set_ul_cqi(uint32_t tti, uint32_t cqi, uint32_t ul_ch_code)
{
ul_cqi = cqi;
ul_cqi_tti = tti;
}
void sched_ue::tpc_inc() {
if (power_headroom > 0) {
next_tpc_pusch = 3;
next_tpc_pucch = 3;
}
log_h->info("SCHED: Set TCP=%d for rnti=0x%x\n", next_tpc_pucch, rnti);
}
void sched_ue::tpc_dec() {
next_tpc_pusch = 0;
next_tpc_pucch = 0;
log_h->info("SCHED: Set TCP=%d for rnti=0x%x\n", next_tpc_pucch, rnti);
}
/*******************************************************
*
* Functions used to generate DCI grants
*
*******************************************************/
// Generates a Format1 grant
int sched_ue::generate_format1(dl_harq_proc *h,
sched_interface::dl_sched_data_t *data,
uint32_t tti,
uint32_t cfi)
{
srslte_ra_dl_dci_t *dci = &data->dci;
bzero(dci, sizeof(srslte_ra_dl_dci_t));
uint32_t sf_idx = tti%10;
int mcs = 0;
int tbs = 0;
dci->alloc_type = SRSLTE_RA_ALLOC_TYPE0;
dci->type0_alloc.rbg_bitmask = h->get_rbgmask();
// If this is the first transmission for this UE, make room for MAC Contention Resolution ID
bool need_conres_ce = false;
if (is_first_dl_tx()) {
need_conres_ce = true;
}
if (h->is_empty()) {
uint32_t req_bytes = get_pending_dl_new_data(tti);
uint32_t nof_prb = format1_count_prb(h->get_rbgmask(), cell.nof_prb);
srslte_ra_dl_grant_t grant;
srslte_ra_dl_dci_to_grant_prb_allocation(dci, &grant, cell.nof_prb);
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);
} else {
tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(fixed_mcs_dl), nof_prb);
mcs = fixed_mcs_dl;
}
h->new_tx(tti, mcs, tbs, data->dci_location.ncce);
Debug("SCHED: Alloc format1 new mcs=%d, tbs=%d, nof_prb=%d, req_bytes=%d\n", mcs, tbs, nof_prb, req_bytes);
} else {
h->new_retx(tti, &mcs, &tbs);
Debug("SCHED: Alloc format1 previous mcs=%d, tbs=%d\n", mcs, tbs);
}
// Allocate MAC ConRes CE
if (need_conres_ce) {
data->pdu[0].lcid = srslte::sch_subh::CON_RES_ID;
data->nof_pdu_elems++;
Info("SCHED: Added MAC Contention Resolution CE for rnti=0x%x\n", rnti);
}
int rem_tbs = tbs;
int x = 0;
do {
x = alloc_pdu(rem_tbs, &data->pdu[data->nof_pdu_elems]);
rem_tbs -= x;
if (x) {
data->nof_pdu_elems++;
}
} while(rem_tbs > 0 && x > 0);
data->rnti = rnti;
if (tbs > 0) {
dci->harq_process = h->get_id();
dci->mcs_idx = mcs;
dci->rv_idx = sched::get_rvidx(h->nof_retx());
dci->ndi = h->get_ndi();
dci->tpc_pucch = next_tpc_pucch;
next_tpc_pucch = 1;
data->tbs = tbs;
dci->tb_en[0] = true;
dci->tb_en[1] = false;
}
return tbs;
}
int sched_ue::generate_format0(ul_harq_proc *h,
sched_interface::ul_sched_data_t *data,
uint32_t tti,
bool cqi_request)
{
srslte_ra_ul_dci_t *dci = &data->dci;
bzero(dci, sizeof(srslte_ra_ul_dci_t));
int mcs = 0;
int tbs = 0;
ul_harq_proc::ul_alloc_t allocation = h->get_alloc();
if (h->get_rar_mcs(&mcs)) {
tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(mcs), allocation.L)/8;
h->new_tx(tti, mcs, tbs);
} else if (h->is_empty()) {
uint32_t req_bytes = get_pending_ul_new_data(tti);
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);
} else {
tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(fixed_mcs_ul), allocation.L);
mcs = fixed_mcs_ul;
}
h->new_tx(tti, mcs, tbs);
} else {
h->new_retx(tti, &mcs, NULL);
tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(mcs), allocation.L)/8;
}
data->rnti = rnti;
data->tbs = tbs;
if (tbs > 0) {
dci->type2_alloc.L_crb = allocation.L;
dci->type2_alloc.RB_start = allocation.RB_start;
dci->mcs_idx = mcs;
dci->rv_idx = sched::get_rvidx(h->nof_retx());
dci->ndi = h->get_ndi();
dci->cqi_request = cqi_request;
dci->freq_hop_fl = srslte_ra_ul_dci_t::SRSLTE_RA_PUSCH_HOP_DISABLED;
dci->tpc_pusch = next_tpc_pusch;
next_tpc_pusch = 1;
}
return tbs;
}
/*******************************************************
*
* Functions used by scheduler or scheduler metric objects
*
*******************************************************/
bool sched_ue::bearer_is_ul(ue_bearer_t *lch) {
return lch->cfg.direction == sched_interface::ue_bearer_cfg_t::UL || lch->cfg.direction == sched_interface::ue_bearer_cfg_t::BOTH;
}
bool sched_ue::bearer_is_dl(ue_bearer_t *lch) {
return lch->cfg.direction == sched_interface::ue_bearer_cfg_t::DL || lch->cfg.direction == sched_interface::ue_bearer_cfg_t::BOTH;
}
uint32_t sched_ue::get_max_retx() {
return cfg.maxharq_tx;
}
bool sched_ue::is_first_dl_tx()
{
for (int i=0;i<SCHED_MAX_HARQ_PROC;i++) {
if (dl_harq[i].nof_tx() > 0) {
return false;
}
}
return true;
}
bool sched_ue::needs_cqi(uint32_t tti, bool will_be_sent)
{
bool ret = false;
if (phy_config_dedicated_enabled &&
cfg.aperiodic_cqi_period &&
get_pending_dl_new_data(tti) > 0)
{
uint32_t interval = srslte_tti_interval(tti, dl_cqi_tti);
bool needscqi = interval >= cfg.aperiodic_cqi_period;
if (needscqi) {
uint32_t interval_sent = srslte_tti_interval(tti, cqi_request_tti);
if (interval_sent >= 16) {
if (will_be_sent) {
cqi_request_tti = tti;
}
Debug("SCHED: Needs_cqi, last_sent=%d, will_be_sent=%d\n", cqi_request_tti, will_be_sent);
ret = true;
}
}
}
return ret;
}
uint32_t sched_ue::get_pending_dl_new_data(uint32_t tti)
{
uint32_t pending_data = 0;
for (int i=0;i<sched_interface::MAX_LC;i++) {
if (bearer_is_dl(&lch[i])) {
pending_data += lch[i].buf_retx + lch[i].buf_tx;
}
}
return pending_data;
}
uint32_t sched_ue::get_pending_ul_new_data(uint32_t tti)
{
uint32_t pending_data = 0;
for (int i=0;i<sched_interface::MAX_LC;i++) {
if (bearer_is_ul(&lch[i])) {
pending_data += lch[i].bsr;
}
}
if (!pending_data && is_sr_triggered()) {
return 512;
}
if (!pending_data && needs_cqi(tti)) {
return 128;
}
uint32_t pending_ul_data = get_pending_ul_old_data();
if (pending_data > pending_ul_data) {
pending_data -= pending_ul_data;
} else {
pending_data = 0;
}
return pending_data;
}
uint32_t sched_ue::get_pending_ul_old_data()
{
uint32_t pending_data = 0;
for (int i=0;i<SCHED_MAX_HARQ_PROC;i++) {
pending_data += ul_harq[i].get_pending_data();
}
return pending_data;
}
uint32_t sched_ue::get_required_prb_dl(uint32_t req_bytes, uint32_t nof_ctrl_symbols)
{
int mcs = 0;
uint32_t nbytes = 0;
uint32_t n = 0;
if (req_bytes == 0) {
return 0;
}
uint32_t nof_re = 0;
int tbs = 0;
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);
} else {
tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(fixed_mcs_dl), n);
}
if (tbs > 0) {
nbytes = tbs;
} else if (tbs < 0) {
return 0;
}
}
return n;
}
uint32_t sched_ue::get_required_prb_ul(uint32_t req_bytes)
{
int mcs = 0;
int tbs = 0;
uint32_t nbytes = 0;
uint32_t N_srs = 0;
uint32_t n = 0;
if (req_bytes == 0) {
return 0;
}
for (n=1;n<cell.nof_prb && nbytes < req_bytes + 4;n++) {
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);
} else {
tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(fixed_mcs_ul), n);
}
if (tbs > 0) {
nbytes = tbs;
}
}
while (!srslte_dft_precoding_valid_prb(n)) {
n++;
}
return n;
}
bool sched_ue::is_sr_triggered()
{
return sr;
}
/* Gets HARQ process with oldest pending retx */
dl_harq_proc* sched_ue::get_pending_dl_harq(uint32_t tti)
{
int oldest_idx=-1;
uint32_t oldest_tti = 0;
for (int i=0;i<SCHED_MAX_HARQ_PROC;i++) {
if (dl_harq[i].has_pending_retx(tti)) {
uint32_t x = srslte_tti_interval(tti, dl_harq[i].get_tti());
if (x > oldest_tti) {
oldest_idx = i;
oldest_tti = x;
}
}
}
if (oldest_idx >= 0) {
return &dl_harq[oldest_idx];
} else {
return NULL;
}
}
dl_harq_proc* sched_ue::get_empty_dl_harq()
{
for (int i=0;i<SCHED_MAX_HARQ_PROC;i++) {
if (dl_harq[i].is_empty()) {
return &dl_harq[i];
}
}
return NULL;
}
ul_harq_proc* sched_ue::get_ul_harq(uint32_t tti)
{
return &ul_harq[tti%SCHED_MAX_HARQ_PROC];
}
/* Find lowest DCI aggregation level supported by the UE spectral efficiency */
uint32_t sched_ue::get_aggr_level(uint32_t nof_bits)
{
uint32_t l=0;
float max_coderate = srslte_cqi_to_coderate(dl_cqi);
float coderate = 99;
do {
coderate = srslte_pdcch_coderate(nof_bits, l);
l++;
} while(l<3 && coderate > max_coderate);
Debug("SCHED: CQI=%d, l=%d, nof_bits=%d, coderate=%.2f, max_coderate=%.2f\n", dl_cqi, l, nof_bits, coderate, max_coderate);
return l;
}
sched_ue::sched_dci_cce_t* sched_ue::get_locations(uint32_t cfi, uint32_t sf_idx)
{
if (cfi > 0 && cfi <= 3) {
return &dci_locations[cfi-1][sf_idx];
} else {
Error("SCHED: Invalid CFI=%d\n", cfi);
return &dci_locations[0][sf_idx];
}
}
/* Allocates first available RLC PDU */
int sched_ue::alloc_pdu(int tbs_bytes, sched_interface::dl_sched_pdu_t* pdu)
{
// TODO: Implement lcid priority (now lowest index is lowest priority)
int x = 0;
int i = 0;
for (i=0;i<sched_interface::MAX_LC && !x;i++) {
if (lch[i].buf_retx) {
x = SRSLTE_MIN(lch[i].buf_retx, tbs_bytes);
lch[i].buf_retx -= x;
} else if (lch[i].buf_tx) {
x = SRSLTE_MIN(lch[i].buf_tx, tbs_bytes);
lch[i].buf_tx -= x;
}
}
if (x) {
pdu->lcid = i-1;
pdu->nbytes = x;
Debug("SCHED: Allocated lcid=%d, nbytes=%d, tbs_bytes=%d\n", pdu->lcid, pdu->nbytes, tbs_bytes);
}
return x;
}
uint32_t sched_ue::format1_count_prb(uint32_t bitmask, uint32_t cell_nof_prb) {
uint32_t P = srslte_ra_type0_P(cell_nof_prb);
uint32_t nb = (int) ceilf((float) cell_nof_prb / P);
uint32_t nof_prb = 0;
for (uint32_t i = 0; i < nb; i++) {
if (bitmask & (1 << (nb - i - 1))) {
for (uint32_t j = 0; j < P; j++) {
if (i*P+j < cell_nof_prb) {
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) {
float max_coderate = srslte_cqi_to_coderate(cqi);
int sel_mcs = max_mcs+1;
float coderate = 99;
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);
if (mcs) {
*mcs = (uint32_t) sel_mcs;
}
return tbs;
}
/* 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)
{
uint32_t sel_mcs = 0;
int tbs = cqi_to_tbs(cqi, nof_prb, nof_re, max_mcs, &sel_mcs)/8;
/* If less bytes are requested, lower the MCS */
if (tbs > (int) req_bytes && req_bytes > 0) {
uint32_t req_tbs_idx = srslte_ra_tbs_to_table_idx(req_bytes*8, nof_prb);
uint32_t req_mcs = srslte_ra_mcs_from_tbs_idx(req_tbs_idx);
if (req_mcs < sel_mcs) {
sel_mcs = req_mcs;
tbs = srslte_ra_tbs_from_idx(req_tbs_idx, nof_prb)/8;
}
}
// Avoid the unusual case n_prb=1, mcs=6 tbs=328 (used in voip)
if (nof_prb == 1 && sel_mcs == 6) {
sel_mcs--;
}
if (mcs && tbs >= 0) {
*mcs = (int) sel_mcs;
}
return tbs;
}
}

366
srsenb/src/mac/ue.cc Normal file
View File

@ -0,0 +1,366 @@
#include <iostream>
#include <string.h>
#include "srslte/interfaces/enb_interfaces.h"
#include "mac/ue.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__)
namespace srsenb {
void ue::config(uint16_t rnti_, uint32_t nof_prb, sched_interface *sched_, rrc_interface_mac *rrc_, rlc_interface_mac *rlc_, srslte::log *log_h_)
{
rnti = rnti_;
rlc = rlc_;
rrc = rrc_;
log_h = log_h_;
sched = sched_;
pdus.init(this, log_h);
for (int i=0;i<NOF_HARQ_PROCESSES;i++) {
srslte_softbuffer_rx_init(&softbuffer_rx[i], nof_prb);
srslte_softbuffer_tx_init(&softbuffer_tx[i], nof_prb);
}
// don't need to reset because just initiated the buffers
bzero(&metrics, sizeof(mac_metrics_t));
nof_failures = 0;
for(int i=0;i<NOF_HARQ_PROCESSES;i++) {
pending_buffers[i] = NULL;
}
}
void ue::reset()
{
bzero(&metrics, sizeof(mac_metrics_t));
nof_failures = 0;
for (int i=0;i<NOF_HARQ_PROCESSES;i++) {
srslte_softbuffer_rx_reset(&softbuffer_rx[i]);
srslte_softbuffer_tx_reset(&softbuffer_tx[i]);
}
}
void ue::start_pcap(srslte::mac_pcap* pcap_)
{
pcap = pcap_;
}
uint32_t ue::rl_failure()
{
nof_failures++;
return nof_failures;
}
void ue::rl_failure_reset()
{
nof_failures = 0;
}
srslte_softbuffer_rx_t* ue::get_rx_softbuffer(uint32_t tti)
{
return &softbuffer_rx[tti%NOF_HARQ_PROCESSES];
}
srslte_softbuffer_tx_t* ue::get_tx_softbuffer(uint32_t harq_process)
{
return &softbuffer_tx[harq_process%NOF_HARQ_PROCESSES];
}
uint8_t* ue::request_buffer(uint32_t tti, uint32_t len)
{
uint8_t *ret = NULL;
pthread_mutex_lock(&mutex);
if (len > 0) {
if (!pending_buffers[tti%NOF_HARQ_PROCESSES]) {
ret = pdus.request(len);
pending_buffers[tti%NOF_HARQ_PROCESSES] = ret;
} else {
log_h->console("Error requesting buffer for pid %d, not pushed yet\n", tti%NOF_HARQ_PROCESSES);
log_h->error("Requesting buffer for pid %d, not pushed yet\n", tti%NOF_HARQ_PROCESSES);
}
} else {
log_h->warning("Requesting buffer for zero bytes\n");
}
pthread_mutex_unlock(&mutex);
return ret;
}
bool ue::process_pdus()
{
return pdus.process_pdus();
}
void ue::set_tti(uint32_t tti) {
last_tti = tti;
}
#include <assert.h>
void ue::process_pdu(uint8_t* pdu, uint32_t nof_bytes, uint32_t tstamp)
{
// Unpack ULSCH MAC PDU
mac_msg_ul.init_rx(nof_bytes, true);
mac_msg_ul.parse_packet(pdu);
if (pcap) {
pcap->write_ul_crnti(pdu, nof_bytes, rnti, true, last_tti);
}
while(mac_msg_ul.next()) {
assert(mac_msg_ul.get());
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());
/* 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;
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++) {
sum += x[i];
}
if (sum == 0) {
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());
}
// 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());
// 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;
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];
}
} 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");
}
}
}
Debug("MAC PDU processed\n");
}
void ue::deallocate_pdu(uint32_t tti)
{
if (pending_buffers[tti%NOF_HARQ_PROCESSES]) {
pdus.deallocate(pending_buffers[tti%NOF_HARQ_PROCESSES]);
pending_buffers[tti%NOF_HARQ_PROCESSES] = NULL;
} else {
log_h->console("Error deallocating buffer for pid=%d. Not requested\n", tti%NOF_HARQ_PROCESSES);
}
}
void ue::push_pdu(uint32_t tti, uint32_t len)
{
if (pending_buffers[tti%NOF_HARQ_PROCESSES]) {
pdus.push(pending_buffers[tti%NOF_HARQ_PROCESSES], len);
pending_buffers[tti%NOF_HARQ_PROCESSES] = NULL;
} else {
log_h->console("Error pushing buffer for pid=%d. Not requested\n", tti%NOF_HARQ_PROCESSES);
}
}
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;
switch(subh->ce_type()) {
case srslte::sch_subh::PHR_REPORT:
phr = subh->get_phr();
Info("CE: Received PHR from rnti=0x%x, value=%.0f\n", rnti, phr);
sched->ul_phr(rnti, (int) phr);
metrics_phr(phr);
break;
case srslte::sch_subh::CRNTI:
old_rnti = subh->get_c_rnti();
Info("CE: Received C-RNTI from temp_rnti=0x%x, rnti=0x%x\n", rnti, old_rnti);
if (sched->ue_exists(old_rnti)) {
rrc->upd_user(rnti, old_rnti);
rnti = old_rnti;
} else {
Error("Updating user C-RNTI: rnti=0x%x already released\n", old_rnti);
}
break;
case srslte::sch_subh::TRUNC_BSR:
case srslte::sch_subh::SHORT_BSR:
case srslte::sch_subh::LONG_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");
}
break;
case srslte::sch_subh::PADDING:
Debug("CE: Received padding for rnti=0x%x\n", rnti);
break;
default:
Error("CE: Invalid lcid=0x%x\n", subh->ce_type());
break;
}
return true;
}
int ue::read_pdu(uint32_t lcid, uint8_t *payload, uint32_t requested_bytes)
{
return rlc->read_pdu(rnti, lcid, payload, requested_bytes);
}
void ue::allocate_sdu(srslte::sch_pdu *pdu, uint32_t lcid, uint32_t total_sdu_len)
{
int sdu_space = pdu->get_sdu_space();
if (sdu_space > 0) {
int sdu_len = SRSLTE_MIN(total_sdu_len, (uint32_t) sdu_space);
int n=1;
while(sdu_len > 0 && n > 0) {
if (pdu->new_subh()) { // there is space for a new subheader
log_h->debug("SDU: set_sdu(), lcid=%d, sdu_len=%d, sdu_space=%d\n", lcid, sdu_len, sdu_space);
n = pdu->get()->set_sdu(lcid, sdu_len, this);
if (n > 0) { // new SDU could be added
sdu_len -= n;
log_h->debug("SDU: rnti=0x%x, lcid=%d, nbytes=%d, rem_len=%d\n",
rnti, lcid, n, sdu_len);
} else {
Debug("Could not add SDU lcid=%d nbytes=%d, space=%d\n", lcid, sdu_len, sdu_space);
pdu->del_subh();
}
} else {
n=0;
}
}
}
}
void ue::allocate_ce(srslte::sch_pdu *pdu, uint32_t lcid)
{
switch((srslte::sch_subh::cetype) lcid) {
case srslte::sch_subh::CON_RES_ID:
if (pdu->new_subh()) {
if (pdu->get()->set_con_res_id(conres_id)) {
Info("CE: Added Contention Resolution ID=0x%lx\n", conres_id);
} else {
Error("CE: Setting Contention Resolution ID CE\n");
}
} else {
Error("CE: Setting Contention Resolution ID CE. No space for a subheader\n");
}
break;
default:
Error("CE: Allocating CE=0x%x. Not supported\n", lcid);
break;
}
}
uint8_t* ue::generate_pdu(sched_interface::dl_sched_pdu_t pdu[sched_interface::MAX_RLC_PDU_LIST],
uint32_t nof_pdu_elems, uint32_t grant_size)
{
uint8_t *ret = NULL;
pthread_mutex_lock(&mutex);
if (rlc)
{
mac_msg_dl.init_tx(tx_payload_buffer, grant_size, false);
for (uint32_t i=0;i<nof_pdu_elems;i++) {
if (pdu[i].lcid <= srslte::sch_subh::PHR_REPORT) {
allocate_sdu(&mac_msg_dl, pdu[i].lcid, pdu[i].nbytes);
} else {
allocate_ce(&mac_msg_dl,pdu[i].lcid);
}
}
ret = mac_msg_dl.write_packet(log_h);
} else {
std::cout << "Error ue not configured (must call config() first" << std::endl;
return NULL;
}
pthread_mutex_unlock(&mutex);
return ret;
}
/******* METRICS interface ***************/
void ue::metrics_read(mac_metrics_t* metrics_)
{
metrics.rnti = rnti;
metrics.ul_buffer = sched->get_ul_buffer(rnti);
metrics.dl_buffer = sched->get_dl_buffer(rnti);
memcpy(metrics_, &metrics, sizeof(mac_metrics_t));
phr_counter = 0;
bzero(&metrics, sizeof(mac_metrics_t));
}
void ue::metrics_phr(float phr) {
metrics.phr = SRSLTE_VEC_CMA(phr, metrics.phr, phr_counter);
phr_counter++;
}
void ue::metrics_rx(bool crc, uint32_t tbs)
{
if (crc) {
metrics.rx_brate += tbs*8;
} else {
metrics.rx_errors++;
}
metrics.rx_pkts++;
}
void ue::metrics_tx(bool crc, uint32_t tbs)
{
if (crc) {
metrics.tx_brate += tbs*8;
} else {
metrics.tx_errors++;
}
metrics.tx_pkts++;
}
}

353
srsenb/src/main.cc Normal file
View File

@ -0,0 +1,353 @@
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
#include <iostream>
#include <fstream>
#include <string>
#include <boost/program_options.hpp>
#include <boost/program_options/parsers.hpp>
#include "enb.h"
#include "metrics_stdout.h"
using namespace std;
using namespace srsenb;
namespace bpo = boost::program_options;
/**********************************************************************
* Program arguments processing
***********************************************************************/
string config_file;
void parse_args(all_args_t *args, int argc, char* argv[]) {
string enb_id;
string cell_id;
string tac;
string mcc;
string mnc;
// Command line only options
bpo::options_description general("General options");
general.add_options()
("help,h", "Produce help message")
("version,v", "Print version information and exit")
;
// Command line or config file options
bpo::options_description common("Configuration options");
common.add_options()
("enb.enb_id", bpo::value<string>(&enb_id)->default_value("0x0"), "eNodeB ID")
("enb.name", bpo::value<string>(&args->enb.s1ap.enb_name)->default_value("srsenb01"), "eNodeB Name")
("enb.cell_id", bpo::value<string>(&cell_id)->default_value("0x0"), "Cell ID")
("enb.tac", bpo::value<string>(&tac)->default_value("0x0"), "Tracking Area Code")
("enb.mcc", bpo::value<string>(&mcc)->default_value("001"), "Mobile Country Code")
("enb.mnc", bpo::value<string>(&mnc)->default_value("01"), "Mobile Network Code")
("enb.mme_addr", bpo::value<string>(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection")
("enb.gtp_bind_addr", bpo::value<string>(&args->enb.s1ap.gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection")
("enb.phy_cell_id", bpo::value<uint32_t>(&args->enb.pci)->default_value(0), "Physical Cell Identity (PCI)")
("enb.n_prb", bpo::value<uint32_t>(&args->enb.n_prb)->default_value(25), "Number of PRB")
("enb_files.sib_config", bpo::value<string>(&args->enb_files.sib_config)->default_value("sib.conf"), "SIB configuration files")
("enb_files.rr_config", bpo::value<string>(&args->enb_files.rr_config)->default_value("rr.conf"), "RR configuration files")
("enb_files.drb_config", bpo::value<string>(&args->enb_files.drb_config)->default_value("drb.conf"), "DRB configuration files")
("rf.dl_earfcn", bpo::value<uint32_t>(&args->rf.dl_earfcn)->default_value(3400), "Downlink EARFCN")
("rf.ul_earfcn", bpo::value<uint32_t>(&args->rf.ul_earfcn)->default_value(0), "Uplink EARFCN (Default based on Downlink EARFCN)")
("rf.rx_gain", bpo::value<float>(&args->rf.rx_gain)->default_value(50), "Front-end receiver gain")
("rf.tx_gain", bpo::value<float>(&args->rf.tx_gain)->default_value(70), "Front-end transmitter gain")
("rf.dl_freq", bpo::value<float>(&args->rf.dl_freq)->default_value(-1), "Downlink Frequency (if positive overrides EARFCN)")
("rf.ul_freq", bpo::value<float>(&args->rf.ul_freq)->default_value(-1), "Uplink Frequency (if positive overrides EARFCN)")
("rf.device_name", bpo::value<string>(&args->rf.device_name)->default_value("auto"), "Front-end device name")
("rf.device_args", bpo::value<string>(&args->rf.device_args)->default_value("auto"), "Front-end device arguments")
("rf.time_adv_nsamples", bpo::value<string>(&args->rf.time_adv_nsamples)->default_value("auto"), "Transmission time advance")
("rf.burst_preamble_us", bpo::value<string>(&args->rf.burst_preamble)->default_value("auto"), "Transmission time advance")
("pcap.enable", bpo::value<bool>(&args->pcap.enable)->default_value(false), "Enable MAC packet captures for wireshark")
("pcap.filename", bpo::value<string>(&args->pcap.filename)->default_value("ue.pcap"), "MAC layer capture filename")
("gui.enable", bpo::value<bool>(&args->gui.enable)->default_value(false), "Enable GUI plots")
("log.phy_level", bpo::value<string>(&args->log.phy_level), "PHY log level")
("log.phy_hex_limit", bpo::value<int>(&args->log.phy_hex_limit), "PHY log hex dump limit")
("log.mac_level", bpo::value<string>(&args->log.mac_level), "MAC log level")
("log.mac_hex_limit", bpo::value<int>(&args->log.mac_hex_limit), "MAC log hex dump limit")
("log.rlc_level", bpo::value<string>(&args->log.rlc_level), "RLC log level")
("log.rlc_hex_limit", bpo::value<int>(&args->log.rlc_hex_limit), "RLC log hex dump limit")
("log.pdcp_level", bpo::value<string>(&args->log.pdcp_level), "PDCP log level")
("log.pdcp_hex_limit",bpo::value<int>(&args->log.pdcp_hex_limit), "PDCP log hex dump limit")
("log.rrc_level", bpo::value<string>(&args->log.rrc_level), "RRC log level")
("log.rrc_hex_limit", bpo::value<int>(&args->log.rrc_hex_limit), "RRC log hex dump limit")
("log.gtpu_level", bpo::value<string>(&args->log.gtpu_level), "GTPU log level")
("log.gtpu_hex_limit",bpo::value<int>(&args->log.gtpu_hex_limit), "GTPU log hex dump limit")
("log.s1ap_level", bpo::value<string>(&args->log.s1ap_level), "S1AP log level")
("log.s1ap_hex_limit",bpo::value<int>(&args->log.s1ap_hex_limit), "S1AP log hex dump limit")
("log.all_level", bpo::value<string>(&args->log.all_level)->default_value("info"), "ALL log level")
("log.all_hex_limit", bpo::value<int>(&args->log.all_hex_limit)->default_value(32), "ALL log hex dump limit")
("log.filename", bpo::value<string>(&args->log.filename)->default_value("/tmp/ue.log"),"Log filename")
/* MCS section */
("scheduler.pdsch_mcs",
bpo::value<int>(&args->expert.mac.sched.pdsch_mcs)->default_value(-1),
"Optional fixed PDSCH MCS (ignores reported CQIs if specified)")
("scheduler.pdsch_max_mcs",
bpo::value<int>(&args->expert.mac.sched.pdsch_max_mcs)->default_value(-1),
"Optional PDSCH MCS limit")
("scheduler.pusch_mcs",
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),
"Optional PUSCH MCS limit")
("scheduler.nof_ctrl_symbols",
bpo::value<int>(&args->expert.mac.sched.nof_ctrl_symbols)->default_value(3),
"Number of control symbols")
/* Expert section */
("expert.metrics_period_secs",
bpo::value<float>(&args->expert.metrics_period_secs)->default_value(1.0),
"Periodicity for metrics in seconds")
("expert.pregenerate_signals",
bpo::value<bool>(&args->expert.phy.pregenerate_signals)->default_value(false),
"Pregenerate uplink signals after attach. Improves CPU performance.")
("expert.pusch_max_its",
bpo::value<int>(&args->expert.phy.pusch_max_its)->default_value(4),
"Maximum number of turbo decoder iterations")
("expert.tx_amplitude",
bpo::value<float>(&args->expert.phy.tx_amplitude)->default_value(0.8),
"Transmit amplitude factor")
("expert.nof_phy_threads",
bpo::value<int>(&args->expert.phy.nof_phy_threads)->default_value(2),
"Number of PHY threads")
("expert.link_failure_nof_err",
bpo::value<int>(&args->expert.mac.link_failure_nof_err)->default_value(10),
"Number of PUSCH failures after which a radio-link failure is triggered")
("expert.max_prach_offset_us",
bpo::value<float>(&args->expert.phy.max_prach_offset_us)->default_value(30),
"Maximum allowed RACH offset (in us)")
("expert.equalizer_mode",
bpo::value<string>(&args->expert.phy.equalizer_mode)->default_value("mmse"),
"Equalizer mode")
("expert.estimator_fil_w",
bpo::value<float>(&args->expert.phy.estimator_fil_w)->default_value(0.1),
"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(5000),
"Inactivity timer in ms")
("rf_calibration.tx_corr_dc_gain", bpo::value<float>(&args->rf_cal.tx_corr_dc_gain)->default_value(0.0), "TX DC offset gain correction")
("rf_calibration.tx_corr_dc_phase", bpo::value<float>(&args->rf_cal.tx_corr_dc_phase)->default_value(0.0), "TX DC offset phase correction")
("rf_calibration.tx_corr_iq_i", bpo::value<float>(&args->rf_cal.tx_corr_iq_i)->default_value(0.0), "TX IQ imbalance inphase correction")
("rf_calibration.tx_corr_iq_q", bpo::value<float>(&args->rf_cal.tx_corr_iq_q)->default_value(0.0), "TX IQ imbalance quadrature correction")
;
// Positional options - config file location
bpo::options_description position("Positional options");
position.add_options()
("config_file", bpo::value< string >(&config_file), "eNodeB configuration file")
;
bpo::positional_options_description p;
p.add("config_file", -1);
// these options are allowed on the command line
bpo::options_description cmdline_options;
cmdline_options.add(common).add(position).add(general);
// parse the command line and store result in vm
bpo::variables_map vm;
bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).positional(p).run(), vm);
bpo::notify(vm);
// help option was given - print usage and exit
if (vm.count("help")) {
cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl;
cout << common << endl << general << endl;
exit(0);
}
// print version number and exit
// print version number and exit
if (vm.count("version")) {
cout << "Version " <<
srslte_get_version_major() << "." <<
srslte_get_version_minor() << "." <<
srslte_get_version_patch() << endl;
exit(0);
}
// no config file given - print usage and exit
if (!vm.count("config_file")) {
cout << "Error: Configuration file not provided" << endl;
cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl;
exit(0);
} else {
cout << "Reading configuration file " << config_file << "..." << endl;
ifstream conf(config_file.c_str(), ios::in);
if(conf.fail()) {
cout << "Failed to read configuration file " << config_file << " - exiting" << endl;
exit(1);
}
bpo::store(bpo::parse_config_file(conf, common), vm);
bpo::notify(vm);
}
// Convert hex strings
{
std::stringstream sstr;
sstr << std::hex << vm["enb.enb_id"].as<std::string>();
sstr >> args->enb.s1ap.enb_id;
}
{
std::stringstream sstr;
sstr << std::hex << vm["enb.cell_id"].as<std::string>();
uint16_t tmp; // Need intermediate uint16_t as uint8_t is treated as char
sstr >> tmp;
args->enb.s1ap.cell_id = tmp;
}
{
std::stringstream sstr;
sstr << std::hex << vm["enb.tac"].as<std::string>();
sstr >> args->enb.s1ap.tac;
}
// Convert MCC/MNC strings
if(!srslte::string_to_mcc(mcc, &args->enb.s1ap.mcc)) {
cout << "Error parsing enb.mcc:" << mcc << " - must be a 3-digit string." << endl;
}
if(!srslte::string_to_mnc(mnc, &args->enb.s1ap.mnc)) {
cout << "Error parsing enb.mnc:" << mnc << " - must be a 2 or 3-digit string." << endl;
}
// Apply all_level to any unset layers
if (vm.count("log.all_level")) {
if(!vm.count("log.phy_level")) {
args->log.phy_level = args->log.all_level;
}
if(!vm.count("log.mac_level")) {
args->log.mac_level = args->log.all_level;
}
if(!vm.count("log.rlc_level")) {
args->log.rlc_level = args->log.all_level;
}
if(!vm.count("log.pdcp_level")) {
args->log.pdcp_level = args->log.all_level;
}
if(!vm.count("log.rrc_level")) {
args->log.rrc_level = args->log.all_level;
}
if(!vm.count("log.gtpu_level")) {
args->log.gtpu_level = args->log.all_level;
}
if(!vm.count("log.s1ap_level")) {
args->log.s1ap_level = args->log.all_level;
}
}
// Apply all_hex_limit to any unset layers
if (vm.count("log.all_hex_limit")) {
if(!vm.count("log.phy_hex_limit")) {
args->log.phy_hex_limit = args->log.all_hex_limit;
}
if(!vm.count("log.mac_hex_limit")) {
args->log.mac_hex_limit = args->log.all_hex_limit;
}
if(!vm.count("log.rlc_hex_limit")) {
args->log.rlc_hex_limit = args->log.all_hex_limit;
}
if(!vm.count("log.pdcp_hex_limit")) {
args->log.pdcp_hex_limit = args->log.all_hex_limit;
}
if(!vm.count("log.rrc_hex_limit")) {
args->log.rrc_hex_limit = args->log.all_hex_limit;
}
if(!vm.count("log.gtpu_hex_limit")) {
args->log.gtpu_hex_limit = args->log.all_hex_limit;
}
if(!vm.count("log.s1ap_hex_limit")) {
args->log.s1ap_hex_limit = args->log.all_hex_limit;
}
}
}
static bool running = true;
static bool do_metrics = false;
void sig_int_handler(int signo)
{
running = false;
}
void *input_loop(void *m)
{
metrics_stdout *metrics = (metrics_stdout*) m;
char key;
while(running) {
cin >> key;
if('t' == key) {
do_metrics = !do_metrics;
if(do_metrics) {
cout << "Enter t to stop trace." << endl;
} else {
cout << "Enter t to restart trace." << endl;
}
metrics->toggle_print(do_metrics);
}
}
return NULL;
}
int main(int argc, char *argv[])
{
signal(SIGINT, sig_int_handler);
all_args_t args;
metrics_stdout metrics;
enb *enb = enb::get_instance();
cout << "--- Software Radio Systems LTE eNodeB ---" << endl << endl;
parse_args(&args, argc, argv);
if(!enb->init(&args)) {
exit(1);
}
metrics.init(enb, args.expert.metrics_period_secs);
pthread_t input;
pthread_create(&input, NULL, &input_loop, &metrics);
bool plot_started = false;
bool signals_pregenerated = false;
while(running) {
if (!plot_started && args.gui.enable) {
enb->start_plot();
plot_started = true;
}
sleep(1);
}
pthread_cancel(input);
metrics.stop();
enb->stop();
cout << "--- exiting ---" << endl;
exit(0);
}

View File

@ -0,0 +1,201 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2015 The srsUE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \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/.
*
*/
#include "metrics_stdout.h"
#include <unistd.h>
#include <sstream>
#include <stdlib.h>
#include <math.h>
#include <float.h>
#include <iomanip>
#include <iostream>
#include <stdio.h>
using namespace std;
namespace srsenb{
char const * const prefixes[2][9] =
{
{ "", "m", "u", "n", "p", "f", "a", "z", "y", },
{ "", "k", "M", "G", "T", "P", "E", "Z", "Y", },
};
metrics_stdout::metrics_stdout()
:started(false)
,do_print(false)
,n_reports(10)
{
}
bool metrics_stdout::init(enb_metrics_interface *u, float report_period_secs)
{
enb_ = u;
metrics_report_period = report_period_secs;
started = true;
pthread_create(&metrics_thread, NULL, &metrics_thread_start, this);
return true;
}
void metrics_stdout::stop()
{
if(started)
{
started = false;
pthread_join(metrics_thread, NULL);
}
}
void metrics_stdout::toggle_print(bool b)
{
do_print = b;
}
void* metrics_stdout::metrics_thread_start(void *m_)
{
metrics_stdout *m = (metrics_stdout*)m_;
m->metrics_thread_run();
return NULL;
}
void metrics_stdout::metrics_thread_run()
{
while(started)
{
usleep(metrics_report_period*1e6);
if(enb_->get_metrics(metrics)) {
if (metrics.rrc.n_ues > 0) {
print_metrics();
}
} else {
print_disconnect();
}
}
}
void metrics_stdout::print_metrics()
{
if(!do_print)
return;
if(++n_reports > 10)
{
n_reports = 0;
cout << endl;
cout << "------DL-------------------UL----------------" << endl;
cout << "rnti mcs brate bler snr phr turbo mcs brate bler" << endl;
}
if (metrics.rrc.n_ues > 0) {
for (int i=0;i<metrics.rrc.n_ues;i++) {
if (metrics.mac[i].tx_errors > metrics.mac[i].tx_pkts) {
printf("tx caution errors %d > %d\n", metrics.mac[i].tx_errors, metrics.mac[i].tx_pkts);
}
if (metrics.mac[i].rx_errors > metrics.mac[i].rx_pkts) {
printf("rx caution errors %d > %d\n", metrics.mac[i].rx_errors, metrics.mac[i].rx_pkts);
}
cout << std::hex << metrics.mac[i].rnti << " ";
cout << float_to_string(metrics.phy[i].dl.mcs, 2);
if (metrics.mac[i].tx_brate > 0 && metrics_report_period) {
cout << float_to_eng_string((float) metrics.mac[i].tx_brate/metrics_report_period, 2);
} else {
cout << float_to_string(0, 2);
}
if (metrics.mac[i].tx_pkts > 0 && metrics.mac[i].tx_errors) {
cout << float_to_string((float) 100*metrics.mac[i].tx_errors/metrics.mac[i].tx_pkts, 2) << "%";
} else {
cout << float_to_string(0, 2) << "%";
}
cout << float_to_string(metrics.phy[i].ul.sinr, 2);
cout << float_to_string(metrics.mac[i].phr, 2);
cout << float_to_string(metrics.phy[i].ul.turbo_iters, 2);
cout << float_to_string(metrics.phy[i].ul.mcs, 2);
if (metrics.mac[i].rx_brate > 0 && metrics_report_period) {
cout << float_to_eng_string((float) metrics.mac[i].rx_brate/metrics_report_period, 2);
} else {
cout << float_to_string(0, 2);
}
if (metrics.mac[i].rx_pkts > 0 && metrics.mac[i].rx_errors > 0) {
cout << float_to_string((float) 100*metrics.mac[i].rx_errors/metrics.mac[i].rx_pkts, 2) << "%";
} else {
cout << float_to_string(0, 2) << "%";
}
cout << endl;
}
} else {
cout << "--- No users ---" << endl;
}
if(metrics.rf.rf_error) {
printf("RF status: O=%d, U=%d, L=%d\n", metrics.rf.rf_o, metrics.rf.rf_u, metrics.rf.rf_l);
}
}
void metrics_stdout::print_disconnect()
{
if(do_print) {
cout << "--- disconnected ---" << endl;
}
}
std::string metrics_stdout::float_to_string(float f, int digits)
{
std::ostringstream os;
const int precision = (f == 0.0) ? digits-1 : digits - log10(fabs(f))-2*DBL_EPSILON;
os << std::setw(6) << std::fixed << std::setprecision(precision) << f;
return os.str();
}
std::string metrics_stdout::float_to_eng_string(float f, int digits)
{
const int degree = (f == 0.0) ? 0 : lrint( floor( log10( fabs( f ) ) / 3) );
std::string factor;
if ( abs( degree ) < 9 )
{
if(degree < 0)
factor = prefixes[0][ abs( degree ) ];
else
factor = prefixes[1][ abs( degree ) ];
} else {
return "failed";
}
const double scaled = f * pow( 1000.0, -degree );
if (degree != 0) {
return float_to_string(scaled, digits) + factor;
} else {
return " " + float_to_string(scaled, digits) + factor;
}
}
} // namespace srsue

120
srsenb/src/parser.cc Normal file
View File

@ -0,0 +1,120 @@
#include "parser.h"
#include <iostream>
namespace srsenb {
using namespace libconfig;
int parser::parse_section(std::string filename, parser::section *s)
{
parser p(filename);
p.add_section(s);
return p.parse();
}
parser::parser(std::string filename_)
{
filename = filename_;
}
void parser::add_section(parser::section* s)
{
sections.push_back(s);
}
int parser::parse()
{
// open file
Config cfg;
try
{
cfg.readFile(filename.c_str());
}
catch(const FileIOException &fioex)
{
std::cerr << "I/O error while reading file: " << filename << std::endl;
return(-1);
}
catch(const ParseException &pex)
{
std::cerr << "Parse error at " << pex.getFile() << ":" << pex.getLine()
<< " - " << pex.getError() << std::endl;
return(-1);
}
for (std::list<section*>::iterator ci = sections.begin(); ci != sections.end(); ++ci) {
section *s = *ci;
if (s->parse(cfg.getRoot())) {
return -1;
}
}
return 0;
}
parser::section::section(std::string name_)
{
name = name_;
enabled_value = NULL;
}
// Fields are allocated dynamically, free all fields added to the section
parser::section::~section()
{
for (std::list<field_itf*>::iterator ci = fields.begin(); ci != fields.end(); ++ci) {
delete *ci;
}
}
void parser::section::add_field(field_itf* f)
{
fields.push_back(f);
}
void parser::section::add_subsection(parser::section* s)
{
sub_sections.push_back(s);
}
void parser::section::set_optional(bool* enabled_value_)
{
enabled_value = enabled_value_;
}
int parser::section::parse(Setting &root)
{
try {
for (std::list<field_itf*>::iterator ci = fields.begin(); ci != fields.end(); ++ci) {
field_itf *f = *ci;
if (f->parse(root[name.c_str()])) {
fprintf(stderr, "Error parsing field %s in section %s\n", f->get_name(), name.c_str());
return -1;
}
}
for (std::list<section*>::iterator ci = sub_sections.begin(); ci != sub_sections.end(); ++ci) {
section *s = *ci;
if (s->parse(root[name.c_str()])) {
fprintf(stderr, "Error parsing section %s\n", name.c_str());
return -1;
}
}
if (enabled_value) {
*enabled_value = true;
}
} catch(const SettingNotFoundException ex) {
if (enabled_value) {
*enabled_value = false;
return 0;
} else {
std::cerr << "Error section " << name.c_str() << " not found." << std::endl;
return -1;
}
}
return 0;
}
}

View File

@ -0,0 +1,8 @@
file(GLOB SOURCES "*.cc")
add_library(srsenb_phy SHARED ${SOURCES})
target_link_libraries(srsenb_phy ${SRSLTE_LIBRARIES} )
if(ENABLE_GUI AND SRSGUI_FOUND)
target_link_libraries(srsenb_phy ${SRSGUI_LIBRARIES})
endif(ENABLE_GUI AND SRSGUI_FOUND)

View File

@ -0,0 +1,115 @@
#include "srslte/common/threads.h"
#include "srslte/common/log.h"
#include "phy/txrx.h"
#include <assert.h>
#include <string.h>
#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
using namespace std;
namespace srsenb {
void phch_common::set_nof_mutex(uint32_t nof_mutex_) {
nof_mutex = nof_mutex_;
assert(nof_mutex <= max_mutex);
}
void phch_common::reset() {
bzero(ul_grants, sizeof(mac_interface_phy::ul_sched_t)*10);
bzero(dl_grants, sizeof(mac_interface_phy::dl_sched_t)*10);
}
bool phch_common::init(srslte_cell_t *cell_, srslte::radio* radio_h_, mac_interface_phy *mac_)
{
radio = radio_h_;
mac = mac_;
memcpy(&cell, cell_, sizeof(srslte_cell_t));
is_first_of_burst = true;
is_first_tx = true;
for (uint32_t i=0;i<nof_mutex;i++) {
pthread_mutex_init(&tx_mutex[i], NULL);
}
reset();
return true;
}
void phch_common::stop() {
for (uint32_t i=0;i<nof_mutex;i++) {
pthread_mutex_trylock(&tx_mutex[i]);
pthread_mutex_unlock(&tx_mutex[i]);
}
}
void phch_common::worker_end(uint32_t tx_mutex_cnt, cf_t* buffer, uint32_t nof_samples, srslte_timestamp_t tx_time)
{
// Wait previous TTIs to be transmitted
if (is_first_tx) {
is_first_tx = false;
} else {
pthread_mutex_lock(&tx_mutex[tx_mutex_cnt%nof_mutex]);
}
radio->set_tti(tx_mutex_cnt);
radio->tx(buffer, nof_samples, tx_time);
// Trigger next transmission
pthread_mutex_unlock(&tx_mutex[(tx_mutex_cnt+1)%nof_mutex]);
// Trigger MAC clock
mac->tti_clock();
}
void phch_common::ack_clear(uint32_t sf_idx)
{
for(std::map<uint16_t,pending_ack_t>::iterator iter=pending_ack.begin(); iter!=pending_ack.end(); ++iter) {
pending_ack_t *p = (pending_ack_t*) &iter->second;
p->is_pending[sf_idx] = false;
}
}
void phch_common::ack_add_rnti(uint16_t rnti)
{
for (int sf_idx=0;sf_idx<10;sf_idx++) {
pending_ack[rnti].is_pending[sf_idx] = false;
}
}
void phch_common::ack_rem_rnti(uint16_t rnti)
{
pending_ack.erase(rnti);
}
void phch_common::ack_set_pending(uint32_t sf_idx, uint16_t rnti, uint32_t last_n_pdcch)
{
if (pending_ack.count(rnti)) {
pending_ack[rnti].is_pending[sf_idx] = true;
pending_ack[rnti].n_pdcch[sf_idx] = last_n_pdcch;
}
}
bool phch_common::ack_is_pending(uint32_t sf_idx, uint16_t rnti, uint32_t *last_n_pdcch)
{
if (pending_ack.count(rnti)) {
bool ret = pending_ack[rnti].is_pending[sf_idx];
pending_ack[rnti].is_pending[sf_idx] = false;
if (ret && last_n_pdcch) {
*last_n_pdcch = pending_ack[rnti].n_pdcch[sf_idx];
}
return ret;
} else {
return false;
}
}
}

View File

@ -0,0 +1,796 @@
#include <assert.h>
#include "srslte/common/threads.h"
#include "srslte/common/log.h"
#include "phy/phch_worker.h"
#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
using namespace std;
// Enable this to log SI
//#define LOG_THIS(a) 1
// Enable this one to skip SI-RNTI
#define LOG_THIS(rnti) (rnti != 0xFFFF)
/* Define GUI-related things */
#ifdef ENABLE_GUI
#include "srsgui/srsgui.h"
#include <semaphore.h>
void init_plots(srsenb::phch_worker *worker);
pthread_t plot_thread;
sem_t plot_sem;
static int plot_worker_id = -1;
#else
#warning Compiling without srsGUI support
#endif
/*********************************************/
//#define DEBUG_WRITE_FILE
namespace srsenb {
phch_worker::phch_worker()
{
phy = NULL;
reset();
}
#ifdef DEBUG_WRITE_FILE
FILE *f;
#endif
void phch_worker::init(phch_common* phy_, srslte::log *log_h_)
{
phy = phy_;
log_h = log_h_;
pthread_mutex_init(&mutex, NULL);
// Init cell here
signal_buffer_rx = (cf_t*) srslte_vec_malloc(2*SRSLTE_SF_LEN_PRB(phy->cell.nof_prb)*sizeof(cf_t));
if (!signal_buffer_rx) {
fprintf(stderr, "Error allocating memory\n");
return;
}
signal_buffer_tx = (cf_t*) srslte_vec_malloc(2*SRSLTE_SF_LEN_PRB(phy->cell.nof_prb)*sizeof(cf_t));
if (!signal_buffer_tx) {
fprintf(stderr, "Error allocating memory\n");
return;
}
if (srslte_enb_dl_init(&enb_dl, phy->cell)) {
fprintf(stderr, "Error initiating ENB DL\n");
return;
}
if (srslte_enb_ul_init(&enb_ul,
phy->cell,
NULL,
&phy->pusch_cfg,
&phy->hopping_cfg,
&phy->pucch_cfg))
{
fprintf(stderr, "Error initiating ENB DL\n");
return;
}
srslte_pucch_set_threshold(&enb_ul.pucch, 0.8, 0.5);
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);
Info("Worker %d configured cell %d PRB\n", get_id(), phy->cell.nof_prb);
initiated = true;
#ifdef DEBUG_WRITE_FILE
f = fopen("test.dat", "w");
#endif
}
void phch_worker::reset()
{
initiated = false;
ue_db.clear();
}
cf_t* phch_worker::get_buffer_rx()
{
return signal_buffer_rx;
}
void phch_worker::set_time(uint32_t tti_, uint32_t tx_mutex_cnt_, srslte_timestamp_t tx_time_)
{
tti_rx = tti_;
tti_tx = (tti_ + 4)%10240;
tti_sched_ul = (tti_ + 8)%10240;
sf_rx = tti_rx%10;
sf_tx = tti_tx%10;
sf_sched_ul = tti_sched_ul%10;
tx_mutex_cnt = tx_mutex_cnt_;
memcpy(&tx_time, &tx_time_, sizeof(srslte_timestamp_t));
}
int phch_worker::add_rnti(uint16_t rnti)
{
if (srslte_enb_dl_add_rnti(&enb_dl, rnti)) {
return -1;
}
if (srslte_enb_ul_add_rnti(&enb_ul, rnti)) {
return -1;
}
// Create user
ue_db[rnti].rnti = rnti;
return SRSLTE_SUCCESS;
}
uint32_t phch_worker::get_nof_rnti() {
return ue_db.size();
}
void phch_worker::set_config_dedicated(uint16_t rnti,
srslte_uci_cfg_t *uci_cfg,
srslte_pucch_sched_t *pucch_sched,
srslte_refsignal_srs_cfg_t *srs_cfg,
uint32_t I_sr, bool pucch_cqi, uint32_t pmi_idx, bool pucch_cqi_ack)
{
pthread_mutex_lock(&mutex);
if (ue_db.count(rnti)) {
pucch_sched->N_pucch_1 = phy->pucch_cfg.n1_pucch_an;
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;
if (pucch_cqi) {
ue_db[rnti].pmi_idx = pmi_idx;
ue_db[rnti].cqi_en = true;
ue_db[rnti].pucch_cqi_ack = pucch_cqi_ack;
} else {
ue_db[rnti].pmi_idx = 0;
ue_db[rnti].cqi_en = false;
}
} else {
Error("Setting config dedicated: rnti=0x%x does not exist\n");
}
pthread_mutex_unlock(&mutex);
}
void phch_worker::rem_rnti(uint16_t rnti)
{
pthread_mutex_lock(&mutex);
if (ue_db.count(rnti)) {
ue_db.erase(rnti);
srslte_enb_dl_rem_rnti(&enb_dl, rnti);
srslte_enb_ul_rem_rnti(&enb_ul, rnti);
// remove any pending grant for each subframe
for (uint32_t i=0;i<10;i++) {
for (uint32_t j=0;j<phy->ul_grants[i].nof_grants;j++) {
if (phy->ul_grants[i].sched_grants[j].rnti == rnti) {
phy->ul_grants[i].sched_grants[j].rnti = 0;
}
}
for (uint32_t j=0;j<phy->dl_grants[i].nof_grants;j++) {
if (phy->dl_grants[i].sched_grants[j].rnti == rnti) {
phy->dl_grants[i].sched_grants[j].rnti = 0;
}
}
}
} else {
Error("Removing user: rnti=0x%x does not exist\n", rnti);
}
pthread_mutex_unlock(&mutex);
}
void phch_worker::work_imp()
{
uint32_t sf_ack;
pthread_mutex_lock(&mutex);
mac_interface_phy::ul_sched_t *ul_grants = phy->ul_grants;
mac_interface_phy::dl_sched_t *dl_grants = phy->dl_grants;
mac_interface_phy *mac = phy->mac;
log_h->step(tti_rx);
Debug("Worker %d running\n", get_id());
for(std::map<uint16_t, ue>::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) {
uint16_t rnti = (uint16_t) iter->first;
ue_db[rnti].has_grant_tti = -1;
}
// Process UL signal
srslte_enb_ul_fft(&enb_ul, signal_buffer_rx);
// Decode pending UL grants for the tti they were scheduled
decode_pusch(ul_grants[sf_rx].sched_grants, ul_grants[sf_rx].nof_grants, sf_rx);
// Decode remaining PUCCH ACKs not associated with PUSCH transmission and SR signals
decode_pucch(tti_rx);
// Get DL scheduling for the TX TTI from MAC
if (mac->get_dl_sched(tti_tx, &dl_grants[sf_tx]) < 0) {
Error("Getting DL scheduling from MAC\n");
goto unlock;
}
if (dl_grants[sf_tx].cfi < 1 || dl_grants[sf_tx].cfi > 3) {
Error("Invalid CFI=%d\n", dl_grants[sf_tx].cfi);
goto unlock;
}
// Get UL scheduling for the TX TTI from MAC
if (mac->get_ul_sched(tti_sched_ul, &ul_grants[sf_sched_ul]) < 0) {
Error("Getting UL scheduling from MAC\n");
goto unlock;
}
// Put base signals (references, PBCH, PCFICH and PSS/SSS) into the resource grid
srslte_enb_dl_clear_sf(&enb_dl);
srslte_enb_dl_set_cfi(&enb_dl, dl_grants[sf_tx].cfi);
srslte_enb_dl_put_base(&enb_dl, tti_tx);
// Put UL/DL grants to resource grid. PDSCH data will be encoded as well.
encode_pdcch_dl(dl_grants[sf_tx].sched_grants, dl_grants[sf_tx].nof_grants, sf_tx);
encode_pdcch_ul(ul_grants[sf_sched_ul].sched_grants, ul_grants[sf_sched_ul].nof_grants, sf_tx);
encode_pdsch(dl_grants[sf_tx].sched_grants, dl_grants[sf_tx].nof_grants, sf_tx);
// Put pending PHICH HARQ ACK/NACK indications into subframe
encode_phich(ul_grants[sf_sched_ul].phich, ul_grants[sf_sched_ul].nof_phich, sf_tx);
// Prepare for receive ACK for DL grants in sf_tx+4
sf_ack = (sf_tx+4)%10;
phy->ack_clear(sf_ack);
for (uint32_t i=0;i<dl_grants[sf_tx].nof_grants;i++) {
// SI-RNTI and RAR-RNTI do not have ACK
if (dl_grants[sf_tx].sched_grants[i].rnti >= SRSLTE_CRNTI_START && dl_grants[sf_tx].sched_grants[i].rnti <= SRSLTE_CRNTI_END) {
phy->ack_set_pending(sf_ack, dl_grants[sf_tx].sched_grants[i].rnti, dl_grants[sf_tx].sched_grants[i].location.ncce);
}
}
// Generate signal and transmit
srslte_enb_dl_gen_signal(&enb_dl, signal_buffer_tx);
Debug("Sending to radio\n");
phy->worker_end(tx_mutex_cnt, signal_buffer_tx, SRSLTE_SF_LEN_PRB(phy->cell.nof_prb), tx_time);
#ifdef DEBUG_WRITE_FILE
fwrite(signal_buffer_tx, SRSLTE_SF_LEN_PRB(phy->cell.nof_prb)*sizeof(cf_t), 1, f);
#endif
#ifdef DEBUG_WRITE_FILE
if (tti_tx == 10) {
fclose(f);
exit(-1);
}
#endif
/* Tell the plotting thread to draw the plots */
#ifdef ENABLE_GUI
if ((int) get_id() == plot_worker_id) {
sem_post(&plot_sem);
}
#endif
unlock:
pthread_mutex_unlock(&mutex);
}
int phch_worker::decode_pusch(srslte_enb_ul_pusch_t *grants, uint32_t nof_pusch, uint32_t tti)
{
srslte_uci_data_t uci_data;
bzero(&uci_data, sizeof(srslte_uci_data_t));
uint32_t wideband_cqi_value = 0;
uint32_t n_rb_ho = 0;
for (uint32_t i=0;i<nof_pusch;i++) {
uint16_t rnti = grants[i].rnti;
if (rnti) {
#ifdef LOG_EXECTIME
char timestr[64];
struct timeval t[3];
gettimeofday(&t[1], NULL);
#endif
// Get pending ACKs with an associated PUSCH transmission
if (phy->ack_is_pending(sf_rx, rnti)) {
uci_data.uci_ack_len = 1;
}
// Configure PUSCH CQI channel
srslte_cqi_value_t cqi_value;
bool cqi_enabled = false;
if (ue_db[rnti].cqi_en && srslte_cqi_send(ue_db[rnti].pmi_idx, tti_rx)) {
cqi_value.type = SRSLTE_CQI_TYPE_WIDEBAND;
cqi_enabled = true;
} else if (grants[i].grant.cqi_request) {
cqi_value.type = SRSLTE_CQI_TYPE_SUBBAND_HL;
cqi_value.subband_hl.N = (phy->cell.nof_prb > 7) ? srslte_cqi_hl_get_no_subbands(phy->cell.nof_prb) : 0;
cqi_enabled = true;
}
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
ue_db[rnti].has_grant_tti = tti_rx;
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,
rnti, grants[i].rv_idx,
grants[i].current_tx_nb,
grants[i].data,
&uci_data,
tti);
} else {
Error("Computing PUSCH grant\n");
return SRSLTE_ERROR;
}
#ifdef LOG_EXECTIME
gettimeofday(&t[2], NULL);
get_time_interval(t);
snprintf(timestr, 64, ", dec_time=%4d us", (int) t[0].tv_usec);
#endif
bool crc_res = (res == 0);
// Save PHICH scheduling for this user. Each user can have just 1 PUSCH grant per TTI
ue_db[rnti].phich_info.n_prb_lowest = enb_ul.pusch_cfg.grant.n_prb_tilde[0];
ue_db[rnti].phich_info.n_dmrs = phy_grant.ncs_dmrs;
char cqi_str[64];
if (cqi_enabled) {
srslte_cqi_value_unpack(uci_data.uci_cqi, &cqi_value);
if (ue_db[rnti].cqi_en) {
wideband_cqi_value = cqi_value.wideband.wideband_cqi;
} else if (grants[i].grant.cqi_request) {
wideband_cqi_value = cqi_value.subband_hl.wideband_cqi;
}
snprintf(cqi_str, 64, ", cqi=%d", wideband_cqi_value);
}
float snr_db = 10*log10(srslte_chest_ul_get_snr(&enb_ul.chest));
/*
if (!crc_res && enb_ul.pusch_cfg.grant.L_prb == 1 && enb_ul.pusch_cfg.grant.n_prb[0] == 0 && snr_db > 5) {
srslte_vec_save_file("sf_symbols", enb_ul.sf_symbols, sizeof(cf_t)*SRSLTE_SF_LEN_RE(25, SRSLTE_CP_NORM));
srslte_vec_save_file("ce", enb_ul.ce, sizeof(cf_t)*SRSLTE_SF_LEN_RE(25, SRSLTE_CP_NORM));
srslte_vec_save_file("d", enb_ul.pusch.d, sizeof(cf_t)*enb_ul.pusch_cfg.nbits.nof_re);
srslte_vec_save_file("ce2", enb_ul.pusch.ce, sizeof(cf_t)*enb_ul.pusch_cfg.nbits.nof_re);
srslte_vec_save_file("z", enb_ul.pusch.z, sizeof(cf_t)*enb_ul.pusch_cfg.nbits.nof_re);
printf("saved sf_idx=%d, mcs=%d, tbs=%d, rnti=%d, rv=%d, snr=%.1f\n", tti%10,
grants[i].grant.mcs_idx, enb_ul.pusch_cfg.cb_segm.tbs, rnti, grants[i].rv_idx, snr_db);
exit(-1);
}
*/
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",
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,
srslte_pusch_last_noi(&enb_ul.pusch),
crc_res?"OK":"KO",
uci_data.uci_ack_len>0?(uci_data.uci_ack?", ack=1":", ack=0"):"",
uci_data.uci_cqi_len>0?cqi_str:"",
timestr);
// Notify MAC of RL status
if (grants[i].grant.rv_idx == 0) {
if (res && snr_db < PUSCH_RL_SNR_DB_TH) {
Debug("PUSCH: Radio-Link failure snr=%.1f dB\n", snr_db);
phy->mac->rl_failure(rnti);
} else {
phy->mac->rl_ok(rnti);
}
}
// Notify MAC new received data and HARQ Indication value
phy->mac->crc_info(tti_rx, rnti, phy_grant.mcs.tbs/8, crc_res);
if (uci_data.uci_ack_len) {
phy->mac->ack_info(tti_rx, rnti, uci_data.uci_ack && (crc_res || snr_db > PUSCH_RL_SNR_DB_TH));
}
// Notify MAC of UL SNR and DL CQI
if (snr_db >= PUSCH_RL_SNR_DB_TH) {
phy->mac->snr_info(tti_rx, rnti, snr_db);
}
if (uci_data.uci_cqi_len>0 && crc_res) {
phy->mac->cqi_info(tti_rx, rnti, wideband_cqi_value);
}
// Save metrics stats
ue_db[rnti].metrics_ul(phy_grant.mcs.idx, 0, snr_db, srslte_pusch_last_noi(&enb_ul.pusch));
}
}
return SRSLTE_SUCCESS;
}
int phch_worker::decode_pucch(uint32_t tti_rx)
{
uint32_t sf_rx = tti_rx%10;
srslte_uci_data_t uci_data;
for(std::map<uint16_t, ue>::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) {
uint16_t rnti = (uint16_t) iter->first;
if (rnti >= SRSLTE_CRNTI_START && rnti <= SRSLTE_CRNTI_END && ue_db[rnti].has_grant_tti != (int) tti_rx) {
// Check if user needs to receive PUCCH
bool needs_pucch = false, needs_ack=false, needs_sr=false, needs_cqi=false;
uint32_t last_n_pdcch = 0;
bzero(&uci_data, sizeof(srslte_uci_data_t));
if (ue_db[rnti].I_sr_en) {
if (srslte_ue_ul_sr_send_tti(ue_db[rnti].I_sr, tti_rx)) {
needs_pucch = true;
needs_sr = true;
uci_data.scheduling_request = true;
}
}
if (phy->ack_is_pending(sf_rx, rnti, &last_n_pdcch)) {
needs_pucch = true;
needs_ack = true;
uci_data.uci_ack_len = 1;
}
srslte_cqi_value_t cqi_value;
if (ue_db[rnti].cqi_en && (ue_db[rnti].pucch_cqi_ack || !needs_ack)) {
if (srslte_cqi_send(ue_db[rnti].pmi_idx, tti_rx)) {
needs_pucch = true;
needs_cqi = true;
cqi_value.type = SRSLTE_CQI_TYPE_WIDEBAND;
uci_data.uci_cqi_len = srslte_cqi_size(&cqi_value);
}
}
if (needs_pucch) {
if (srslte_enb_ul_get_pucch(&enb_ul, rnti, last_n_pdcch, sf_rx, &uci_data)) {
fprintf(stderr, "Error getting PUCCH\n");
return SRSLTE_ERROR;
}
if (uci_data.uci_ack_len > 0) {
phy->mac->ack_info(tti_rx, rnti, uci_data.uci_ack && (srslte_pucch_get_last_corr(&enb_ul.pucch) >= PUCCH_RL_CORR_TH));
}
if (uci_data.scheduling_request) {
phy->mac->sr_detected(tti_rx, rnti);
}
char cqi_str[64];
if (uci_data.uci_cqi_len) {
srslte_cqi_value_unpack(uci_data.uci_cqi, &cqi_value);
phy->mac->cqi_info(tti_rx, rnti, cqi_value.wideband.wideband_cqi);
sprintf(cqi_str, ", cqi=%d", cqi_value.wideband.wideband_cqi);
}
log_h->info("PUCCH: rnti=0x%x, corr=%.2f, n_pucch=%d, n_prb=%d%s%s%s\n",
rnti,
srslte_pucch_get_last_corr(&enb_ul.pucch),
enb_ul.pucch.last_n_pucch, enb_ul.pucch.last_n_prb,
needs_ack?(uci_data.uci_ack?", ack=1":", ack=0"):"",
needs_sr?(uci_data.scheduling_request?", sr=yes":", sr=no"):"",
needs_cqi?cqi_str:"");
// Notify MAC of RL status
if (!needs_sr) {
if (srslte_pucch_get_last_corr(&enb_ul.pucch) < PUCCH_RL_CORR_TH) {
Debug("PUCCH: Radio-Link failure corr=%.1f\n", srslte_pucch_get_last_corr(&enb_ul.pucch));
phy->mac->rl_failure(rnti);
} else {
phy->mac->rl_ok(rnti);
}
}
}
}
}
return 0;
}
int phch_worker::encode_phich(srslte_enb_dl_phich_t *acks, uint32_t nof_acks, uint32_t sf_idx)
{
for (uint32_t i=0;i<nof_acks;i++) {
uint16_t rnti = acks[i].rnti;
if (rnti) {
srslte_enb_dl_put_phich(&enb_dl, acks[i].ack,
ue_db[rnti].phich_info.n_prb_lowest,
ue_db[rnti].phich_info.n_dmrs,
sf_idx);
Info("PHICH: rnti=0x%x, hi=%d, I_lowest=%d, n_dmrs=%d, tti_tx=%d\n",
rnti, acks[i].ack,
ue_db[rnti].phich_info.n_prb_lowest,
ue_db[rnti].phich_info.n_dmrs, tti_tx);
}
}
return SRSLTE_SUCCESS;
}
int phch_worker::encode_pdcch_ul(srslte_enb_ul_pusch_t *grants, uint32_t nof_grants, uint32_t sf_idx)
{
for (uint32_t i=0;i<nof_grants;i++) {
uint16_t rnti = grants[i].rnti;
if (grants[i].needs_pdcch && rnti) {
if (srslte_enb_dl_put_pdcch_ul(&enb_dl, &grants[i].grant, grants[i].location, rnti, sf_idx)) {
fprintf(stderr, "Error putting PUSCH %d\n",i);
return SRSLTE_ERROR;
}
Info("PDCCH: UL DCI Format0 rnti=0x%x, cce_index=%d, L=%d, tti_tx=%d\n",
rnti, grants[i].location.ncce, (1<<grants[i].location.L), tti_tx);
}
}
return SRSLTE_SUCCESS;
}
int phch_worker::encode_pdcch_dl(srslte_enb_dl_pdsch_t *grants, uint32_t nof_grants, uint32_t sf_idx)
{
for (uint32_t i=0;i<nof_grants;i++) {
uint16_t rnti = grants[i].rnti;
if (rnti) {
srslte_dci_format_t format = SRSLTE_DCI_FORMAT1;
switch(grants[i].grant.alloc_type) {
case SRSLTE_RA_ALLOC_TYPE0:
case SRSLTE_RA_ALLOC_TYPE1:
format = SRSLTE_DCI_FORMAT1;
break;
case SRSLTE_RA_ALLOC_TYPE2:
format = SRSLTE_DCI_FORMAT1A;
break;
}
if (srslte_enb_dl_put_pdcch_dl(&enb_dl, &grants[i].grant, format, grants[i].location, rnti, sf_idx)) {
fprintf(stderr, "Error putting PDCCH %d\n",i);
return SRSLTE_ERROR;
}
if (LOG_THIS(rnti)) {
Info("PDCCH: DL DCI %s rnti=0x%x, cce_index=%d, L=%d, tti_tx=%d\n", srslte_dci_format_string(format),
rnti, grants[i].location.ncce, (1<<grants[i].location.L), tti_tx);
}
}
}
return 0;
}
int phch_worker::encode_pdsch(srslte_enb_dl_pdsch_t *grants, uint32_t nof_grants, uint32_t sf_idx)
{
for (uint32_t i=0;i<nof_grants;i++) {
uint16_t rnti = grants[i].rnti;
if (rnti) {
bool rnti_is_user = true;
if (rnti == SRSLTE_SIRNTI || rnti == SRSLTE_PRNTI || rnti == SRSLTE_MRNTI) {
rnti_is_user = false;
}
srslte_ra_dl_grant_t phy_grant;
srslte_ra_dl_dci_to_grant(&grants[i].grant, enb_dl.cell.nof_prb, rnti, &phy_grant);
char grant_str[64];
switch(grants[i].grant.alloc_type) {
case SRSLTE_RA_ALLOC_TYPE0:
sprintf(grant_str, "mask=0x%x",grants[i].grant.type0_alloc.rbg_bitmask);
break;
case SRSLTE_RA_ALLOC_TYPE1:
sprintf(grant_str, "mask=0x%x",grants[i].grant.type1_alloc.vrb_bitmask);
break;
default:
sprintf(grant_str, "rb_start=%d",grants[i].grant.type2_alloc.RB_start);
break;
}
if (LOG_THIS(rnti)) {
uint8_t x = 0;
uint8_t *ptr = grants[i].data;
uint32_t len = phy_grant.mcs.tbs/8;
if (!ptr) {
ptr = &x;
len = 1;
}
log_h->info_hex(ptr, len,
"PDSCH: rnti=0x%x, l_crb=%2d, %s, harq=%d, tbs=%d, mcs=%d, rv=%d, tti_tx=%d\n",
rnti, phy_grant.nof_prb, grant_str, grants[i].grant.harq_process,
phy_grant.mcs.tbs/8, phy_grant.mcs.idx, grants[i].grant.rv_idx, tti_tx);
}
if (srslte_enb_dl_put_pdsch(&enb_dl, &phy_grant, grants[i].softbuffer, rnti, grants[i].grant.rv_idx, sf_idx,
grants[i].data))
{
fprintf(stderr, "Error putting PDSCH %d\n",i);
return SRSLTE_ERROR;
}
// Save metrics stats
ue_db[rnti].metrics_dl(phy_grant.mcs.idx);
}
}
return SRSLTE_SUCCESS;
}
/************ METRICS interface ********************/
uint32_t phch_worker::get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS])
{
uint32_t cnt=0;
for(std::map<uint16_t, ue>::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) {
ue *u = (ue*) &iter->second;
uint16_t rnti = iter->first;
if (rnti >= SRSLTE_CRNTI_START && rnti <= SRSLTE_CRNTI_END) {
u->metrics_read(&metrics[cnt]);
cnt++;
}
}
return cnt;
}
void phch_worker::ue::metrics_read(phy_metrics_t* metrics_)
{
memcpy(metrics_, &metrics, sizeof(phy_metrics_t));
bzero(&metrics, sizeof(phy_metrics_t));
}
void phch_worker::ue::metrics_dl(uint32_t mcs)
{
metrics.dl.mcs = SRSLTE_VEC_CMA(mcs, metrics.dl.mcs, metrics.dl.n_samples);
metrics.dl.n_samples++;
}
void phch_worker::ue::metrics_ul(uint32_t mcs, float rssi, float sinr, uint32_t turbo_iters)
{
metrics.ul.mcs = SRSLTE_VEC_CMA((float) mcs, metrics.ul.mcs, metrics.ul.n_samples);
metrics.ul.sinr = SRSLTE_VEC_CMA((float) sinr, metrics.ul.sinr, metrics.ul.n_samples);
metrics.ul.rssi = SRSLTE_VEC_CMA((float) sinr, metrics.ul.rssi, metrics.ul.n_samples);
metrics.ul.turbo_iters = SRSLTE_VEC_CMA((float) turbo_iters, metrics.ul.turbo_iters, metrics.ul.n_samples);
metrics.ul.n_samples++;
}
void phch_worker::start_plot() {
#ifdef ENABLE_GUI
if (plot_worker_id == -1) {
plot_worker_id = get_id();
log_h->console("Starting plot for worker_id=%d\n", plot_worker_id);
init_plots(this);
} else {
log_h->console("Trying to start a plot but already started by worker_id=%d\n", plot_worker_id);
}
#else
log_h->console("Trying to start a plot but plots are disabled (ENABLE_GUI constant in phch_worker.cc)\n");
#endif
}
int phch_worker::read_ce_abs(float *ce_abs) {
uint32_t i=0;
int sz = srslte_symbol_sz(phy->cell.nof_prb);
bzero(ce_abs, sizeof(float)*sz);
int g = (sz - 12*phy->cell.nof_prb)/2;
for (i = 0; i < 12*phy->cell.nof_prb; i++) {
ce_abs[g+i] = 20 * log10(cabs(enb_ul.ce[i]));
if (isinf(ce_abs[g+i])) {
ce_abs[g+i] = -80;
}
}
return sz;
}
int phch_worker::read_pusch_d(cf_t* pdsch_d)
{
int nof_re = 400;//enb_ul.pusch_cfg.nbits.nof_re
memcpy(pdsch_d, enb_ul.pusch.d, nof_re*sizeof(cf_t));
return nof_re;
}
}
/***********************************************************
*
* PLOT TO VISUALIZE THE CHANNEL RESPONSEE
*
***********************************************************/
#ifdef ENABLE_GUI
plot_real_t pce;
plot_scatter_t pconst;
#define SCATTER_PUSCH_BUFFER_LEN (20*6*SRSLTE_SF_LEN_RE(SRSLTE_MAX_PRB, SRSLTE_CP_NORM))
#define SCATTER_PUSCH_PLOT_LEN 4000
float tmp_plot[SCATTER_PUSCH_BUFFER_LEN];
cf_t tmp_plot2[SRSLTE_SF_LEN_RE(SRSLTE_MAX_PRB, SRSLTE_CP_NORM)];
void *plot_thread_run(void *arg) {
srsenb::phch_worker *worker = (srsenb::phch_worker*) arg;
sdrgui_init_title("srsENB");
plot_real_init(&pce);
plot_real_setTitle(&pce, (char*) "Channel Response - Magnitude");
plot_real_setLabels(&pce, (char*) "Index", (char*) "dB");
plot_real_setYAxisScale(&pce, -40, 40);
plot_scatter_init(&pconst);
plot_scatter_setTitle(&pconst, (char*) "PUSCH - Equalized Symbols");
plot_scatter_setXAxisScale(&pconst, -4, 4);
plot_scatter_setYAxisScale(&pconst, -4, 4);
plot_real_addToWindowGrid(&pce, (char*)"srsenb", 0, 0);
plot_scatter_addToWindowGrid(&pconst, (char*)"srsenb", 0, 1);
int n;
int readed_pusch_re=0;
while(1) {
sem_wait(&plot_sem);
n = worker->read_pusch_d(tmp_plot2);
plot_scatter_setNewData(&pconst, tmp_plot2, n);
n = worker->read_ce_abs(tmp_plot);
plot_real_setNewData(&pce, tmp_plot, n);
}
return NULL;
}
void init_plots(srsenb::phch_worker *worker) {
if (sem_init(&plot_sem, 0, 0)) {
perror("sem_init");
exit(-1);
}
pthread_attr_t attr;
struct sched_param param;
param.sched_priority = 0;
pthread_attr_init(&attr);
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
pthread_attr_setschedparam(&attr, &param);
if (pthread_create(&plot_thread, &attr, plot_thread_run, worker)) {
perror("pthread_create");
exit(-1);
}
}
#endif

212
srsenb/src/phy/phy.cc Normal file
View File

@ -0,0 +1,212 @@
#include <string>
#include <sstream>
#include <string.h>
#include <strings.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/mman.h>
#include "srslte/common/threads.h"
#include "srslte/common/log.h"
#include "phy/phy.h"
#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
using namespace std;
namespace srsenb {
phy::phy() : workers_pool(MAX_WORKERS),
workers(MAX_WORKERS),
workers_common(txrx::MUTEX_X_WORKER*MAX_WORKERS)
{
}
void phy::parse_config(phy_cfg_t* cfg)
{
// PRACH configuration
prach_cfg.config_idx = cfg->prach_cnfg.prach_cnfg_info.prach_config_index;
prach_cfg.hs_flag = cfg->prach_cnfg.prach_cnfg_info.high_speed_flag;
prach_cfg.root_seq_idx = cfg->prach_cnfg.root_sequence_index;
prach_cfg.zero_corr_zone = cfg->prach_cnfg.prach_cnfg_info.zero_correlation_zone_config;
prach_cfg.freq_offset = cfg->prach_cnfg.prach_cnfg_info.prach_freq_offset;
// PUSCH DMRS configuration
workers_common.pusch_cfg.cyclic_shift = cfg->pusch_cnfg.ul_rs.cyclic_shift;
workers_common.pusch_cfg.delta_ss = cfg->pusch_cnfg.ul_rs.group_assignment_pusch;
workers_common.pusch_cfg.group_hopping_en = cfg->pusch_cnfg.ul_rs.group_hopping_enabled;
workers_common.pusch_cfg.sequence_hopping_en = cfg->pusch_cnfg.ul_rs.sequence_hopping_enabled;
// PUSCH hopping configuration
workers_common.hopping_cfg.hop_mode = cfg->pusch_cnfg.hopping_mode == LIBLTE_RRC_HOPPING_MODE_INTRA_AND_INTER_SUBFRAME ?
srslte_pusch_hopping_cfg_t::SRSLTE_PUSCH_HOP_MODE_INTRA_SF :
srslte_pusch_hopping_cfg_t::SRSLTE_PUSCH_HOP_MODE_INTER_SF; ;
workers_common.hopping_cfg.n_sb = cfg->pusch_cnfg.n_sb;
workers_common.hopping_cfg.hopping_offset = cfg->pusch_cnfg.pusch_hopping_offset;
// PUCCH configuration
workers_common.pucch_cfg.delta_pucch_shift = liblte_rrc_delta_pucch_shift_num[cfg->pucch_cnfg.delta_pucch_shift%LIBLTE_RRC_DELTA_PUCCH_SHIFT_N_ITEMS];
workers_common.pucch_cfg.N_cs = cfg->pucch_cnfg.n_cs_an;
workers_common.pucch_cfg.n_rb_2 = cfg->pucch_cnfg.n_rb_cqi;
workers_common.pucch_cfg.srs_configured = false;
workers_common.pucch_cfg.n1_pucch_an = cfg->pucch_cnfg.n1_pucch_an;;
}
bool phy::init(phy_args_t *args,
phy_cfg_t *cfg,
srslte::radio* radio_handler_,
mac_interface_phy *mac,
srslte::log* log_h)
{
std::vector<void*> log_vec;
for (int i=0;i<args->nof_phy_threads;i++) {
log_vec[i] = (void*) log_h;
}
init(args, cfg, radio_handler_, mac, log_vec);
return true;
}
bool phy::init(phy_args_t *args,
phy_cfg_t *cfg,
srslte::radio* radio_handler_,
mac_interface_phy *mac,
std::vector<void*> log_vec)
{
mlockall(MCL_CURRENT | MCL_FUTURE);
radio_handler = radio_handler_;
nof_workers = args->nof_phy_threads;
workers_common.params = *args;
workers_common.init(&cfg->cell, radio_handler, mac);
parse_config(cfg);
// Add workers to workers pool and start threads
for (uint32_t i=0;i<nof_workers;i++) {
workers[i].init(&workers_common, (srslte::log*) log_vec[i]);
workers_pool.init_worker(i, &workers[i], WORKERS_THREAD_PRIO);
}
prach.init(&cfg->cell, &prach_cfg, mac, (srslte::log*) log_vec[0], PRACH_WORKER_THREAD_PRIO);
prach.set_max_prach_offset_us(args->max_prach_offset_us);
// Warning this must be initialized after all workers have been added to the pool
tx_rx.init(radio_handler, &workers_pool, &workers_common, &prach, (srslte::log*) log_vec[0], SF_RECV_THREAD_PRIO);
return true;
}
void phy::stop()
{
tx_rx.stop();
workers_common.stop();
workers_pool.stop();
prach.stop();
}
uint32_t phy::tti_to_SFN(uint32_t tti) {
return tti/10;
}
uint32_t phy::tti_to_subf(uint32_t tti) {
return tti%10;
}
/***** MAC->PHY interface **********/
int phy::add_rnti(uint16_t rnti)
{
if (rnti >= SRSLTE_CRNTI_START && rnti <= SRSLTE_CRNTI_END) {
workers_common.ack_add_rnti(rnti);
}
for (uint32_t i=0;i<nof_workers;i++) {
if (workers[i].add_rnti(rnti)) {
return SRSLTE_ERROR;
}
}
return SRSLTE_SUCCESS;
}
void phy::rem_rnti(uint16_t rnti)
{
if (rnti >= SRSLTE_CRNTI_START && rnti <= SRSLTE_CRNTI_END) {
workers_common.ack_rem_rnti(rnti);
}
for (uint32_t i=0;i<nof_workers;i++) {
workers[i].rem_rnti(rnti);
}
}
void phy::get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS])
{
phy_metrics_t metrics_tmp[ENB_METRICS_MAX_USERS];
uint32_t nof_users = workers[0].get_nof_rnti();
bzero(metrics, sizeof(phy_metrics_t)*ENB_METRICS_MAX_USERS);
int n_tot = 0;
for (uint32_t i=0;i<nof_workers;i++) {
workers[i].get_metrics(metrics_tmp);
for (uint32_t j=0;j<nof_users;j++) {
metrics[j].dl.n_samples += metrics_tmp[j].dl.n_samples;
metrics[j].dl.mcs += metrics_tmp[j].dl.n_samples*metrics_tmp[j].dl.mcs;
metrics[j].ul.n_samples += metrics_tmp[j].ul.n_samples;
metrics[j].ul.mcs += metrics_tmp[j].ul.n_samples*metrics_tmp[j].ul.mcs;
metrics[j].ul.n += metrics_tmp[j].ul.n_samples*metrics_tmp[j].ul.n;
metrics[j].ul.rssi += metrics_tmp[j].ul.n_samples*metrics_tmp[j].ul.rssi;
metrics[j].ul.sinr += metrics_tmp[j].ul.n_samples*metrics_tmp[j].ul.sinr;
metrics[j].ul.turbo_iters += metrics_tmp[j].ul.n_samples*metrics_tmp[j].ul.turbo_iters;
}
}
for (uint32_t j=0;j<nof_users;j++) {
metrics[j].dl.mcs /= metrics[j].dl.n_samples;
metrics[j].ul.mcs /= metrics[j].ul.n_samples;
metrics[j].ul.n /= metrics[j].ul.n_samples;
metrics[j].ul.rssi /= metrics[j].ul.n_samples;
metrics[j].ul.sinr /= metrics[j].ul.n_samples;
metrics[j].ul.turbo_iters /= metrics[j].ul.n_samples;
}
}
/***** RRC->PHY interface **********/
void phy::set_config_dedicated(uint16_t rnti, LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT* dedicated)
{
// Parse RRC config
srslte_uci_cfg_t uci_cfg;
srslte_pucch_sched_t pucch_sched;
/* PUSCH UCI configuration */
bzero(&uci_cfg, sizeof(srslte_uci_cfg_t));
uci_cfg.I_offset_ack = dedicated->pusch_cnfg_ded.beta_offset_ack_idx;
uci_cfg.I_offset_cqi = dedicated->pusch_cnfg_ded.beta_offset_cqi_idx;
uci_cfg.I_offset_ri = dedicated->pusch_cnfg_ded.beta_offset_ri_idx;
/* PUCCH Scheduling configuration */
bzero(&pucch_sched, sizeof(srslte_pucch_sched_t));
pucch_sched.n_pucch_2 = dedicated->cqi_report_cnfg.report_periodic.pucch_resource_idx;
pucch_sched.n_pucch_sr = dedicated->sched_request_cnfg.sr_pucch_resource_idx;
for (uint32_t i=0;i<nof_workers;i++) {
workers[i].set_config_dedicated(rnti, &uci_cfg, &pucch_sched, NULL,
dedicated->sched_request_cnfg.sr_cnfg_idx,
dedicated->cqi_report_cnfg.report_periodic_setup_present,
dedicated->cqi_report_cnfg.report_periodic.pmi_cnfg_idx,
dedicated->cqi_report_cnfg.report_periodic.simult_ack_nack_and_cqi);
}
}
// Start GUI
void phy::start_plot() {
((phch_worker) workers[0]).start_plot();
}
}

View File

@ -0,0 +1,134 @@
#include "phy/prach_worker.h"
namespace srsenb {
int prach_worker::init(srslte_cell_t *cell_, srslte_prach_cfg_t *prach_cfg_, mac_interface_phy* mac_, srslte::log* log_h_, int priority)
{
log_h = log_h_;
mac = mac_;
memcpy(&prach_cfg, prach_cfg_, sizeof(srslte_prach_cfg_t));
memcpy(&cell, cell_, sizeof(srslte_cell_t));
max_prach_offset_us = 50;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cvar, NULL);
if (srslte_prach_init_cfg(&prach, &prach_cfg, cell.nof_prb)) {
fprintf(stderr, "Error initiating PRACH\n");
return -1;
}
srslte_prach_set_detect_factor(&prach, 60);
nof_sf = (uint32_t) ceilf(prach.T_tot*1000);
signal_buffer_rx = (cf_t*) srslte_vec_malloc(sizeof(cf_t)*nof_sf*SRSLTE_SF_LEN_PRB(cell.nof_prb));
if (!signal_buffer_rx) {
perror("malloc");
return -1;
}
start(priority);
initiated = true;
pending_tti = 0;
processed_tti = 0;
return 0;
}
void prach_worker::stop()
{
pthread_mutex_lock(&mutex);
processed_tti = 99999;
running = false;
pthread_cond_signal(&cvar);
pthread_mutex_unlock(&mutex);
wait_thread_finish();
}
void prach_worker::set_max_prach_offset_us(float delay_us)
{
max_prach_offset_us = delay_us;
}
int prach_worker::new_tti(uint32_t tti_rx, cf_t* buffer_rx)
{
// Save buffer only if it's a PRACH TTI
if (srslte_prach_tti_opportunity(&prach, tti_rx, -1) || sf_cnt) {
memcpy(&signal_buffer_rx[sf_cnt*SRSLTE_SF_LEN_PRB(cell.nof_prb)], buffer_rx, sizeof(cf_t)*SRSLTE_SF_LEN_PRB(cell.nof_prb));
sf_cnt++;
if (sf_cnt == nof_sf) {
sf_cnt = 0;
if ((int) pending_tti != processed_tti) {
log_h->warning("PRACH thread did not finish processing TTI=%d\n", pending_tti);
}
pthread_mutex_lock(&mutex);
if (tti_rx+1 > nof_sf) {
pending_tti = tti_rx+1-nof_sf;
} else {
pending_tti = 10240+(tti_rx+1-nof_sf);
}
pthread_cond_signal(&cvar);
pthread_mutex_unlock(&mutex);
}
}
return 0;
}
int prach_worker::run_tti(uint32_t tti_rx)
{
if (srslte_prach_tti_opportunity(&prach, tti_rx, -1))
{
// Detect possible PRACHs
if (srslte_prach_detect_offset(&prach,
prach_cfg.freq_offset,
&signal_buffer_rx[prach.N_cp],
nof_sf*SRSLTE_SF_LEN_PRB(cell.nof_prb)-prach.N_cp,
prach_indices,
prach_offsets,
prach_p2avg,
&prach_nof_det))
{
log_h->error("Error detecting PRACH\n");
return SRSLTE_ERROR;
}
if (prach_nof_det) {
for (uint32_t i=0;i<prach_nof_det;i++) {
log_h->info("PRACH: %d/%d, preamble=%d, offset=%.1f us, peak2avg=%.1f, max_offset=%.1f us\n",
i, prach_nof_det, prach_indices[i], prach_offsets[i]*1e6, prach_p2avg[i], max_prach_offset_us);
if (prach_offsets[i]*1e6 < max_prach_offset_us) {
mac->rach_detected(tti_rx, prach_indices[i], (uint32_t) (prach_offsets[i]*1e6));
}
}
}
}
return 0;
}
void prach_worker::run_thread()
{
running = true;
while(running) {
pthread_mutex_lock(&mutex);
while(processed_tti == (int) pending_tti) {
pthread_cond_wait(&cvar, &mutex);
}
pthread_mutex_unlock(&mutex);
log_h->debug("Processing pending_tti=%d\n", pending_tti);
if (running) {
if (run_tti(pending_tti)) {
running = false;
}
processed_tti = pending_tti;
}
}
}
}

117
srsenb/src/phy/txrx.cc Normal file
View File

@ -0,0 +1,117 @@
#include <unistd.h>
#include "srslte/common/threads.h"
#include "srslte/common/log.h"
#include "phy/txrx.h"
#include "phy/phch_worker.h"
#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
using namespace std;
namespace srsenb {
txrx::txrx()
{
running = false;
radio_h = NULL;
log_h = NULL;
workers_pool = NULL;
worker_com = NULL;
}
bool txrx::init(srslte::radio* radio_h_, srslte::thread_pool* workers_pool_, phch_common* worker_com_, prach_worker *prach_, srslte::log* log_h_, uint32_t prio_)
{
radio_h = radio_h_;
log_h = log_h_;
workers_pool = workers_pool_;
worker_com = worker_com_;
prach = prach_;
tx_mutex_cnt = 0;
running = true;
nof_tx_mutex = MUTEX_X_WORKER*workers_pool->get_nof_workers();
worker_com->set_nof_mutex(nof_tx_mutex);
start(prio_);
return true;
}
void txrx::stop()
{
running = false;
wait_thread_finish();
}
void txrx::run_thread()
{
phch_worker *worker = NULL;
cf_t *buffer = NULL;
srslte_timestamp_t rx_time, tx_time;
uint32_t sf_len = SRSLTE_SF_LEN_PRB(worker_com->cell.nof_prb);
float samp_rate = srslte_sampling_freq_hz(worker_com->cell.nof_prb);
if (30720%((int) samp_rate/1000) == 0) {
radio_h->set_master_clock_rate(30.72e6);
} else {
radio_h->set_master_clock_rate(23.04e6);
}
log_h->console("Setting Sampling frequency %.2f MHz\n", (float) samp_rate/1000000);
// Configure radio
radio_h->set_rx_srate(samp_rate);
radio_h->set_tx_srate(samp_rate);
log_h->info("Starting RX/TX thread nof_prb=%d, sf_len=%d\n",worker_com->cell.nof_prb, sf_len);
// Start streaming RX samples
radio_h->start_rx();
// Set TTI so that first TX is at tti=0
tti = 10235;
printf("\n==== eNodeB started ===\n");
printf("Type <t> to view trace\n");
// Main loop
while (running) {
tti = (tti+1)%10240;
worker = (phch_worker*) workers_pool->wait_worker(tti);
if (worker) {
buffer = worker->get_buffer_rx();
radio_h->rx_now(buffer, sf_len, &rx_time);
/* Compute TX time: Any transmission happens in TTI+4 thus advance 4 ms the reception time */
srslte_timestamp_copy(&tx_time, &rx_time);
srslte_timestamp_add(&tx_time, 0, 4e-3);
Debug("Settting TTI=%d, tx_mutex=%d, tx_time=%d:%f to worker %d\n",
tti, tx_mutex_cnt,
tx_time.full_secs, tx_time.frac_secs,
worker->get_id());
worker->set_time(tti, tx_mutex_cnt, tx_time);
tx_mutex_cnt = (tx_mutex_cnt+1)%nof_tx_mutex;
// Trigger phy worker execution
workers_pool->start_worker(worker);
// Trigger prach worker execution
prach->new_tti(tti, buffer);
} else {
// wait_worker() only returns NULL if it's being closed. Quit now to avoid unnecessary loops here
running = false;
}
}
}
}

View File

@ -0,0 +1,3 @@
file(GLOB SOURCES "*.cc")
add_library(srsenb_upper SHARED ${SOURCES})
target_link_libraries(srsenb_upper ${SRSLTE_LIBRARIES})

240
srsenb/src/upper/gtpu.cc Normal file
View File

@ -0,0 +1,240 @@
#include "upper/gtpu.h"
#include <unistd.h>
using namespace srslte;
namespace srsenb {
bool gtpu::init(std::string gtp_bind_addr_, std::string mme_addr_, srsenb::pdcp_interface_gtpu* pdcp_, srslte::log* gtpu_log_)
{
pdcp = pdcp_;
gtpu_log = gtpu_log_;
gtp_bind_addr = gtp_bind_addr_;
mme_addr = mme_addr_;
pthread_mutex_init(&mutex, NULL);
pool = byte_buffer_pool::get_instance();
if(0 != srslte_netsource_init(&src, gtp_bind_addr.c_str(), GTPU_PORT, SRSLTE_NETSOURCE_UDP)) {
gtpu_log->error("Failed to create source socket on %s:%d", gtp_bind_addr.c_str(), GTPU_PORT);
return false;
}
if(0 != srslte_netsink_init(&snk, mme_addr.c_str(), GTPU_PORT, SRSLTE_NETSINK_UDP)) {
gtpu_log->error("Failed to create sink socket on %s:%d", mme_addr.c_str(), GTPU_PORT);
return false;
}
srslte_netsink_set_nonblocking(&snk);
// Setup a thread to receive packets from the src socket
start(THREAD_PRIO);
return true;
}
void gtpu::stop()
{
if(run_enable) {
run_enable = false;
// Wait thread to exit gracefully otherwise might leave a mutex locked
int cnt=0;
while(running && cnt<100) {
usleep(10000);
cnt++;
}
if (running) {
thread_cancel();
}
wait_thread_finish();
}
srslte_netsink_free(&snk);
srslte_netsource_free(&src);
}
// gtpu_interface_pdcp
void gtpu::write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t* pdu)
{
gtpu_log->info_hex(pdu->msg, pdu->N_bytes, "TX PDU, RNTI: 0x%x, LCID: %d", rnti, lcid);
gtpu_header_t header;
header.flags = 0x30;
header.message_type = 0xFF;
header.length = pdu->N_bytes;
header.teid = rnti_bearers[rnti].teids_out[lcid];
gtpu_write_header(&header, pdu);
srslte_netsink_write(&snk, pdu->msg, pdu->N_bytes);
pool->deallocate(pdu);
}
// gtpu_interface_rrc
void gtpu::add_bearer(uint16_t rnti, uint32_t lcid, uint32_t teid_out, uint32_t *teid_in)
{
// Allocate a TEID for the incoming tunnel
rntilcid_to_teidin(rnti, lcid, teid_in);
gtpu_log->info("Adding bearer for rnti: 0x%x, lcid: %d, teid_out: 0x%x, teid_in: 0x%x\n", rnti, lcid, teid_out, *teid_in);
// Initialize maps if it's a new RNTI
if(rnti_bearers.count(rnti) == 0) {
for(int i=0;i<SRSENB_N_RADIO_BEARERS;i++) {
rnti_bearers[rnti].teids_in[i] = 0;
rnti_bearers[rnti].teids_out[i] = 0;
}
}
rnti_bearers[rnti].teids_in[lcid] = *teid_in;
rnti_bearers[rnti].teids_out[lcid] = teid_out;
}
void gtpu::rem_bearer(uint16_t rnti, uint32_t lcid)
{
gtpu_log->info("Removing bearer for rnti: 0x%x, lcid: %d\n", rnti, lcid);
rnti_bearers[rnti].teids_in[lcid] = 0;
rnti_bearers[rnti].teids_out[lcid] = 0;
// Remove RNTI if all bearers are removed
bool rem = true;
for(int i=0;i<SRSENB_N_RADIO_BEARERS; i++) {
if(rnti_bearers[rnti].teids_in[i] != 0) {
rem = false;
}
}
if(rem) {
rnti_bearers.erase(rnti);
}
}
void gtpu::rem_user(uint16_t rnti)
{
pthread_mutex_lock(&mutex);
rnti_bearers.erase(rnti);
pthread_mutex_unlock(&mutex);
}
void gtpu::run_thread()
{
byte_buffer_t *pdu = pool->allocate();
run_enable = true;
running=true;
while(run_enable) {
pdu->reset();
gtpu_log->debug("Waiting for read...\n");
pdu->N_bytes = srslte_netsource_read(&src, pdu->msg, SRSENB_MAX_BUFFER_SIZE_BYTES - SRSENB_BUFFER_HEADER_OFFSET);
gtpu_header_t header;
gtpu_read_header(pdu, &header);
uint16_t rnti = 0;
uint16_t lcid = 0;
teidin_to_rntilcid(header.teid, &rnti, &lcid);
pthread_mutex_lock(&mutex);
bool user_exists = (rnti_bearers.count(rnti) > 0);
pthread_mutex_unlock(&mutex);
if(!user_exists) {
gtpu_log->error("Unrecognized RNTI for DL PDU: 0x%x - dropping packet\n", rnti);
continue;
}
if(lcid < SRSENB_N_SRB || lcid >= SRSENB_N_RADIO_BEARERS) {
gtpu_log->error("Invalid LCID for DL PDU: %d - dropping packet\n", lcid);
continue;
}
gtpu_log->info_hex(pdu->msg, pdu->N_bytes, "RX GTPU PDU rnti=0x%x, lcid=%d", rnti, lcid);
pdcp->write_sdu(rnti, lcid, pdu);
do {
pdu = pool->allocate();
if (!pdu) {
gtpu_log->console("GTPU Buffer pool empty. Trying again...\n");
usleep(10000);
}
} while(!pdu);
}
running=false;
}
/****************************************************************************
* Header pack/unpack helper functions
* Ref: 3GPP TS 29.281 v10.1.0 Section 5
***************************************************************************/
bool gtpu::gtpu_write_header(gtpu_header_t *header, srslte::byte_buffer_t *pdu)
{
if(header->flags != 0x30) {
gtpu_log->error("gtpu_write_header - Unhandled header flags: 0x%x\n", header->flags);
return false;
}
if(header->message_type != 0xFF) {
gtpu_log->error("gtpu_write_header - Unhandled message type: 0x%x\n", header->message_type);
return false;
}
if(pdu->get_headroom() < GTPU_HEADER_LEN) {
gtpu_log->error("gtpu_write_header - No room in PDU for header\n");
return false;
}
pdu->msg -= GTPU_HEADER_LEN;
pdu->N_bytes += GTPU_HEADER_LEN;
uint8_t *ptr = pdu->msg;
*ptr = header->flags;
ptr++;
*ptr = header->message_type;
ptr++;
uint16_to_uint8(header->length, ptr);
ptr += 2;
uint32_to_uint8(header->teid, ptr);
return true;
}
bool gtpu::gtpu_read_header(srslte::byte_buffer_t *pdu, gtpu_header_t *header)
{
uint8_t *ptr = pdu->msg;
pdu->msg += GTPU_HEADER_LEN;
pdu->N_bytes -= GTPU_HEADER_LEN;
header->flags = *ptr;
ptr++;
header->message_type = *ptr;
ptr++;
uint8_to_uint16(ptr, &header->length);
ptr += 2;
uint8_to_uint32(ptr, &header->teid);
if(header->flags != 0x30) {
gtpu_log->error("gtpu_read_header - Unhandled header flags: 0x%x\n", header->flags);
return false;
}
if(header->message_type != 0xFF) {
gtpu_log->error("gtpu_read_header - Unhandled message type: 0x%x\n", header->message_type);
return false;
}
return true;
}
/****************************************************************************
* TEID to RNIT/LCID helper functions
***************************************************************************/
void gtpu::teidin_to_rntilcid(uint32_t teidin, uint16_t *rnti, uint16_t *lcid)
{
*lcid = teidin & 0xFFFF;
*rnti = (teidin >> 16) & 0xFFFF;
}
void gtpu::rntilcid_to_teidin(uint16_t rnti, uint16_t lcid, uint32_t *teidin)
{
*teidin = (rnti << 16) | lcid;
}
} // namespace srsenb

122
srsenb/src/upper/pdcp.cc Normal file
View File

@ -0,0 +1,122 @@
#include "upper/pdcp.h"
namespace srsenb {
void pdcp::init(rlc_interface_pdcp* rlc_, rrc_interface_pdcp* rrc_, gtpu_interface_pdcp* gtpu_, srslte::log* pdcp_log_)
{
rlc = rlc_;
rrc = rrc_;
gtpu = gtpu_;
log_h = pdcp_log_;
pool = srslte::byte_buffer_pool::get_instance();
}
void pdcp::stop()
{
for(std::map<uint32_t, user_interface>::iterator iter=users.begin(); iter!=users.end(); ++iter) {
rem_user((uint32_t) iter->first);
}
users.clear();
}
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);
users[rnti].rlc_itf.rnti = rnti;
users[rnti].gtpu_itf.rnti = rnti;
users[rnti].rrc_itf.rnti = rnti;
users[rnti].rrc_itf.rrc = rrc;
users[rnti].rlc_itf.rlc = rlc;
users[rnti].gtpu_itf.gtpu = gtpu;
users[rnti].pdcp = obj;
}
}
void pdcp::rem_user(uint16_t rnti)
{
if (users.count(rnti)) {
users[rnti].pdcp->stop();
delete users[rnti].pdcp;
users[rnti].pdcp = NULL;
users.erase(rnti);
}
}
void pdcp::add_bearer(uint16_t rnti, uint32_t lcid, LIBLTE_RRC_PDCP_CONFIG_STRUCT* cnfg)
{
if (users.count(rnti)) {
users[rnti].pdcp->add_bearer(lcid, cnfg);
}
}
void pdcp::reset(uint16_t rnti)
{
if (users.count(rnti)) {
users[rnti].pdcp->reset();
}
}
void pdcp::config_security(uint16_t rnti, uint32_t lcid, uint8_t* k_rrc_enc_, uint8_t* k_rrc_int_,
srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo_,
srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo_)
{
if (users.count(rnti)) {
users[rnti].pdcp->config_security(lcid, k_rrc_enc_, k_rrc_int_, cipher_algo_, integ_algo_);
}
}
void pdcp::write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t* sdu)
{
if (users.count(rnti)) {
users[rnti].pdcp->write_pdu(lcid, sdu);
} else {
pool->deallocate(sdu);
}
}
void pdcp::write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t* sdu)
{
if (users.count(rnti)) {
users[rnti].pdcp->write_sdu(lcid, sdu);
} else {
pool->deallocate(sdu);
}
}
void pdcp::user_interface_gtpu::write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu)
{
gtpu->write_pdu(rnti, lcid, pdu);
}
void pdcp::user_interface_rlc::write_sdu(uint32_t lcid, srslte::byte_buffer_t* sdu)
{
rlc->write_sdu(rnti, lcid, sdu);
}
void pdcp::user_interface_rrc::write_pdu(uint32_t lcid, srslte::byte_buffer_t* pdu)
{
rrc->write_pdu(rnti, lcid, pdu);
}
void pdcp::user_interface_rrc::write_pdu_bcch_bch(srslte::byte_buffer_t* pdu)
{
fprintf(stderr, "Error: Received BCCH from ue=%d\n", rnti);
}
void pdcp::user_interface_rrc::write_pdu_bcch_dlsch(srslte::byte_buffer_t* pdu)
{
fprintf(stderr, "Error: Received BCCH from ue=%d\n", rnti);
}
void pdcp::user_interface_rrc::write_pdu_pcch(srslte::byte_buffer_t* pdu)
{
fprintf(stderr, "Error: Received PCCH from ue=%d\n", rnti);
}
}

161
srsenb/src/upper/rlc.cc Normal file
View File

@ -0,0 +1,161 @@
#include "upper/rlc.h"
namespace srsenb {
void rlc::init(pdcp_interface_rlc* pdcp_, rrc_interface_rlc* rrc_, mac_interface_rlc *mac_,
srslte::mac_interface_timers *mac_timers_, srslte::log* log_h_)
{
pdcp = pdcp_;
rrc = rrc_,
log_h = log_h_;
mac = mac_;
mac_timers = mac_timers_;
pool = srslte::byte_buffer_pool::get_instance();
}
void rlc::stop()
{
for(std::map<uint32_t, user_interface>::iterator iter=users.begin(); iter!=users.end(); ++iter) {
rem_user((uint32_t) iter->first);
}
users.clear();
}
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);
users[rnti].rnti = rnti;
users[rnti].pdcp = pdcp;
users[rnti].rrc = rrc;
users[rnti].rlc = obj;
users[rnti].parent = this;
}
}
void rlc::rem_user(uint16_t rnti)
{
if (users.count(rnti)) {
users[rnti].rlc->stop();
delete users[rnti].rlc;
users[rnti].rlc = NULL;
users.erase(rnti);
}
}
void rlc::reset(uint16_t rnti)
{
if (users.count(rnti)) {
users[rnti].rlc->reset();
}
}
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();
for (int i=0;i<SRSUE_N_RADIO_BEARERS;i++) {
mac->rlc_buffer_state(rnti, i, 0, 0);
}
}
}
void rlc::add_bearer(uint16_t rnti, uint32_t lcid)
{
if (users.count(rnti)) {
users[rnti].rlc->add_bearer(lcid);
}
}
void rlc::add_bearer(uint16_t rnti, uint32_t lcid, LIBLTE_RRC_RLC_CONFIG_STRUCT* cnfg)
{
if (users.count(rnti)) {
users[rnti].rlc->add_bearer(lcid, cnfg);
}
}
void rlc::read_pdu_pcch(uint8_t* payload, uint32_t buffer_size)
{
rrc->read_pdu_pcch(payload, buffer_size);
}
int rlc::read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes)
{
int ret = users[rnti].rlc->read_pdu(lcid, payload, nof_bytes);
// In the eNodeB, there is no polling for buffer state from the scheduler, thus
// communicate buffer state every time a PDU is read
uint32_t tx_queue = users[rnti].rlc->get_total_buffer_state(lcid);
uint32_t retx_queue = 0;
log_h->debug("Buffer state PDCP: rnti=0x%x, lcid=%d, tx_queue=%d\n", rnti, lcid, tx_queue);
mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue);
return ret;
}
void rlc::write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes)
{
if (users.count(rnti)) {
users[rnti].rlc->write_pdu(lcid, payload, nof_bytes);
// In the eNodeB, there is no polling for buffer state from the scheduler, thus
// communicate buffer state every time a new PDU is written
uint32_t tx_queue = users[rnti].rlc->get_total_buffer_state(lcid);
uint32_t retx_queue = 0;
log_h->debug("Buffer state PDCP: rnti=0x%x, lcid=%d, tx_queue=%d\n", rnti, lcid, tx_queue);
mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue);
}
}
void rlc::read_pdu_bcch_dlsch(uint32_t sib_index, uint8_t *payload)
{
// RLC is transparent for BCCH
rrc->read_pdu_bcch_dlsch(sib_index, payload);
}
void rlc::write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t* sdu)
{
if (users.count(rnti)) {
users[rnti].rlc->write_sdu(lcid, sdu);
// In the eNodeB, there is no polling for buffer state from the scheduler, thus
// communicate buffer state every time a new SDU is written
uint32_t tx_queue = users[rnti].rlc->get_total_buffer_state(lcid);
uint32_t retx_queue = 0;
log_h->info("Buffer state: rnti=0x%x, lcid=%d, tx_queue=%d\n", rnti, lcid, tx_queue);
mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue);
} else {
pool->deallocate(sdu);
}
}
void rlc::user_interface::max_retx_attempted()
{
rrc->max_retx_attempted(rnti);
}
void rlc::user_interface::write_pdu(uint32_t lcid, srslte::byte_buffer_t* sdu)
{
pdcp->write_pdu(rnti, lcid, sdu);
}
void rlc::user_interface::write_pdu_bcch_bch(srslte::byte_buffer_t* sdu)
{
fprintf(stderr, "Error: Received BCCH from ue=%d\n", rnti);
}
void rlc::user_interface::write_pdu_bcch_dlsch(srslte::byte_buffer_t* sdu)
{
fprintf(stderr, "Error: Received BCCH from ue=%d\n", rnti);
}
void rlc::user_interface::write_pdu_pcch(srslte::byte_buffer_t* sdu)
{
fprintf(stderr, "Error: Received PCCH from ue=%d\n", rnti);
}
}

1612
srsenb/src/upper/rrc.cc Normal file

File diff suppressed because it is too large Load Diff

1037
srsenb/src/upper/s1ap.cc Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
add_subdirectory(mac)
add_subdirectory(upper)

View File

@ -0,0 +1,9 @@
# Scheduler test
add_executable(scheduler_test scheduler_test.cc)
target_link_libraries(scheduler_test srsenb_mac
srsenb_phy
srslte_common
srslte_phy
${CMAKE_THREAD_LIBS_INIT}
${Boost_LIBRARIES})

View File

@ -0,0 +1,136 @@
#include <unistd.h>
#include "mac/mac.h"
#include "phy/phy.h"
#include "srslte/interfaces/enb_interfaces.h"
#include "srslte/interfaces/sched_interface.h"
#include "srslte/common/log_stdout.h"
#include "srslte/radio/radio.h"
#include "srslte/phy/utils/debug.h"
uint8_t sib1_payload[18] = {0x60,0x40,0x04,0x03,0x00,0x01,0x1a,0x2d,0x00,0x18,0x02,0x81,0x80,0x42,0x0c,0x80,0x00,0x00};
uint8_t sib2_payload[41] = {0x00,0x80,0x1c,0x31,0x18,0x6f,0xe1,0x20,0x00,0x35,0x84,0x8c,
0xe2,0xd0,0x00,0x02,0x00,0x78,0xee,0x31,0x6a,0xa5,0x37,0x30,
0xa0,0x70,0xc9,0x49,0xfa,0x8d,0xd2,0x78,0x1a,0x02,0x77,0x4a,
0x92,0x40,0x00,0x00,0x00};
// Define dummy RLC always transmitts
class rlc : public srsenb::rlc_interface_mac
{
public:
uint32_t get_buffer_state(uint16_t rnti, uint32_t lcid)
{
return 1;
}
int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t *payload, uint32_t nof_bytes)
{
for (uint32_t i=0;i<nof_bytes;i++) {
payload[i] = i;
}
return nof_bytes;
}
void read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) {
}
void read_pdu_bcch_dlsch(uint32_t sib_index, uint8_t payload[srsenb::sched_interface::MAX_SIB_PAYLOAD_LEN])
{
switch(sib_index) {
case 0:
memcpy(payload, sib1_payload, 18);
break;
case 1:
memcpy(payload, sib2_payload, 41);
break;
}
}
void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t *payload, uint32_t nof_bytes)
{
}
private:
};
// Create classes
srslte::log_stdout log_out("ALL");
srsenb::sched my_sched;
srsenb::dl_metric_rr dl_metric;
srsenb::ul_metric_rr ul_metric;
rlc my_rlc;
int main(int argc, char *argv[])
{
log_out.set_level(srslte::LOG_LEVEL_INFO);
/* Set PHY cell configuration */
srslte_cell_t cell_cfg_phy;
cell_cfg_phy.id = 1;
cell_cfg_phy.cp = SRSLTE_CP_NORM;
cell_cfg_phy.nof_ports = 1;
cell_cfg_phy.nof_prb = 25;
cell_cfg_phy.phich_length = SRSLTE_PHICH_NORM;
cell_cfg_phy.phich_resources = SRSLTE_PHICH_R_1;
srsenb::sched_interface::cell_cfg_t cell_cfg;
/* Set MAC cell configuration */
bzero(&cell_cfg, sizeof(srsenb::sched_interface::cell_cfg_t));
memcpy(&cell_cfg.cell, &cell_cfg_phy, sizeof(srslte_cell_t));
cell_cfg.sibs[0].len = 18;
cell_cfg.sibs[0].period_rf = 8;
cell_cfg.sibs[1].len = 41;
cell_cfg.sibs[1].period_rf = 16;
cell_cfg.si_window_ms = 40;
my_sched.init(NULL, &log_out);
my_sched.set_metric(&dl_metric, &ul_metric);
my_sched.cell_cfg(&cell_cfg);
srsenb::sched_interface::dl_sched_res_t sched_result_dl;
srsenb::sched_interface::ul_sched_res_t sched_result_ul;
uint32_t dl_info_len = 0;
srsenb::sched_interface::ue_cfg_t ue_cfg;
bzero(&ue_cfg, sizeof(srsenb::sched_interface::ue_cfg_t));
uint16_t rnti = 30;
ue_cfg.aperiodic_cqi_period = 40;
ue_cfg.maxharq_tx = 5;
srsenb::sched_interface::ue_bearer_cfg_t bearer_cfg;
bzero(&bearer_cfg, sizeof(srsenb::sched_interface::ue_bearer_cfg_t));
bearer_cfg.direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH;
my_sched.ue_cfg(rnti, &ue_cfg);
my_sched.bearer_ue_cfg(rnti, 0, &bearer_cfg);
//my_sched.dl_rlc_buffer_state(rnti, 0, 1e6, 0);
my_sched.ul_bsr(rnti, 0, 1e6);
bool running = true;
uint32_t tti = 0;
while(running) {
log_out.step(tti);
if (tti > 50) {
running = false;
}
my_sched.dl_sched(tti, &sched_result_dl);
my_sched.ul_sched(tti, &sched_result_ul);
tti = (tti+1)%10240;
if (tti >= 4) {
my_sched.ul_crc_info(tti, rnti, tti%2);
}
}
}

View File

@ -0,0 +1,17 @@
# IP tx/rx program test
add_executable(ip_test_enb ip_test.cc)
target_link_libraries(ip_test_enb srsenb_upper
srsenb_mac
srsenb_phy
srslte_common
srslte_phy
srslte_upper
srslte_radio
${CMAKE_THREAD_LIBS_INIT}
${Boost_LIBRARIES}
${POLAR_LIBRARIES})
# Simple PLMN -> MCC/MNC test
add_executable(plmn_test plmn_test.cc)
target_link_libraries(plmn_test srsenb_upper srslte_asn1 )

View File

@ -0,0 +1,646 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <linux/ip.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include "mac/mac.h"
#include "phy/phy.h"
#include "srslte/common/threads.h"
#include "srslte/interfaces/enb_interfaces.h"
#include "srslte/common/common.h"
#include "srslte/common/buffer_pool.h"
#include "srslte/common/logger.h"
#include "srslte/common/log_filter.h"
#include "srslte/upper/rlc.h"
#include "srslte/radio/radio.h"
#include "srslte/phy/utils/debug.h"
#define START_TUNTAP
#define USE_RADIO
/**********************************************************************
* Program arguments processing
***********************************************************************/
#define LCID 3
typedef struct {
float rx_freq;
float tx_freq;
float rx_gain;
float tx_gain;
bool enable_gui;
int time_adv;
std::string ip_address;
}prog_args_t;
uint32_t srsapps_verbose = 1;
prog_args_t prog_args;
void args_default(prog_args_t *args) {
args->rx_freq = 2.505e9;
args->tx_freq = 2.625e9;
args->rx_gain = 50.0;
args->tx_gain = 70.0;
args->enable_gui = false;
args->time_adv = -1; // calibrated for b210
args->ip_address = "192.168.3.1";
}
void usage(prog_args_t *args, char *prog) {
printf("Usage: %s [gGIrfFdv] \n", prog);
printf("\t-f RX frequency [Default %.1f MHz]\n", args->rx_freq/1e6);
printf("\t-F TX frequency [Default %.1f MHz]\n", args->tx_freq/1e6);
printf("\t-g RX gain [Default %.1f]\n", args->rx_gain);
printf("\t-G TX gain [Default %.1f]\n", args->tx_gain);
printf("\t-I IP address [Default %s]\n", args->ip_address.c_str());
printf("\t-t time advance (in samples) [Default %d]\n", args->time_adv);
printf("\t-d Enable gui [Default disabled]\n");
printf("\t-v [increase verbosity, default none]\n");
}
void parse_args(prog_args_t *args, int argc, char **argv) {
int opt;
args_default(args);
while ((opt = getopt(argc, argv, "gGfFItdv")) != -1) {
switch (opt) {
case 'd':
args->enable_gui = true;
break;
case 'g':
args->rx_gain = atof(argv[optind]);
break;
case 'G':
args->tx_gain = atof(argv[optind]);
break;
case 'f':
args->rx_freq = atof(argv[optind]);
break;
case 'F':
args->tx_freq = atof(argv[optind]);
break;
case 'I':
args->ip_address = argv[optind];
break;
case 't':
args->time_adv = atoi(argv[optind]);
break;
case 'v':
srsapps_verbose++;
break;
default:
usage(args, argv[0]);
exit(-1);
}
}
if (args->rx_freq < 0 || args->tx_freq < 0) {
usage(args, argv[0]);
exit(-1);
}
}
LIBLTE_BYTE_MSG_STRUCT sib_buffer[2];
int setup_if_addr(char *ip_addr);
class tester : public srsue::pdcp_interface_rlc,
public srsue::rrc_interface_rlc,
public srsue::ue_interface,
public srsenb::rlc_interface_mac,
public srsenb::rrc_interface_mac,
public thread
{
public:
tester() {
rnti = 0;
}
void init(srslte::rlc *rlc_, srsenb::mac *mac_, srsenb::phy *phy_, srslte::log *log_h_, std::string ip_address) {
log_h = log_h_;
rlc = rlc_;
mac = mac_;
phy = phy_;
tun_fd = 0;
#ifdef START_TUNTAP
if (init_tuntap((char*) ip_address.c_str())) {
log_h->error("Initiating IP address\n");
}
#endif
pool = srslte::byte_buffer_pool::get_instance();
// Start reader thread
running=true;
start();
}
void write_pdu_bcch_bch(srslte::byte_buffer_t *sdu) {}
void write_pdu_bcch_dlsch(srslte::byte_buffer_t *sdu) {}
void write_pdu_pcch(srslte::byte_buffer_t *sdu) {}
void max_retx_attempted(){}
void add_user(uint16_t rnti) {}
void release_user(uint16_t rnti) {}
void upd_user(uint16_t rnti, uint16_t old_rnti) {}
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) {}
void write_pdu(uint32_t lcid, srslte::byte_buffer_t *sdu)
{
int n = write(tun_fd, sdu->msg, sdu->N_bytes);
if (n != (int) sdu->N_bytes) {
log_h->error("TUN/TAP write failure n=%d, nof_bytes=%d\n", n, sdu->N_bytes);
return;
}
log_h->debug_hex(sdu->msg, sdu->N_bytes,
"Wrote %d bytes to TUN/TAP\n",
sdu->N_bytes);
pool->deallocate(sdu);
}
int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t *payload, uint32_t nof_bytes)
{
return rlc->read_pdu(lcid, payload, nof_bytes);
}
void read_pdu_bcch_dlsch(uint32_t sib_index, uint8_t payload[srsenb::sched_interface::MAX_SIB_PAYLOAD_LEN])
{
if (sib_index < 2) {
memcpy(payload, sib_buffer[sib_index].msg, sib_buffer[sib_index].N_bytes);
}
}
void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t *payload, uint32_t nof_bytes)
{
srslte::byte_buffer_t *sdu = NULL;
log_h->info("Received PDU rnti=0x%x, lcid=%d, nof_bytes=%d\n", rnti, lcid, nof_bytes);
switch(lcid) {
case LCID:
rlc->write_pdu(lcid, payload, nof_bytes);
break;
case 0:
log_h->info("Received ConnectionRequest from rnti=0x%x\n", rnti);
// Configure User in MAC
srsenb::sched_interface::ue_cfg_t uecfg;
bzero(&uecfg, sizeof(srsenb::sched_interface::ue_cfg_t));
uecfg.maxharq_tx = 5;
uecfg.continuous_pusch = false;
uecfg.ue_bearers[0].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH;
uecfg.ue_bearers[LCID].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH;
mac->ue_cfg(rnti, &uecfg);
// configure DRB1 as UM
LIBLTE_RRC_RLC_CONFIG_STRUCT cfg;
bzero(&cfg, sizeof(LIBLTE_RRC_RLC_CONFIG_STRUCT));
cfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_BI;
cfg.dl_um_bi_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS100;
cfg.dl_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE10;
cfg.ul_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE10;
rlc->add_bearer(LCID, &cfg);
// Send dummy ConnectionSetup. MAC will send contention resolution ID automatically.
log_h->info("Sending ConnectionSetup\n");
sdu = pool->allocate();
sdu->msg[0] = 0xab;
sdu->N_bytes = 1;
rlc->write_sdu(0, sdu);
// Indicate RLC status to mac
mac->rlc_buffer_state(rnti, 0, 1, 0);
LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT dedicated;
bzero(&dedicated, sizeof(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT));
dedicated.pusch_cnfg_ded.beta_offset_ack_idx = 5;
dedicated.pusch_cnfg_ded.beta_offset_ri_idx = 12;
dedicated.pusch_cnfg_ded.beta_offset_cqi_idx = 15;
dedicated.pusch_cnfg_ded_present = true;
dedicated.sched_request_cnfg.dsr_trans_max = LIBLTE_RRC_DSR_TRANS_MAX_N4;
dedicated.sched_request_cnfg.sr_pucch_resource_idx = 0;
dedicated.sched_request_cnfg.sr_cnfg_idx = 35;
dedicated.sched_request_cnfg_present = true;
phy->set_config_dedicated(rnti, &dedicated);
usleep(500);
break;
default:
log_h->error("Received message for lcid=%d\n", lcid);
break;
}
}
void rl_failure(uint16_t rnti)
{
log_h->console("Disconnecting rnti=0x%x.\n", rnti);
mac->ue_rem(rnti);
rlc->reset();
}
private:
int tun_fd;
bool running;
srslte::log *log_h;
srslte::byte_buffer_pool *pool;
srslte::rlc *rlc;
srsenb::mac *mac;
srsenb::phy *phy;
uint16_t rnti;
bool read_enable;
int init_tuntap(char *ip_address) {
read_enable = true;
tun_fd = setup_if_addr(ip_address);
if (tun_fd<0) {
fprintf(stderr, "Error setting up IP %s\n", ip_address);
return -1;
}
printf("Created tun/tap interface at IP %s\n", ip_address);
return 0;
}
void run_thread() {
struct iphdr *ip_pkt;
uint32_t idx = 0;
int32_t N_bytes = 0;
srslte::byte_buffer_t *pdu = pool->allocate();
log_h->info("TUN/TAP reader thread running\n");
int first=1;
while(running) {
if (tun_fd > 0) {
pdu->msg[0] = 0x0;
N_bytes = read(tun_fd, &pdu->msg[idx], SRSUE_MAX_BUFFER_SIZE_BYTES-SRSUE_BUFFER_HEADER_OFFSET - idx);
}
if(N_bytes > 0)
{
if (read_enable && pdu->msg[0] != 0x60) {
pdu->N_bytes = idx + N_bytes;
ip_pkt = (struct iphdr*)pdu->msg;
log_h->debug_hex(pdu->msg, pdu->N_bytes,
"Read %d bytes from TUN/TAP\n",
N_bytes);
// Check if entire packet was received
if(ntohs(ip_pkt->tot_len) == pdu->N_bytes)
{
// Send PDU directly to RLC
pdu->set_timestamp();
rlc->write_sdu(LCID, pdu);
// Indicate RLC status to mac
mac->rlc_buffer_state(rnti, LCID, rlc->get_buffer_state(LCID), 0);
pdu = pool->allocate();
idx = 0;
} else{
idx += N_bytes;
}
}
}else{
log_h->error("Failed to read from TUN interface - gw receive thread exiting.\n");
break;
}
}
}
};
// Create classes
srslte::logger logger;
srslte::log_filter log_phy;
srslte::log_filter log_mac;
srslte::log_filter log_rlc;
srslte::log_filter log_tester;
srsenb::phy my_phy;
srsenb::mac my_mac;
srslte::rlc my_rlc;
srslte::radio my_radio;
// Local classes for testing
tester my_tester;
void generate_cell_configuration(srsenb::sched_interface::cell_cfg_t *mac_cfg, srsenb::phy_cfg_t *phy_cfg)
{
// Main cell configuration
srslte_cell_t cell;
cell.id = 0;
cell.cp = SRSLTE_CP_NORM;
cell.nof_ports = 1;
cell.nof_prb = 25;
cell.phich_length = SRSLTE_PHICH_NORM;
cell.phich_resources = SRSLTE_PHICH_R_1;
// Generate SIB1
LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT msg[2];
bzero(&msg[0], sizeof(LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT));
bzero(&msg[1], sizeof(LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT));
msg[0].N_sibs = 1;
msg[0].sibs[0].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1;
LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *sib1 = &msg[0].sibs[0].sib.sib1;
sib1->cell_id = 0x1234;
sib1->tracking_area_code = 0x1234;
sib1->freq_band_indicator = 2;
sib1->N_plmn_ids = 1;
sib1->plmn_id[0].id.mcc = 1;
sib1->plmn_id[0].id.mnc = 1;
sib1->plmn_id[0].resv_for_oper = LIBLTE_RRC_NOT_RESV_FOR_OPER;
sib1->cell_barred = LIBLTE_RRC_CELL_NOT_BARRED;
sib1->intra_freq_reselection = LIBLTE_RRC_INTRA_FREQ_RESELECTION_ALLOWED;
sib1->q_rx_lev_min = -140;
sib1->q_rx_lev_min_offset = 1;
sib1->p_max = 10;
sib1->p_max_present = true;
sib1->si_window_length = LIBLTE_RRC_SI_WINDOW_LENGTH_MS40;
sib1->N_sched_info = 1;
sib1->sched_info[0].si_periodicity = LIBLTE_RRC_SI_PERIODICITY_RF16;
sib1->sched_info[0].N_sib_mapping_info = 0;
sib1->system_info_value_tag = 8;
// Generate SIB2
msg[1].N_sibs = 2;
msg[1].sibs[0].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2;
msg[1].sibs[1].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3;
LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2 = &msg[1].sibs[0].sib.sib2;
// RACH configuration
sib2->rr_config_common_sib.rach_cnfg.num_ra_preambles = LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N64;
sib2->rr_config_common_sib.rach_cnfg.preambles_group_a_cnfg.present = false;
sib2->rr_config_common_sib.rach_cnfg.preamble_init_rx_target_pwr = LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_DBM_N90;
sib2->rr_config_common_sib.rach_cnfg.pwr_ramping_step = LIBLTE_RRC_POWER_RAMPING_STEP_DB6;
sib2->rr_config_common_sib.rach_cnfg.preamble_trans_max = LIBLTE_RRC_PREAMBLE_TRANS_MAX_N10;
sib2->rr_config_common_sib.rach_cnfg.ra_resp_win_size = LIBLTE_RRC_RA_RESPONSE_WINDOW_SIZE_SF10;
sib2->rr_config_common_sib.rach_cnfg.mac_con_res_timer = LIBLTE_RRC_MAC_CONTENTION_RESOLUTION_TIMER_SF40;
sib2->rr_config_common_sib.rach_cnfg.max_harq_msg3_tx = 4;
// BCCH
sib2->rr_config_common_sib.bcch_cnfg.modification_period_coeff = LIBLTE_RRC_MODIFICATION_PERIOD_COEFF_N16;
// PCCH
sib2->rr_config_common_sib.pcch_cnfg.default_paging_cycle = LIBLTE_RRC_DEFAULT_PAGING_CYCLE_RF128;
sib2->rr_config_common_sib.pcch_cnfg.nB = LIBLTE_RRC_NB_ONE_THIRTY_SECOND_T;
// PRACH Configuration
sib2->rr_config_common_sib.prach_cnfg.root_sequence_index = 41;
sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.high_speed_flag = false;
sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_config_index = 4;
sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_freq_offset = 2;
sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.zero_correlation_zone_config = 11;
// PDSCH configuration
sib2->rr_config_common_sib.pdsch_cnfg.p_b = 0;
sib2->rr_config_common_sib.pdsch_cnfg.rs_power = -5;
// PUSCH configuration
sib2->rr_config_common_sib.pusch_cnfg.n_sb = 1;
sib2->rr_config_common_sib.pusch_cnfg.hopping_mode = LIBLTE_RRC_HOPPING_MODE_INTER_SUBFRAME;
sib2->rr_config_common_sib.pusch_cnfg.pusch_hopping_offset = 4;
sib2->rr_config_common_sib.pusch_cnfg.enable_64_qam = false;
sib2->rr_config_common_sib.pusch_cnfg.ul_rs.cyclic_shift = 0;
sib2->rr_config_common_sib.pusch_cnfg.ul_rs.group_assignment_pusch = 0;
sib2->rr_config_common_sib.pusch_cnfg.ul_rs.group_hopping_enabled = false;
sib2->rr_config_common_sib.pusch_cnfg.ul_rs.sequence_hopping_enabled = false;
// PUCCH configuration
sib2->rr_config_common_sib.pucch_cnfg.delta_pucch_shift = LIBLTE_RRC_DELTA_PUCCH_SHIFT_DS2;
sib2->rr_config_common_sib.pucch_cnfg.n_rb_cqi = 2;
sib2->rr_config_common_sib.pucch_cnfg.n_cs_an = 0;
sib2->rr_config_common_sib.pucch_cnfg.n1_pucch_an = 12;
// SRS configuration
sib2->rr_config_common_sib.srs_ul_cnfg.present = false;
// UL power control
sib2->rr_config_common_sib.ul_pwr_ctrl.p0_nominal_pusch = -80;
sib2->rr_config_common_sib.ul_pwr_ctrl.alpha = LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_1;
sib2->rr_config_common_sib.ul_pwr_ctrl.p0_nominal_pucch = -80;
sib2->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_1 = LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1_0;
sib2->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_1b = LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1B_5;
sib2->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_2 = LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2_2;
sib2->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_2a = LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2A_2;
sib2->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_2b = LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2B_2;
sib2->rr_config_common_sib.ul_pwr_ctrl.delta_preamble_msg3 = 4;
sib2->rr_config_common_sib.ul_cp_length = LIBLTE_RRC_UL_CP_LENGTH_1;
sib2->ue_timers_and_constants.t300 = LIBLTE_RRC_T300_MS1000;
sib2->ue_timers_and_constants.t301 = LIBLTE_RRC_T301_MS1000;
sib2->ue_timers_and_constants.n310 = LIBLTE_RRC_N310_N10;
sib2->ue_timers_and_constants.t311 = LIBLTE_RRC_T311_MS1000;
sib2->ue_timers_and_constants.n311 = LIBLTE_RRC_N311_N1;
sib2->time_alignment_timer = LIBLTE_RRC_TIME_ALIGNMENT_TIMER_INFINITY;
sib2->additional_spectrum_emission = 1;
sib2->arfcn_value_eutra.present = false;
sib2->ul_bw.present = false;
LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *sib3 = &msg[1].sibs[1].sib.sib3;
bzero(sib3, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT));
sib3->q_hyst = LIBLTE_RRC_Q_HYST_DB_2;
sib3->s_non_intra_search = 6;
sib3->s_non_intra_search_present = true;
sib3->thresh_serving_low = 4;
sib3->cell_resel_prio = 6;
sib3->q_rx_lev_min = -122;
sib3->p_max = 23;
sib3->p_max_present = true;
sib3->s_intra_search = 10;
sib3->s_intra_search_present = true;
sib3->presence_ant_port_1 = true;
sib3->neigh_cell_cnfg = 1;
sib3->t_resel_eutra = 1;
// Genreate payload
LIBLTE_BIT_MSG_STRUCT bitbuffer[2];
for (int i=0;i<2;i++) {
liblte_rrc_pack_bcch_dlsch_msg(&msg[i], &bitbuffer[i]);
srslte_bit_pack_vector(bitbuffer[i].msg, sib_buffer[i].msg, bitbuffer[i].N_bits);
sib_buffer[i].N_bytes = (bitbuffer[i].N_bits-1)/8+1;
}
// Fill MAC scheduler configuration
bzero(mac_cfg, sizeof(srsenb::sched_interface::cell_cfg_t));
memcpy(&mac_cfg->cell, &cell, sizeof(srslte_cell_t));
mac_cfg->sibs[0].len = sib_buffer[0].N_bytes;
mac_cfg->sibs[0].period_rf = 8; // Fixed to 8 rf
mac_cfg->sibs[1].len = sib_buffer[1].N_bytes;
mac_cfg->sibs[1].period_rf = liblte_rrc_si_periodicity_num[sib1->sched_info[0].si_periodicity];
mac_cfg->si_window_ms = liblte_rrc_si_window_length_num[sib1->si_window_length];
mac_cfg->prach_rar_window = liblte_rrc_ra_response_window_size_num[sib2->rr_config_common_sib.rach_cnfg.ra_resp_win_size];
// Copy PHY common configuration
bzero(phy_cfg, sizeof(srsenb::phy_cfg_t));
memcpy(&phy_cfg->cell, &cell, sizeof(srslte_cell_t));
memcpy(&phy_cfg->prach_cnfg, &sib2->rr_config_common_sib.prach_cnfg, sizeof(LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT));
memcpy(&phy_cfg->pdsch_cnfg, &sib2->rr_config_common_sib.pdsch_cnfg, sizeof(LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT));
memcpy(&phy_cfg->pusch_cnfg, &sib2->rr_config_common_sib.pusch_cnfg, sizeof(LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT));
memcpy(&phy_cfg->pucch_cnfg, &sib2->rr_config_common_sib.pucch_cnfg, sizeof(LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT));
memcpy(&phy_cfg->srs_ul_cnfg, &sib2->rr_config_common_sib.srs_ul_cnfg, sizeof(LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT));
}
int main(int argc, char *argv[])
{
parse_args(&prog_args, argc, argv);
logger.init("/tmp/ip_test.log");
log_phy.init("PHY ", &logger, true);
log_mac.init("MAC ", &logger, true);
log_rlc.init("RLC ", &logger);
log_tester.init("TEST", &logger);
logger.log("\n\n");
if (srsapps_verbose == 1) {
log_phy.set_level(srslte::LOG_LEVEL_INFO);
log_phy.set_hex_limit(100);
log_mac.set_level(srslte::LOG_LEVEL_DEBUG);
log_mac.set_hex_limit(100);
log_rlc.set_level(srslte::LOG_LEVEL_DEBUG);
log_rlc.set_hex_limit(1000);
log_tester.set_level(srslte::LOG_LEVEL_DEBUG);
log_tester.set_hex_limit(100);
printf("Log level info\n");
}
if (srsapps_verbose == 2) {
log_phy.set_level(srslte::LOG_LEVEL_DEBUG);
log_phy.set_hex_limit(100);
log_mac.set_level(srslte::LOG_LEVEL_DEBUG);
log_mac.set_hex_limit(100);
log_rlc.set_level(srslte::LOG_LEVEL_DEBUG);
log_rlc.set_hex_limit(100);
log_tester.set_level(srslte::LOG_LEVEL_DEBUG);
log_tester.set_hex_limit(100);
srslte_verbose = SRSLTE_VERBOSE_DEBUG;
printf("Log level debug\n");
}
// Init Radio and PHY
#ifdef USE_RADIO
my_radio.init();
#else
my_radio.init(NULL, (char*) "dummy");
#endif
my_radio.set_tx_freq(prog_args.tx_freq);
my_radio.set_tx_gain(prog_args.tx_gain);
my_radio.set_rx_freq(prog_args.rx_freq);
my_radio.set_rx_gain(prog_args.rx_gain);
//my_radio.set_tx_adv_neg(true);
if (prog_args.time_adv >= 0) {
printf("Setting TA=%d samples\n", prog_args.time_adv);
my_radio.set_tx_adv(prog_args.time_adv);
}
// Configuure cell
srsenb::phy_cfg_t phy_cfg;
srsenb::sched_interface::cell_cfg_t mac_cfg;
srsenb::mac_args_t mac_args;
srsenb::phy_args_t phy_args;
mac_args.link_failure_nof_err = 10;
phy_args.equalizer_mode = "mmse";
phy_args.estimator_fil_w = 0.2;
phy_args.max_prach_offset_us = 50;
phy_args.nof_phy_threads = 1;
phy_args.pusch_max_its = 5;
generate_cell_configuration(&mac_cfg, &phy_cfg);
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_tester.init(&my_rlc, &my_mac, &my_phy, &log_tester, prog_args.ip_address);
if (prog_args.enable_gui) {
sleep(1);
my_phy.start_plot();
}
bool running = true;
while(running) {
printf("Main running\n");
sleep(1);
}
my_phy.stop();
my_mac.stop();
}
/******************* This is copied from srsue gw **********************/
int setup_if_addr(char *ip_addr)
{
char *dev = (char*) "tun_srsenb";
// Construct the TUN device
int tun_fd = open("/dev/net/tun", O_RDWR);
if(0 > tun_fd)
{
perror("open");
return(-1);
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
strncpy(ifr.ifr_ifrn.ifrn_name, dev, IFNAMSIZ);
if(0 > ioctl(tun_fd, TUNSETIFF, &ifr))
{
perror("ioctl1");
return -1;
}
// Bring up the interface
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if(0 > ioctl(sock, SIOCGIFFLAGS, &ifr))
{
perror("socket");
return -1;
}
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
if(0 > ioctl(sock, SIOCSIFFLAGS, &ifr))
{
perror("ioctl2");
return -1;
}
// Setup the IP address
sock = socket(AF_INET, SOCK_DGRAM, 0);
ifr.ifr_addr.sa_family = AF_INET;
((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = inet_addr(ip_addr);
if(0 > ioctl(sock, SIOCSIFADDR, &ifr))
{
perror("ioctl");
return -1;
}
ifr.ifr_netmask.sa_family = AF_INET;
((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr("255.255.255.0");
if(0 > ioctl(sock, SIOCSIFNETMASK, &ifr))
{
perror("ioctl");
return -1;
}
return(tun_fd);
}

View File

@ -0,0 +1,77 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2016 Software Radio Systems Limited
*
*
*/
#include <assert.h>
#include "upper/common_enb.h"
#include "srslte/asn1/liblte_rrc.h"
void rrc_plmn_test()
{
LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_in, plmn_out;
plmn_in.mcc = 0xF123;
plmn_in.mnc = 0xFF45;
// 2-digit MNC test
uint8_t bit_buf[32];
uint8_t *ie_ptr = bit_buf;
liblte_rrc_pack_plmn_identity_ie(&plmn_in, &ie_ptr);
uint8_t byte_buf[4];
liblte_pack(bit_buf, 22, byte_buf);
uint8_t ref[3] = {0x89, 0x19, 0x05};
for(int i=0;i<3;i++) {
assert(ref[i] == byte_buf[i]);
}
ie_ptr = bit_buf;
liblte_rrc_unpack_plmn_identity_ie(&ie_ptr, &plmn_out);
assert(plmn_in.mcc == plmn_out.mcc);
assert(plmn_in.mnc == plmn_out.mnc);
// 3-digit MNC test
plmn_in.mnc = 0xF456;
ie_ptr = bit_buf;
liblte_rrc_pack_plmn_identity_ie(&plmn_in, &ie_ptr);
liblte_pack(bit_buf, 26, byte_buf);
uint8_t ref2[4] = {0x89, 0x1D, 0x15, 0x02};
for(int i=0;i<3;i++) {
assert(ref2[i] == byte_buf[i]);
}
ie_ptr = bit_buf;
liblte_rrc_unpack_plmn_identity_ie(&ie_ptr, &plmn_out);
assert(plmn_in.mcc == plmn_out.mcc);
assert(plmn_in.mnc == plmn_out.mnc);
}
void s1ap_plmn_test()
{
uint16_t mcc = 0xF123;
uint16_t mnc = 0xFF45;
uint32_t plmn;
// 2-digit MNC test
srsenb::s1ap_mccmnc_to_plmn(mcc, mnc, &plmn);
assert(plmn == 0x21F354);
srsenb::s1ap_plmn_to_mccmnc(plmn, &mcc, &mnc);
assert(mcc == 0xF123);
assert(mnc == 0xFF45);
// 3-digit MNC test
mnc = 0xF456;
srsenb::s1ap_mccmnc_to_plmn(mcc, mnc, &plmn);
assert(plmn == 0x216354);
srsenb::s1ap_plmn_to_mccmnc(plmn, &mcc, &mnc);
assert(mcc == 0xF123);
assert(mnc == 0xF456);
}
int main(int argc, char **argv)
{
rrc_plmn_test();
s1ap_plmn_test();
}