implemented phy controller FSM to perform cell selection & search. The controller also tracks the phy sync state

This commit is contained in:
Francisco Paisana 2020-07-07 17:29:57 +01:00
parent 6fb01d61a7
commit 489969722e
7 changed files with 464 additions and 1 deletions

View File

@ -679,6 +679,9 @@ private:
ProcFSM* proc_ptr = nullptr;
};
template <typename Event>
using event_callback = std::function<void(const Event&)>;
} // namespace srslte
#endif // SRSLTE_FSM_H

View File

@ -65,6 +65,30 @@ public:
void write_pdu_mch(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) override {}
};
class phy_dummy_interface : public phy_interface_rrc_lte
{
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(srslte::mbsfn_sf_cfg_t* cfg_list, uint32_t nof_cfgs) {}
void set_config_mbsfn_sib13(const srslte::sib13_t& sib13) {}
void set_config_mbsfn_mcch(const srslte::mcch_msg_t& mcch) {}
/* Measurements interface */
void set_cells_to_meas(uint32_t earfcn, const std::set<uint32_t>& pci) {}
void meas_stop() {}
/* Cell search and selection procedures */
cell_search_ret_t cell_search(phy_cell_t* cell) { return {}; }
bool cell_select(const phy_cell_t* cell = nullptr) { return true; }
bool cell_is_camping() { return false; }
void reset() {}
void enable_pregen_signals(bool enable) {}
};
} // namespace srsue
#endif // SRSUE_DUMMY_CLASSES_H

View File

@ -0,0 +1,160 @@
/*
* Copyright 2013-2020 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 SRSLTE_PHY_CONTROLLER_H
#define SRSLTE_PHY_CONTROLLER_H
#include "srslte/common/fsm.h"
#include "srslte/common/logmap.h"
#include "srslte/interfaces/ue_interfaces.h"
namespace srsue {
class phy_controller : public srslte::fsm_t<phy_controller>
{
using phy_cell_t = phy_interface_rrc_lte::phy_cell_t;
using cell_search_ret_t = phy_interface_rrc_lte::cell_search_ret_t;
public:
static const uint32_t wait_sync_timeout_ms = 50;
// events
struct cell_srch_res {
cell_search_ret_t cs_ret;
phy_cell_t found_cell;
};
using cell_sel_res = bool;
struct cell_sel_cmd {
phy_cell_t phy_cell;
srslte::event_callback<cell_sel_res> callback;
};
using cell_search_cmd = srslte::event_callback<cell_srch_res>;
struct in_sync_ev {};
struct out_sync_ev {};
struct timeout_ev {};
explicit phy_controller(phy_interface_rrc_lte* phy_, stack_interface_rrc* stack_);
// PHY procedures interfaces
bool start_cell_select(const phy_cell_t& phy_cell, const srslte::event_callback<cell_sel_res>& on_complete);
bool start_cell_search(const srslte::event_callback<cell_srch_res>& on_complete);
bool cell_search_completed(cell_search_ret_t cs_ret, phy_cell_t found_cell);
bool cell_selection_completed(bool outcome);
void in_sync() { trigger(in_sync_ev{}); }
void out_sync() { trigger(out_sync_ev{}); }
// state getters
bool cell_is_camping() { return phy->cell_is_camping(); }
bool is_in_sync() const { return is_in_state<in_sync_st>(); }
private:
phy_interface_rrc_lte* phy = nullptr;
stack_interface_rrc* stack = nullptr;
protected:
// states
struct unknown_st {};
struct in_sync_st {};
struct out_sync_st {};
struct selecting_cell : public subfsm_t<selecting_cell> {
struct timeout_ev {};
struct wait_result {};
struct wait_in_sync {
void enter(selecting_cell* f, const cell_sel_res& ev);
};
explicit selecting_cell(phy_controller* parent_);
void enter(phy_controller* f, const cell_sel_cmd& ev);
void exit(phy_controller* f);
srslte::timer_handler::unique_timer wait_in_sync_timer;
phy_cell_t target_cell = {};
cell_sel_res result = {};
srslte::event_callback<cell_sel_res> csel_callback;
protected:
// guard functions
bool is_cell_selected(wait_result& s, const cell_sel_res& ev) { return ev; }
// event handlers
void set_success(wait_in_sync& s, const in_sync_ev& ev) { result = true; }
state_list<wait_result, wait_in_sync> states{this};
// clang-format off
using c = selecting_cell;
using transitions = transition_table<
// Start Target Event Action Guard
// +----------------+---------------+--------------+------------------+----------------------+
row< wait_result, wait_in_sync, cell_sel_res, nullptr, &c::is_cell_selected >,
row< wait_result, unknown_st, cell_sel_res >,
// +----------------+---------------+--------------+------------------+----------------------+
row< wait_in_sync, in_sync_st, in_sync_ev, &c::set_success >,
row< wait_in_sync, out_sync_st, timeout_ev >
// +----------------+---------------+--------------+------------------+----------------------+
>;
// clang-format on
};
struct searching_cell {
void enter(phy_controller* f, const cell_search_cmd& cmd);
std::vector<srslte::event_callback<cell_srch_res> > csearch_callbacks;
};
state_list<unknown_st, in_sync_st, out_sync_st, searching_cell, selecting_cell> states{this,
unknown_st{},
in_sync_st{},
out_sync_st{},
searching_cell{},
selecting_cell{this}};
// event handlers
void handle_cell_search_res(searching_cell& s, const cell_srch_res& result);
void share_cell_search_res(searching_cell& s, const cell_search_cmd& cmd);
// clang-format off
using c = phy_controller;
using transitions = transition_table<
// Start Target Event Action Guard
// +----------------+-----------------+------------------+------------------------------+---------------------+
row< unknown_st, selecting_cell, cell_sel_cmd >,
row< unknown_st, searching_cell, cell_search_cmd >,
row< unknown_st, in_sync_st, in_sync_ev >,
row< unknown_st, out_sync_st, out_sync_ev >,
// +----------------+-----------------+------------------+------------------------------+---------------------+
row< in_sync_st, selecting_cell, cell_sel_cmd >,
row< in_sync_st, searching_cell, cell_search_cmd >,
row< in_sync_st, out_sync_st, out_sync_ev >,
// +----------------+-----------------+------------------+------------------------------+---------------------+
row< out_sync_st, selecting_cell, cell_sel_cmd >,
row< out_sync_st, searching_cell, cell_search_cmd >,
row< out_sync_st, in_sync_st, in_sync_ev >,
// +----------------+-----------------+------------------+------------------------------+---------------------+
row< searching_cell, unknown_st, cell_srch_res, &c::handle_cell_search_res >,
upd< searching_cell, cell_search_cmd, &c::share_cell_search_res >
// +----------------+-----------------+------------------+------------------------------+---------------------+
>;
// clang-format on
};
} // namespace srsue
#endif // SRSLTE_PHY_CONTROLLER_H

View File

@ -18,7 +18,7 @@
# and at http://www.gnu.org/licenses/.
#
set(SOURCES rrc.cc rrc_procedures.cc rrc_meas.cc rrc_cell.cc)
set(SOURCES rrc.cc rrc_procedures.cc rrc_meas.cc rrc_cell.cc phy_controller.cc)
add_library(srsue_rrc STATIC ${SOURCES})
if(ENABLE_5GNR)

View File

@ -0,0 +1,149 @@
/*
* Copyright 2013-2020 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 "srsue/hdr/stack/rrc/phy_controller.h"
namespace srsue {
std::string to_string(const phy_interface_rrc_lte::phy_cell_t& cell)
{
char buffer[128];
snprintf(buffer, sizeof(buffer), "{pci=%d, dl_earfcn=%d}", cell.pci, cell.earfcn);
return buffer;
}
phy_controller::phy_controller(srsue::phy_interface_rrc_lte* phy_, srsue::stack_interface_rrc* stack_) :
base_t(srslte::log_ref{"RRC"}),
phy(phy_),
stack(stack_)
{}
/**************************************
* PHY Cell Select Procedure
*************************************/
bool phy_controller::start_cell_select(const phy_cell_t& phy_cell,
const srslte::event_callback<cell_sel_res>& on_complete)
{
if (not trigger(cell_sel_cmd{phy_cell, on_complete})) {
log_h->warning("Failed to launch cell selection\n");
return false;
}
return true;
}
phy_controller::selecting_cell::selecting_cell(phy_controller* parent_) : nested_fsm_t(parent_)
{
wait_in_sync_timer = parent_fsm()->stack->get_unique_timer();
}
void phy_controller::selecting_cell::enter(phy_controller* f, const cell_sel_cmd& ev)
{
target_cell = ev.phy_cell;
csel_callback = ev.callback;
result = false;
f->log_h->info("Starting \"%s\" for pci=%d, earfcn=%d\n",
srslte::get_type_name(*this).c_str(),
target_cell.pci,
target_cell.earfcn);
f->stack->start_cell_select(&target_cell);
}
void phy_controller::selecting_cell::exit(phy_controller* f)
{
wait_in_sync_timer.stop();
if (result) {
log_h->info("Cell %s successfully selected\n", to_string(target_cell).c_str());
} else {
log_h->warning("Failed to select cell %s\n", to_string(target_cell).c_str());
}
// Signal result back to FSM that called cell selection
csel_callback(result);
}
void phy_controller::selecting_cell::wait_in_sync::enter(selecting_cell* f, const cell_sel_res& ev)
{
f->wait_in_sync_timer.set(wait_sync_timeout_ms, [f](uint32_t tid) { f->trigger(timeout_ev{}); });
f->wait_in_sync_timer.run();
}
/**************************************
* PHY Cell Search Procedure
*************************************/
//! Searches for a cell in the current frequency and retrieves SIB1 if not retrieved yet
bool phy_controller::start_cell_search(const srslte::event_callback<cell_srch_res>& on_complete)
{
if (not trigger(on_complete)) {
log_h->warning("Failed to launch cell search\n");
return false;
}
return true;
}
bool phy_controller::cell_search_completed(cell_search_ret_t cs_ret, phy_cell_t found_cell)
{
return trigger(cell_srch_res{cs_ret, found_cell});
}
bool phy_controller::cell_selection_completed(bool outcome)
{
return trigger(outcome);
}
void phy_controller::searching_cell::enter(phy_controller* f, const cell_search_cmd& cmd)
{
f->log_h->info("Initiated Cell search\n");
csearch_callbacks.emplace_back(cmd);
f->stack->start_cell_search();
}
void phy_controller::handle_cell_search_res(searching_cell& s, const cell_srch_res& result)
{
switch (result.cs_ret.found) {
case cell_search_ret_t::CELL_FOUND:
log_h->info("PHY cell search completed. Found cell %s\n", to_string(result.found_cell).c_str());
break;
case cell_search_ret_t::CELL_NOT_FOUND:
log_h->warning("PHY cell search completed. No cells found\n");
break;
default:
log_h->error("Invalid cell search result\n");
// TODO: check what errors can happen (currently not handled in our code)
}
// Signal back completion
for (auto& f : s.csearch_callbacks) {
f(result);
}
s.csearch_callbacks.clear();
}
void phy_controller::share_cell_search_res(searching_cell& s, const cell_search_cmd& callback)
{
log_h->info("Cell Search already running. Re-utilizing result.\n");
s.csearch_callbacks.emplace_back(callback);
}
} // namespace srsue

View File

@ -43,6 +43,10 @@ add_executable(tft_test tft_test.cc)
target_link_libraries(tft_test srsue_upper srslte_upper srslte_phy)
add_test(tft_test tft_test)
add_executable(rrc_phy_ctrl_test rrc_phy_ctrl_test.cc)
target_link_libraries(rrc_phy_ctrl_test srslte_common srsue_rrc)
add_test(rrc_phy_ctrl_test rrc_phy_ctrl_test)
########################################################################
# Option to run command after build (useful for remote builds)
########################################################################

View File

@ -0,0 +1,123 @@
/*
* Copyright 2013-2020 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/common/test_common.h"
#include "srslte/test/ue_test_interfaces.h"
#include "srsue/hdr/stack/rrc/phy_controller.h"
namespace srsue {
int test_phy_ctrl_fsm()
{
srslte::log_ref test_log{"TEST"};
stack_test_dummy stack;
phy_dummy_interface phy;
phy_controller phy_ctrl{&phy, &stack};
TESTASSERT(not phy_ctrl.is_in_sync());
TESTASSERT(not phy_ctrl.cell_is_camping());
// TEST: Sync event changes phy controller state
phy_ctrl.in_sync();
TESTASSERT(phy_ctrl.is_in_sync());
phy_ctrl.out_sync();
TESTASSERT(not phy_ctrl.is_in_sync());
phy_ctrl.in_sync();
TESTASSERT(phy_ctrl.is_in_sync());
// TEST: Correct initiation of Cell Search state
bool csearch_res_present = false;
phy_controller::cell_srch_res csearch_res = {};
auto cell_sel_callback = [&csearch_res_present, &csearch_res](const phy_controller::cell_srch_res& result) {
csearch_res_present = true;
csearch_res = result;
};
TESTASSERT(phy_ctrl.start_cell_search(cell_sel_callback));
TESTASSERT(not phy_ctrl.is_in_sync());
// TEST: Cell Search only listens to a cell search result event and calls provided callback on completion
phy_ctrl.in_sync();
TESTASSERT(not phy_ctrl.is_in_sync());
phy_ctrl.out_sync();
TESTASSERT(not phy_ctrl.is_in_sync());
TESTASSERT(phy_ctrl.current_state_name() == "searching_cell");
phy_interface_rrc_lte::cell_search_ret_t cs_ret;
cs_ret.found = phy_interface_rrc_lte::cell_search_ret_t::CELL_FOUND;
phy_interface_rrc_lte::phy_cell_t found_cell;
found_cell.pci = 1;
found_cell.earfcn = 2;
phy_ctrl.cell_search_completed(cs_ret, found_cell);
TESTASSERT(phy_ctrl.current_state_name() != "searching_cell");
TESTASSERT(csearch_res_present);
TESTASSERT(csearch_res.cs_ret.found == cs_ret.found);
TESTASSERT(csearch_res.found_cell.pci == found_cell.pci);
TESTASSERT(csearch_res.found_cell.earfcn == found_cell.earfcn);
phy_ctrl.in_sync();
TESTASSERT(phy_ctrl.is_in_sync());
phy_ctrl.out_sync();
// TEST: Correct initiation of Cell Select state
int cell_select_success = -1;
auto csel_callback = [&cell_select_success](const bool& res) { cell_select_success = res ? 1 : 0; };
phy_ctrl.start_cell_select(found_cell, csel_callback);
TESTASSERT(not phy_ctrl.is_in_sync());
TESTASSERT(phy_ctrl.current_state_name() == "selecting_cell");
// TEST: Cell Selection state ignores events other than the cell selection result, and callback is called
phy_ctrl.in_sync();
TESTASSERT(not phy_ctrl.is_in_sync());
phy_ctrl.cell_selection_completed(true);
// Note: Still in cell selection, but now waiting for the first in_sync
TESTASSERT(phy_ctrl.current_state_name() == "selecting_cell");
TESTASSERT(not phy_ctrl.is_in_sync());
TESTASSERT(cell_select_success < 0);
phy_ctrl.in_sync();
TESTASSERT(phy_ctrl.is_in_sync());
TESTASSERT(phy_ctrl.current_state_name() != "selecting_cell");
TESTASSERT(cell_select_success == 1);
// TEST: Cell Selection with timeout being reached
cell_select_success = -1;
phy_ctrl.start_cell_select(found_cell, csel_callback);
TESTASSERT(not phy_ctrl.is_in_sync());
phy_ctrl.cell_selection_completed(true);
TESTASSERT(phy_ctrl.current_state_name() == "selecting_cell");
TESTASSERT(cell_select_success < 0);
for (uint32_t i = 0; i < phy_controller::wait_sync_timeout_ms; ++i) {
TESTASSERT(phy_ctrl.current_state_name() == "selecting_cell");
stack.run_tti();
}
TESTASSERT(phy_ctrl.current_state_name() != "selecting_cell");
TESTASSERT(cell_select_success == 0);
test_log->info("Finished RRC PHY controller test successfully\n");
return SRSLTE_SUCCESS;
}
} // namespace srsue
int main()
{
srslte::logmap::set_default_log_level(srslte::LOG_LEVEL_INFO);
TESTASSERT(srsue::test_phy_ctrl_fsm() == SRSLTE_SUCCESS);
}