mirror of https://github.com/pvnis/srsRAN_4G.git
added srsENB code
parent
8a367bf825
commit
e5ae82aef1
@ -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)
|
@ -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)
|
@ -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)
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
|
@ -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
|
@ -0,0 +1,5 @@
|
||||
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/version.h.in
|
||||
${PROJECT_BINARY_DIR}/version.h
|
||||
)
|
@ -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
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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)
|
||||
};
|
||||
};
|
@ -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.
|
||||
};
|
||||
|
@ -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 "")
|
||||
|
@ -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, >pu, &pdcp_log);
|
||||
rrc.init(&rrc_cfg, &phy, &mac, &rlc, &pdcp, &s1ap, >pu, &rrc_log);
|
||||
s1ap.init(args->enb.s1ap, &rrc, &s1ap_log);
|
||||
gtpu.init(args->enb.s1ap.gtp_bind_addr, args->enb.s1ap.mme_addr, &pdcp, >pu_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
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -0,0 +1,5 @@
|
||||
file(GLOB SOURCES "*.cc")
|
||||
add_library(srsenb_mac SHARED ${SOURCES})
|
||||
target_link_libraries(srsenb_mac)
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -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(®s, 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(®s, &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(®s, &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, ®s, 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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++;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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, ¶m);
|
||||
if (pthread_create(&plot_thread, &attr, plot_thread_run, worker)) {
|
||||
perror("pthread_create");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
file(GLOB SOURCES "*.cc")
|
||||
add_library(srsenb_upper SHARED ${SOURCES})
|
||||
target_link_libraries(srsenb_upper ${SRSLTE_LIBRARIES})
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,3 @@
|
||||
|
||||
add_subdirectory(mac)
|
||||
add_subdirectory(upper)
|
@ -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})
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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 )
|
||||
|
@ -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);
|
||||
}
|
@ -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();
|
||||
}
|
Loading…
Reference in New Issue