mirror of https://github.com/pvnis/srsRAN_4G.git
implemented phy controller FSM to perform cell selection & search. The controller also tracks the phy sync state
parent
6fb01d61a7
commit
489969722e
@ -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
|
@ -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
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue