add initial TTCN3 code

master
Andre Puschmann 5 years ago
parent 95b2239dd5
commit 9e1c46dfb0

@ -74,6 +74,8 @@ option(ENABLE_SOAPYSDR "Enable SoapySDR" ON)
option(ENABLE_ZEROMQ "Enable ZeroMQ" ON)
option(ENABLE_HARDSIM "Enable support for SIM cards" ON)
option(ENABLE_TTCN3 "Enable TTCN3 test binaries" OFF)
option(BUILD_STATIC "Attempt to statically link external deps" OFF)
option(RPATH "Enable RPATH" OFF)
option(ENABLE_ASAN "Enable gcc/clang address sanitizer" OFF)
@ -227,6 +229,14 @@ if(ENABLE_GUI)
endif(SRSGUI_FOUND)
endif(ENABLE_GUI)
if (ENABLE_TTCN3)
find_package(RapidJSON REQUIRED)
add_definitions(-DENABLE_TTCN3)
include_directories(${RAPIDJSON_INCLUDE_DIRS})
link_directories(${RAPIDJSON_LIBRARY_DIRS})
message(STATUS "Building with TTCN3 binaries")
endif (ENABLE_TTCN3)
########################################################################
# Install Dirs
########################################################################

@ -28,6 +28,7 @@
#define SRSUE_UE_LTE_PHY_BASE_H
#include "srslte/common/log_filter.h"
#include "srslte/interfaces/common_interfaces.h"
#include "srsue/hdr/phy/ue_phy_base.h"
namespace srsue {

@ -20,6 +20,10 @@
add_subdirectory(upper)
if (ENABLE_TTCN3)
add_subdirectory(ttcn3)
endif (ENABLE_TTCN3)
add_executable(metrics_test metrics_test.cc ../src/metrics_stdout.cc ../src/metrics_csv.cc)
target_link_libraries(metrics_test srslte_phy srslte_common)
add_test(metrics_test metrics_test -o ${CMAKE_CURRENT_BINARY_DIR}/ue_metrics.csv)

@ -0,0 +1,22 @@
#
# Copyright 2013-2019 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/.
#
add_subdirectory(src)
add_subdirectory(test)

@ -0,0 +1,23 @@
srsUE conformance testing
=========================
This folder includes an srsUE extension that allows to test
the upper-layer protocol stack against the UE conformance tests specified
by 3GPP/ETSI in TS 36.523 [1].
The tester itself is built with Eclipse Titan [2] using the 3GPP EUTRA (LTE/EPC) UE Test Suites [3]. It is, however,
not part of the srsLTE repository.
The extensions present in this folder add a basic system simulator (SS) that talks over an ideal PHY to the
fully unmodified UE upper-layer stack. It SS itself interfaces over multiple TCP/IP sockets to the TTCN3 tester, i.e.,
implements (a subset of) the interfaces specified in [1].
Resources
---------
[1] https://www.etsi.org/deliver/etsi_ts/136500_136599/13652301
[2] https://projects.eclipse.org/projects/tools.titan
[3] http://www.ttcn-3.org/index.php/downloads/publicts/publicts-3gpp

@ -0,0 +1,77 @@
/*
* Copyright 2013-2019 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/.
*
*/
/*! \brief Utility functions for the DUT.
*
*/
#ifndef SRSUE_TTCN3_DUT_UTILS_H
#define SRSUE_TTCN3_DUT_UTILS_H
#include <set>
#include <sstream>
std::string get_filename_with_tc_name(const std::string& str, const uint32_t run_id, const std::string tc_name)
{
// split base path into components
std::set<char> delims;
delims.insert('/');
std::vector<std::string> result;
char const* pch = str.c_str();
char const* start = pch;
for (; *pch; ++pch) {
if (delims.find(*pch) != delims.end()) {
if (start != pch) {
std::string str(start, pch);
result.push_back(str);
} else {
result.push_back("");
}
start = pch + 1;
}
}
result.push_back(start);
// prepend TC name to last element (the actual filename)
stringstream filename_ss;
filename_ss << tc_name << "_"
<< "run" << run_id << "_" << result.back();
std::string final_path;
std::vector<std::string>::iterator path_it;
for (path_it = result.begin() + 1; path_it != --result.end(); ++path_it) {
final_path += "/";
final_path += *path_it;
}
final_path += "/";
final_path += filename_ss.str();
return final_path;
}
std::string get_tc_name(const std::string& str)
{
// split after dot
std::string::size_type pos = str.find('.');
return (pos == str.npos ? str : str.substr(pos + 1, -1));
}
#endif // SRSUE_TTCN3_DUT_UTILS_H

@ -0,0 +1,149 @@
/*
* Copyright 2013-2019 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/.
*
*/
#ifndef SRSUE_TTCN3_LTE_PHY_H
#define SRSUE_TTCN3_LTE_PHY_H
#include "srsue/hdr/phy/ue_lte_phy_base.h"
#include "srsue/hdr/ue.h"
#include "ttcn3_interfaces.h"
#include <srslte/interfaces/ue_interfaces.h>
#include <srslte/phy/phch/dci.h>
using namespace srsue;
using namespace srslte;
namespace srsue {
class lte_ttcn3_phy : public ue_lte_phy_base
{
public:
typedef struct {
srslte_cell_t info;
float power;
uint32_t earfcn;
} cell_t;
typedef std::vector<cell_t> cell_list_t;
lte_ttcn3_phy(srslte::logger* logger_);
~lte_ttcn3_phy();
int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, syssim_interface_phy* syssim_);
int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, radio_interface_phy* radio_);
// ue_phy_base interface
int init(const phy_args_t& args_);
void stop();
void set_earfcn(std::vector<uint32_t> earfcns);
void force_freq(float dl_freq, float ul_freq);
void wait_initialize();
void start_plot();
void get_metrics(phy_metrics_t* m);
std::string get_type();
// The interface for the SS
void set_cell_map(const cell_list_t& cells_);
// phy_interface_rrc_lte
void get_current_cell(srslte_cell_t* cell_, uint32_t* earfcn_ = NULL);
uint32_t get_current_earfcn();
uint32_t get_current_pci();
void set_config_scell(asn1::rrc::scell_to_add_mod_r10_s* scell_config);
void enable_pregen_signals(bool enable);
void set_activation_deactivation_scell(uint32_t cmd);
void
set_config(srslte::phy_cfg_t& config, uint32_t cc_idx = 0, uint32_t earfcn = 0, srslte_cell_t* cell_info = nullptr);
void set_config_tdd(srslte_tdd_config_t& tdd_config);
void set_config_mbsfn_sib2(asn1::rrc::sib_type2_s* sib2);
void set_config_mbsfn_sib13(asn1::rrc::sib_type13_r9_s* sib13);
void set_config_mbsfn_mcch(asn1::rrc::mcch_msg_s* mcch);
// Measurements interface
void meas_reset();
int meas_start(uint32_t earfcn, int pci = -1);
int meas_stop(uint32_t earfcn, int pci = -1);
// phy_interface_mac_lte
void set_mch_period_stop(uint32_t stop);
// Cell search and selection procedures
cell_search_ret_t cell_search(phy_cell_t* found_cell);
bool cell_select(phy_cell_t* cell);
bool cell_is_camping();
void reset();
// phy_interface_mac_lte
void configure_prach_params();
void prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm);
prach_info_t prach_get_info();
void sr_send();
int sr_last_tx_tti();
// phy_interface_mac_common
void set_crnti(uint16_t rnti);
void set_timeadv_rar(uint32_t ta_cmd);
void set_timeadv(uint32_t ta_cmd);
void set_rar_grant(uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN], uint16_t rnti);
uint32_t get_current_tti();
float get_phr();
float get_pathloss_db();
// phy_interface_syssim
void set_current_tti(uint32_t tti);
void new_grant_ul(mac_interface_phy_lte::mac_grant_ul_t ul_mac_grant);
void new_tb(const srsue::mac_interface_phy_lte::mac_grant_dl_t, const uint8_t* data);
// Radio interface
void radio_overflow();
void radio_failure();
void run_tti();
private:
srslte::logger* logger = nullptr;
srslte::log_filter log;
// The current cell
cell_list_t cells;
cell_t pcell = {};
phy_cfg_t phy_cfg = {};
uint32_t current_tti = 0;
uint16_t current_temp_rnti = 0;
uint32_t cc_idx = 0;
int prach_tti_tx = -1;
int sr_tx_tti = -1;
bool sr_pending = false;
uint32_t ra_trans_cnt = 0;
stack_interface_phy_lte* stack = nullptr;
syssim_interface_phy* syssim = nullptr;
};
} // namespace srsue
#endif

@ -0,0 +1,360 @@
/*
* Copyright 2013-2019 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/.
*
*/
/*! \brief This class provides helpers for encoding JSON-formated responses
* for TTCN3.
*
*/
#ifndef SRSUE_TTCN3_HELPERS_H
#define SRSUE_TTCN3_HELPERS_H
#include "rapidjson/document.h" // rapidjson's DOM-style API
#include "rapidjson/prettywriter.h" // for stringify JSON
#include <algorithm>
#include <assert.h>
#include <bitset>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
using namespace rapidjson;
class ttcn3_helpers
{
public:
typedef struct {
bool rb_is_srb;
uint8_t rb_id;
uint32_t ul_value;
uint32_t dl_value;
} pdcp_count_t;
static std::string get_ctrl_cnf(const std::string protocol_, const std::string version_, const std::string addr_)
{
Document resp;
resp.SetObject();
// Create members of common object
Value ctrl(kObjectType);
// The connection ID object
Value conn_id(kObjectType);
// Protocol
Value protocol(protocol_.c_str(), resp.GetAllocator());
conn_id.AddMember("Protocol", protocol, resp.GetAllocator());
// Version
Value ipAddr(kObjectType);
Value version(version_.c_str(), resp.GetAllocator());
Value addr(addr_.c_str(), resp.GetAllocator());
if (version_ == "V4") {
Value v4(kObjectType);
v4.AddMember("Addr", addr, resp.GetAllocator());
ipAddr.AddMember("V4", v4, resp.GetAllocator());
} else if (version_ == "V6") {
Value v6(kObjectType);
v6.AddMember("Addr", addr, resp.GetAllocator());
ipAddr.AddMember("V6", v6, resp.GetAllocator());
} else {
fprintf(stderr, "Unsupported protocol version: %s.\n", version_.c_str());
return std::string("");
}
// Local
Value local(kObjectType);
local.AddMember("IpAddr", ipAddr, resp.GetAllocator());
if (protocol_ == "udp") {
local.AddMember("Port", 33, resp.GetAllocator());
}
// Remote
Value rem(kObjectType);
// Add members to Connection ID
conn_id.AddMember("Local", local, resp.GetAllocator());
conn_id.AddMember("Remote", rem, resp.GetAllocator());
// The Indication object
Value ind(kObjectType);
Value ind_protocol_val(kObjectType);
ind_protocol_val.AddMember("SocketCnf", true, resp.GetAllocator());
if (protocol_ == "udp") {
ind.AddMember("UDP", ind_protocol_val, resp.GetAllocator());
} else if (protocol_ == "icmpv6") {
ind.AddMember("ICMP", ind_protocol_val, resp.GetAllocator());
} else {
fprintf(stderr, "Unsupported protocol %s.\n", protocol_.c_str());
return std::string("");
}
ctrl.AddMember("ConnectionId", conn_id, resp.GetAllocator());
ctrl.AddMember("Ind", ind, resp.GetAllocator());
resp.AddMember("CTRL", ctrl, resp.GetAllocator());
// JSON-ize
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
resp.Accept(writer);
// Return as std::string
return std::string(buffer.GetString());
}
static std::string get_drbmux_common_ind_cnf()
{
Document resp;
resp.SetObject();
resp.AddMember("Confirm", true, resp.GetAllocator());
// JSON-ize
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
resp.Accept(writer);
// Return as std::string
return std::string(buffer.GetString());
}
static std::string get_pdcp_count_response(const std::string cell_,
const std::vector<ttcn3_helpers::pdcp_count_t>& bearers)
{
Document resp;
resp.SetObject();
// Create members of common object
// Cell
Value cell(cell_.c_str(), resp.GetAllocator());
// RoutingInfo
Value routing_info(kObjectType);
routing_info.AddMember("None", true, resp.GetAllocator());
// TimingInfo
Value timing_info(kObjectType);
timing_info.AddMember("Now", true, resp.GetAllocator());
// Result
Value result(kObjectType);
result.AddMember("Success", true, resp.GetAllocator());
// Now, create the common object itself and add members
Value common(kObjectType);
common.AddMember("CellId", cell, resp.GetAllocator());
common.AddMember("RoutingInfo", routing_info, resp.GetAllocator());
common.AddMember("TimingInfo", timing_info, resp.GetAllocator());
common.AddMember("Result", result, resp.GetAllocator());
// Add all bearers
rapidjson::Value bearer_array(rapidjson::kArrayType);
std::vector<ttcn3_helpers::pdcp_count_t>::const_iterator it;
for (it = bearers.begin(); it != bearers.end(); ++it) {
// SRB0
Value radio_bearer_id(kObjectType);
radio_bearer_id.AddMember(it->rb_is_srb ? "Srb" : "Drb", it->rb_id, resp.GetAllocator());
Value ul(kObjectType);
Value rb_format_ul(it->rb_is_srb ? "PdcpCount_Srb" : "PdcpCount_DrbLongSQN", resp.GetAllocator());
ul.AddMember("Format", rb_format_ul, resp.GetAllocator());
// convert int to sstream, then to RapidJSON value and add as member
stringstream value_ss;
value_ss << std::bitset<32>(it->ul_value);
Value ul_value(value_ss.str().c_str(), resp.GetAllocator());
ul.AddMember("Value", ul_value, resp.GetAllocator());
Value dl(kObjectType);
Value rb_format_dl(it->rb_is_srb ? "PdcpCount_Srb" : "PdcpCount_DrbLongSQN", resp.GetAllocator());
dl.AddMember("Format", rb_format_dl, resp.GetAllocator());
// do the same conversion for the DL value
value_ss.str("");
value_ss.clear();
value_ss << std::bitset<32>(it->dl_value);
Value dl_value(value_ss.str().c_str(), resp.GetAllocator());
dl.AddMember("Value", dl_value, resp.GetAllocator());
// The Get object combines the information of all requested bearer
Value get_element(kObjectType);
get_element.AddMember("RadioBearerId", radio_bearer_id, resp.GetAllocator());
get_element.AddMember("UL", ul, resp.GetAllocator());
get_element.AddMember("DL", dl, resp.GetAllocator());
bearer_array.PushBack(get_element, resp.GetAllocator());
}
// The confirm object
Value confirm_key(kObjectType);
// Add array
confirm_key.AddMember("Get", bearer_array, resp.GetAllocator());
Value confirm(kObjectType);
confirm.AddMember("PdcpCount", confirm_key, resp.GetAllocator());
resp.AddMember("Common", common, resp.GetAllocator());
resp.AddMember("Confirm", confirm, resp.GetAllocator());
// JSON-ize
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
resp.Accept(writer);
// Return as std::string
return std::string(buffer.GetString());
}
static std::string get_basic_sys_req_cnf(const std::string cell_, const std::string confirm_key_)
{
Document resp;
resp.SetObject();
// Create members of common object
// Cell
Value cell(cell_.c_str(), resp.GetAllocator());
// RoutingInfo
Value routing_info(kObjectType);
routing_info.AddMember("None", true, resp.GetAllocator());
// TimingInfo
Value timing_info(kObjectType);
timing_info.AddMember("Now", true, resp.GetAllocator());
// Result
Value result(kObjectType);
result.AddMember("Success", true, resp.GetAllocator());
// Now, create the common object itself and add members
Value common(kObjectType);
common.AddMember("CellId", cell, resp.GetAllocator());
common.AddMember("RoutingInfo", routing_info, resp.GetAllocator());
common.AddMember("TimingInfo", timing_info, resp.GetAllocator());
common.AddMember("Result", result, resp.GetAllocator());
// The confirm object
Value confirm_key(confirm_key_.c_str(), resp.GetAllocator());
Value confirm_val(true);
Value confirm(kObjectType);
confirm.AddMember(confirm_key, confirm_val, resp.GetAllocator());
resp.AddMember("Common", common, resp.GetAllocator());
resp.AddMember("Confirm", confirm, resp.GetAllocator());
// JSON-ize
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
resp.Accept(writer);
// Return as std::string
return std::string(buffer.GetString());
}
static std::string
get_sys_req_cnf_with_time(const std::string cell_, const std::string confirm_key_, const uint32_t tti)
{
Document resp;
resp.SetObject();
// Create members of common object
// Cell
Value cell(cell_.c_str(), resp.GetAllocator());
// RoutingInfo
Value routing_info(kObjectType);
routing_info.AddMember("None", true, resp.GetAllocator());
// TimingInfo
// SFN
uint32_t sfn = tti / 10;
Value sfn_key(kObjectType);
sfn_key.AddMember("Number", sfn, resp.GetAllocator());
// Actual subframe index
uint32_t sf_idx = tti % 10;
Value sf_idx_key(kObjectType);
sf_idx_key.AddMember("Number", sf_idx, resp.GetAllocator());
// Put it all together
Value subframe_key(kObjectType);
subframe_key.AddMember("SFN", sfn_key, resp.GetAllocator());
subframe_key.AddMember("Subframe", sf_idx_key, resp.GetAllocator());
Value timing_info(kObjectType);
timing_info.AddMember("SubFrame", subframe_key, resp.GetAllocator());
// Result
Value result(kObjectType);
result.AddMember("Success", true, resp.GetAllocator());
// Now, create the common object itself and add members
Value common(kObjectType);
common.AddMember("CellId", cell, resp.GetAllocator());
common.AddMember("RoutingInfo", routing_info, resp.GetAllocator());
common.AddMember("TimingInfo", timing_info, resp.GetAllocator());
common.AddMember("Result", result, resp.GetAllocator());
// The confirm object
Value confirm_key(confirm_key_.c_str(), resp.GetAllocator());
Value confirm_val(true);
Value confirm(kObjectType);
confirm.AddMember(confirm_key, confirm_val, resp.GetAllocator());
resp.AddMember("Common", common, resp.GetAllocator());
resp.AddMember("Confirm", confirm, resp.GetAllocator());
// JSON-ize
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
resp.Accept(writer);
// Return as std::string
return std::string(buffer.GetString());
}
static bool requires_confirm(Document& document)
{
const Value& a = document["Common"];
// check cnf flag
assert(a.HasMember("ControlInfo"));
const Value& b = a["ControlInfo"];
assert(b.HasMember("CnfFlag"));
const Value& config_flag = b["CnfFlag"];
assert(config_flag.IsBool());
return config_flag.GetBool();
}
};
#endif // SRSUE_TTCN3_HELPERS_H

@ -0,0 +1,72 @@
/*
* Copyright 2013-2019 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/.
*
*/
#ifndef SRSUE_TTCN3_INTERFACES_H
#define SRSUE_TTCN3_INTERFACES_H
#include "srslte/common/common.h"
#include "srslte/interfaces/ue_interfaces.h"
// Interface used by system interface to communicate with main component
class syssim_interface
{
public:
virtual void tc_start(const char* name) = 0;
virtual void tc_end() = 0;
virtual void power_off_ue() = 0;
virtual void switch_on_ue() = 0;
virtual void switch_off_ue() = 0;
virtual void set_cell_config(std::string cell_name, uint32_t earfcn, srslte_cell_t cell, const float power) = 0;
virtual void set_cell_attenuation(std::string cell_name, const float attenuation) = 0;
virtual void add_bcch_pdu(srslte::unique_byte_buffer_t pdu) = 0;
virtual void add_ccch_pdu(srslte::unique_byte_buffer_t pdu) = 0;
virtual void add_dcch_pdu(uint32_t lcid, srslte::unique_byte_buffer_t pdu) = 0;
virtual void add_pch_pdu(srslte::unique_byte_buffer_t pdu) = 0;
virtual void add_srb(uint32_t lcid, srslte::pdcp_config_t pdcp_config) = 0;
virtual void del_srb(uint32_t lcid) = 0;
virtual uint32_t get_tti() = 0;
virtual int set_as_security(const uint32_t lcid,
const std::array<uint8_t, 32> k_rrc_enc,
const std::array<uint8_t, 32> k_rrc_int,
const std::array<uint8_t, 32> k_up_enc,
const srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo,
const srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo) = 0;
};
class syssim_interface_phy
{
public:
virtual void prach_indication(uint32_t preamble_index, const uint32_t& cell_id) = 0;
virtual void sr_req(uint32_t tti_tx) = 0;
// 0; virtual void
virtual void tx_pdu(const uint8_t* payload, const int len, const uint32_t tx_tti) = 0;
};
class phy_interface_syssim
{
public:
virtual void set_current_tti(uint32_t tti) = 0;
virtual uint16_t get_dl_sched_rnti(uint32_t tti) = 0;
virtual void new_grant_ul(const srsue::mac_interface_phy_lte::mac_grant_ul_t grant) = 0;
virtual void new_tb(const srsue::mac_interface_phy_lte::mac_grant_dl_t mac_grant, const uint8_t* data) = 0;
};
#endif // SRSUE_TTCN3_INTERFACES_H

@ -0,0 +1,125 @@
/*
* Copyright 2013-2019 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/.
*
*/
#ifndef SRSUE_TTCN3_IP_CTRL_INTERFACE_H
#define SRSUE_TTCN3_IP_CTRL_INTERFACE_H
#include "rapidjson/document.h"
#include "rapidjson/prettywriter.h"
#include "srslte/common/netsource_handler.h"
#include "srslte/phy/io/netsource.h"
#include "ttcn3_helpers.h"
using namespace rapidjson;
// The IP CTRL interface to the IP_PTC
class ttcn3_ip_ctrl_interface : public netsource_handler
{
public:
ttcn3_ip_ctrl_interface() : netsource_handler("TTCN3_IP_CTRL_IF"){};
~ttcn3_ip_ctrl_interface(){};
void init(srslte::log* log_, std::string net_ip_, uint32_t net_port_)
{
net_ip = net_ip_;
net_port = net_port_;
log = log_;
initialized = true;
log->debug("Initialized.\n");
}
private:
void run_thread()
{
// open TCP socket
if (srslte_netsource_init(&net_source, net_ip.c_str(), net_port, SRSLTE_NETSOURCE_TCP)) {
fprintf(stderr, "Error creating input TCP socket at port %d\n", net_port);
exit(-1);
}
log->info("Listening on %s:%d for incoming connections ..\n", net_ip.c_str(), net_port);
running = true;
int n;
while (run_enable) {
log->debug("Reading from IP_CTRL port ..\n");
n = srslte_netsource_read(&net_source, rx_buf->begin(), RX_BUF_SIZE);
if (n > 0) {
rx_buf->at(n) = '\0';
Document document;
if (document.Parse((char*)rx_buf->begin()).HasParseError()) {
log->error_hex(rx_buf->begin(), n, "Error parsing incoming data.\n");
break;
}
assert(document.IsObject());
// Pretty-print
StringBuffer buffer;
PrettyWriter<StringBuffer> writer(buffer);
document.Accept(writer);
log->info("Received %d bytes\n%s\n", n, (char*)buffer.GetString());
// Get message
if (document.HasMember("RoutingInfo")) {
log->info("Received RoutingInfo\n");
handle_routing_info(document);
} else {
log->error("Received unknown request.\n");
}
} else if (n == 0) {
log->error("Receiving null from network\n");
} else {
log->error("Error receiving from network\n");
}
}
running = false;
srslte_netsource_free(&net_source);
}
void handle_routing_info(Document& document)
{
// get CTRL
const Value& routes = document["RoutingInfo"];
assert(routes.IsArray());
// iterate over all routes
for (Value::ConstValueIterator itr = routes.Begin(); itr != routes.End(); ++itr) {
assert(itr->HasMember("IpInfo"));
// printf("Configuring attenuation of %s to %ddB\n", id.GetString(), att["Value"].GetInt());
// FIXME: actually do configuration
}
// What else to check?
std::string resp = ttcn3_helpers::get_drbmux_common_ind_cnf();
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length());
}
};
#endif // SRSUE_TTCN3_IP_CTRL_INTERFACE_H

@ -0,0 +1,134 @@
/*
* Copyright 2013-2019 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/.
*
*/
#ifndef SRSUE_TTCN3_IP_SOCK_INTERFACE_H
#define SRSUE_TTCN3_IP_SOCK_INTERFACE_H
// The IP Socket interface to the IP_PTC
class ttcn3_ip_sock_interface : public netsource_handler
{
public:
ttcn3_ip_sock_interface() : netsource_handler("TTCN3_IP_SOCK_IF"){};
~ttcn3_ip_sock_interface(){};
void init(srslte::log* log_, std::string net_ip_, uint32_t net_port_)
{
net_ip = net_ip_;
net_port = net_port_;
log = log_;
initialized = true;
log->debug("Initialized.\n");
}
private:
void run_thread()
{
if (!initialized) {
fprintf(stderr, "IP_SOCK interface not initialized. Exiting.\n");
exit(-1);
}
// open TCP socket
if (srslte_netsource_init(&net_source, net_ip.c_str(), net_port, SRSLTE_NETSOURCE_TCP)) {
fprintf(stderr, "Error creating input TCP socket at port %d\n", net_port);
exit(-1);
}
log->info("Listening on %s:%d for incoming connections ..\n", net_ip.c_str(), net_port);
running = true;
int n;
while (run_enable) {
log->debug("Reading from IP_SOCK port ..\n");
n = srslte_netsource_read(&net_source, rx_buf->begin(), RX_BUF_SIZE);
if (n > 0) {
rx_buf->at(n) = '\0';
Document document;
if (document.Parse((char*)rx_buf->begin()).HasParseError()) {
log->error_hex(rx_buf->begin(), n, "Error parsing incoming data.\n");
break;
}
assert(document.IsObject());
// Pretty-print
StringBuffer buffer;
PrettyWriter<StringBuffer> writer(buffer);
document.Accept(writer);
log->info("Received %d bytes\n%s\n", n, (char*)buffer.GetString());
// Get message
if (document.HasMember("CTRL")) {
log->info("Received CTRL command.\n");
handle_ctrl(document);
} else {
log->error("Received unknown request.\n");
}
} else if (n == 0) {
log->error("Receiving null from network\n");
} else {
log->error("Error receiving from network\n");
// exit(-1);
}
}
running = false;
srslte_netsource_free(&net_source);
}
void handle_ctrl(Document& document)
{
// get CTRL
const Value& ctrl = document["CTRL"];
assert(ctrl.HasMember("ConnectionId"));
const Value& conn_id = ctrl["ConnectionId"];
assert(conn_id.HasMember("Protocol"));
const Value& protocol = conn_id["Protocol"];
assert(protocol.IsString());
// Get IP version and addr
std::string ip_version;
const Value& local = conn_id["Local"];
assert(local.HasMember("IpAddr"));
const Value& ipAddr = local["IpAddr"];
if (ipAddr.HasMember("V4")) {
ip_version = "V4";
} else if (ipAddr.HasMember("V6")) {
ip_version = "V6";
}
assert(ipAddr[ip_version.c_str()].HasMember("Addr"));
const Value& addr = ipAddr[ip_version.c_str()]["Addr"];
// Todo: Handle command
// Send response
string resp = ttcn3_helpers::get_ctrl_cnf(protocol.GetString(), ip_version, addr.GetString());
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length());
}
};
#endif // SRSUE_TTCN3_IP_SOCK_INTERFACE_H

@ -0,0 +1,173 @@
/*
* Copyright 2013-2019 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/.
*
*/
#ifndef SRSUE_TTCN3_SRB_INTERFACE_H
#define SRSUE_TTCN3_SRB_INTERFACE_H
#include "srslte/common/buffer_pool.h"
#include "srslte/common/common.h"
#include "srslte/common/pdu.h"
#include "ttcn3_interfaces.h"
#include <srslte/interfaces/ue_interfaces.h>
using namespace srslte;
// The SRB interface
class ttcn3_srb_interface : public netsource_handler
{
public:
ttcn3_srb_interface() : syssim(nullptr), pool(byte_buffer_pool::get_instance()), netsource_handler("TTCN3_SRB_IF"){};
~ttcn3_srb_interface(){};
void init(syssim_interface* syssim_, srslte::log* log_, std::string net_ip_, uint32_t net_port_)
{
syssim = syssim_;
log = log_;
net_ip = net_ip_;
net_port = net_port_;
initialized = true;
log->debug("Initialized.\n");
}
void tx(unique_byte_buffer_t pdu)
{
if (running) {
log->info_hex(pdu->msg, pdu->N_bytes, "Sending %d B to Titan\n", pdu->N_bytes);
srslte_netsource_write(&net_source, (void*)pdu->msg, pdu->N_bytes);
} else {
log->error("Trying to transmit but port not connected.\n");
}
}
private:
void run_thread()
{
if (!initialized) {
fprintf(stderr, "SRB interface not initialized. Exiting.\n");
exit(-1);
}
// open TCP socket
if (srslte_netsource_init(&net_source, net_ip.c_str(), net_port, SRSLTE_NETSOURCE_TCP)) {
fprintf(stderr, "Error creating input TCP socket at port %d\n", net_port);
exit(-1);
}
running = true;
int n;
while (run_enable) {
log->debug("Reading from SRB port ..\n");
n = srslte_netsource_read(&net_source, rx_buf->begin(), RX_BUF_SIZE);
if (n > 0) {
rx_buf->at(n) = '\0';
log->debug_hex(rx_buf->begin(), n, "Received %d B from remote.\n", n);
// Chop incoming msg, first two bytes are length of the JSON
// (see IPL4_EUTRA_SYSTEM_Definitions.ttcn
uint16_t json_len = ((uint16_t)rx_buf->at(0) << 8) | rx_buf->at(1);
// Copy JSON from received buffer and null-terminate
char json[json_len + 1];
memcpy(json, &rx_buf->at(2), json_len);
json[json_len] = '\0';
// The data part after the JSON starts right here but handling
// is done in the respective functions
uint16_t rx_buf_offset = json_len + 2;
Document document;
if (document.Parse(json).HasParseError()) {
log->error_hex((uint8*)json, json_len, "Error parsing incoming data.\n");
break;
}
assert(document.IsObject());
// Pretty-print
StringBuffer buffer;
PrettyWriter<StringBuffer> writer(buffer);
document.Accept(writer);
log->info("Received JSON with %d B\n%s\n", json_len, (char*)buffer.GetString());
// check for common
assert(document.HasMember("Common"));
assert(document["Common"].IsObject());
// Check for request type
assert(document.HasMember("RrcPdu"));
assert(document["RrcPdu"].IsObject());
// Get request type
const Value& rrcpdu = document["RrcPdu"];
if (rrcpdu.HasMember("Ccch")) {
rx_buf_offset += 2;
handle_ccch_pdu(document, &rx_buf->at(rx_buf_offset), n - rx_buf_offset);
} else if (rrcpdu.HasMember("Dcch")) {
rx_buf_offset += 2;
uint32_t lcid = 1;
handle_dcch_pdu(document, lcid, &rx_buf->at(rx_buf_offset), n - rx_buf_offset);
} else {
log->error("Received unknown request.\n");
}
} else if (n == 0) {
log->error("Receiving null from network\n");
} else {
log->error("Error receiving from network\n");
}
}
running = false;
srslte_netsource_free(&net_source);
}
// Todo: move to SYSSIM
void handle_ccch_pdu(Document& document, const uint8_t* payload, const uint16_t len)
{
log->info_hex(payload, len, "Received CCCH RRC PDU\n");
// pack into byte buffer
unique_byte_buffer_t pdu = pool_allocate_blocking;
pdu->N_bytes = len;
memcpy(pdu->msg, payload, pdu->N_bytes);
syssim->add_ccch_pdu(std::move(pdu));
}
// Todo: move to SYSSIM
void handle_dcch_pdu(Document& document, const uint16_t lcid, const uint8_t* payload, const uint16_t len)
{
log->info_hex(payload, len, "Received DCCH RRC PDU\n");
// pack into byte buffer
unique_byte_buffer_t pdu = pool_allocate_blocking;
pdu->N_bytes = len;
memcpy(pdu->msg, payload, pdu->N_bytes);
syssim->add_dcch_pdu(lcid, std::move(pdu));
}
syssim_interface* syssim = nullptr;
byte_buffer_pool* pool = nullptr;
};
#endif // SRSUE_TTCN3_SRB_INTERFACE_H

@ -0,0 +1,603 @@
/*
* Copyright 2013-2019 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/.
*
*/
#ifndef SRSUE_TTCN3_SYS_INTERFACE_H
#define SRSUE_TTCN3_SYS_INTERFACE_H
#include "srslte/common/buffer_pool.h"
#include "ttcn3_helpers.h"
#include "ttcn3_interfaces.h"
using namespace srslte;
// The EUTRA.SYS interface
class ttcn3_sys_interface : public netsource_handler
{
public:
ttcn3_sys_interface() : netsource_handler("TTCN3_SYS_IF"){};
~ttcn3_sys_interface(){};
void init(syssim_interface* syssim_, srslte::log* log_, std::string net_ip_, uint32_t net_port_)
{
syssim = syssim_;
net_ip = net_ip_;
net_port = net_port_;
log = log_;
initialized = true;
log->debug("Initialized.\n");
pool = byte_buffer_pool::get_instance();
}
private:
void handle_request_cell_basic(Document& document, const uint8_t* payload, const uint16_t len)
{
if (document["Request"]["Cell"]["AddOrReconfigure"]["Basic"].HasMember("StaticCellInfo")) {
// Extract EARFCN
const Value& earfcn =
document["Request"]["Cell"]["AddOrReconfigure"]["Basic"]["StaticCellInfo"]["Downlink"]["Earfcn"];
assert(earfcn.IsInt());
// Extract cell config
const Value& common_config = document["Request"]["Cell"]["AddOrReconfigure"]["Basic"]["StaticCellInfo"]["Common"];
const Value& dl_config = document["Request"]["Cell"]["AddOrReconfigure"]["Basic"]["StaticCellInfo"]["Downlink"];
const Value& phy_dl_config = document["Request"]["Cell"]["AddOrReconfigure"]["Basic"]["PhysicalLayerConfigDL"];
srslte_cell_t cell = {};
cell.id = common_config["PhysicalCellId"].GetInt();
cell.cp = (strcmp(dl_config["CyclicPrefix"].GetString(), "normal") == 0) ? SRSLTE_CP_NORM : SRSLTE_CP_EXT;
cell.nof_ports =
(strcmp(phy_dl_config["AntennaGroup"]["AntennaInfoCommon"]["R8"]["antennaPortsCount"].GetString(), "an1") ==
0)
? 1
: 2;
cell.nof_prb = (strcmp(dl_config["Bandwidth"].GetString(), "n25") == 0) ? 25 : 0;
cell.phich_length =
(strcmp(phy_dl_config["Phich"]["PhichConfig"]["R8"]["phich_Duration"].GetString(), "normal") == 0)
? SRSLTE_PHICH_NORM
: SRSLTE_PHICH_EXT;
cell.phich_resources =
(strcmp(phy_dl_config["Phich"]["PhichConfig"]["R8"]["phich_Resource"].GetString(), "one") == 0)
? SRSLTE_PHICH_R_1
: SRSLTE_PHICH_R_1_6;
log->info("DL EARFCN is %d with n_prb=%d\n", earfcn.GetInt(), cell.nof_prb);
const Value& ref_power =
document["Request"]["Cell"]["AddOrReconfigure"]["Basic"]["InitialCellPower"]["MaxReferencePower"];
assert(ref_power.IsInt());
// That is the cellId or name that the testsuites uses to refer to a specific cell
const Value& cell_name = document["Common"]["CellId"];
assert(cell_name.IsString());
// Now configure cell
syssim->set_cell_config(cell_name.GetString(), earfcn.GetInt(), cell, ref_power.GetInt());
// Pull out SIBs and send to syssim
uint16_t consumed_bytes = 0;
const uint8_t* payload_ptr = payload;
while (consumed_bytes < len) {
uint16_t tb_len = ((uint16_t)payload_ptr[0] << 8) | payload_ptr[1];
payload_ptr += 2;
unique_byte_buffer_t sib = pool_allocate_blocking;
memcpy(sib->msg, payload_ptr, tb_len);
payload_ptr += tb_len;
sib->N_bytes = tb_len;
// Push to main component
log->info_hex(sib->msg, sib->N_bytes, "Received BCCH DL-SCH\n");
syssim->add_bcch_pdu(std::move(sib));
consumed_bytes = payload_ptr - payload;
}
// Create response for template car_CellConfig_CNF(CellId_Type p_CellId)
std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_name.GetString(), "Cell");
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length());
}
}
void handle_request_cell_active(Document& document, const uint8_t* payload, const uint16_t len)
{
// Create response for template car_CellConfig_CNF(CellId_Type p_CellId)
std::string cell_id = document["Common"]["CellId"].GetString();
std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id, "Cell");
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length());
}
void handle_request_cell(Document& document, const uint8_t* payload, const uint16_t len)
{
// get Cmd and make sure it has MMI
const Value& a = document["Common"];
assert(a.HasMember("CellId"));
// check cnf flag
assert(a.HasMember("ControlInfo"));
const Value& b = a["ControlInfo"];
assert(b.HasMember("CnfFlag"));
// Handle cell creation
if (document["Request"]["Cell"].HasMember("AddOrReconfigure")) {
if (document["Request"]["Cell"]["AddOrReconfigure"].HasMember("Basic")) {
// basic information for a cell (e.g. broadcasting)
handle_request_cell_basic(document, payload, len);
} else if (document["Request"]["Cell"]["AddOrReconfigure"].HasMember("Active")) {
// additional configuration for active cell (i.e. cell being capable to receive RACH preamble)
handle_request_cell_active(document, payload, len);
}
} else if (document["Request"]["Cell"].HasMember("Release")) {
log->info("Received cell release command\n");
// do nothing more
}
}
void handle_request_l1_mac_ind_ctrl(Document& document)
{
const Value& a = document["Common"];
assert(a.HasMember("CellId"));
const Value& cell_id = a["CellId"];
// check cnf flag
assert(a.HasMember("ControlInfo"));
const Value& b = a["ControlInfo"];
assert(b.HasMember("CnfFlag"));
// check request
const Value& req = document["Request"];
assert(req.HasMember("L1MacIndCtrl"));
const Value& mac_ind_ctrl = req["L1MacIndCtrl"];
if (mac_ind_ctrl.HasMember("HarqError")) {
assert(mac_ind_ctrl["HarqError"].IsString());
bool harq_error = (strcmp(mac_ind_ctrl["HarqError"].GetString(), "enable") == 0) ? true : false;
log->info("Setting HarqError to %s\n", harq_error ? "True" : "False");
}
if (ttcn3_helpers::requires_confirm(document)) {
std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id.GetString(), "L1MacIndCtrl");
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length());
}
}
void handle_request_radio_bearer_list(Document& document)
{
const Value& a = document["Common"];
assert(a.HasMember("CellId"));
const Value& cell_id = a["CellId"];
// check cnf flag
assert(a.HasMember("ControlInfo"));
const Value& b = a["ControlInfo"];
assert(b.HasMember("CnfFlag"));
// check request
const Value& req = document["Request"];
assert(req.HasMember("RadioBearerList"));
const Value& bearers = req["RadioBearerList"];
assert(bearers.IsArray());
// iterate over all bearers and configure them
for (Value::ConstValueIterator itr = bearers.Begin(); itr != bearers.End(); ++itr) {
assert(itr->HasMember("Id"));
const Value& id = (*itr)["Id"];
if (id.HasMember("Srb")) {
const Value& config = (*itr)["Config"];
if (config.HasMember("AddOrReconfigure")) {
uint32_t lcid = id["Srb"].GetInt();
if (lcid > 0) {
log->info("Configure SRB%d\n", lcid);
pdcp_config_t pdcp_cfg = {.bearer_id = static_cast<uint8_t>(lcid),
.rb_type = PDCP_RB_IS_SRB,
.tx_direction = SECURITY_DIRECTION_DOWNLINK,
.rx_direction = SECURITY_DIRECTION_UPLINK,
.sn_len = PDCP_SN_LEN_5};
syssim->add_srb(lcid, pdcp_cfg);
}
} else if (config.HasMember("Release")) {
log->info("Releasing SRB%d\n", id["Srb"].GetInt());
uint32_t lcid = id["Srb"].GetInt();
syssim->del_srb(lcid);
} else {
log->error("Unknown config.\n");
}
} else if (id.HasMember("Drb")) {
log->info("Configure DRB%d\n", id["Drb"].GetInt());
}
// FIXME: actually do configuration
}
std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id.GetString(), "RadioBearerList");
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length());
}
void handle_request_cell_attenuation_list(Document& document)
{
const Value& a = document["Common"];
assert(a.HasMember("CellId"));
const Value& cell_id = a["CellId"];
// check cnf flag
assert(a.HasMember("ControlInfo"));
const Value& b = a["ControlInfo"];
assert(b.HasMember("CnfFlag"));
// check request
const Value& req = document["Request"];
assert(req.HasMember("CellAttenuationList"));
const Value& cells = req["CellAttenuationList"];
assert(cells.IsArray());
// iterate over all bearers and configure them
for (Value::ConstValueIterator itr = cells.Begin(); itr != cells.End(); ++itr) {
assert(itr->HasMember("CellId"));
const Value& id = (*itr)["CellId"];
assert(itr->HasMember("Attenuation"));
const Value& att = (*itr)["Attenuation"];
float att_value = 0;
if (att.HasMember("Value")) {
att_value = att["Value"].GetInt();
} else if (att.HasMember("Off")) {
// is there other values than Off=True?
assert(att["Off"].GetBool() == true);
if (att["Off"].GetBool() == true) {
// use high attenuation value (-145dB RX power as per TS 36.508 Sec 6.2.2.1-1 is a non-suitable Off cell)
att_value = 90.0;
}
}
log->info("Configuring attenuation of %s to %.2f dB\n", id.GetString(), att_value);
syssim->set_cell_attenuation(id.GetString(), att_value);
}
std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id.GetString(), "CellAttenuationList");
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length());
}
void handle_request_pdcp_count(Document& document)
{
const Value& a = document["Common"];
assert(a.HasMember("CellId"));
const Value& cell_id = a["CellId"];
// check cnf flag
assert(a.HasMember("ControlInfo"));
const Value& b = a["ControlInfo"];
assert(b.HasMember("CnfFlag"));
// check request
const Value& req = document["Request"];
assert(req.HasMember("PdcpCount"));
const Value& pdcp_count = req["PdcpCount"];
assert(pdcp_count.HasMember("Get"));
const Value& get = pdcp_count["Get"];
assert(get.HasMember("AllRBs"));
// prepare response to SS
std::vector<ttcn3_helpers::pdcp_count_t> bearers;
ttcn3_helpers::pdcp_count_t srb1;
srb1.rb_is_srb = true;
srb1.rb_id = 1;
srb1.dl_value = 0;
srb1.ul_value = 1;
bearers.push_back(srb1);
std::string resp = ttcn3_helpers::get_pdcp_count_response(cell_id.GetString(), bearers);
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length());
}
void handle_request_as_security(Document& document)
{
const Value& a = document["Common"];
assert(a.HasMember("CellId"));
const Value& cell_id = a["CellId"];
// check cnf flag
assert(a.HasMember("ControlInfo"));
const Value& b = a["ControlInfo"];
assert(b.HasMember("CnfFlag"));
const Value& config_flag = b["CnfFlag"];
assert(config_flag.IsBool());
// check request
const Value& req = document["Request"];
assert(req.HasMember("AS_Security"));
// check AS security start
const Value& as_sec = req["AS_Security"];
if (as_sec.HasMember("StartRestart")) {
// get integrity algo
srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo = {};
std::string int_algo_string = as_sec["StartRestart"]["Integrity"]["Algorithm"].GetString();
if (int_algo_string == "eia0") {
integ_algo = srslte::INTEGRITY_ALGORITHM_ID_EIA0;
} else if (int_algo_string == "eia1") {
integ_algo = srslte::INTEGRITY_ALGORITHM_ID_128_EIA1;
} else if (int_algo_string == "eia2") {
integ_algo = srslte::INTEGRITY_ALGORITHM_ID_128_EIA2;
} else {
log->error("Unsupported integrity algorithm %s\n", int_algo_string.c_str());
}
// get integrity key
std::string integ_key_string = as_sec["StartRestart"]["Integrity"]["KRRCint"].GetString();
std::array<uint8_t, 32> k_rrc_int = get_key_from_string(integ_key_string);
log->debug_hex(k_rrc_int.data(), k_rrc_int.size(), "K_rrc_int");
// get enc algo
srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo = {};
std::string cipher_algo_string = as_sec["StartRestart"]["Ciphering"]["Algorithm"].GetString();
if (cipher_algo_string == "eea0") {
cipher_algo = srslte::CIPHERING_ALGORITHM_ID_EEA0;
} else if (int_algo_string == "eea1") {
cipher_algo = srslte::CIPHERING_ALGORITHM_ID_128_EEA1;
} else if (int_algo_string == "eea2") {
cipher_algo = srslte::CIPHERING_ALGORITHM_ID_128_EEA2;
} else {
log->error("Unsupported ciphering algorithm %s\n", cipher_algo_string.c_str());
}
// get cipher key
std::string cipher_key_string = as_sec["StartRestart"]["Ciphering"]["KRRCenc"].GetString();
std::array<uint8_t, 32> k_rrc_enc = get_key_from_string(cipher_key_string);
log->debug_hex(k_rrc_enc.data(), k_rrc_enc.size(), "K_rrc_enc");
// get UP enc key
std::string up_enc_key_string = as_sec["StartRestart"]["Ciphering"]["KUPenc"].GetString();
std::array<uint8_t, 32> k_up_enc = get_key_from_string(up_enc_key_string);
log->debug_hex(k_up_enc.data(), k_up_enc.size(), "K_UP_enc");
// get LCID
uint32_t lcid = 0;
if (as_sec["StartRestart"]["Ciphering"].HasMember("ActTimeList")) {
const Value& act_time_list = as_sec["StartRestart"]["Ciphering"]["ActTimeList"];
if (act_time_list.IsArray()) {
for (Value::ConstValueIterator itr = act_time_list.Begin(); itr != act_time_list.End(); ++itr) {
if (itr->HasMember("RadioBearerId") && (*itr)["RadioBearerId"].HasMember("Srb")) {
lcid = (*itr)["RadioBearerId"]["Srb"].GetInt();
}
}
}
}
// configure SS to use AS security
syssim->set_as_security(lcid, k_rrc_enc, k_rrc_int, k_up_enc, cipher_algo, integ_algo);
}
if (config_flag.GetBool() == true) {
std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id.GetString(), "AS_Security");
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length());
} else {
log->info("Skipping response for AS_Security message.\n");
}
}
std::array<uint8_t, 32> get_key_from_string(const std::string& str)
{
std::array<uint8_t, 32> key = {};
if (str.size() == 128) {
for (int i = 0; i < 16; i++) {
std::string byte_string(str, i * 8, 8);
key.at(i + 16) = std::stoul(byte_string, 0, 2);
}
}
return key;
}
void handle_request_enquire_timing(Document& document)
{
const Value& a = document["Common"];
assert(a.HasMember("CellId"));
const Value& cell_id = a["CellId"];
// check cnf flag
assert(a.HasMember("ControlInfo"));
const Value& b = a["ControlInfo"];
assert(b.HasMember("CnfFlag"));
// check request
const Value& req = document["Request"];
assert(req.HasMember("EnquireTiming"));
std::string resp =
ttcn3_helpers::get_sys_req_cnf_with_time(cell_id.GetString(), "EnquireTiming", syssim->get_tti());
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length());
}
void handle_request_paging(Document& document, const uint8_t* payload, const uint16_t len)
{
const Value& a = document["Common"];
assert(a.HasMember("CellId"));
const Value& cell_id = a["CellId"];
// check cnf flag
assert(a.HasMember("ControlInfo"));
const Value& b = a["ControlInfo"];
assert(b.HasMember("CnfFlag"));
// check request
const Value& req = document["Request"];
assert(req.HasMember("Paging"));
// Pack payload into
// Inform SYSSIM about paging
uint16_t consumed_bytes = 0;
const uint8_t* payload_ptr = payload;
uint16_t tb_len = ((uint16_t)payload_ptr[0] << 8) | payload_ptr[1];
payload_ptr += 2;
unique_byte_buffer_t pch = pool_allocate_blocking;
memcpy(pch->msg, payload_ptr, tb_len);
payload_ptr += tb_len;
pch->N_bytes = tb_len;
// Push to main component
log->info_hex(pch->msg, pch->N_bytes, "Received PCH DL-SCH\n");
syssim->add_pch_pdu(std::move(pch));
if (ttcn3_helpers::requires_confirm(document)) {
std::string resp = ttcn3_helpers::get_sys_req_cnf_with_time(cell_id.GetString(), "Paging", syssim->get_tti());
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length());
} else {
log->info("Skipping response for Paging message.\n");
}
}
void run_thread()
{
if (!initialized) {
fprintf(stderr, "SYS interface not initialized. Exiting.\n");
exit(-1);
}
// open TCP socket
if (srslte_netsource_init(&net_source, net_ip.c_str(), net_port, SRSLTE_NETSOURCE_TCP)) {
fprintf(stderr, "Error creating input TCP socket at port %d\n", net_port);
exit(-1);
}
log->info("Listening on %s:%d for incoming connections ..\n", net_ip.c_str(), net_port);
running = true;
int n;
while (run_enable) {
log->debug("Reading from SYS port ..\n");
n = srslte_netsource_read(&net_source, rx_buf->begin(), RX_BUF_SIZE);
if (n > 0) {
rx_buf->at(n) = '\0';
log->debug("Received %d B from remote.\n", n);
// Chop incoming msg, first two bytes are length of the JSON
// (see IPL4_EUTRA_SYSTEM_Definitions.ttcn
uint16_t json_len = ((uint16_t)rx_buf->at(0) << 8) | rx_buf->at(1);
// Copy JSON from received buffer and null-terminate
char json[json_len + 1];
memcpy(json, &rx_buf->at(2), json_len);
json[json_len] = '\0';
// The data part after the JSON starts right here but handling
// is done in the respective functions
uint16_t rx_buf_offset = json_len + 2;
Document document;
if (document.Parse(json).HasParseError()) {
log->error_hex((uint8*)json, json_len, "Error parsing incoming data.\n");
break;
}
assert(document.IsObject());
// Pretty-print
StringBuffer buffer;
PrettyWriter<StringBuffer> writer(buffer);
document.Accept(writer);
log->info("Received %d bytes\n%s\n", json_len, (char*)buffer.GetString());
// check for common
assert(document.HasMember("Common"));
assert(document["Common"].IsObject());
// Check for request type
assert(document.HasMember("Request"));
assert(document["Request"].IsObject());
// Get request type
const Value& request = document["Request"];
if (request.HasMember("Cell")) {
log->info("Received Cell request.\n");
handle_request_cell(document, &rx_buf->at(rx_buf_offset), n - rx_buf_offset);
} else if (request.HasMember("L1MacIndCtrl")) {
log->info("Received L1MacIndCtrl request.\n");
handle_request_l1_mac_ind_ctrl(document);
} else if (request.HasMember("RadioBearerList")) {
log->info("Received RadioBearerList request.\n");
handle_request_radio_bearer_list(document);
} else if (request.HasMember("CellAttenuationList")) {
log->info("Received CellAttenuationList request.\n");
handle_request_cell_attenuation_list(document);
} else if (request.HasMember("PdcpCount")) {
log->info("Received PdcpCount request.\n");
handle_request_pdcp_count(document);
} else if (request.HasMember("AS_Security")) {
log->info("Received AS_Security request.\n");
handle_request_as_security(document);
} else if (request.HasMember("EnquireTiming")) {
log->info("Received EnquireTiming request.\n");
handle_request_enquire_timing(document);
} else if (request.HasMember("Paging")) {
log->info("Received Paging request.\n");
handle_request_paging(document, &rx_buf->at(rx_buf_offset), n - rx_buf_offset);
} else {
log->error("Received unknown request.\n");
}
} else {
log->error("Error receiving from network\n");
}
}
running = false;
srslte_netsource_free(&net_source);
}
phy_interface_syssim* phy = nullptr;
syssim_interface* syssim = nullptr;
byte_buffer_pool* pool = nullptr;
};
#endif // SRSUE_TTCN3_SYS_INTERFACE_H

@ -0,0 +1,885 @@
/*
* Copyright 2013-2019 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/.
*
*/
#ifndef SRSUE_TTCN3_SYSSIM_H
#define SRSUE_TTCN3_SYSSIM_H
#include "dut_utils.h"
#include "srslte/common/netsource_handler.h"
#include "srslte/common/pdu_queue.h"
#include "srslte/upper/pdcp.h"
#include "srslte/upper/rlc.h"
#include "ttcn3_ip_ctrl_interface.h"
#include "ttcn3_ip_sock_interface.h"
#include "ttcn3_srb_interface.h"
#include "ttcn3_sys_interface.h"
#include "ttcn3_ue.h"
#include "ttcn3_ut_interface.h"
#include <pthread.h>
#include <srslte/interfaces/ue_interfaces.h>
#define TTCN3_CRNTI (0x1001)
class ttcn3_syssim : public thread,
public syssim_interface_phy,
public syssim_interface,
public rrc_interface_rlc,
public rlc_interface_pdcp,
public rrc_interface_pdcp,
public srslte::pdu_queue::process_callback
{
public:
ttcn3_syssim(srslte::logger_file* logger_file_) :
mac_msg_ul(20, &ss_mac_log),
mac_msg_dl(20, &ss_mac_log),
timers(8),
pdus(128),
logger(logger_file_),
logger_file(logger_file_),
pool(byte_buffer_pool::get_instance()),
thread("TTCN3_SYSSIM"),
rlc(&ss_rlc_log),
pdcp(&ss_pdcp_log){};
~ttcn3_syssim(){};
void init(const all_args_t& args_)
{
args = args_;
// Make sure to get SS logging as well
if (args.log.filename == "stdout") {
logger = &logger_stdout;
}
// init and configure logging
log.init("SYS-SIM", logger);
ut_log.init("UT ", logger);
sys_log.init("SYS ", logger);
ip_sock_log.init("IP_SOCK", logger);
ip_ctrl_log.init("IP_CTRL", logger);
srb_log.init("SRB ", logger);
ss_mac_log.init("SS-MAC ", logger);
ss_rlc_log.init("SS-RLC ", logger);
ss_pdcp_log.init("SS-PDCP", logger);
log.set_level(args.log.all_level);
ut_log.set_level(args.log.all_level);
sys_log.set_level(args.log.all_level);
ip_sock_log.set_level(args.log.all_level);
ip_ctrl_log.set_level(args.log.all_level);
srb_log.set_level(args.log.all_level);
ss_mac_log.set_level(args.log.all_level);
ss_rlc_log.set_level(args.log.all_level);
ss_pdcp_log.set_level(args.log.all_level);
log.set_hex_limit(args.log.all_hex_limit);
ut_log.set_hex_limit(args.log.all_hex_limit);
sys_log.set_hex_limit(args.log.all_hex_limit);
ip_sock_log.set_hex_limit(args.log.all_hex_limit);
ip_ctrl_log.set_hex_limit(args.log.all_hex_limit);
srb_log.set_hex_limit(args.log.all_hex_limit);
ss_mac_log.set_hex_limit(args.log.all_hex_limit);
ss_rlc_log.set_hex_limit(args.log.all_hex_limit);
ss_pdcp_log.set_hex_limit(args.log.all_hex_limit);
// init system interfaces to tester
ut.init(this, &ut_log, "0.0.0.0", 2222);
sys.init(this, &sys_log, "0.0.0.0", 2223);
ip_sock.init(&ip_sock_log, "0.0.0.0", 2224);
ip_ctrl.init(&ip_ctrl_log, "0.0.0.0", 2225);
srb.init(this, &srb_log, "0.0.0.0", 2226);
ut.start(-2);
sys.start(-2);
ip_sock.start(-2);
ip_ctrl.start(-2);
srb.start(-2);
pdus.init(this, &log);
rlc.init(&pdcp, this, &timers, 0 /* RB_ID_SRB0 */);
pdcp.init(&rlc, this, nullptr);
}
void stop()
{
running = false;
if (ue != NULL) {
ue->stop();
}
// Stopping system interface
ut.stop();
sys.stop();
ip_sock.stop();
ip_ctrl.stop();
srb.stop();
}
void reset()
{
rlc.reset();
pdcp.reset();
cells.clear();
pcell = {};
}
// Called from UT before starting testcase
void tc_start(const char* name)
{
if (ue == nullptr) {
// strip testsuite name
std::string tc_name = get_tc_name(name);
// Make a copy of the UE args for this run
all_args_t local_args = args;
// set up logging
if (args.log.filename == "stdout") {
logger = &logger_stdout;
} else {
logger_file->init(get_filename_with_tc_name(local_args.log.filename, run_id, tc_name).c_str(), -1);
logger = logger_file;
}
log.info("Initializing UE ID=%d for TC=%s\n", run_id, tc_name.c_str());
log.console("Initializing UE ID=%d for TC=%s\n", run_id, tc_name.c_str());
// Patch UE config
local_args.stack.pcap.filename = get_filename_with_tc_name(args.stack.pcap.filename, run_id, tc_name);
local_args.stack.pcap.nas_filename = get_filename_with_tc_name(args.stack.pcap.nas_filename, run_id, tc_name);
// bring up UE
ue = std::unique_ptr<ttcn3_ue>(new ttcn3_ue());
if (ue->init(local_args, logger, this, tc_name)) {
ue->stop();
ue.reset(nullptr);
std::string err("Couldn't initialize UE.\n");
log.error("%s\n", err.c_str());
log.console("%s\n", err.c_str());
return;
}
// Start simulator thread
running = true;
start();
} else {
log.error("UE hasn't been deallocated properly because TC didn't finish correctly.\n");
log.console("UE hasn't been deallocated properly because TC didn't finish correctly.\n");
}
}
void tc_end()
{
if (ue != NULL) {
// ask periodic thread to stop
running = false;
log.info("Deinitializing UE ID=%d\n", run_id);
log.console("Deinitializing UE ID=%d\n", run_id);
ue->stop();
// wait until SS main thread has terminated before resetting UE
wait_thread_finish();
ue.reset();
// Reset SS' RLC and PDCP
reset();
logger_file->stop();
run_id++;
} else {
log.error("UE is not allocated. Nothing needs to be done.\n");
log.console("UE is not allocated. Nothing needs to be done.\n");
}
}
void power_off_ue()
{
// only return after new UE instance is up and running
}
void switch_on_ue()
{
if (ue != nullptr) {
log.info("Switching on UE ID=%d\n", run_id);
log.console("Switching on UE ID=%d\n", run_id);
// Trigger attach procedure
ue->switch_on();
} else {
log.error("UE not initialized. Can't switch UE on.\n");
}
}
void switch_off_ue()
{
if (ue != nullptr) {
log.info("Switching off UE ID=%d\n", run_id);
log.console("Switching off UE ID=%d\n", run_id);
ue->switch_off();
} else {
log.error("UE not initialized. Can't switch UE off.\n");
}
}
// Interface for PHY
void prach_indication(uint32_t preamble_index_, const uint32_t& cell_id)
{
// store TTI for providing UL grant for Msg3 transmission
prach_tti = tti;
prach_preamble_index = preamble_index_;
// update active pcell (chosen by UE) in syssim
for (auto& cell : cells) {
if (cell.cell.id == cell_id) {
pcell = cell;
break;
}
}
}
void send_rar(uint32_t preamble_index)
{
log.info("Sending RAR for RAPID=%d\n", preamble_index);
// Prepare RAR grant
uint8_t grant_buffer[64] = {};
srslte_dci_rar_grant_t rar_grant = {};
rar_grant.tpc_pusch = 3;
srslte_dci_rar_pack(&rar_grant, grant_buffer);
// Create MAC PDU and add RAR subheader
srslte::rar_pdu rar_pdu;
rar_buffer.clear();
const int rar_pdu_len = 64;
rar_pdu.init_tx(&rar_buffer, rar_pdu_len);
rar_pdu.set_backoff(11); // Backoff of 480ms to prevent UE from PRACHing too fast
if (rar_pdu.new_subh()) {
rar_pdu.get()->set_rapid(preamble_index);
rar_pdu.get()->set_ta_cmd(0);
rar_pdu.get()->set_temp_crnti(crnti);
rar_pdu.get()->set_sched_grant(grant_buffer);
}
rar_pdu.write_packet(rar_buffer.msg);
rar_buffer.N_bytes = rar_pdu_len;
// Prepare grant and pass all to MAC
mac_interface_phy_lte::mac_grant_dl_t dl_grant = {};
dl_grant.pid = get_pid(tti);
dl_grant.rnti = 0x1; // must be a valid RAR-RNTI
dl_grant.tb[0].tbs = rar_buffer.N_bytes;
dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti);
// send grant and pass payload to TB data (grant contains length)
ue->new_tb(dl_grant, rar_buffer.msg);
// reset last PRACH transmission tti
prach_tti = -1;
}
void send_msg3_grant()
{
log.info("Sending Msg3 grant for C-RNTI=%d\n", crnti);
mac_interface_phy_lte::mac_grant_ul_t ul_grant = {};
ul_grant.tb.tbs = 32;
ul_grant.tb.ndi_present = true;
ul_grant.tb.ndi = get_ndi_for_new_ul_tx(tti);
ul_grant.rnti = crnti;
ul_grant.pid = get_pid(tti);
ue->new_grant_ul(ul_grant);
}
void sr_req(uint32_t tti_tx)
{
log.info("Received SR from PHY\n");
// Provide new UL grant to UE
mac_interface_phy_lte::mac_grant_ul_t ul_grant = {};
ul_grant.tb.tbs = 100; // FIXME: reasonable size?
ul_grant.tb.ndi_present = true;
ul_grant.tb.ndi = get_ndi_for_new_ul_tx(tti);
ul_grant.rnti = crnti;
ul_grant.pid = get_pid(tti);
ue->new_grant_ul(ul_grant);
}
void tx_pdu(const uint8_t* payload, const int len, const uint32_t tx_tti)
{
if (payload == NULL) {
ss_mac_log.error("Received NULL as PDU payload. Dropping.\n");
return;
}
// Parse MAC
mac_msg_ul.init_rx(len, true);
mac_msg_ul.parse_packet((uint8_t*)payload);
while (mac_msg_ul.next()) {
assert(mac_msg_ul.get());
if (mac_msg_ul.get()->is_sdu()) {
// Route logical channel
ss_mac_log.info_hex(mac_msg_ul.get()->get_sdu_ptr(),
mac_msg_ul.get()->get_payload_size(),
"PDU: rnti=0x%x, lcid=%d, %d bytes\n",
0xdead,
mac_msg_ul.get()->get_sdu_lcid(),
mac_msg_ul.get()->get_payload_size());
// Push PDU to our own RLC (needed to handle status reporting, etc. correctly
ss_mac_log.info_hex(mac_msg_ul.get()->get_sdu_ptr(),
mac_msg_ul.get()->get_payload_size(),
"Route PDU to LCID=%d (%d B)\n",
mac_msg_ul.get()->get_sdu_lcid(),
mac_msg_ul.get()->get_payload_size());
rlc.write_pdu(
mac_msg_ul.get()->get_sdu_lcid(), mac_msg_ul.get()->get_sdu_ptr(), mac_msg_ul.get()->get_payload_size());
// Save contention resolution if lcid == 0
if (mac_msg_ul.get()->get_sdu_lcid() == 0) {
int nbytes = srslte::sch_subh::MAC_CE_CONTRES_LEN;
if (mac_msg_ul.get()->get_payload_size() >= (uint32_t)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 (int i = 0; i < nbytes; i++) {
ue_cri_ptr[nbytes - i - 1] = pkt_ptr[i];
}
ss_mac_log.info_hex(ue_cri_ptr, nbytes, "Contention resolution ID:\n");
} else {
ss_mac_log.error("Received CCCH UL message of invalid size=%d bytes\n",
mac_msg_ul.get()->get_payload_size());
}
}
}
}
mac_msg_ul.reset();
/* Process CE after all SDUs because we need to update BSR after */
bool bsr_received = false;
while (mac_msg_ul.next()) {
assert(mac_msg_ul.get());
if (!mac_msg_ul.get()->is_sdu()) {
// Process MAC Control Element
bsr_received |= process_ce(mac_msg_ul.get());
}
}
}
bool process_ce(srslte::sch_subh* subh)
{
uint16_t rnti = dl_rnti;
uint32_t buff_size[4] = {0, 0, 0, 0};
float phr = 0;
int32_t idx = 0;
uint16_t old_rnti = 0;
bool is_bsr = false;
switch (subh->ce_type()) {
case srslte::sch_subh::PHR_REPORT:
phr = subh->get_phr();
ss_mac_log.info("CE: Received PHR from rnti=0x%x, value=%.0f\n", rnti, phr);
#if 0
//sched->ul_phr(rnti, (int) phr);
//metrics_phr(phr);
#endif
break;
case srslte::sch_subh::CRNTI:
old_rnti = subh->get_c_rnti();
ss_mac_log.info("CE: Received C-RNTI from temp_rnti=0x%x, rnti=0x%x\n", rnti, old_rnti);
#if 0
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);
}
#endif
break;
case srslte::sch_subh::TRUNC_BSR:
case srslte::sch_subh::SHORT_BSR:
idx = subh->get_bsr(buff_size);
if (idx == -1) {
ss_mac_log.error("Invalid Index Passed to lc groups\n");
break;
}
#if 0
for (uint32_t i=0;i<lc_groups[idx].size();i++) {
// Indicate BSR to scheduler
sched->ul_bsr(rnti, lc_groups[idx][i], buff_size[idx]);
}
#endif
ss_mac_log.info("CE: Received %s BSR rnti=0x%x, lcg=%d, value=%d\n",
subh->ce_type() == srslte::sch_subh::SHORT_BSR ? "Short" : "Trunc",
rnti,
idx,
buff_size[idx]);
is_bsr = true;
break;
case srslte::sch_subh::LONG_BSR:
subh->get_bsr(buff_size);
#if 0
for (idx=0;idx<4;idx++) {
for (uint32_t i=0;i<lc_groups[idx].size();i++) {
sched->ul_bsr(rnti, lc_groups[idx][i], buff_size[idx]);
}
}
#endif
is_bsr = true;
ss_mac_log.info("CE: Received Long BSR rnti=0x%x, value=%d,%d,%d,%d\n",
rnti,
buff_size[0],
buff_size[1],
buff_size[2],
buff_size[3]);
break;
case srslte::sch_subh::PADDING:
ss_mac_log.debug("CE: Received padding for rnti=0x%x\n", rnti);
break;
default:
ss_mac_log.error("CE: Invalid lcid=0x%x\n", subh->ce_type());
break;
}
return is_bsr;
}
uint32_t get_pid(const uint32_t tti) { return tti % (2 * FDD_HARQ_DELAY_MS); }
bool get_ndi_for_new_ul_tx(const uint32_t tti_)
{
// toggle NDI to always create new Tx
const uint32_t pid = get_pid(tti_);
last_ul_ndi[pid] = !last_ul_ndi[pid];
log.info("UL-PID=%d NDI=%s\n", pid, last_ul_ndi[pid] ? "1" : "0");
return last_ul_ndi[pid];
}
bool get_ndi_for_new_dl_tx(const uint32_t tti_)
{
// toggle NDI to always create new Tx
const uint32_t pid = get_pid(tti_);
last_dl_ndi[pid] = !last_dl_ndi[pid];
log.info("DL-PID=%d NDI=%s\n", pid, last_dl_ndi[pid] ? "1" : "0");
return last_dl_ndi[pid];
}
void run_thread()
{
uint32_t sib_idx = 0;
while (running) {
log.debug("SYSSIM-TTI=%d\n", tti);
ue->set_current_tti(tti);
dl_rnti = ue->get_dl_sched_rnti(tti);
if (SRSLTE_RNTI_ISSI(dl_rnti)) {
// deliver SIBs one after another
mac_interface_phy_lte::mac_grant_dl_t dl_grant = {};
dl_grant.pid = get_pid(tti);
dl_grant.rnti = dl_rnti;
dl_grant.tb[0].tbs = sibs[sib_idx]->N_bytes;
dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti);
ue->new_tb(dl_grant, sibs[sib_idx]->msg);
sib_idx = (sib_idx + 1) % sibs.size();
} else if (SRSLTE_RNTI_ISRAR(dl_rnti)) {
if (prach_tti != -1) {
rar_tti = (prach_tti + 3) % 10240;
if (tti == rar_tti) {
send_rar(prach_preamble_index);
}
}
} else if (SRSLTE_RNTI_ISPA(dl_rnti)) {
log.debug("Searching for paging RNTI\n");
// PCH will be triggered from SYSSIM after receiving Paging
} else if (SRSLTE_RNTI_ISUSER(dl_rnti)) {
// check if this is for contention resolution after PRACH/RAR
if (dl_rnti == crnti) {
log.debug("Searching for C-RNTI=%d\n", crnti);
if (rar_tti != -1) {
msg3_tti = (rar_tti + 3) % 10240;
if (tti == msg3_tti) {
send_msg3_grant();
rar_tti = -1;
}
}
}
if (dl_rnti != 0) {
log.debug("Searching for RNTI=%d\n", dl_rnti);
// look for DL data to be send in each bearer and provide grant accordingly
for (int lcid = 0; lcid < SRSLTE_N_RADIO_BEARERS; lcid++) {
uint32_t buf_state = rlc.get_buffer_state(lcid);
if (buf_state > 0) {
log.debug("LCID=%d, buffer_state=%d\n", lcid, buf_state);
const uint32_t mac_header_size = 10; // Add MAC header (10 B for all subheaders, etc)
if (tmp_rlc_buffer.get_tailroom() > (buf_state + mac_header_size)) {
uint32_t pdu_size = rlc.read_pdu(lcid, tmp_rlc_buffer.msg, buf_state);
tx_payload_buffer.clear();
mac_msg_dl.init_tx(&tx_payload_buffer, pdu_size + mac_header_size, false);
// check if this is Msg4 that needs to contain the contention resolution ID CE
if (msg3_tti != -1) {
if (lcid == 0) {
if (mac_msg_dl.new_subh()) {
if (mac_msg_dl.get()->set_con_res_id(conres_id)) {
log.info("CE: Added Contention Resolution ID=0x%lx\n", conres_id);
} else {
log.error("CE: Setting Contention Resolution ID CE\n");
}
} else {
log.error("CE: Setting Contention Resolution ID CE. No space for a subheader\n");
}
msg3_tti = -1;
}
}
// Add payload
if (mac_msg_dl.new_subh()) {
int n = mac_msg_dl.get()->set_sdu(lcid, pdu_size, tmp_rlc_buffer.msg);
if (n == -1) {
log.error("Error while adding SDU (%d B) to MAC PDU\n", pdu_size);
mac_msg_dl.del_subh();
}
}
uint8_t* mac_pdu_ptr = mac_msg_dl.write_packet(&log);
if (mac_pdu_ptr != nullptr) {
log.info_hex(mac_pdu_ptr, mac_msg_dl.get_pdu_len(), "DL MAC PDU (%d B):\n", mac_msg_dl.get_pdu_len());
// Prepare MAC grant for CCCH
mac_interface_phy_lte::mac_grant_dl_t dl_grant = {};
dl_grant.pid = get_pid(tti);
dl_grant.rnti = dl_rnti;
dl_grant.tb[0].tbs = mac_msg_dl.get_pdu_len();
dl_grant.tb[0].ndi_present = true;
dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti);
ue->new_tb(dl_grant, (const uint8_t*)mac_pdu_ptr);
} else {
log.error("Error writing DL MAC PDU\n");
}
mac_msg_dl.reset();
} else {
log.error("Can't fit RLC PDU into buffer (%d > %d)\n", buf_state, tmp_rlc_buffer.get_tailroom());
}
}
}
// Check if we need to provide a UL grant as well
}
} else {
log.error("Not handling RNTI=%d\n", dl_rnti);
}
usleep(1000);
tti = (tti + 1) % 10240;
}
log.info("Leaving main thread.\n");
log.console("Leaving main thread.\n");
}
uint32_t get_tti() { return tti; }
void process_pdu(uint8_t* buff, uint32_t len, pdu_queue::channel_t channel) { log.info("%s\n", __PRETTY_FUNCTION__); }
void set_cell_config(std::string name, uint32_t earfcn_, srslte_cell_t cell_, const float power)
{
// check if cell already exists
if (not syssim_has_cell(name)) {
// insert new cell
log.info("Adding cell %s with cellId=%d and power=%.2f dBm\n", name.c_str(), cell_.id, power);
syssim_cell_t cell = {};
cell.name = name;
cell.cell = cell_;
cell.initial_power = power;
cell.earfcn = earfcn_;
cells.push_back(cell);
} else {
// cell is already there
log.info("Cell already there, reconfigure\n");
}
update_cell_map();
}
bool syssim_has_cell(std::string cell_name)
{
for (uint32_t i = 0; i < cells.size(); ++i) {
if (cells[i].name == cell_name) {
return true;
}
}
return false;
}
void set_cell_attenuation(std::string cell_name, const float value)
{
if (not syssim_has_cell(cell_name)) {
log.error("Can't set cell power. Cell not found.\n");
}
// update cell's power
for (uint32_t i = 0; i < cells.size(); ++i) {
if (cells[i].name == cell_name) {
cells[i].attenuation = value;
break;
}
}
update_cell_map();
}
void update_cell_map()
{
// Find cell with highest power and select as serving cell
if (ue != NULL) {
// convert syssim cell list to phy cell list
lte_ttcn3_phy::cell_list_t phy_cells;
for (uint32_t i = 0; i < cells.size(); ++i) {
lte_ttcn3_phy::cell_t phy_cell = {};
phy_cell.info = cells[i].cell;
phy_cell.power = cells[i].initial_power - cells[i].attenuation;
phy_cell.earfcn = cells[i].earfcn;
log.debug("Configuring cell %d with PCI=%d with TxPower=%f\n", i, phy_cell.info.id, phy_cell.power);
phy_cells.push_back(phy_cell);
}
// SYSSIM defines what cells the UE can connect to
ue->set_cell_map(phy_cells);
} else {
log.error("Can't configure cell. UE not initialized.\n");
}
}
void add_bcch_pdu(unique_byte_buffer_t pdu) { sibs.push_back(std::move(pdu)); }
void add_ccch_pdu(unique_byte_buffer_t pdu)
{
// Add to SRB0 Tx queue
rlc.write_sdu(0, std::move(pdu));
}
void add_dcch_pdu(uint32_t lcid, unique_byte_buffer_t pdu)
{
// push to PDCP and create DL grant for it
log.info("Writing PDU (%d B) to LCID=%d\n", pdu->N_bytes, lcid);
pdcp.write_sdu(lcid, std::move(pdu));
}
void add_pch_pdu(unique_byte_buffer_t pdu)
{
log.info("Received PCH PDU (%d B)\n", pdu->N_bytes);
// Prepare MAC grant for PCH
mac_interface_phy_lte::mac_grant_dl_t dl_grant = {};
dl_grant.pid = get_pid(tti);
dl_grant.rnti = SRSLTE_PRNTI;
dl_grant.tb[0].tbs = pdu->N_bytes;
dl_grant.tb[0].ndi_present = true;
dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti);
ue->new_tb(dl_grant, (const uint8_t*)pdu->msg);
}
srslte::timers::timer* timer_get(uint32_t timer_id) { return timers.get(timer_id); }
uint32_t timer_get_unique_id() { return timers.get_unique_id(); }
void timer_release_id(uint32_t timer_id) { timers.release_id(timer_id); }
void step_timer() { timers.step_all(); }
void add_srb(uint32_t lcid, pdcp_config_t pdcp_config)
{
pdcp.add_bearer(lcid, pdcp_config);
rlc.add_bearer(lcid, srslte::rlc_config_t::srb_config(lcid));
}
void del_srb(uint32_t lcid)
{
// Only delete SRB1/2
if (lcid > 0) {
pdcp.del_bearer(lcid);
rlc.del_bearer(lcid);
}
// Reset HARQ to generate new transmissions
if (lcid == 0) {
log.info("Resetting UL/DL NDI counters\n");
memset(last_dl_ndi, 0, sizeof(last_dl_ndi));
memset(last_ul_ndi, 0, sizeof(last_ul_ndi));
}
}
// RRC interface for PDCP, PDCP calls RRC to push RRC SDU
void write_pdu(uint32_t lcid, unique_byte_buffer_t pdu)
{
log.info_hex(pdu->msg, pdu->N_bytes, "RRC SDU received for LCID=%d (%d B)\n", lcid, pdu->N_bytes);
// We don't handle RRC, prepend LCID
pdu->msg--;
*pdu->msg = lcid;
pdu->N_bytes++;
// prepend pcell PCID
pdu->msg--;
*pdu->msg = static_cast<uint8_t>(pcell.cell.id);
pdu->N_bytes++;
// push content to Titan
srb.tx(std::move(pdu));
}
// Not supported right now
void write_pdu_bcch_bch(unique_byte_buffer_t pdu) { log.error("%s not implemented.\n", __FUNCTION__); }
void write_pdu_bcch_dlsch(unique_byte_buffer_t pdu) { log.error("%s not implemented.\n", __FUNCTION__); }
void write_pdu_pcch(unique_byte_buffer_t pdu) { log.error("%s not implemented.\n", __FUNCTION__); }
void write_pdu_mch(uint32_t lcid, unique_byte_buffer_t pdu) { log.error("%s not implemented.\n", __FUNCTION__); }
void max_retx_attempted() { log.debug("max_retx_attempted\n"); }
std::string get_rb_name(uint32_t lcid)
{
if (lcid < rb_id_vec.size()) {
return rb_id_vec.at(lcid);
}
return std::string("RB");
};
void write_sdu(uint32_t lcid, unique_byte_buffer_t sdu, bool blocking = true)
{
log.info_hex(sdu->msg, sdu->N_bytes, "Received SDU on LCID=%d\n", lcid);
uint8_t* mac_pdu_ptr;
mac_pdu_ptr = mac_msg_dl.write_packet(&log);
log.info_hex(mac_pdu_ptr, mac_msg_dl.get_pdu_len(), "DL MAC PDU:\n");
// Prepare MAC grant for CCCH
mac_interface_phy_lte::mac_grant_dl_t dl_grant = {};
dl_grant.pid = get_pid(tti);
dl_grant.rnti = dl_rnti;
dl_grant.tb[0].tbs = mac_msg_dl.get_pdu_len();
dl_grant.tb[0].ndi_present = true;
dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti);
ue->new_tb(dl_grant, (const uint8_t*)mac_pdu_ptr);
}
bool rb_is_um(uint32_t lcid) { return false; }
int set_as_security(const uint32_t lcid,
std::array<uint8_t, 32> k_rrc_enc,
std::array<uint8_t, 32> k_rrc_int,
std::array<uint8_t, 32> k_up_enc,
const srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo,
const srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo)
{
pdcp.config_security(lcid, k_rrc_enc.data(), k_rrc_int.data(), k_up_enc.data(), cipher_algo, integ_algo);
pdcp.enable_integrity(lcid);
pdcp.enable_encryption(lcid);
return 0;
}
private:
// SYS interface
ttcn3_ut_interface ut;
ttcn3_sys_interface sys;
ttcn3_ip_sock_interface ip_sock;
ttcn3_ip_ctrl_interface ip_ctrl;
ttcn3_srb_interface srb;
// Logging stuff
srslte::logger_stdout logger_stdout;
srslte::logger_file* logger_file = nullptr;
srslte::logger* logger = nullptr;
srslte::log_filter log;
srslte::log_filter ut_log;
srslte::log_filter sys_log;
srslte::log_filter ip_sock_log;
srslte::log_filter ip_ctrl_log;
srslte::log_filter srb_log;
srslte::log_filter ss_mac_log;
srslte::log_filter ss_rlc_log;
srslte::log_filter ss_pdcp_log;
all_args_t args = {};
bool running = false;
srslte::byte_buffer_pool* pool = nullptr;
// Simulator vars
unique_ptr<ttcn3_ue> ue = nullptr;
uint32_t run_id = 0;
std::vector<unique_byte_buffer_t> sibs;
int32_t tti = 0;
int32_t prach_tti = -1;
int32_t rar_tti = -1;
int32_t msg3_tti = -1;
uint32_t prach_preamble_index = 0;
uint16_t dl_rnti = 0;
uint16_t crnti = TTCN3_CRNTI;
srslte::timers timers;
bool last_dl_ndi[2 * FDD_HARQ_DELAY_MS] = {};
bool last_ul_ndi[2 * FDD_HARQ_DELAY_MS] = {};
// Map between the cellId (name) used by 3GPP test suite and srsLTE cell struct
typedef struct {
std::string name;
srslte_cell_t cell;
float initial_power;
float attenuation;
uint32_t earfcn;
} syssim_cell_t;
std::vector<syssim_cell_t> cells;
syssim_cell_t pcell = {};
srslte::pdu_queue pdus;
srslte::sch_pdu mac_msg_dl, mac_msg_ul;
// buffer for DL transmissions
srslte::byte_buffer_t rar_buffer;
srslte::byte_buffer_t tmp_rlc_buffer; // Used to buffer RLC PDU
srslte::byte_buffer_t tx_payload_buffer; // Used to buffer final MAC PDU
uint64_t conres_id = 0;
// Simulator objects
srslte::rlc rlc;
srslte::pdcp pdcp;
std::vector<std::string> rb_id_vec = {
"SRB0", "SRB1", "SRB2", "DRB1", "DRB2", "DRB3", "DRB4", "DRB5", "DRB6", "DRB7", "DRB8"};
};
#endif // SRSUE_TTCN3_SYSSIM_H

@ -0,0 +1,150 @@
/*
* Copyright 2013-2019 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/.
*
*/
#ifndef SRSUE_TTCN3_UE_H
#define SRSUE_TTCN3_UE_H
#include "lte_ttcn3_phy.h"
#include "srsue/hdr/stack/ue_stack_lte.h"
#include <sstream>
class ttcn3_ue : public phy_interface_syssim, public gw_interface_stack
{
public:
ttcn3_ue() : run_id(0), logger(nullptr) {}
virtual ~ttcn3_ue() {}
int init(all_args_t args, srslte::logger* logger_, syssim_interface_phy* syssim_, const std::string tc_name_)
{
logger = logger_;
// Init UE log
log.init("UE ", logger);
log.set_level(srslte::LOG_LEVEL_INFO);
// Patch args
args.stack.nas.force_imsi_attach = true;
args.stack.nas.eia = "0,1,2";
args.stack.nas.eea = "0,1,2";
// Configure default parameters
args.stack.usim.algo = "xor";
args.stack.usim.imei = "356092040793011";
args.stack.usim.imsi = "001010123456789"; // Anritsu test USIM
args.stack.usim.k = "000102030405060708090A0B0C0D0E0F"; // fixed as per TS 34.108 Sec. 8.2
args.stack.rrc.feature_group = 0xe6041000;
args.stack.rrc.ue_category_str = SRSLTE_UE_CATEGORY_DEFAULT;
args.stack.rrc.ue_category = atoi(args.stack.rrc.ue_category_str.c_str());
args.stack.rrc.nof_supported_bands = 1;
args.stack.rrc.supported_bands[0] = 7;
args.stack.rrc.release = 8;
args.stack.rrc.mbms_service_id = -1;
args.phy.dl_earfcn = "3400";
args.rf.type = "none";
args.stack.type = "lte";
args.phy.type = "lte_ttcn3";
args.phy.log.phy_level = "debug";
// Instantiate layers and stack together our UE
if (args.stack.type == "lte") {
stack = std::unique_ptr<ue_stack_lte>(new ue_stack_lte());
if (!stack) {
log.console("Error creating LTE stack instance.\n");
return SRSLTE_ERROR;
}
phy = std::unique_ptr<srsue::lte_ttcn3_phy>(new srsue::lte_ttcn3_phy(logger));
if (!phy) {
log.console("Error creating LTE PHY instance.\n");
return SRSLTE_ERROR;
}
} else {
log.console("Invalid stack type %s. Supported values are [lte].\n", args.stack.type.c_str());
return SRSLTE_ERROR;
}
// init layers
if (phy->init(args.phy, stack.get(), syssim_)) {
log.console("Error initializing PHY.\n");
return SRSLTE_ERROR;
}
if (stack->init(args.stack, logger, phy.get(), this)) {
log.console("Error initializing stack.\n");
return SRSLTE_ERROR;
}
return SRSLTE_SUCCESS;
}
void stop()
{
// nothing to do here
}
bool switch_on() { return stack->switch_on(); }
bool switch_off() { return stack->switch_off(); }
// The interface for SYSSIM
void set_cell_map(lte_ttcn3_phy::cell_list_t phy_cell_map) { phy->set_cell_map(phy_cell_map); }
void new_grant_ul(const srsue::mac_interface_phy_lte::mac_grant_ul_t grant) { phy->new_grant_ul(grant); }
void new_tb(const srsue::mac_interface_phy_lte::mac_grant_dl_t mac_grant, const uint8_t* data)
{
phy->new_tb(mac_grant, data);
}
void set_current_tti(uint32_t tti) { phy->set_current_tti(tti); }
uint16_t get_dl_sched_rnti(uint32_t tti) { return stack->get_dl_sched_rnti(tti); }
// GW interface
void add_mch_port(uint32_t lcid, uint32_t port) {}
void write_pdu(uint32_t lcid, srslte::unique_byte_buffer_t pdu) {}
void write_pdu_mch(uint32_t lcid, srslte::unique_byte_buffer_t pdu) {}
int setup_if_addr(uint32_t lcid, uint8_t pdn_type, uint32_t ip_addr, uint8_t* ipv6_if_id, char* err_str) { return 0; }
int apply_traffic_flow_template(const uint8_t& eps_bearer_id,
const uint8_t& lcid,
const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft)
{
return 0;
}
private:
std::unique_ptr<lte_ttcn3_phy> phy;
std::unique_ptr<ue_stack_lte> stack;
// Generic logger members
srslte::logger* logger = nullptr;
srslte::log_filter log; // Own logger for UE
all_args_t args;
srslte::byte_buffer_pool* pool = nullptr;
uint32_t run_id;
};
#endif // SRSUE_TTCN3_UE_H

@ -0,0 +1,170 @@
/*
* Copyright 2013-2019 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/.
*
*/
#ifndef SRSUE_TTCN3_UT_INTERFACE_H
#define SRSUE_TTCN3_UT_INTERFACE_H
#include "rapidjson/document.h"
#include "rapidjson/prettywriter.h"
#include "srslte/common/log.h"
#include "srslte/common/netsource_handler.h"
#include "ttcn3_interfaces.h"
using namespace rapidjson;
// The UpperTester interface
class ttcn3_ut_interface : public netsource_handler
{
public:
ttcn3_ut_interface() : netsource_handler("TTCN3_UT_IF") {}
~ttcn3_ut_interface(){};
void init(syssim_interface* syssim_, srslte::log* log_, std::string net_ip_, uint32_t net_port_)
{
syssim = syssim_;
log = log_;
net_ip = net_ip_;
net_port = net_port_;
initialized = true;
log->debug("Initialized.\n");
}
private:
void run_thread()
{
if (!initialized) {
fprintf(stderr, "UT interface not initialized. Exiting.\n");
exit(-1);
}
// open TCP socket
if (srslte_netsource_init(&net_source, net_ip.c_str(), net_port, SRSLTE_NETSOURCE_TCP)) {
fprintf(stderr, "Error creating input TCP socket at port %d\n", net_port);
exit(-1);
}
log->info("Listening on %s:%d for incoming connections ..\n", net_ip.c_str(), net_port);
running = true;
int n;
while (run_enable) {
log->debug("Reading from UT port ..\n");
n = srslte_netsource_read(&net_source, rx_buf->begin(), RX_BUF_SIZE);
if (n > 0) {
// Terminate
rx_buf->at(n) = '\0';
Document document;
if (document.Parse((char*)rx_buf->begin()).HasParseError()) {
log->error_hex(rx_buf->begin(), n, "Error parsing incoming data.\n");
continue;
}
assert(document.IsObject());
// Pretty-print
StringBuffer buffer;
PrettyWriter<StringBuffer> writer(buffer);
document.Accept(writer);
log->info("Received %d bytes\n%s\n", n, (char*)buffer.GetString());
// check for command
assert(document.HasMember("Cmd"));
assert(document["Cmd"].IsObject());
// get Cmd
const Value& a = document["Cmd"];
if (a.HasMember("MMI")) {
assert(a.HasMember("MMI"));
// get MMI and make sure it has another Cmd nested
const Value& mmi = a["MMI"];
assert(mmi.HasMember("Cmd"));
// get MMI cmd
const Value& mmi_cmd = mmi["Cmd"];
assert(mmi_cmd.IsString());
// check for CnfRequired
assert(document.HasMember("CnfRequired"));
if (strcmp(mmi_cmd.GetString(), "POWER_OFF") == 0) {
log->info("Received POWER_OFF command.\n");
handle_power_off(document);
} else if (strcmp(mmi_cmd.GetString(), "SWITCH_ON") == 0) {
log->info("Received SWITCH_ON command.\n");
syssim->switch_on_ue();
} else if (strcmp(mmi_cmd.GetString(), "SWITCH_OFF") == 0) {
log->info("Received SWITCH_OFF command.\n");
syssim->switch_off_ue();
} else {
log->error("Received unknown command: %s\n", mmi_cmd.GetString());
}
} else if (a.HasMember("AT")) {
log->error("AT commands not implemented.\n");
} else if (a.HasMember("TC_START")) {
log->info("Received TC_START command.\n");
const Value& cmd = a["TC_START"];
assert(cmd.HasMember("Name"));
const Value& tc_name = cmd["Name"];
syssim->tc_start(tc_name.GetString());
} else if (a.HasMember("TC_END")) {
log->info("Received TC_END command.\n");
syssim->tc_end();
} else {
log->error("Unknown command type.\n");
}
} else if (n == 0) {
log->error("Connection closed on UT interface.\n");
} else {
log->error("Error receiving from network\n");
exit(-1);
}
}
running = false;
srslte_netsource_free(&net_source);
}
void handle_power_off(Document& document)
{
syssim->power_off_ue();
// Create response
Document resp;
resp.SetObject();
resp.AddMember("Result", true, resp.GetAllocator());
// Serialize and send to tester
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
resp.Accept(writer);
log->info("Sending %s to tester (%zd B)\n", buffer.GetString(), buffer.GetSize());
srslte_netsource_write(&net_source, (char*)buffer.GetString(), buffer.GetSize());
}
syssim_interface* syssim = nullptr;
};
#endif // SRSUE_TTCN3_UT_INTERFACE_H

@ -0,0 +1,34 @@
#
# Copyright 2013-2019 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/.
#
add_executable(ttcn3_dut ttcn3_dut.cc lte_ttcn3_phy.cc)
target_link_libraries(ttcn3_dut srsue_stack
srsue_upper
srsue_rrc
srslte_upper
srslte_common
srslte_phy
srslte_radio
srslte_upper
srsue_phy
srsue_mac
rrc_asn1
${Boost_LIBRARIES})
include_directories(${PROJECT_SOURCE_DIR}/srsue/test/ttcn3/hdr)

@ -0,0 +1,381 @@
/*
* Copyright 2013-2019 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/.
*
*/
#include "lte_ttcn3_phy.h"
namespace srsue {
#define MIN_IN_SYNC_POWER (-100)
#define DEFAULT_RSRQ (-3.0)
lte_ttcn3_phy::lte_ttcn3_phy(srslte::logger* logger_) : logger(logger_) {}
lte_ttcn3_phy::~lte_ttcn3_phy() {}
int lte_ttcn3_phy::init(const phy_args_t& args_, stack_interface_phy_lte* stack_, syssim_interface_phy* syssim_)
{
stack = stack_;
syssim = syssim_;
return init(args_);
}
int lte_ttcn3_phy::init(const phy_args_t& args_, stack_interface_phy_lte* stack_, radio_interface_phy* radio_)
{
return init(args_);
}
// ue_phy_base interface
int lte_ttcn3_phy::init(const phy_args_t& args_)
{
log.init("PHY ", logger);
log.set_level(args_.log.phy_level);
return SRSLTE_SUCCESS;
}
void lte_ttcn3_phy::stop(){};
void lte_ttcn3_phy::set_earfcn(std::vector<uint32_t> earfcns) {}
void lte_ttcn3_phy::force_freq(float dl_freq, float ul_freq) {}
void lte_ttcn3_phy::wait_initialize() {}
void lte_ttcn3_phy::start_plot() {}
void lte_ttcn3_phy::get_metrics(phy_metrics_t* m) {}
// The interface for the SS
void lte_ttcn3_phy::set_cell_map(const cell_list_t& cells_)
{
cells = cells_;
}
// The interface for RRC
void lte_ttcn3_phy::get_current_cell(srslte_cell_t* cell_, uint32_t* earfcn_)
{
if (cell_) {
memcpy(cell_, &pcell.info, sizeof(srslte_cell_t));
}
if (earfcn_) {
*earfcn_ = pcell.earfcn;
}
}
uint32_t lte_ttcn3_phy::get_current_earfcn()
{
return pcell.earfcn;
}
uint32_t lte_ttcn3_phy::get_current_pci()
{
return pcell.info.id;
}
void lte_ttcn3_phy::set_config_tdd(srslte_tdd_config_t& tdd_config) {}
void lte_ttcn3_phy::set_config_scell(asn1::rrc::scell_to_add_mod_r10_s* scell_config)
{
log.debug("%s not implemented.\n", __FUNCTION__);
}
void lte_ttcn3_phy::enable_pregen_signals(bool enable)
{
log.debug("%s not implemented.\n", __FUNCTION__);
}
void lte_ttcn3_phy::set_activation_deactivation_scell(uint32_t cmd)
{
log.debug("%s not implemented.\n", __FUNCTION__);
}
void lte_ttcn3_phy::set_config(srslte::phy_cfg_t& config, uint32_t cc_idx, uint32_t earfcn, srslte_cell_t* cell_info)
{
log.debug("%s not implemented.\n", __FUNCTION__);
}
// Measurements interface
void lte_ttcn3_phy::meas_reset(){};
int lte_ttcn3_phy::meas_start(uint32_t earfcn, int pci)
{
return 0;
}
int lte_ttcn3_phy::meas_stop(uint32_t earfcn, int pci)
{
return 0;
};
// eMBMS interface
void lte_ttcn3_phy::set_mch_period_stop(uint32_t stop){};
void lte_ttcn3_phy::set_config_mbsfn_sib2(asn1::rrc::sib_type2_s* sib2){};
void lte_ttcn3_phy::set_config_mbsfn_sib13(asn1::rrc::sib_type13_r9_s* sib13){};
void lte_ttcn3_phy::set_config_mbsfn_mcch(asn1::rrc::mcch_msg_s* mcch){};
/* Cell search and selection procedures */
phy_interface_rrc_lte::cell_search_ret_t lte_ttcn3_phy::cell_search(phy_cell_t* found_cell)
{
log.info("Running cell search in PHY\n");
cell_search_ret_t ret = {};
// select strongest cell as PCell
float max_power = -145;
int max_index = 0;
for (uint32_t i = 0; i < cells.size(); ++i) {
if (cells[i].power > max_power) {
max_power = cells[i].power;
max_index = i;
}
}
// Consider cell found if above -100dBm
if (max_power >= MIN_IN_SYNC_POWER) {
pcell = cells[max_index];
log.info("Setting PCell to EARFCN=%d CellId=%d with RS power=%.2f\n", pcell.earfcn, pcell.info.id, max_power);
if (found_cell) {
found_cell->earfcn = pcell.earfcn;
found_cell->cell = pcell.info;
}
ret.found = cell_search_ret_t::CELL_FOUND;
ret.last_freq = cell_search_ret_t::NO_MORE_FREQS;
} else {
// no suitable cell found
ret.found = cell_search_ret_t::CELL_NOT_FOUND;
}
return ret;
};
bool lte_ttcn3_phy::cell_select(phy_cell_t* cell)
{
log.debug("%s not implemented.\n", __FUNCTION__);
return true;
};
bool lte_ttcn3_phy::cell_is_camping()
{
return true;
};
void lte_ttcn3_phy::reset()
{
log.debug("%s not implemented.\n", __FUNCTION__);
};
// The interface for MAC
void lte_ttcn3_phy::configure_prach_params()
{
log.debug("%s not implemented.\n", __FUNCTION__);
};
void lte_ttcn3_phy::prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm)
{
log.info("Sending PRACH with preamble %d on PCID=%d\n", preamble_idx, pcell.info.id);
prach_tti_tx = current_tti;
ra_trans_cnt++;
syssim->prach_indication(preamble_idx, pcell.info.id);
};
std::string lte_ttcn3_phy::get_type()
{
return "lte_ttcn3";
}
phy_interface_mac_lte::prach_info_t lte_ttcn3_phy::prach_get_info()
{
prach_info_t info = {};
if (prach_tti_tx != -1) {
info.is_transmitted = true;
info.tti_ra = prach_tti_tx;
}
log.info("Return prach_tti_tx=%d\n", prach_tti_tx);
return info;
}
/* Indicates the transmission of a SR signal in the next opportunity */
void lte_ttcn3_phy::sr_send()
{
sr_pending = true;
sr_tx_tti = -1;
}
int lte_ttcn3_phy::sr_last_tx_tti()
{
return sr_tx_tti;
}
// The RAT-agnostic interface for MAC
/* Sets a C-RNTI allowing the PHY to pregenerate signals if necessary */
void lte_ttcn3_phy::set_crnti(uint16_t rnti)
{
current_temp_rnti = rnti;
log.info("Set Temp-RNTI=%d\n", rnti);
}
/* Time advance commands */
void lte_ttcn3_phy::set_timeadv_rar(uint32_t ta_cmd)
{
log.debug("%s not implemented.\n", __FUNCTION__);
}
void lte_ttcn3_phy::set_timeadv(uint32_t ta_cmd)
{
log.debug("%s not implemented.\n", __FUNCTION__);
}
// Sets RAR grant payload
void lte_ttcn3_phy::set_rar_grant(uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN], uint16_t rnti)
{
// Empty, SYSSIM knows when to provide UL grant for Msg3
}
// Called from the SYSSIM to configure the current TTI
void lte_ttcn3_phy::set_current_tti(uint32_t tti)
{
current_tti = tti;
run_tti();
}
// Called from MAC to retrieve the current TTI
uint32_t lte_ttcn3_phy::get_current_tti()
{
return current_tti;
}
float lte_ttcn3_phy::get_phr()
{
log.debug("%s not implemented.\n", __FUNCTION__);
return 0.1;
}
float lte_ttcn3_phy::get_pathloss_db()
{
log.debug("%s not implemented.\n", __FUNCTION__);
return 85.0;
}
// Only provides a new UL grant, Tx is then triggered
void lte_ttcn3_phy::new_grant_ul(mac_interface_phy_lte::mac_grant_ul_t ul_mac_grant)
{
mac_interface_phy_lte::tb_action_ul_t ul_action = {};
// Deliver grant and retrieve payload
stack->new_grant_ul(cc_idx, ul_mac_grant, &ul_action);
// Deliver MAC PDU to SYSSIM
if (ul_action.tb.enabled and ul_action.tb.payload != nullptr) {
syssim->tx_pdu(ul_action.tb.payload, ul_mac_grant.tb.tbs, ul_mac_grant.rnti);
}
}
// Provides DL grant, copy data into DL action and pass up to MAC
void lte_ttcn3_phy::new_tb(const srsue::mac_interface_phy_lte::mac_grant_dl_t dl_grant, const uint8_t* data)
{
if (data == nullptr) {
log.error("Invalid data buffer passed\n");
return;
}
// pass grant to MAC to retrieve DL action
mac_interface_phy_lte::tb_action_dl_t dl_action = {};
stack->new_grant_dl(cc_idx, dl_grant, &dl_action);
bool dl_ack[SRSLTE_MAX_CODEWORDS] = {};
if (dl_action.tb[0].enabled && dl_action.tb[0].payload != nullptr) {
log.info_hex(data,
dl_grant.tb[0].tbs,
"TB received rnti=%d, tti=%d, n_bytes=%d\n",
dl_grant.rnti,
current_tti,
dl_grant.tb[0].tbs);
if (dl_action.generate_ack) {
log.debug("Calling generate ACK callback\n");
// action.generate_ack_callback(action.generate_ack_callback_arg);
}
memcpy(dl_action.tb->payload, data, dl_grant.tb[0].tbs);
// ACK first TB and pass up
dl_ack[0] = true;
log.info("TB processed correctly\n");
} else {
log.error("Couldn't get buffer for TB\n");
}
stack->tb_decoded(cc_idx, dl_grant, dl_ack);
}
void lte_ttcn3_phy::radio_overflow()
{
log.debug("%s not implemented.\n", __FUNCTION__);
}
void lte_ttcn3_phy::radio_failure()
{
log.debug("%s not implemented.\n", __FUNCTION__);
}
void lte_ttcn3_phy::run_tti()
{
// send report for each cell
for (auto& cell : cells) {
stack->new_phy_meas(cell.power, DEFAULT_RSRQ, current_tti, cell.earfcn, cell.info.id);
}
// check if Pcell is in sync
for (auto& cell : cells) {
if (cell.info.id == pcell.info.id) {
if (cell.power >= MIN_IN_SYNC_POWER) {
log.info("PCell id=%d power=%.2f -> sync\n", pcell.info.id, cell.power);
stack->in_sync();
} else {
log.info("PCell id=%d power=%.2f -> out of sync\n", pcell.info.id, cell.power);
stack->out_of_sync();
}
break; // make sure to call stack only once
}
}
log.step(current_tti);
// Check for SR
if (sr_pending) {
syssim->sr_req(current_tti);
sr_pending = false;
sr_tx_tti = current_tti;
}
stack->run_tti(current_tti);
}
} // namespace srsue

@ -0,0 +1,152 @@
/*
* Copyright 2013-2019 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/.
*
*/
#include "rapidjson/document.h" // rapidjson's DOM-style API
#include "rapidjson/prettywriter.h" // for stringify JSON
#include "srslte/build_info.h"
#include "srslte/common/log_filter.h"
#include "srslte/common/logger_stdout.h"
#include "srsue/hdr/ue.h"
#include "ttcn3_helpers.h"
#include "ttcn3_syssim.h"
#include <assert.h>
#include <boost/program_options.hpp>
#include <boost/program_options/parsers.hpp>
#include <iostream>
#include <pthread.h>
#include <signal.h>
#include <srslte/interfaces/ue_interfaces.h>
using namespace srslte;
using namespace srsue;
;
using namespace std;
using namespace rapidjson;
namespace bpo = boost::program_options;
typedef struct {
pcap_args_t pcap;
std::string log_filename;
std::string log_level;
int32_t log_hex_level;
} ttcn3_dut_args_t;
all_args_t parse_args(ttcn3_dut_args_t* args, int argc, char* argv[])
{
// 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");
// clang-format off
common.add_options()
("pcap.enable", bpo::value<bool>(&args->pcap.enable)->default_value(true), "Enable MAC packet captures for wireshark")
("pcap.filename", bpo::value<string>(&args->pcap.filename)->default_value("/tmp/ttcn3_ue.pcap"), "MAC layer capture filename")
("pcap.nas_enable", bpo::value<bool>(&args->pcap.nas_enable)->default_value(false), "Enable NAS packet captures for wireshark")
("pcap.nas_filename", bpo::value<string>(&args->pcap.nas_filename)->default_value("/tmp/ttcn3_ue_nas.pcap"), "NAS layer capture filename (useful when NAS encryption is enabled)")
("logfilename", bpo::value<std::string>(&args->log_filename)->default_value("/tmp/ttcn3_ue.log"), "Filename of log file")
("loglevel", bpo::value<std::string>(&args->log_level)->default_value("warning"), "Log level (Error,Warning,Info,Debug)")
("loghexlevel", bpo::value<int32_t>(&args->log_hex_level)->default_value(64), "Log hex level (-1 unbounded)");
// clang-format on
// these options are allowed on the command line
bpo::options_description cmdline_options;
cmdline_options.add(common).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).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);
}
all_args_t all_args = {};
all_args.stack.pcap.enable = args->pcap.enable;
all_args.stack.pcap.nas_enable = args->pcap.nas_enable;
all_args.stack.pcap.filename = args->pcap.filename;
all_args.stack.pcap.nas_filename = args->pcap.nas_filename;
all_args.log.filename = args->log_filename;
all_args.log.all_level = args->log_level;
all_args.log.all_hex_limit = args->log_hex_level;
all_args.stack.log.mac_level = args->log_level;
all_args.stack.log.rlc_level = args->log_level;
all_args.stack.log.pdcp_level = args->log_level;
all_args.stack.log.rrc_level = args->log_level;
all_args.stack.log.nas_level = args->log_level;
all_args.stack.log.gw_level = args->log_level;
all_args.stack.log.usim_level = args->log_level;
all_args.stack.log.mac_hex_limit = args->log_hex_level;
all_args.stack.log.rlc_hex_limit = args->log_hex_level;
all_args.stack.log.pdcp_hex_limit = args->log_hex_level;
all_args.stack.log.rrc_hex_limit = args->log_hex_level;
all_args.stack.log.nas_hex_limit = args->log_hex_level;
all_args.stack.log.gw_hex_limit = args->log_hex_level;
all_args.stack.log.usim_hex_limit = args->log_hex_level;
return all_args;
}
bool go_exit = false;
void sig_int_handler(int signo)
{
printf("SIGINT received. Exiting...\n");
if (signo == SIGINT) {
go_exit = true;
}
}
int main(int argc, char** argv)
{
std::cout << "Built in " << srslte_get_build_mode() << " mode using " << srslte_get_build_info() << "." << std::endl;
ttcn3_dut_args_t dut_args;
all_args_t ue_args = parse_args(&dut_args, argc, argv);
signal(SIGINT, sig_int_handler);
// Instantiate file logger
srslte::logger_file logger_file;
// create and init SYSSIM
ttcn3_syssim syssim(&logger_file);
syssim.init(ue_args);
// Loop until finished ..
while (!go_exit) {
sleep(1);
}
syssim.stop();
return SRSLTE_SUCCESS;
}

@ -0,0 +1,28 @@
#
# Copyright 2013-2019 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/.
#
include_directories(${PROJECT_SOURCE_DIR}/srsue/test/ttcn3/hdr)
add_executable(rapidjson_test rapidjson_test.cc)
add_test(rapidjson_test rapidjson_test)
add_executable(ttcn3_if_handler_test ttcn3_if_handler_test.cc)
target_link_libraries(ttcn3_if_handler_test srslte_phy srslte_common)
add_test(ttcn3_if_handler_test ttcn3_if_handler_test)

@ -0,0 +1,153 @@
/*
* Copyright 2013-2019 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/.
*
*/
#include "ttcn3_helpers.h"
#include <assert.h>
#include <iostream>
#include <srsue/test/ttcn3/hdr/ttcn3_helpers.h>
#include <stdio.h>
#include <vector>
int SYSTEM_CTRL_CNF_test()
{
char titan_result[] = "{\"Common\":{\"CellId\":\"eutra_Cell1\",\"RoutingInfo\":{\"None\":true},\"TimingInfo\":{"
"\"Now\":true},\"Result\":{\"Success\":true}},\"Confirm\":{\"Cell\":true}}";
printf("%s\n", titan_result);
printf("SYSTEM_CTRL_CNF_test()\n");
const char cellid[] = "eutra_Cell1";
Document resp;
resp.SetObject();
// Create members of common object
// RoutingInfo
Value routing_info(kObjectType);
routing_info.AddMember("None", true, resp.GetAllocator());
// TimingInfo
Value timing_info(kObjectType);
timing_info.AddMember("Now", true, resp.GetAllocator());
// Result
Value result(kObjectType);
result.AddMember("Success", true, resp.GetAllocator());
// Now, create the common object itself and add members
Value common(kObjectType);
common.AddMember("CellId", cellid, resp.GetAllocator());
common.AddMember("RoutingInfo", routing_info, resp.GetAllocator());
common.AddMember("TimingInfo", timing_info, resp.GetAllocator());
common.AddMember("Result", result, resp.GetAllocator());
// The confirm object
Value confirm(kObjectType);
confirm.AddMember("Cell", true, resp.GetAllocator());
resp.AddMember("Common", common, resp.GetAllocator());
resp.AddMember("Confirm", confirm, resp.GetAllocator());
// printf("SYSTEM_CTRL_CNF\n%s\n", (char*)buffer.GetString());
return 0;
}
void pretty_print(std::string json)
{
Document document;
if (document.Parse((char*)json.c_str()).HasParseError()) {
fprintf(stderr, "Error parsing incoming data. Exiting\n");
exit(-1);
}
assert(document.IsObject());
// Pretty-print
StringBuffer buffer;
PrettyWriter<StringBuffer> writer(buffer);
document.Accept(writer);
printf("%s\n", (char*)buffer.GetString());
}
// UDP v4 test
void IP_SOCK_CTRL_udp_v4_test()
{
printf("IP_SOCK_CTRL_udp_v4_test()\n");
char titan_result[] = "{\\\"CTRL\\\":{\\\"ConnectionId\\\":{\\\"Protocol\\\":\\\"udp\\\",\\\"Local\\\":{"
"\\\"IpAddr\\\":{\\\"V4\\\":{\\\"Addr\\\":\\\"127.0.0.1\\\"}},\\\"Port\\\":11},\\\"Remote\\\":{"
"\\\"Port\\\":22}},\\\"Ind\\\":{\\\"UDP\\\":{\\\"SocketCnf\\\":true}}}}\")";
printf("%s\n", titan_result);
string resp = ttcn3_helpers::get_ctrl_cnf("udp", "V4", "127.0.0.1");
pretty_print(resp);
};
// UDP v4 test
void IP_SOCK_CTRL_icmp_v6_test()
{
printf("IP_SOCK_CTRL_icmp_v6_test()\n");
// char titan_result[] =
// "{\\\"CTRL\\\":{\\\"ConnectionId\\\":{\\\"Protocol\\\":\\\"udp\\\",\\\"Local\\\":{\\\"IpAddr\\\":{\\\"V4\\\":{\\\"Addr\\\":\\\"127.0.0.1\\\"}},\\\"Port\\\":11},\\\"Remote\\\":{\\\"Port\\\":22}},\\\"Ind\\\":{\\\"UDP\\\":{\\\"SocketCnf\\\":true}}}}\")";
// printf("%s\n", titan_result);
//{ V6 := { Addr := "", ScopeId := omit } }
string resp = ttcn3_helpers::get_ctrl_cnf("icmp6", "V6", "127.0.0.1");
pretty_print(resp);
};
void PdcpCountGetReq_test()
{
// Titan encoding test
//"{\"Common\":{\"CellId\":\"eutra_Cell1\",\"RoutingInfo\":{\"None\":true},\"TimingInfo\":{\"Now\":true},\"Result\":{\"Success\":true}},\"Confirm\":{\"PdcpCount\":{\"Get\":[{\"RadioBearerId\":{\"Srb\":1},\"UL\":{\"Format\":\"PdcpCount_Srb\",\"Value\":\"00000000000000000000000000000000\"},\"DL\":{\"Format\":\"PdcpCount_Srb\",\"Value\":\"00000000000000000000000000000000\"}}]}}}")
std::vector<ttcn3_helpers::pdcp_count_t> bearers;
ttcn3_helpers::pdcp_count_t srb1;
srb1.rb_is_srb = true;
srb1.rb_id = 1;
srb1.dl_value = 0;
srb1.ul_value = 1;
bearers.push_back(srb1);
string resp = ttcn3_helpers::get_pdcp_count_response("cell1", bearers);
pretty_print(resp);
}
void EnquireTiming_test()
{
string resp = ttcn3_helpers::get_sys_req_cnf_with_time("cell1", "EnquireTiming", 7289);
pretty_print(resp);
}
int main(int argc, char** argv)
{
// SYSTEM_CTRL_CNF_test();
// IP_SOCK_CTRL_udp_v4_test();
// IP_SOCK_CTRL_icmp_v6_test();
// PdcpCountGetReq_test();
EnquireTiming_test();
return 0;
}

@ -0,0 +1,48 @@
/*
* Copyright 2013-2019 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/.
*
*/
#include "srslte/srslte.h"
#include "ttcn3_ip_ctrl_interface.h"
#include "ttcn3_ip_sock_interface.h"
#include "ttcn3_srb_interface.h"
#include "ttcn3_sys_interface.h"
#include "ttcn3_ut_interface.h"
#include <iostream>
#include <stdio.h>
#include <vector>
int if_handler_test()
{
ttcn3_ut_interface ut;
ttcn3_sys_interface sys;
ttcn3_ip_sock_interface ip_sock;
ttcn3_ip_ctrl_interface ip_ctrl;
ttcn3_srb_interface srb;
return SRSLTE_SUCCESS;
}
int main(int argc, char** argv)
{
if_handler_test();
return SRSLTE_SUCCESS;
}
Loading…
Cancel
Save