srsLTE 源码分析 UE_07 PLMN选择

前沿

之前的文章介绍PLMN是如何被搜索到的,在搜索到PLMN全部搜索之后,UE会选择其中的一个PLMN进行驻留。这个过程相对前面的内容来讲比较简单,本文来分析下srsLTE的代码是如何选择PLMN的。

RRC层的PLMN搜索过程

RRC层会进行全频段的PLMN扫描,收集周围的全部PLMN,并进行存储,并在全频段扫描之后,通知NAS层PLMN搜索过程结果,NAS层会进行PLMN 选择,选择完之后会发起RRC 信令连接的过程。

rrc::plmn_search_proc::plmn_search_proc(rrc* parent_) : rrc_ptr(parent_), log_h(srslte::logmap::get("RRC")) {}

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()
{
  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;
  }
//解到SIB1之后,保存PLMN和TAC
  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
      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");
    }
  }

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

void rrc::plmn_search_proc::then(const srslte::proc_state_t& result) const
{
  // on cleanup, call plmn_search_completed
  if (result.is_success()) {
    Info("completed with success\n");
    //RRC层的PLMN search过程结束之后将结果通知给NAS层
    rrc_ptr->nas->plmn_search_completed(found_plmns, nof_plmns);
  } else {
    Error("PLMN Search completed with an error\n");
    rrc_ptr->nas->plmn_search_completed(nullptr, -1);
  }
}

NAS层的PLMN选择

NAS层的plmn_search_completed函数接口,会trigger NAS层的PLMN搜索过程的react函数。

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_search_proc的react函数  plmn_searcher.trigger(plmn_search_proc::plmn_search_complete_t(found_plmns, nof_plmns));
}

react函数的代码流程

关键步骤如下:
1、NAS层保存PLMN搜索结果;
2、执行PLMN选择;
3、发起建立RRC连接的过程。

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

PLMN选择

PLMN选择会将搜索的结果和本地的PLMN(home_plmn)进行比较,如果找到,返回成功。如果没有,就选择第一个PLMN。
这里的home_plmn是在SIM卡存储的信息,开机时,NAS会从通过接口从SIM卡中获取信息,此外还有s-tmsi等信息。这里多介绍下SIM卡,SIM除了有存储的功能,还有处理加密等计算的功能。

void nas::select_plmn()
{
  plmn_is_selected = false;

  // First find if Home PLMN is available
  for (const srslte::plmn_id_t& known_plmn : known_plmns) {
    if (known_plmn == home_plmn) {
      nas_log->info("Selecting Home PLMN Id=%s\n", known_plmn.to_string().c_str());
      plmn_is_selected = true;
      current_plmn     = known_plmn;
      return;
    }
  }

  // If not, select the first available PLMN
  if (not known_plmns.empty()) {
    nas_log->info("Could not find Home PLMN Id=%s, trying to connect to PLMN Id=%s\n",
                  home_plmn.to_string().c_str(),
                  known_plmns[0].to_string().c_str());

    nas_log->console("Could not find Home PLMN Id=%s, trying to connect to PLMN Id=%s\n",
                     home_plmn.to_string().c_str(),
                     known_plmns[0].to_string().c_str());
    plmn_is_selected = true;
    current_plmn     = known_plmns[0];
  }

  // reset attach attempt counter (Sec. 5.2.2.3.4)
  if (plmn_is_selected) {
    attach_attempt_counter = 0;
  }
}

总结

至此,现在才获取PLMN,但是UE的入网流程还有很远,虽然从代码上来分析,从NAS->RRC->MAC层,以这样的方式来看比较直观,但是,从空口报文的分析来看,MAC层的随机接入是在前面的,后续的过程,我们将以空口的报文顺序来分析代码流程。

如对本系列文章感兴趣,欢迎关注、收藏,有新的文档,平台会第一时间推送。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值