mirror of https://github.com/pvnis/srsRAN_4G.git
add initial TTCN3 code
parent
95b2239dd5
commit
9e1c46dfb0
@ -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…
Reference in New Issue