当前位置: 首页 > 工具软件 > srsLTE > 使用案例 >

srsLTE 源码分析 UE_04 PLMN选择之小区搜索

袁建木
2023-12-01

小区搜索代码流程

PLMN选择LTE的第一个流程,但是由于PLMN涉及的代码量较大,放在一篇文章中,内容过多,因此,这一篇主要先针对小区搜索的流程进行代码梳理。小区搜索主要是PSS、SSS、MIB的解码。

UE通过PSS、SSS的解调可以得到以下信息:

  • 小区的 PCI;
  • 小区特定的参考信号及其时频位置与 PCI是对应的,因此也就知道了该小区的下行小区特定的参考信号及其时频位置;
  • 子帧号(SF)
  • FDD or TDD
  • 循环前缀配置

UE通过MIB的解调,可以得到以下信息:

  • 小区的下行系统带宽
  • PHICH配置
  • 系统帧号(SFN)
  • 小区特定的天线端口
  • 用于 L1/L2控制信令的传输分集模式

1 main.cc

在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();

2 ue_stack_lte.cc

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();
    }
  }
}

3 nas.cc

刚开机的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));
}

4 rrc.cc

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();
    //......
}

5 rrc_procedures.cc

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过程分为三个步骤:

  1. 解下行同步信号
  2. 解MIB信息
  3. 解SIB信息,这个需要RRC的调度,LTE的SIB采用ASN.1的编码,只有RRC才有解码的能力,因此,收到的SIB数据,RRC还需要解码,才能获得SIB的内容。

/* 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;
}

6 ue_stack_lte.cc

background_tasks是一个线程池,background_tasks.push_task,则将创建一个新的task任务,线程池的run_thread程序会将pop出任务 并执行。这里需要特别需要说明的几点:

  • ue_stack_lte::start_cell_search()在执行完background_tasks.push_task之后立刻返回,不会阻塞;
  • phy->cell_search(&found_cell)是在background_tasks的线程执行函数里面运行的;
  • phy->cell_search(&found_cell)的设计是阻塞,在介绍之前,他是不会返回的。
  • 小区搜索完成之后,pending_tasks.push(background_queue_id, this, found_cell, ret { rrc.cell_search_completed(ret, found_cell); });
  • ue_stack_lte::run_thread()线程,会调用rrc.cell_search_completed函数,将小区搜索结果通知给RRC

ue_stack_lte线程是高层协议栈(NAS、RRC、MAC)的总的执行容器,它的设计是采用task的方式进行的,这种方式可以很好的进行分层设计和解耦。ue_stack_lte需要特别说明几点:

  • 协议接口的部门采用task方式;
  • run_tti也是采用task方式,每个TTI会调用mac、nas、rrc的run_tti函数,定时器的调用也是在run_tti中;
  • mac、nas、rrc都是提供了run_tti的接口,他们的执行不是放在单独的线程,而是统一在ue_stack_lte线程中,1ms运行一次。
  • 需要注意的一点,这种task的任务不能有长时间的阻塞,如sleep等操作,这会影响整个协议的运作;
  • 有阻塞的任务可以放在background_tasks线程池执行。
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();
    }
  }
}

6 sync.cc & phy.cc

前面讲到的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进一步的处理。主要做以下几个事情:

  1. add_neighbour_cell
  2. set_serving_cell
  3. 启动start_cell_select过程

在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代码的读者能有所帮助。

 类似资料: