PLMN选择LTE的第一个流程,但是由于PLMN涉及的代码量较大,放在一篇文章中,内容过多,因此,这一篇主要先针对小区搜索的流程进行代码梳理。小区搜索主要是PSS、SSS、MIB的解码。
UE通过PSS、SSS的解调可以得到以下信息:
UE通过MIB的解调,可以得到以下信息:
在main.cc文件的Line 632行,通过调用switch_on()函数,来执行开机流程,而开机流程的第一个流程就是启动UE的attach流程,在执行attach流程之前,还要执行小区搜索,PLMN选择,随机接入等流程,本文主要任务是介绍小区搜索的代码。
// Create UE instance
srsue::ue ue;
if (ue.init(args, logger)) {
ue.stop();
return SRSLTE_SUCCESS;
}
//中间代码省略 ......
cout << "Attaching UE..." << endl;
ue.switch_on();
ue_stack_lte的switch_on函数调用了try_push函数,该函数的作用是增加一个nas.start_attach_proc的任务,当然该任务不会立即执行,start_attach_proc任务真正是在ue_stack_lte类的run_thread中执行。
对于this { nas.start_attach_proc(nullptr, srslte::establishment_cause_t::mo_sig); }这个写法,如果是C++11的新手,则会有些陌生,这个是C++ 11 中的 Lambda 表达式,它是用于定义并创建匿名的函数对象,以简化编程工作。可以看出,Lambda 主要分为五个部分:[函数对象参数]、(操作符重载函数参数)、mutable 或 exception 声明、-> 返回值类型、{函数体}.
[函数对象参数] (操作符重载函数参数) mutable 或 exception 声明 -> 返回值类型 {函数体}
Lambda 表达式的详细说明可以参考https://www.cnblogs.com/jimodetiantang/p/9016826.html 这篇文章的介绍。
bool ue_stack_lte::switch_on()
{
if (running) {
pending_tasks.try_push(ue_queue_id,
[this]() { nas.start_attach_proc(nullptr, srslte::establishment_cause_t::mo_sig); });
return true;
}
return false;
}
ue_stack_lte类继承了srslte::thread类,在初始化时会创建一个线程实体,run_thread函数是一个死循环,它就是线程的执行函数,它的作用就是不断的从任务队列里面取任务,如果有任务,则执行,没有任务就阻塞,直到下次有任务push进任务队列时唤醒。
class ue_stack_lte final : public ue_stack_base,
public stack_interface_phy_lte,
public stack_interface_gw,
public stack_interface_rrc,
public srslte::thread
{
public:
ue_stack_lte();
~ue_stack_lte();
//中间代码省略 ......
}
void ue_stack_lte::run_thread()
{
while (running) {
srslte::move_task_t task{};
if (pending_tasks.wait_pop(&task) >= 0) {
task();
}
}
}
刚开机的NAS层的状态是EMM_STATE_DEREGISTERED,plmn_is_selected一开始未被赋值,因此,代码会执行plmn search的流程,PLMN search的过程就是从这句话plmn_searcher.launch()开始的。
void nas::start_attach_proc(srslte::proc_state_t* result, srslte::establishment_cause_t cause_)
{
nas_log->info("Attach Request with cause %s.\n", to_string(cause_).c_str());
switch (state) {
case EMM_STATE_DEREGISTERED:
// Search PLMN is not selected
if (!plmn_is_selected) {
nas_log->info("No PLMN selected. Starting PLMN Search...\n");
// nas层启动plmn_searcher.launch()过程
if (not plmn_searcher.launch()) {
if (result != nullptr) {
result->set_error();
}
return;
}
plmn_searcher.then([this, result, cause_](const proc_state_t& res) {
nas_log->info("Attach Request from PLMN Search %s\n", res.is_success() ? "finished successfully" : "failed");
if (result != nullptr) {
*result = res;
}
if (!res.is_success()) {
// try again ..
task_handler->defer_callback(reattach_timer_duration_ms, [&]() { start_attach_proc(nullptr, cause_); });
}
});
} else {
nas_log->info("PLMN selected in state %s\n", emm_state_text[state]);
if (not rrc_connector.launch(cause_, nullptr)) {
nas_log->error("Cannot initiate concurrent rrc connection procedures\n");
if (result != nullptr) {
result->set_error();
}
return;
}
rrc_connector.then([this, result](const proc_state_t& res) {
if (res.is_success()) {
nas_log->info("NAS attached successfully\n");
} else {
nas_log->error("Could not attach from attach_request\n");
}
if (result != nullptr) {
*result = res;
}
});
callbacks.add_proc(rrc_connector);
}
break;
case EMM_STATE_REGISTERED:
if (rrc->is_connected()) {
nas_log->info("NAS is already registered and RRC connected\n");
if (result != nullptr) {
result->set_val();
}
} else {
nas_log->info("NAS is already registered but RRC disconnected. Connecting now...\n");
if (not rrc_connector.launch(cause_, nullptr)) {
nas_log->error("Cannot initiate concurrent rrc connection procedures\n");
if (result != nullptr) {
result->set_error();
}
return;
}
rrc_connector.then([this, result](const proc_state_t& res) {
if (res.is_success()) {
nas_log->info("NAS attached successfully\n");
} else {
nas_log->error("Could not attach from attach_request\n");
}
if (result != nullptr) {
*result = res;
}
});
callbacks.add_proc(rrc_connector);
}
break;
default:
nas_log->info("Attach request ignored. State = %s\n", emm_state_text[state]);
if (result != nullptr) {
result->set_error();
}
}
}
plmn_searcher的实现是一个过程类,它相当于是一个状态机的机制,代码中有很多这样的过程,都是类似的机制。这个过程对象通常和 srslte::proc_manager_list_t callbacks 这样的过程管理列表一起使用,我们通过一段代码来看一下它的具体使用方法。
//proc_base_t类的定义,step和run_then都是需要被重载的
class proc_base_t
{
public:
virtual ~proc_base_t() = default;
//! common proc::run() interface. Returns true if procedure is still running
bool run()
{
if (is_busy()) {
// callbacks.run()函数会执行step()函数
proc_outcome_t outcome = step();
handle_outcome(outcome);
}
return is_busy();
}
//! interface to check if proc is still running
bool is_busy() const { return proc_state == proc_status_t::on_going; }
bool is_idle() const { return proc_state == proc_status_t::idle; }
protected:
enum class proc_status_t { idle, on_going };
virtual proc_outcome_t step() = 0;
virtual void run_then(bool is_success) = 0;
void handle_outcome(proc_outcome_t outcome)
{
if (outcome == proc_outcome_t::error or outcome == proc_outcome_t::success) {
bool success = outcome == proc_outcome_t::success;
run_then(success);
}
}
proc_status_t proc_state = proc_status_t::idle;
};
//通过callbacks增加rrc_connector过程,后面就可以通过callbacks进行托管了
callbacks.add_proc(rrc_connector);
//run_tti()函数会执行rrc_connector的run函数,run函数又会进一步调用step();过程
//因此,过程类通常会重载step函数
//nas::run_tti()该函数每个TTI(1ms)会执行一次
void nas::run_tti()
{
callbacks.run();
}
//plmn_searcher.launch会调用init函数
//在init函数里面调用了nas_ptr->rrc->plmn_search(),执行rrc的plmn search过程
//nas对象的plmn_search过程并没有加入callbacks,原因是rrc对象的plmn_search会加入callbacks列表
//nas对象启动了rrc对象的plmn_search过程之后,剩下的事情就是等待rrc对象的plmn_search完成的结果,并且对结果进行后续处理.
proc_outcome_t nas::plmn_search_proc::init()
{
// start RRC
state = state_t::plmn_search;
if (not nas_ptr->rrc->plmn_search()) {
ProcError("ProcError while searching for PLMNs\n");
return proc_outcome_t::error;
}
ProcInfo("Starting...\n");
return proc_outcome_t::yield;
}
proc_outcome_t nas::plmn_search_proc::step()
{
return proc_outcome_t::yield;
}
void nas::plmn_search_proc::then(const srslte::proc_state_t& result)
{
ProcInfo("Completed with %s\n", result.is_success() ? "success" : "failure");
if (result.is_error()) {
nas_ptr->enter_emm_deregistered();
}
}
// react提供两个函数,根据入参的不同进行区分,这个C++的函数重载特性,这个react是 rrc_connect_complete事件的执行函数。
proc_outcome_t nas::plmn_search_proc::react(const rrc_connect_proc::rrc_connect_complete_ev& t)
{
if (state != state_t::rrc_connect) {
// not expecting a rrc connection result
ProcWarning("Received unexpected RRC Connection Result event\n");
return proc_outcome_t::yield;
}
return t.is_success() ? proc_outcome_t::success : proc_outcome_t::error;
}
// plmn 搜索完成之后,会通过trigger函数调用这个react函数
proc_outcome_t nas::plmn_search_proc::react(const plmn_search_complete_t& t)
{
if (state != state_t::plmn_search) {
ProcWarning("PLMN Search Complete was received but PLMN Search is not running.\n");
return proc_outcome_t::yield; // ignore
}
// check whether the state hasn't changed
if (nas_ptr->state != EMM_STATE_DEREGISTERED or nas_ptr->plmn_is_selected) {
ProcError("ProcError while searching for PLMNs\n");
return proc_outcome_t::error;
}
if (t.nof_plmns < 0) {
ProcError("Error while searching for PLMNs\n");
return proc_outcome_t::error;
}
if (t.nof_plmns == 0) {
ProcWarning("Did not find any PLMN in the set of frequencies.\n");
return proc_outcome_t::error;
}
// Save PLMNs
nas_ptr->known_plmns.clear();
for (int i = 0; i < t.nof_plmns; i++) {
nas_ptr->known_plmns.push_back(t.found_plmns[i].plmn_id);
nas_ptr->nas_log->info(
"Found PLMN: Id=%s, TAC=%d\n", t.found_plmns[i].plmn_id.to_string().c_str(), t.found_plmns[i].tac);
nas_ptr->nas_log->console(
"Found PLMN: Id=%s, TAC=%d\n", t.found_plmns[i].plmn_id.to_string().c_str(), t.found_plmns[i].tac);
}
nas_ptr->select_plmn();
// Select PLMN in request establishment of RRC connection
if (not nas_ptr->plmn_is_selected) {
ProcError("PLMN is not selected because no suitable PLMN was found\n");
return proc_outcome_t::error;
}
nas_ptr->rrc->plmn_select(nas_ptr->current_plmn);
state = state_t::rrc_connect;
if (not nas_ptr->rrc_connector.launch(srslte::establishment_cause_t::mo_sig, nullptr)) {
ProcError("Unable to initiate RRC connection.\n");
return proc_outcome_t::error;
}
nas_ptr->callbacks.add_proc(nas_ptr->rrc_connector);
return proc_outcome_t::yield;
}
//nas类对外的PLMN 搜索完成的接口函数,RRC PLMN搜索完成之后会调用该接口
void nas::plmn_search_completed(const rrc_interface_nas::found_plmn_t found_plmns[rrc_interface_nas::MAX_FOUND_PLMNS],
int nof_plmns)
{
// trigger函数会调用plmn_searcher的react函数
plmn_searcher.trigger(plmn_search_proc::plmn_search_complete_t(found_plmns, nof_plmns));
}
nas对象通过nas_ptr->rrc->plmn_search()将PLMN搜索的任务交给RRC对象。RRC调用plmn_searcher.launch()启动RRC对象的PLMN搜索。但是和NAS层不同的是,RRC会 通过 callback_list.add_proc(plmn_searcher)将PLMN search过程加入回调列表。plmn search过程的step函数将每个TTI被执行一遍。
实际上,真个srsLTE代码比较难以理解的地方就是函数的调用不是顺序线性的,里面有很多的回调,会让整个过程看起来比较晦涩,如果对LTE协议本身不是很了解或者对C++不是很了解的话,的确会看的比较累。因此,文章在这一点上,尽量将一些难理解的细节多介绍一些。对srsLTE代码就比较熟悉的读者可以自行跳过哪些细枝末节。
bool rrc::plmn_search()
{
if (not plmn_searcher.launch()) {
rrc_log->error("Unable to initiate PLMN search\n");
return false;
}
//plmn search的过程加入callback_list
callback_list.add_proc(plmn_searcher);
return true;
}
/*
* RRC State Machine
*
*/
void rrc::run_tti()
{
//......
// Process on-going callbacks, and clear finished callbacks
//plmn search的过程在这里会被调用
callback_list.run();
//......
}
plmn_search_proc过程的launch会调用plmn_search_proc::init()函数,在这个函数里面会执行rrc_ptr->cell_searcher.launch()函数,启动小区搜索的过程。cell_searcher_proc并没有加入到callback_list中,而是在plmn_search_proc过程的step中直接调用rrc_ptr->cell_searcher.run(),这个函数会调用cell_searcher_proc的step函数,而前面介绍过,plmn_search_proc过程是加入到callback_list中的,因此,每个TTI cell_searcher_proc的step函数也会被执行一次。cell_searcher_proc是plmn_search_proc的子流程,plmn_search_proc需要遍历所有的频点,因此plmn_search_proc需要执行多次的cell_searcher_proc过程。PLMN是在SIB1广播消息携带的,由于共建共享的需求,SIB1中可能会携带多个支持的PLMN。
cell_searcher_proc过程分为三个步骤:
/* PLMN 过程 */
proc_outcome_t rrc::plmn_search_proc::init()
{
Info("Starting PLMN search\n");
nof_plmns = 0;
cell_search_fut = rrc_ptr->cell_searcher.get_future();
if (not rrc_ptr->cell_searcher.launch(&cell_search_fut)) {
Error("Failed due to fail to init cell search...\n");
return proc_outcome_t::error;
}
return step();
}
/* NAS interface to search for available PLMNs.
* It goes through all known frequencies, synchronizes and receives SIB1 for each to extract PLMN.
* The function is blocking and waits until all frequencies have been
* searched and PLMNs are obtained.
*/
proc_outcome_t rrc::plmn_search_proc::step()
{
//cell_searcher.run()会调用cell_searcher.step()函数
if (rrc_ptr->cell_searcher.run()) {
// wait for new TTI
return proc_outcome_t::yield;
}
if (cell_search_fut.is_error() or cell_search_fut.value()->found == phy_interface_rrc_lte::cell_search_ret_t::ERROR) {
// stop search
nof_plmns = -1;
Error("Failed due to failed cell search sub-procedure\n");
return proc_outcome_t::error;
}
if (cell_search_fut.value()->found == phy_interface_rrc_lte::cell_search_ret_t::CELL_FOUND) {
if (rrc_ptr->serving_cell->has_sib1()) {
// Save PLMN and TAC to NAS
//SIB1消息中可能包含多个PLMN信息,需要遍历,保存PLMN信息
for (uint32_t i = 0; i < rrc_ptr->serving_cell->nof_plmns(); i++) {
if (nof_plmns < MAX_FOUND_PLMNS) {
found_plmns[nof_plmns].plmn_id = rrc_ptr->serving_cell->get_plmn(i);
found_plmns[nof_plmns].tac = rrc_ptr->serving_cell->get_tac();
nof_plmns++;
} else {
Error("No more space for plmns (%d)\n", nof_plmns);
}
}
} else {
Error("SIB1 not acquired\n");
}
}
//如果所有频点都搜索完,则结束PLMN搜索,否则换个频点继续小区搜索
if (cell_search_fut.value()->last_freq == phy_interface_rrc_lte::cell_search_ret_t::NO_MORE_FREQS) {
Info("completed PLMN search\n");
return proc_outcome_t::success;
}
//执行下个频点的小区搜索
if (not rrc_ptr->cell_searcher.launch(&cell_search_fut)) {
Error("Failed due to fail to init cell search...\n");
return proc_outcome_t::error;
}
// run again
return step();
}
// 小区搜索过程
proc_outcome_t rrc::cell_search_proc::init()
{
Info("Starting...\n");
state = state_t::phy_cell_search;
//调用协议栈的start_cell_search接口
rrc_ptr->stack->start_cell_search();
return proc_outcome_t::yield;
}
/* Implements the SI acquisition procedure. Configures MAC/PHY scheduling to retrieve SI messages.*/
proc_outcome_t rrc::cell_search_proc::step()
{
switch (state) {
case state_t::phy_cell_search:
case state_t::phy_cell_select:
// Waits for cell select/search to complete
return proc_outcome_t::yield;
case state_t::si_acquire:
return step_si_acquire();
case state_t::wait_measurement:
return step_wait_measurement();
}
return proc_outcome_t::yield;
}
proc_outcome_t rrc::cell_search_proc::step_si_acquire()
{
if (not si_acquire_fut.is_complete()) {
return proc_outcome_t::yield;
}
// SI Acquire has completed
if (si_acquire_fut.is_error()) {
Error("Failed to trigger SI acquire for SIB0\n");
return proc_outcome_t::error;
}
Info("Completed successfully\n");
return proc_outcome_t::success;
}
proc_outcome_t rrc::cell_search_proc::handle_cell_found(const phy_interface_rrc_lte::phy_cell_t& new_cell)
{
Info("Cell found in this frequency. Setting new serving cell EARFCN=%d PCI=%d ...\n", new_cell.earfcn, new_cell.pci);
// Create a cell with NaN RSRP. Will be updated by new_phy_meas() during SIB search.
if (not rrc_ptr->add_neighbour_cell(unique_cell_t(new cell_t(new_cell)))) {
Error("Could not add new found cell\n");
return proc_outcome_t::error;
}
rrc_ptr->set_serving_cell(new_cell, false);
// set new serving cell in PHY
state = state_t::phy_cell_select;
rrc_ptr->stack->start_cell_select(&rrc_ptr->serving_cell->phy_cell);
return proc_outcome_t::yield;
}
proc_outcome_t rrc::cell_search_proc::step_wait_measurement()
{
if (not std::isnormal(rrc_ptr->serving_cell->get_rsrp())) {
return proc_outcome_t::yield;
}
if (rrc_ptr->serving_cell->has_sib1()) {
Info("Cell has SIB1\n");
// What do we do????
return proc_outcome_t::success;
}
Info("Cell has no SIB1. Obtaining SIB1...\n");
if (not rrc_ptr->si_acquirer.launch(&si_acquire_fut, 0)) {
// disallow concurrent si_acquire
Error("SI Acquire is already running...\n");
return proc_outcome_t::error;
}
state = state_t::si_acquire;
return step();
}
proc_outcome_t rrc::cell_search_proc::react(const cell_select_event_t& event)
{
if (state != state_t::phy_cell_select) {
Warning("Received unexpected cell search result\n");
return proc_outcome_t::yield;
}
if (not event.cs_ret) {
Error("Couldn't select new serving cell\n");
return proc_outcome_t::error;
}
if (not rrc_ptr->phy->cell_is_camping()) {
Warning("Could not camp on found cell.\n");
return proc_outcome_t::error;
}
if (not std::isnormal(rrc_ptr->serving_cell->get_rsrp())) {
Info("No valid measurement found for the serving cell. Wait for valid measurement...\n");
}
state = state_t::wait_measurement;
return proc_outcome_t::yield;
}
proc_outcome_t rrc::cell_search_proc::react(const cell_search_event_t& event)
{
if (state != state_t::phy_cell_search) {
Error("Received unexpected cell search result\n");
return proc_outcome_t::error;
}
search_result = event;
Info("PHY cell search completed.\n");
// Transition to SI Acquire or finish
switch (search_result.cs_ret.found) {
case phy_interface_rrc_lte::cell_search_ret_t::CELL_FOUND:
return handle_cell_found(search_result.found_cell);
case phy_interface_rrc_lte::cell_search_ret_t::CELL_NOT_FOUND:
rrc_ptr->phy_sync_state = phy_unknown_sync;
Info("No cells found.\n");
// do nothing
return proc_outcome_t::success;
case phy_interface_rrc_lte::cell_search_ret_t::ERROR:
Error("Error while performing cell search\n");
// TODO: check what errors can happen (currently not handled in our code)
return proc_outcome_t::error;
}
return proc_outcome_t::yield;
}
background_tasks是一个线程池,background_tasks.push_task,则将创建一个新的task任务,线程池的run_thread程序会将pop出任务 并执行。这里需要特别需要说明的几点:
ue_stack_lte线程是高层协议栈(NAS、RRC、MAC)的总的执行容器,它的设计是采用task的方式进行的,这种方式可以很好的进行分层设计和解耦。ue_stack_lte需要特别说明几点:
void ue_stack_lte::start_cell_search()
{
background_tasks.push_task([this](uint32_t worker_id) {
phy_interface_rrc_lte::phy_cell_t found_cell;
phy_interface_rrc_lte::cell_search_ret_t ret = phy->cell_search(&found_cell);
// notify back RRC
pending_tasks.push(background_queue_id, [this, found_cell, ret]() { rrc.cell_search_completed(ret, found_cell); });
});
}
/* 1ms调用一次,也就是1ms就执行一次run_tti_impl */
void ue_stack_lte::run_tti(uint32_t tti, uint32_t tti_jump)
{
pending_tasks.push(sync_queue_id, [this, tti, tti_jump]() { run_tti_impl(tti, tti_jump); });
}
/* 每个tti的入口函数*/
void ue_stack_lte::run_tti_impl(uint32_t tti, uint32_t tti_jump)
{
//.......
// Perform pending stack deferred tasks
for (auto& task : deferred_stack_tasks) {
task();
}
deferred_stack_tasks.clear();
// perform tasks for the received TTI range
for (uint32_t i = 0; i < tti_jump; ++i) {
uint32_t next_tti = TTI_SUB(tti, (tti_jump - i - 1));
mac.run_tti(next_tti);
timers.step_all();
}
rrc.run_tti();
nas.run_tti();
//.......
}
/* ue_stack_lte 的线程回调函数,它一直等待任务并运行 */
void ue_stack_lte::run_thread()
{
while (running) {
srslte::move_task_t task{};
if (pending_tasks.wait_pop(&task) >= 0) {
task();
}
}
}
前面讲到的background_tasks线程,最终会调用run_cell_search()函数,并阻塞等待结果。
//这里直接调用sfsync的cell_search流程
phy_interface_rrc_lte::cell_search_ret_t phy::cell_search(phy_cell_t* cell)
{
return sfsync.cell_search(cell);
}
void run_cell_search()
{
std::lock_guard<std::mutex> lock(outside);
//状态机设置为CELL_SEARCH,等待结果
go_state(CELL_SEARCH);
wait_state_run();
wait_state_next();
}
phy_interface_rrc_lte::cell_search_ret_t sync::cell_search(phy_interface_rrc_lte::phy_cell_t* found_cell)
{
std::unique_lock<std::mutex> ul(rrc_mutex);
//.......
//设置小区搜索频点
set_frequency();
//.......
//这里会阻塞,一直等到小区搜索结束
phy_state.run_cell_search();
//.......
return ret;
}
真正的小区搜索过程是在sync的线程中完成的,在CELL_SEARCH分支中,调用search_p.run(&cell, mib),解PSS、SSS以及MIB。物理层的代码主要是要抓到空口的IQ数据,并根据算法进行相应的解码工作,由于笔者没有做过物理层的开发,这部分的代码就不展开讲了。
物理层小区搜索完成之后会调用phy_state.state_exit()函数,该函数会让 phy_state.run_cell_search();函数推出等待的状态。这也就意味着之前的background_tasks的线程也会退出等待状态,
void sync::run_thread()
{
//......
while (running) {
//......
switch (phy_state.run_state()) {
case sync_state::CELL_SEARCH:
/* Search for a cell in the current frequency and go to IDLE.
* The function search_p.run() will not return until the search finishes
*/
cell_search_ret = search_p.run(&cell, mib);
if (cell_search_ret == search::CELL_FOUND) {
//解出来MIB信息,需要调用协议栈进行解码
stack->bch_decoded_ok(SYNC_CC_IDX, mib.data(), mib.size() / 8);
}
phy_state.state_exit();
break;
}
// Increase TTI counter
tti = (tti + 1) % 10240;
}
}
background_tasks退出阻塞之后,会调用pending_tasks.push函数。
rrc.cell_search_completed将会在后续任务调度中被执行。
pending_tasks.push(background_queue_id, [this, found_cell, ret]() { rrc.cell_search_completed(ret, found_cell); });
rrc::cell_search_completed函数中会调用cell_searcher.trigger函数,这个函数会让cell_searcher过程的react函数得到调用。
void rrc::cell_search_completed(const phy_interface_rrc_lte::cell_search_ret_t& cs_ret,
const phy_interface_rrc_lte::phy_cell_t& found_cell)
{
cell_searcher.trigger(cell_search_proc::cell_search_event_t{cs_ret, found_cell});
}
cell_search_proc::react函数中会调用cell_search_proc::handle_cell_found进一步的处理。主要做以下几个事情:
在start_cell_select过程中,还会触发参考信号的测量和SIB的解调过程,这个在后续的代码解读中再进一步分析。
proc_outcome_t rrc::cell_search_proc::handle_cell_found(const phy_interface_rrc_lte::phy_cell_t& new_cell)
{
Info("Cell found in this frequency. Setting new serving cell EARFCN=%d PCI=%d ...\n", new_cell.earfcn, new_cell.pci);
// Create a cell with NaN RSRP. Will be updated by new_phy_meas() during SIB search.
if (not rrc_ptr->add_neighbour_cell(unique_cell_t(new cell_t(new_cell)))) {
Error("Could not add new found cell\n");
return proc_outcome_t::error;
}
rrc_ptr->set_serving_cell(new_cell, false);
// set new serving cell in PHY
state = state_t::phy_cell_select;
rrc_ptr->stack->start_cell_select(&rrc_ptr->serving_cell->phy_cell);
return proc_outcome_t::yield;
}
proc_outcome_t rrc::cell_search_proc::react(const cell_search_event_t& event)
{
if (state != state_t::phy_cell_search) {
Error("Received unexpected cell search result\n");
return proc_outcome_t::error;
}
search_result = event;
Info("PHY cell search completed.\n");
// Transition to SI Acquire or finish
switch (search_result.cs_ret.found) {
case phy_interface_rrc_lte::cell_search_ret_t::CELL_FOUND:
return handle_cell_found(search_result.found_cell);
case phy_interface_rrc_lte::cell_search_ret_t::CELL_NOT_FOUND:
rrc_ptr->phy_sync_state = phy_unknown_sync;
Info("No cells found.\n");
// do nothing
return proc_outcome_t::success;
case phy_interface_rrc_lte::cell_search_ret_t::ERROR:
Error("Error while performing cell search\n");
// TODO: check what errors can happen (currently not handled in our code)
return proc_outcome_t::error;
}
return proc_outcome_t::yield;
}
至此,小区搜索的流程的大致代码就分析结束了。这个涉及到多个线程,多个对象之间的交互,流程有些乱,希望本文对各位学习sysLTE代码的读者能有所帮助。