added an ho required message send from SeNB to MME

master
Francisco Paisana 5 years ago committed by Francisco Paisana
parent 2b7ef9b4a8
commit 57cd40ca31

@ -284,7 +284,6 @@ inline void s1ap_mccmnc_to_plmn(uint16_t mcc, uint16_t mnc, uint32_t *plmn)
*plmn |= nibbles[5];
}
} // namespace srslte
#endif // SRSLTE_BCD_HELPERS_H

@ -307,6 +307,10 @@ public:
virtual void ue_ctxt_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT* res) = 0;
virtual void ue_erab_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT* res) = 0;
virtual bool is_mme_connected() = 0;
virtual bool send_ho_required(uint16_t rnti,
uint32_t target_eci,
srslte::plmn_id_t target_plmn,
srslte::unique_byte_buffer_t rrc_container) = 0;
};
// Combined interface for PHY to access stack (MAC and RRC)

@ -83,6 +83,27 @@ struct plmn_id_t {
}
return SRSLTE_SUCCESS;
}
std::pair<uint16_t, uint16_t> to_number()
{
uint16_t mcc_num, mnc_num;
srslte::bytes_to_mcc(&mcc[0], &mcc_num);
srslte::bytes_to_mnc(&mnc[0], &mnc_num, nof_mnc_digits);
return std::make_pair(mcc_num, mnc_num);
}
uint32_t to_s1ap_plmn()
{
auto mcc_mnc_pair = to_number();
uint32_t s1ap_plmn;
srslte::s1ap_mccmnc_to_plmn(mcc_mnc_pair.first, mcc_mnc_pair.second, &s1ap_plmn);
return s1ap_plmn;
}
void to_s1ap_plmn_bytes(uint8_t* plmn_bytes)
{
uint32_t s1ap_plmn = to_s1ap_plmn();
s1ap_plmn = htonl(s1ap_plmn);
uint8_t* plmn_ptr = (uint8_t*)&s1ap_plmn;
memcpy(&plmn_bytes[0], plmn_ptr + 1, 3);
}
int from_string(const std::string& plmn_str)
{
if (plmn_str.size() < 5 or plmn_str.size() > 6) {

@ -71,7 +71,7 @@ typedef struct {
struct meas_cell_cfg_t {
uint32_t earfcn;
uint16_t pci;
uint32_t cell_id;
uint32_t eci;
float q_offset;
};
@ -285,10 +285,15 @@ public:
bool is_csfb = false;
private:
// args
srslte::byte_buffer_pool* pool = nullptr;
struct timeval t_last_activity;
struct timeval t_ue_init;
// cached for ease of context transfer
asn1::rrc::rrc_conn_recfg_r8_ies_s last_rrc_conn_recfg;
asn1::rrc::security_algorithm_cfg_s last_security_mode_cmd;
asn1::rrc::establishment_cause_e establishment_cause;
std::unique_ptr<rrc_mobility> mobility_handler;
@ -315,6 +320,7 @@ public:
LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT bitrates;
LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT security_capabilities;
bool eutra_capabilities_unpacked = false;
asn1::rrc::ue_eutra_cap_s eutra_capabilities;
typedef struct {

@ -57,6 +57,10 @@ public:
// getters
const asn1::rrc::meas_obj_to_add_mod_list_l& meas_objs() const { return var_meas.meas_obj_list; }
const asn1::rrc::report_cfg_to_add_mod_list_l& rep_cfgs() const { return var_meas.report_cfg_list; }
const asn1::rrc::meas_id_to_add_mod_list_l& meas_ids() const { return var_meas.meas_id_list; }
asn1::rrc::meas_obj_to_add_mod_list_l& meas_objs() { return var_meas.meas_obj_list; }
asn1::rrc::report_cfg_to_add_mod_list_l& rep_cfgs() { return var_meas.report_cfg_list; }
asn1::rrc::meas_id_to_add_mod_list_l& meas_ids() { return var_meas.meas_id_list; }
private:
asn1::rrc::var_meas_cfg_s var_meas;
@ -71,6 +75,7 @@ public:
std::shared_ptr<const var_meas_cfg_t> current_meas_cfg; ///< const to enable ptr comparison as identity comparison
private:
// args
const rrc_cfg_t* cfg = nullptr;
};
@ -79,8 +84,13 @@ class rrc::ue::rrc_mobility
public:
explicit rrc_mobility(srsenb::rrc::ue* outer_ue);
bool fill_conn_recfg_msg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn_recfg);
void handle_ue_meas_report(const asn1::rrc::meas_report_s& msg);
private:
enum class ho_interface_t { S1, X2, interSector };
bool send_s1_ho_required(uint32_t target_eci, uint8_t measobj_id, bool fwd_direct_path_available);
rrc::ue* rrc_ue = nullptr;
rrc* rrc_enb = nullptr;
rrc::mobility_cfg* cfg = nullptr;
@ -90,16 +100,35 @@ private:
// vars
std::shared_ptr<const var_meas_cfg_t> ue_var_meas;
class mobility_proc_t
class sourceenb_ho_proc_t
{
public:
srslte::proc_outcome_t init() { return srslte::proc_outcome_t::yield; }
explicit sourceenb_ho_proc_t(rrc_mobility* ue_mobility_);
srslte::proc_outcome_t init(const asn1::rrc::meas_id_to_add_mod_s& measid_,
const asn1::rrc::meas_obj_to_add_mod_s& measobj_,
const asn1::rrc::report_cfg_to_add_mod_s& repcfg_,
const asn1::rrc::cells_to_add_mod_s& cell_,
const asn1::rrc::meas_result_eutra_s& meas_res_,
uint32_t target_eci_);
srslte::proc_outcome_t step() { return srslte::proc_outcome_t::yield; }
static const char* name() { return "Handover"; }
private:
enum class state_t { ho_started };
// args
rrc_mobility* parent = nullptr;
// run args
const asn1::rrc::meas_id_to_add_mod_s* measid = nullptr;
const asn1::rrc::meas_obj_to_add_mod_s* measobj = nullptr;
const asn1::rrc::report_cfg_to_add_mod_s* repcfg = nullptr;
const asn1::rrc::cells_to_add_mod_s* cell = nullptr;
asn1::rrc::meas_result_eutra_s meas_res;
uint32_t target_eci = 0;
enum class state_t { ho_preparation } state{};
ho_interface_t ho_interface{};
bool fwd_direct_path_available = false;
};
srslte::proc_t<mobility_proc_t> mobility_proc;
srslte::proc_t<sourceenb_ho_proc_t> source_ho_proc;
};
} // namespace srsenb

@ -42,11 +42,10 @@ typedef struct {
uint32_t MME_UE_S1AP_ID;
bool release_requested;
uint16_t stream_id;
struct timeval init_timestamp;
} ue_ctxt_t;
class s1ap
:public s1ap_interface_rrc
,public thread
class s1ap : public s1ap_interface_rrc, public thread
{
public:
s1ap();
@ -105,6 +104,7 @@ private:
void build_tai_cgi();
bool connect_mme();
bool setup_s1();
bool sctp_send_s1ap_pdu(LIBLTE_S1AP_S1AP_PDU_STRUCT* tx_pdu, uint32_t rnti, const char* procedure_name);
bool handle_s1ap_rx_pdu(srslte::byte_buffer_t* pdu);
bool handle_initiatingmessage(LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT* msg);
@ -135,10 +135,14 @@ private:
// bool send_ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps)
bool send_uectxmodifyresp(uint16_t rnti);
bool send_uectxmodifyfailure(uint16_t rnti, LIBLTE_S1AP_CAUSE_STRUCT* cause);
// handover
bool send_ho_required(uint16_t rnti,
uint32_t target_eci,
srslte::plmn_id_t target_plmn,
srslte::unique_byte_buffer_t rrc_container);
bool find_mme_ue_id(uint32_t mme_ue_id, uint16_t* rnti, uint32_t* enb_ue_id);
std::string get_cause(LIBLTE_S1AP_CAUSE_STRUCT* c);
};
} // namespace srsenb

@ -1168,13 +1168,13 @@ static int parse_meas_cell_list(rrc_meas_cfg_t* meas_cfg, Setting& root)
for (uint32_t i = 0; i < meas_cfg->meas_cells.size(); ++i) {
meas_cfg->meas_cells[i].earfcn = root[i]["dl_earfcn"];
meas_cfg->meas_cells[i].pci = (unsigned int)root[i]["pci"] % 504;
meas_cfg->meas_cells[i].cell_id = (unsigned int)root[i]["cell_idx"];
meas_cfg->meas_cells[i].eci = (unsigned int)root[i]["eci"];
meas_cfg->meas_cells[i].q_offset = 0; // LIBLTE_RRC_Q_OFFSET_RANGE_DB_0; // TODO
// // FIXME: TEMP
// printf("PARSER: neighbor cell: {dl_earfcn=%d pci=%d cell_idx=0x%x}\n",
// meas_cfg->meas_cells[i].earfcn,
// meas_cfg->meas_cells[i].pci,
// meas_cfg->meas_cells[i].cell_id);
// meas_cfg->meas_cells[i].eci);
}
return 0;
}

@ -1212,6 +1212,12 @@ void rrc::ue::parse_ul_dcch(uint32_t lcid, srslte::unique_byte_buffer_t pdu)
state = RRC_STATE_IDLE;
}
break;
case ul_dcch_msg_type_c::c1_c_::types::meas_report:
printf("Received MEASUREMENT REPORT!\n");
if (mobility_handler != nullptr) {
mobility_handler->handle_ue_meas_report(ul_dcch_msg.msg.c1().meas_report());
}
break;
default:
parent->rrc_log->error("Msg: %s not supported\n", ul_dcch_msg.msg.c1().type().to_string().c_str());
break;
@ -1302,6 +1308,7 @@ bool rrc::ue::handle_ue_cap_info(ue_cap_info_s* msg)
parent->rrc_log->error("Failed to unpack EUTRA capabilities message\n");
return false;
}
eutra_capabilities_unpacked = true;
parent->rrc_log->info("UE rnti: 0x%x category: %d\n", rnti, eutra_capabilities.ue_category);
}
}
@ -1781,11 +1788,9 @@ void rrc::ue::send_connection_reconf(srslte::unique_byte_buffer_t pdu)
conn_reconf->rr_cfg_ded.phys_cfg_ded_present = true;
phys_cfg_ded_s* phy_cfg = &conn_reconf->rr_cfg_ded.phys_cfg_ded;
phy_cfg->ant_info_present = true;
phy_cfg->ant_info.set(phys_cfg_ded_s::ant_info_c_::types::explicit_value);
phy_cfg->ant_info.explicit_value() = parent->cfg.antenna_info;
// Configure PHY layer
phy_cfg->ant_info_present = true;
phy_cfg->ant_info.set_explicit_value() = parent->cfg.antenna_info;
phy_cfg->cqi_report_cfg_present = true;
if (parent->cfg.cqi_cfg.mode == RRC_CFG_CQI_MODE_APERIODIC) {
phy_cfg->cqi_report_cfg.cqi_report_mode_aperiodic_present = true;
@ -1797,12 +1802,11 @@ void rrc::ue::send_connection_reconf(srslte::unique_byte_buffer_t pdu)
}
} else {
phy_cfg->cqi_report_cfg.cqi_report_periodic_present = true;
phy_cfg->cqi_report_cfg.cqi_report_periodic.set_setup();
cqi_get(&phy_cfg->cqi_report_cfg.cqi_report_periodic.setup().cqi_pmi_cfg_idx,
&phy_cfg->cqi_report_cfg.cqi_report_periodic.setup().cqi_pucch_res_idx);
phy_cfg->cqi_report_cfg.cqi_report_periodic.setup().cqi_format_ind_periodic.set(
auto& cqi_rep = phy_cfg->cqi_report_cfg.cqi_report_periodic.set_setup();
cqi_get(&cqi_rep.cqi_pmi_cfg_idx, &cqi_rep.cqi_pucch_res_idx);
cqi_rep.cqi_format_ind_periodic.set(
cqi_report_periodic_c::setup_s_::cqi_format_ind_periodic_c_::types::wideband_cqi);
phy_cfg->cqi_report_cfg.cqi_report_periodic.setup().simul_ack_nack_and_cqi = parent->cfg.cqi_cfg.simultaneousAckCQI;
cqi_rep.simul_ack_nack_and_cqi = parent->cfg.cqi_cfg.simultaneousAckCQI;
if (phy_cfg->ant_info_present and
((phy_cfg->ant_info.explicit_value().tx_mode == ant_info_ded_s::tx_mode_e_::tm3) ||
(phy_cfg->ant_info.explicit_value().tx_mode == ant_info_ded_s::tx_mode_e_::tm4))) {
@ -1908,6 +1912,7 @@ void rrc::ue::send_connection_reconf(srslte::unique_byte_buffer_t pdu)
if (mobility_handler != nullptr) {
mobility_handler->fill_conn_recfg_msg(conn_reconf);
}
last_rrc_conn_recfg = *conn_reconf;
// Reuse same PDU
pdu->clear();
@ -1987,6 +1992,7 @@ void rrc::ue::send_security_mode_command()
(ciphering_algorithm_r12_e::options)cipher_algo;
comm->crit_exts.c1().security_mode_cmd_r8().security_cfg_smc.security_algorithm_cfg.integrity_prot_algorithm =
(security_algorithm_cfg_s::integrity_prot_algorithm_e_::options)integ_algo;
last_security_mode_cmd = comm->crit_exts.c1().security_mode_cmd_r8().security_cfg_smc.security_algorithm_cfg;
send_dl_dcch(&dl_dcch_msg);
}

@ -31,6 +31,12 @@
namespace srsenb {
#define Info(fmt, ...) rrc_log->info("Mobility: " fmt, ##__VA_ARGS__)
#define Error(fmt, ...) rrc_log->error("Mobility: " fmt, ##__VA_ARGS__)
#define Warning(fmt, ...) rrc_log->warning("Mobility: " fmt, ##__VA_ARGS__)
#define Debug(fmt, ...) rrc_log->debug("Mobility: " fmt, ##__VA_ARGS__)
#define procInfo(fmt, ...) parent->rrc_log->info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
#define procError(fmt, ...) parent->rrc_log->error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
using namespace asn1::rrc;
@ -45,6 +51,56 @@ uint32_t eci_to_cellid(uint32_t eci)
{
return eci & 0xFFu;
}
uint16_t compute_mac_i(uint16_t crnti,
uint32_t cellid,
uint16_t pci,
srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo,
uint8_t* k_rrc_int)
{
// Compute shortMAC-I
uint8_t varShortMAC_packed[16];
bzero(varShortMAC_packed, 16);
uint8_t mac_key[4];
// ASN.1 encode VarShortMAC-Input
asn1::rrc::var_short_mac_input_s var_short_mac;
var_short_mac.cell_id.from_number(cellid);
var_short_mac.pci = pci;
var_short_mac.c_rnti.from_number(crnti);
asn1::bit_ref bref(varShortMAC_packed, sizeof(varShortMAC_packed));
var_short_mac.pack(bref); // already zeroed, so no need to align
uint32_t N_bytes = bref.distance_bytes();
printf("Encoded varShortMAC: cellId=0x%x, PCI=%d, rnti=0x%x (%d bytes)\n", cellid, pci, crnti, N_bytes);
// Compute MAC-I
switch (integ_algo) {
case srslte::INTEGRITY_ALGORITHM_ID_128_EIA1:
srslte::security_128_eia1(&k_rrc_int[16],
0xffffffff, // 32-bit all to ones
0x1f, // 5-bit all to ones
1, // 1-bit to one
varShortMAC_packed,
N_bytes,
mac_key);
break;
case srslte::INTEGRITY_ALGORITHM_ID_128_EIA2:
srslte::security_128_eia2(&k_rrc_int[16],
0xffffffff, // 32-bit all to ones
0x1f, // 5-bit all to ones
1, // 1-bit to one
varShortMAC_packed,
N_bytes,
mac_key);
break;
default:
printf("Unsupported integrity algorithm.\n");
}
uint16_t short_mac_i = (((uint16_t)mac_key[2] << 8u) | (uint16_t)mac_key[3]);
return short_mac_i;
}
//! convenience function overload to extract Id from MeasObj/MeasId/ReportCfg/Cells
constexpr uint8_t get_id(const cells_to_add_mod_s& obj)
@ -96,8 +152,13 @@ using meas_id_cmp = field_id_cmp<meas_id_to_add_mod_s>;
template <typename Container, typename IdType>
typename Container::iterator binary_find(Container& c, IdType id)
{
using item_type = decltype(*Container{}.begin());
auto it = std::lower_bound(c.begin(), c.end(), id, field_id_cmp<item_type>{});
auto it = std::lower_bound(c.begin(), c.end(), id, field_id_cmp<decltype(*c.begin())>{});
return (it == c.end() or get_id(*it) != id) ? c.end() : it;
}
template <typename Container, typename IdType>
typename Container::const_iterator binary_find(const Container& c, IdType id)
{
auto it = std::lower_bound(c.begin(), c.end(), id, field_id_cmp<decltype(*c.begin())>{});
return (it == c.end() or get_id(*it) != id) ? c.end() : it;
}
@ -242,8 +303,8 @@ var_meas_cfg_t::add_cell_cfg(const meas_cell_cfg_t& cellcfg)
using namespace rrc_details;
bool inserted_flag = true;
// FIXME: cellcfg.cell_id is the ECI
uint32_t cell_id = rrc_details::eci_to_cellid(cellcfg.cell_id);
// FIXME: cellcfg.eci is the ECI
uint32_t cell_id = rrc_details::eci_to_cellid(cellcfg.eci);
q_offset_range_e offset;
asn1::number_to_enum(offset, (int8_t)cellcfg.q_offset); // FIXME: What's the difference
@ -264,7 +325,7 @@ var_meas_cfg_t::add_cell_cfg(const meas_cell_cfg_t& cellcfg)
inserted_flag = false;
}
} else {
// cell_id not found. create new cell
// eci not found. create new cell
auto& cell_list = ret.first->meas_obj.meas_obj_eutra().cells_to_add_mod_list;
cell_list.push_back(new_cell);
std::sort(cell_list.begin(), cell_list.end(), rrc_details::cell_id_cmp{});
@ -586,7 +647,8 @@ rrc::ue::rrc_mobility::rrc_mobility(rrc::ue* outer_ue) :
rrc_enb(outer_ue->parent),
cfg(outer_ue->parent->enb_mobility_cfg.get()),
pool(outer_ue->pool),
rrc_log(outer_ue->parent->rrc_log)
rrc_log(outer_ue->parent->rrc_log),
source_ho_proc(this)
{
ue_var_meas = std::make_shared<var_meas_cfg_t>(outer_ue->parent->rrc_log);
}
@ -594,8 +656,9 @@ rrc::ue::rrc_mobility::rrc_mobility(rrc::ue* outer_ue) :
//! Method to add Mobility Info to a RRC Connection Reconfiguration Message
bool rrc::ue::rrc_mobility::fill_conn_recfg_msg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn_recfg)
{
// only reconfigure meas_cfg if no handover is occurring
if (mobility_proc.is_busy()) {
// only reconfigure meas_cfg if no handover is occurring.
// NOTE: We basically freeze ue_var_meas for the whole duration of the handover procedure
if (source_ho_proc.is_busy()) {
return false;
}
@ -616,4 +679,233 @@ bool rrc::ue::rrc_mobility::fill_conn_recfg_msg(asn1::rrc::rrc_conn_recfg_r8_ies
return false;
}
//! Method called whenever the eNB receives a MeasReport from the UE. In normal situations, an HO procedure is started
void rrc::ue::rrc_mobility::handle_ue_meas_report(const meas_report_s& msg)
{
const meas_results_s& meas_res = msg.crit_exts.c1().meas_report_r8().meas_results;
const meas_id_to_add_mod_list_l& l = ue_var_meas->meas_ids();
auto measid_it = rrc_details::binary_find(l, meas_res.meas_id);
if (measid_it == l.end()) {
Warning("The measurement ID %d provided by the UE does not exist.\n", meas_res.meas_id);
return;
}
if (not meas_res.meas_result_neigh_cells_present) {
Info("Received a MeasReport, but the UE did not detect any cell.\n");
return;
}
if (meas_res.meas_result_neigh_cells.type().value !=
meas_results_s::meas_result_neigh_cells_c_::types::meas_result_list_eutra) {
Error("MeasReports regarding non-EUTRA are not supported!\n");
return;
}
const meas_obj_to_add_mod_list_l& objs = ue_var_meas->meas_objs();
const report_cfg_to_add_mod_list_l& reps = ue_var_meas->rep_cfgs();
auto obj_it = rrc_details::binary_find(objs, measid_it->meas_obj_id);
auto rep_it = rrc_details::binary_find(reps, measid_it->report_cfg_id);
const meas_result_list_eutra_l& eutra_list = meas_res.meas_result_neigh_cells.meas_result_list_eutra();
// iterate from strongest to weakest cell
// NOTE: From now we just look at the strongest.
if (eutra_list.size() > 0) {
uint32_t i = 0;
uint16_t pci = eutra_list[i].pci;
const cells_to_add_mod_list_l& cells = obj_it->meas_obj.meas_obj_eutra().cells_to_add_mod_list;
const cells_to_add_mod_s* cell_it =
std::find_if(cells.begin(), cells.end(), [pci](const cells_to_add_mod_s& c) { return c.pci == pci; });
if (cell_it == cells.end()) {
rrc_log->error("The PCI=%d inside the MeasReport is not recognized.\n", pci);
return;
}
// eNB found the respective cell. eNB takes "HO Decision"
// TODO: check what to do here to take the decision.
// NOTE: for now just accept anything.
// HO going forward.
auto& L = rrc_enb->cfg.meas_cfg.meas_cells;
uint32_t target_eci = std::find_if(L.begin(), L.end(), [pci](meas_cell_cfg_t& c) { return c.pci == pci; })->eci;
if (not source_ho_proc.launch(*measid_it, *obj_it, *rep_it, *cell_it, eutra_list[i], target_eci)) {
Error("Failed to start HO procedure, as it is already on-going\n");
return;
}
}
}
/**
* Description: Send "HO Required" message from source eNB to MME
* - 1st Message of the handover preparation phase
* - includes info about the target eNB and the radio resources of the source eNB
*/
bool rrc::ue::rrc_mobility::send_s1_ho_required(uint32_t target_eci, uint8_t measobj_id, bool fwd_direct_path_available)
{
if (fwd_direct_path_available) {
Error("Direct tunnels not supported supported\n");
return false;
}
srslte::plmn_id_t target_plmn =
srslte::make_plmn_id_t(rrc_enb->cfg.sib1.cell_access_related_info.plmn_id_list[0].plmn_id);
/*** Fill HO Preparation Info ***/
asn1::rrc::ho_prep_info_s hoprep;
asn1::rrc::ho_prep_info_r8_ies_s& hoprep_r8 = hoprep.crit_exts.set_c1().set_ho_prep_info_r8();
if (not rrc_ue->eutra_capabilities_unpacked) {
// FIXME: temporary. Made up something to please target eNB. (there must be at least one capability in this packet)
hoprep_r8.ue_radio_access_cap_info.resize(1);
hoprep_r8.ue_radio_access_cap_info[0].rat_type = asn1::rrc::rat_type_e::eutra;
asn1::rrc::ue_eutra_cap_s capitem;
capitem.access_stratum_release = asn1::rrc::access_stratum_release_e::rel8;
capitem.ue_category = 4;
capitem.pdcp_params.max_num_rohc_context_sessions_present = true;
capitem.pdcp_params.max_num_rohc_context_sessions = asn1::rrc::pdcp_params_s::max_num_rohc_context_sessions_e_::cs2;
bzero(&capitem.pdcp_params.supported_rohc_profiles,
sizeof(asn1::rrc::rohc_profile_support_list_r15_s)); // FIXME: why is it r15?
capitem.phy_layer_params.ue_specific_ref_sigs_supported = false;
capitem.phy_layer_params.ue_tx_ant_sel_supported = false;
capitem.rf_params.supported_band_list_eutra.resize(1);
capitem.rf_params.supported_band_list_eutra[0].band_eutra = 7;
capitem.rf_params.supported_band_list_eutra[0].half_duplex = false;
capitem.meas_params.band_list_eutra.resize(1);
capitem.meas_params.band_list_eutra[0].inter_rat_band_list_present = false;
capitem.meas_params.band_list_eutra[0].inter_freq_band_list.resize(1);
capitem.meas_params.band_list_eutra[0].inter_freq_band_list[0].inter_freq_need_for_gaps = false;
capitem.feature_group_inds_present = true;
capitem.feature_group_inds.from_number(0xe6041000); // 0x5d0ffc80); // 0xe6041c00;
{
hoprep_r8.ue_radio_access_cap_info[0].ue_cap_rat_container.resize(128);
asn1::bit_ref bref(&hoprep_r8.ue_radio_access_cap_info[0].ue_cap_rat_container[0],
hoprep_r8.ue_radio_access_cap_info[0].ue_cap_rat_container.size());
if (capitem.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) {
rrc_log->error("Failed to pack UE EUTRA Capability\n");
}
hoprep_r8.ue_radio_access_cap_info[0].ue_cap_rat_container.resize((uint32_t)bref.distance_bytes());
}
Debug("UE RA Category: %d\n", capitem.ue_category);
} else {
hoprep_r8.ue_radio_access_cap_info.resize(1);
for (ue_cap_rat_container_s& ratcntr : hoprep_r8.ue_radio_access_cap_info) {
ratcntr.rat_type = asn1::rrc::rat_type_e::eutra;
asn1::bit_ref bref(&ratcntr.ue_cap_rat_container[0], ratcntr.ue_cap_rat_container.size());
rrc_ue->eutra_capabilities.pack(bref);
}
}
/*** fill AS-Config ***/
hoprep_r8.as_cfg_present = true;
// NOTE: set source_meas_cnfg equal to the UE's current var_meas_cfg
var_meas_cfg_t empty_meascfg{rrc_log}, target_var_meas = *ue_var_meas;
// // however, reset the MeasObjToAdd Cells, so that the UE does not measure again the target eNB
// meas_obj_to_add_mod_s* obj = rrc_details::binary_find(target_var_meas.meas_objs(), measobj_id);
// obj->meas_obj.meas_obj_eutra().cells_to_add_mod_list.resize(0);
empty_meascfg.compute_diff_meas_cfg(target_var_meas, &hoprep_r8.as_cfg.source_meas_cfg);
// - fill source RR Config
hoprep_r8.as_cfg.source_rr_cfg.sps_cfg_present = false; // TODO: CHECK
hoprep_r8.as_cfg.source_rr_cfg.mac_main_cfg_present = rrc_ue->last_rrc_conn_recfg.rr_cfg_ded.mac_main_cfg_present;
hoprep_r8.as_cfg.source_rr_cfg.mac_main_cfg = rrc_ue->last_rrc_conn_recfg.rr_cfg_ded.mac_main_cfg;
hoprep_r8.as_cfg.source_rr_cfg.phys_cfg_ded_present = rrc_ue->last_rrc_conn_recfg.rr_cfg_ded.phys_cfg_ded_present;
hoprep_r8.as_cfg.source_rr_cfg.phys_cfg_ded = rrc_ue->last_rrc_conn_recfg.rr_cfg_ded.phys_cfg_ded;
// Add SRB2 to the message
hoprep_r8.as_cfg.source_rr_cfg.srb_to_add_mod_list_present =
rrc_ue->last_rrc_conn_recfg.rr_cfg_ded.srb_to_add_mod_list_present;
hoprep_r8.as_cfg.source_rr_cfg.srb_to_add_mod_list = rrc_ue->last_rrc_conn_recfg.rr_cfg_ded.srb_to_add_mod_list;
// hoprep_r8.as_cfg.source_rr_cfg.srb_to_add_mod_list_present = true;
// asn1::rrc::srb_to_add_mod_list_l& srb_list = hoprep_r8.as_cfg.source_rr_cfg.srb_to_add_mod_list;
// srb_list.resize(1);
// srb_list[0].srb_id = 2;
// srb_list[0].lc_ch_cfg_present = true;
// srb_list[0].lc_ch_cfg.set(asn1::rrc::srb_to_add_mod_s::lc_ch_cfg_c_::types::default_value);
// srb_list[0].rlc_cfg_present = true;
// srb_list[0].rlc_cfg.set_explicit_value();
// auto& am = srb_list[0].rlc_cfg.explicit_value().set_am(); // FIXME: Which rlc cfg??? I took from a pcap for now
// am.ul_am_rlc.t_poll_retx = asn1::rrc::t_poll_retx_e::ms60;
// am.ul_am_rlc.poll_pdu = asn1::rrc::poll_pdu_e::p_infinity;
// am.ul_am_rlc.poll_byte.value = asn1::rrc::poll_byte_e::kbinfinity;
// am.ul_am_rlc.max_retx_thres.value = asn1::rrc::ul_am_rlc_s::max_retx_thres_e_::t32;
// am.dl_am_rlc.t_reordering.value = asn1::rrc::t_reordering_e::ms45;
// am.dl_am_rlc.t_status_prohibit.value = asn1::rrc::t_status_prohibit_e::ms0;
// Get DRB1 configuration
hoprep_r8.as_cfg.source_rr_cfg.drb_to_add_mod_list_present =
rrc_ue->last_rrc_conn_recfg.rr_cfg_ded.drb_to_add_mod_list_present;
hoprep_r8.as_cfg.source_rr_cfg.drb_to_add_mod_list = rrc_ue->last_rrc_conn_recfg.rr_cfg_ded.drb_to_add_mod_list;
// hoprep_r8.as_cfg.source_rr_cfg.drb_to_add_mod_list_present = true;
// asn1::rrc::drb_to_add_mod_list_l& drb_list = hoprep_r8.as_cfg.source_rr_cfg.drb_to_add_mod_list;
// drb_list.resize(1);
// rrc_ue->get_drbid_config(&hoprep_r8.as_cfg.source_rr_cfg.drb_to_add_mod_list[0], 1);
// hoprep_r8.as_cfg.source_rr_cfg.drb_to_release_list_present = true;
// hoprep_r8.as_cfg.source_rr_cfg.drb_to_release_list.resize(1);
// hoprep_r8.as_cfg.source_rr_cfg.drb_to_release_list[0] = 1;
hoprep_r8.as_cfg.source_security_algorithm_cfg = rrc_ue->last_security_mode_cmd;
hoprep_r8.as_cfg.source_ue_id.from_number(rrc_ue->rnti);
asn1::number_to_enum(hoprep_r8.as_cfg.source_mib.dl_bw, rrc_enb->cfg.cell.nof_prb);
hoprep_r8.as_cfg.source_mib.phich_cfg.phich_dur.value =
(asn1::rrc::phich_cfg_s::phich_dur_e_::options)rrc_enb->cfg.cell.phich_length;
hoprep_r8.as_cfg.source_mib.phich_cfg.phich_res.value =
(asn1::rrc::phich_cfg_s::phich_res_e_::options)rrc_enb->cfg.cell.phich_resources;
hoprep_r8.as_cfg.source_mib.sys_frame_num.from_number(0); // NOTE: The TS says this can go empty
hoprep_r8.as_cfg.source_sib_type1 = rrc_enb->cfg.sib1;
hoprep_r8.as_cfg.source_sib_type2 = rrc_enb->sib2;
asn1::number_to_enum(hoprep_r8.as_cfg.ant_info_common.ant_ports_count, rrc_enb->cfg.cell.nof_ports);
hoprep_r8.as_cfg.source_dl_carrier_freq = rrc_enb->cfg.dl_earfcn;
// - fill as_context
hoprep_r8.as_context_present = true;
hoprep_r8.as_context.reest_info_present = true;
hoprep_r8.as_context.reest_info.source_pci = rrc_enb->cfg.pci;
hoprep_r8.as_context.reest_info.target_cell_short_mac_i.from_number(
rrc_details::compute_mac_i(rrc_ue->rnti,
rrc_enb->cfg.sib1.cell_access_related_info.cell_id.to_number(),
rrc_enb->cfg.pci,
rrc_ue->integ_algo,
rrc_ue->k_rrc_int));
/*** pack HO Preparation Info into an RRC container buffer ***/
srslte::unique_byte_buffer_t buffer = srslte::allocate_unique_buffer(*pool);
asn1::bit_ref bref(buffer->msg, buffer->get_tailroom());
if (hoprep.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) {
Error("Failed to pack HO preparation msg\n");
return false;
}
buffer->N_bytes = bref.distance_bytes();
bool success = rrc_enb->s1ap->send_ho_required(rrc_ue->rnti, target_eci, target_plmn, std::move(buffer));
Info("sent s1ap msg with HO Required\n");
return success;
}
/*************************************************************************************************
* sourceenb_ho_proc_t class
************************************************************************************************/
rrc::ue::rrc_mobility::sourceenb_ho_proc_t::sourceenb_ho_proc_t(rrc_mobility* ue_mobility_) : parent(ue_mobility_) {}
srslte::proc_outcome_t rrc::ue::rrc_mobility::sourceenb_ho_proc_t::init(const meas_id_to_add_mod_s& measid_,
const meas_obj_to_add_mod_s& measobj_,
const report_cfg_to_add_mod_s& repcfg_,
const cells_to_add_mod_s& cell_,
const meas_result_eutra_s& meas_res_,
uint32_t target_eci_)
{
measid = &measid_;
measobj = &measobj_;
repcfg = &repcfg_;
cell = &cell_;
meas_res = meas_res_;
target_eci = target_eci_;
// TODO: Check X2 is available first. If fail, go for S1.
// NOTE: For now only S1-HO is supported. X2 also not available for fwd direct path
ho_interface = ho_interface_t::S1;
fwd_direct_path_available = false;
state = state_t::ho_preparation;
procInfo("Started Handover of rnti=0x%x to %s.\n", parent->rrc_ue->rnti, rrc_details::to_string(*cell).c_str());
if (not parent->send_s1_ho_required(target_eci, measobj->meas_obj_id, fwd_direct_path_available)) {
procError("Failed to send HO Required to MME.\n");
return srslte::proc_outcome_t::error;
}
return srslte::proc_outcome_t::yield;
}
} // namespace srsenb

@ -191,6 +191,7 @@ void s1ap::initial_ue(uint16_t rnti, LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM ca
ue_ctxt_map[rnti].eNB_UE_S1AP_ID = next_eNB_UE_S1AP_ID++;
ue_ctxt_map[rnti].stream_id = 1;
ue_ctxt_map[rnti].release_requested = false;
gettimeofday(&ue_ctxt_map[rnti].init_timestamp, nullptr);
enbid_to_rnti_map[ue_ctxt_map[rnti].eNB_UE_S1AP_ID] = rnti;
send_initialuemessage(rnti, cause, std::move(pdu), false);
}
@ -1146,6 +1147,125 @@ bool s1ap::send_uectxmodifyfailure(uint16_t rnti, LIBLTE_S1AP_CAUSE_STRUCT* caus
return true;
}
/*********************
* Handover Messages
********************/
bool s1ap::send_ho_required(uint16_t rnti,
uint32_t target_eci,
srslte::plmn_id_t target_plmn,
srslte::unique_byte_buffer_t rrc_container)
{
if (!mme_connected) {
return false;
}
auto ueit = ue_ctxt_map.find(rnti);
if (ueit == ue_ctxt_map.end()) {
s1ap_log->error("rnti=0x%x is not recognized.\n", rnti);
return false;
}
ue_ctxt_t* uectxt = &ueit->second;
/*** Setup S1AP PDU as HandoverRequired ***/
LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu;
bzero(&tx_pdu, sizeof(tx_pdu));
tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE;
tx_pdu.choice.initiatingMessage.choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_HANDOVERREQUIRED;
tx_pdu.choice.initiatingMessage.criticality = LIBLTE_S1AP_CRITICALITY_IGNORE;
tx_pdu.choice.initiatingMessage.procedureCode = LIBLTE_S1AP_PROC_ID_HANDOVERPREPARATION;
LIBLTE_S1AP_MESSAGE_HANDOVERREQUIRED_STRUCT& horeq = tx_pdu.choice.initiatingMessage.choice.HandoverRequired;
/*** fill HO Required message ***/
horeq.eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = uectxt->eNB_UE_S1AP_ID;
horeq.MME_UE_S1AP_ID.MME_UE_S1AP_ID = uectxt->MME_UE_S1AP_ID;
horeq.Direct_Forwarding_Path_Availability_present = false; // NOTE: X2 for fwd path not supported
horeq.HandoverType.e = LIBLTE_S1AP_HANDOVERTYPE_INTRALTE; // NOTE: only intra-LTE HO supported
horeq.Cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK;
horeq.Cause.choice.radioNetwork.e = LIBLTE_S1AP_CAUSERADIONETWORK_UNSPECIFIED;
// LIBLTE_S1AP_CAUSERADIONETWORK_S1_INTRA_SYSTEM_HANDOVER_TRIGGERED;
/*** set the target eNB ***/
horeq.TargetID.choice_type = LIBLTE_S1AP_TARGETID_CHOICE_TARGETENB_ID;
LIBLTE_S1AP_TARGETENB_ID_STRUCT* targetenb = &horeq.TargetID.choice.targeteNB_ID;
horeq.CSG_Id_present = false; // NOTE: CSG/hybrid target cell not supported
horeq.CellAccessMode_present = false; // only for hybrid cells
// no GERAN/UTRAN/PS
horeq.SRVCCHOIndication_present = false;
horeq.MSClassmark2_present = false;
horeq.MSClassmark3_present = false;
horeq.PS_ServiceNotAvailable_present = false;
// set PLMN of target and TAI
if (horeq.TargetID.choice_type != LIBLTE_S1AP_TARGETID_CHOICE_TARGETENB_ID) {
s1ap_log->error("Non-intraLTE HO not supported.\n");
return false;
}
// NOTE: Only HO without TAU supported.
uint16_t tmp16;
tmp16 = htons(args.tac);
memcpy(targetenb->selected_TAI.tAC.buffer, &tmp16, sizeof(uint16_t));
target_plmn.to_s1ap_plmn_bytes(targetenb->selected_TAI.pLMNidentity.buffer);
// NOTE: Only HO to different Macro eNB is supported.
targetenb->global_ENB_ID.eNB_ID.choice_type = LIBLTE_S1AP_ENB_ID_CHOICE_MACROENB_ID;
target_plmn.to_s1ap_plmn_bytes(targetenb->global_ENB_ID.pLMNidentity.buffer);
uint32_t tmp32 = htonl(target_eci >> 8u);
uint8_t enb_id_bits[sizeof(uint32_t) * 8];
liblte_unpack((uint8_t*)&tmp32, sizeof(uint32_t), enb_id_bits);
memcpy(targetenb->global_ENB_ID.eNB_ID.choice.macroENB_ID.buffer,
&enb_id_bits[32 - LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN],
LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN);
/*** fill the transparent container ***/
horeq.Source_ToTarget_TransparentContainer_Secondary_present = false;
LIBLTE_S1AP_SOURCEENB_TOTARGETENB_TRANSPARENTCONTAINER_STRUCT transparent_cntr;
bzero(&transparent_cntr, sizeof(LIBLTE_S1AP_SOURCEENB_TOTARGETENB_TRANSPARENTCONTAINER_STRUCT));
transparent_cntr.e_RABInformationList_present = false; // TODO: CHECK
transparent_cntr.subscriberProfileIDforRFP_present = false; // TODO: CHECK
// - set target cell ID
target_plmn.to_s1ap_plmn_bytes(transparent_cntr.targetCell_ID.pLMNidentity.buffer);
tmp32 = htonl(target_eci);
uint8_t eci_bits[32];
liblte_unpack((uint8_t*)&tmp32, sizeof(uint32_t), eci_bits);
memcpy(transparent_cntr.targetCell_ID.cell_ID.buffer,
&eci_bits[32 - LIBLTE_S1AP_CELLIDENTITY_BIT_STRING_LEN],
LIBLTE_S1AP_CELLIDENTITY_BIT_STRING_LEN); // [ENBID|CELLID|0]
// info specific to source cell and history of UE
// - set as last visited cell the source eNB PLMN & Cell ID
transparent_cntr.uE_HistoryInformation.len = 1;
transparent_cntr.uE_HistoryInformation.buffer[0].choice_type = LIBLTE_S1AP_LASTVISITEDCELL_ITEM_CHOICE_E_UTRAN_CELL;
LIBLTE_S1AP_LASTVISITEDEUTRANCELLINFORMATION_STRUCT* lastvisited =
&transparent_cntr.uE_HistoryInformation.buffer[0].choice.e_UTRAN_Cell;
lastvisited->cellType.cell_Size.e = LIBLTE_S1AP_CELL_SIZE_MEDIUM;
target_plmn.to_s1ap_plmn_bytes(lastvisited->global_Cell_ID.pLMNidentity.buffer);
memcpy(
lastvisited->global_Cell_ID.cell_ID.buffer, eutran_cgi.cell_ID.buffer, LIBLTE_S1AP_CELLIDENTITY_BIT_STRING_LEN);
// - set time spent in current source cell
struct timeval ts[3];
memcpy(&ts[1], &uectxt->init_timestamp, sizeof(struct timeval));
gettimeofday(&ts[2], nullptr);
get_time_interval(ts);
lastvisited->time_UE_StayedInCell.Time_UE_StayedInCell = (uint16_t)(ts[0].tv_usec / 1.0e6 + ts[0].tv_sec);
lastvisited->time_UE_StayedInCell.Time_UE_StayedInCell =
std::min(lastvisited->time_UE_StayedInCell.Time_UE_StayedInCell, (uint16_t)4095);
// - fill RRC container
memcpy(transparent_cntr.rRC_Container.buffer, rrc_container->buffer, rrc_container->N_bytes);
transparent_cntr.rRC_Container.n_octets = rrc_container->N_bytes;
/*** pack Transparent Container into HORequired message ***/
LIBLTE_BYTE_MSG_STRUCT bytemsg;
bytemsg.N_bytes = 0;
LIBLTE_BIT_MSG_STRUCT bitmsg;
uint8_t* msg_ptr = bitmsg.msg;
liblte_s1ap_pack_sourceenb_totargetenb_transparentcontainer(&transparent_cntr, &msg_ptr);
bitmsg.N_bits = msg_ptr - bitmsg.msg;
liblte_pack(&bitmsg, &bytemsg);
memcpy(horeq.Source_ToTarget_TransparentContainer.buffer, bytemsg.msg, bytemsg.N_bytes);
horeq.Source_ToTarget_TransparentContainer.n_octets = bytemsg.N_bytes;
// TODO: Start timer TS1_{RELOCprep}
return sctp_send_s1ap_pdu(&tx_pdu, rnti, "HORequired");
}
// bool s1ap::send_ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps)
//{
// srslte::byte_buffer_t msg;
@ -1182,13 +1302,40 @@ bool s1ap::send_uectxmodifyfailure(uint16_t rnti, LIBLTE_S1AP_CAUSE_STRUCT* caus
/* General helpers
********************************************************************************/
bool s1ap::sctp_send_s1ap_pdu(LIBLTE_S1AP_S1AP_PDU_STRUCT* tx_pdu, uint32_t rnti, const char* procedure_name)
{
srslte::unique_byte_buffer_t buf = srslte::allocate_unique_buffer(*pool, false);
if (buf == nullptr) {
s1ap_log->error("Fatal Error: Couldn't allocate buffer for %s.\n", procedure_name);
return false;
}
liblte_s1ap_pack_s1ap_pdu(tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)buf.get());
s1ap_log->info_hex(buf->msg, buf->N_bytes, "Sending %s for rnti=0x%x", procedure_name, rnti);
ssize_t n_sent = sctp_sendmsg(socket_fd,
buf->msg,
buf->N_bytes,
(struct sockaddr*)&mme_addr,
sizeof(struct sockaddr_in),
htonl(PPID),
0,
ue_ctxt_map[rnti].stream_id,
0,
0);
if (n_sent == -1) {
s1ap_log->error("Failed to send %s for rnti=0x%x\n", procedure_name, rnti);
return false;
}
return true;
}
bool s1ap::find_mme_ue_id(uint32_t mme_ue_id, uint16_t* rnti, uint32_t* enb_ue_id)
{
typedef std::map<uint16_t, ue_ctxt_t>::iterator it_t;
for(it_t it=ue_ctxt_map.begin(); it!=ue_ctxt_map.end(); ++it) {
if(it->second.MME_UE_S1AP_ID == mme_ue_id) {
*rnti = it->second.rnti;
*enb_ue_id = it->second.eNB_UE_S1AP_ID;
for (auto& it : ue_ctxt_map) {
if (it.second.MME_UE_S1AP_ID == mme_ue_id) {
*rnti = it.second.rnti;
*enb_ue_id = it.second.eNB_UE_S1AP_ID;
return true;
}
}

@ -59,7 +59,7 @@ meas_cell_cfg_t generate_cell1()
cell1.earfcn = 3400;
cell1.pci = 1;
cell1.q_offset = 0;
cell1.cell_id = 0x19C01;
cell1.eci = 0x19C01;
return cell1;
}
@ -82,7 +82,7 @@ report_cfg_eutra_s generate_rep1()
bool is_cell_cfg_equal(const meas_cell_cfg_t& cfg, const cells_to_add_mod_s& cell)
{
return cfg.pci == cell.pci and cell.cell_individual_offset.to_number() == (int8_t)round(cfg.q_offset) and
cell.cell_idx == (cfg.cell_id & 0xFFu);
cell.cell_idx == (cfg.eci & 0xFFu);
}
int test_correct_insertion()
@ -90,7 +90,7 @@ int test_correct_insertion()
meas_cell_cfg_t cell1 = generate_cell1(), cell2{}, cell3{}, cell4{};
cell2 = cell1;
cell2.pci = 2;
cell2.cell_id = 0x19C02;
cell2.eci = 0x19C02;
cell3 = cell1;
cell3.earfcn = 2850;
cell4 = cell1;
@ -129,8 +129,8 @@ int test_correct_insertion()
TESTASSERT(eutra.carrier_freq == cell1.earfcn);
TESTASSERT(eutra.cells_to_add_mod_list.size() == 2);
const cells_to_add_mod_s* cell_it = eutra.cells_to_add_mod_list.begin();
TESTASSERT(cell_it[0].cell_idx == (cell1.cell_id & 0xFFu));
TESTASSERT(cell_it[1].cell_idx == (cell2.cell_id & 0xFFu));
TESTASSERT(cell_it[0].cell_idx == (cell1.eci & 0xFFu));
TESTASSERT(cell_it[1].cell_idx == (cell2.eci & 0xFFu));
TESTASSERT(cell_it[1].pci == cell2.pci);
// TEST 3: insertion of cell in another frequency
@ -148,7 +148,7 @@ int test_correct_insertion()
TESTASSERT(objs.size() == 2 and objs[0].meas_obj_id == 1);
TESTASSERT(eutra3.carrier_freq == cell4.earfcn);
TESTASSERT(eutra3.cells_to_add_mod_list.size() == 2);
TESTASSERT(eutra3.cells_to_add_mod_list[0].cell_idx == (cell1.cell_id & 0xFFu));
TESTASSERT(eutra3.cells_to_add_mod_list[0].cell_idx == (cell1.eci & 0xFFu));
TESTASSERT(eutra3.cells_to_add_mod_list[0].cell_individual_offset.to_number() == 1);
}
@ -163,10 +163,10 @@ int test_correct_meascfg_calculation()
cell1.earfcn = 3400;
cell1.pci = 1;
cell1.q_offset = 0;
cell1.cell_id = 0x19C01;
cell1.eci = 0x19C01;
cell2 = cell1;
cell2.pci = 2;
cell2.cell_id = 0x19C02;
cell2.eci = 0x19C02;
report_cfg_eutra_s rep1 = generate_rep1(), rep2{}, rep3{};
rep2 = rep1;
@ -250,7 +250,7 @@ int test_correct_meascfg_calculation()
eutra = item->meas_obj.meas_obj_eutra();
TESTASSERT(not eutra.cells_to_add_mod_list_present and eutra.cells_to_rem_list_present);
TESTASSERT(eutra.cells_to_rem_list.size() == 1);
TESTASSERT(eutra.cells_to_rem_list[0] == (cell1.cell_id & 0xFFu));
TESTASSERT(eutra.cells_to_rem_list[0] == (cell1.eci & 0xFFu));
TESTASSERT(result_meascfg.report_cfg_to_add_mod_list_present and not result_meascfg.report_cfg_to_rem_list_present);
TESTASSERT(result_meascfg.report_cfg_to_add_mod_list.size() == 1);
TESTASSERT(result_meascfg.report_cfg_to_add_mod_list[0].report_cfg_id == 2);

Loading…
Cancel
Save