srsLTE 源码分析 UE_09 随机接入 之PRACH发送

前言

前一篇文章介绍SIB2相关的内容,PRACH必须要再SIB2之后再进行发送,原因是PRACH相关参数是在SIB2携带。这一篇文章将详细介绍下,PRACH的发送过程,这个过程在代码的流程有些长,如果是对协议和srsLTE代码不太熟悉的读者会有些困难,读完本文如果有一些知识点还不太清楚的读者可以自行翻阅下协议或者在评论区留言。
先整体介绍下过程大致的流程:

  • RRC层发送rrc connection request信令;
  • RRC通过RLC的write_pdu接口写到RB_ID_SRB0承载上;
  • MAC层BSR过程检测到RLC有新数据到达会触发常规BSR;
  • MAC层的常规BSR会触发SR;
  • 由于UE还未接入,PUCCH没有配置SR资源;
  • SR进一步发起随机接入过程;
  • 随机接入过程选择preamble,并通知物理层接口,通知物理层发送PRACH;
  • 物理层会在指定的位置发送PRACH序列。

这个流程牵扯的模块和流程有些多,所以很多读者在理解上存在一些困难。下面来按照流程逐步的来分析下具体的代码流程。

RRC层发送rrc connection request

NAS层的rrc_connect_proc过程,会调用RRC的connection_request,触发RRC层的rrc_connect_proc过程。在调用之前,NAS层会设置ue_id,这个ue_id可能是S-TMSI(核心网分配的临时标识),也有可能是随机数(UE初次接入)。这个ue_id是开机接入场景的MSG3(rrc connection request)的主要内容。
调用流程代码调用流程如下:

  • nas_ptr->rrc->set_ue_identity(s_tmsi);
  • nas_ptr->rrc->connection_request(cause_, std::move(pdu)
  • conn_req_proc.launch(cause, std::move(dedicated_info_nas_)
  • send_con_request(srslte::establishment_cause_t cause)
  • send_ul_ccch_msg(const ul_ccch_msg_s& msg)
  • rlc->write_sdu(lcid, std::move(pdcp_buf));
proc_outcome_t nas::rrc_connect_proc::init(srslte::establishment_cause_t cause_, srslte::unique_byte_buffer_t pdu)
{
  if (nas_ptr->rrc->is_connected()) {
    ProcInfo("Stopping. Reason: Already connected\n");
    return proc_outcome_t::success;
  }

  if (pdu == nullptr) {
    // Generate service request or attach request message
    pdu = srslte::allocate_unique_buffer(*nas_ptr->pool, true);
    if (!pdu) {
      ProcError("Fatal Error: Couldn't allocate PDU.\n");
      return proc_outcome_t::error;
    }

    if (nas_ptr->state == EMM_STATE_REGISTERED) {
      nas_ptr->gen_service_request(pdu);
    } else {
      nas_ptr->gen_attach_request(pdu);
    }
  }

  // Provide UE-Identity to RRC if have one
  if (nas_ptr->have_guti) {
    srslte::s_tmsi_t s_tmsi;
    s_tmsi.mmec   = nas_ptr->ctxt.guti.mme_code;
    s_tmsi.m_tmsi = nas_ptr->ctxt.guti.m_tmsi;
   //设置UE标识为s-tmsi
    nas_ptr->rrc->set_ue_identity(s_tmsi);
  }

  ProcInfo("Starting...\n");
  state = state_t::conn_req;
  //启动RRC层的connection request proc
  if (not nas_ptr->rrc->connection_request(cause_, std::move(pdu))) {
    ProcError("Failed to initiate a connection request procedure\n");
    return proc_outcome_t::error;
  }
  return proc_outcome_t::yield;
}


/* 5.3.3.2 Initiation of RRC Connection Establishment procedure
 *
 * Higher layers request establishment of RRC connection while UE is in RRC_IDLE
 *
 * This procedure selects a suitable cell for transmission of RRCConnectionRequest and configures
 * it. Sends connectionRequest message and returns if message transmitted successfully.
 * It does not wait until completition of Connection Establishment procedure
 */
bool rrc::connection_request(srslte::establishment_cause_t cause, srslte::unique_byte_buffer_t dedicated_info_nas_)
{
  //启动rrc层的conn_req_proc过程
  if (not conn_req_proc.launch(cause, std::move(dedicated_info_nas_))) {
    rrc_log->error("Failed to initiate connection request procedure\n");
    return false;
  }
  callback_list.add_proc(conn_req_proc);
  return true;
}

proc_outcome_t rrc::connection_request_proc::step()
{
  if (state == state_t::cell_selection) {
    // NOTE: cell selection will signal back with an event trigger
    return proc_outcome_t::yield;
  } else if (state == state_t::config_serving_cell) {
    if (rrc_ptr->serv_cell_cfg.run()) {
      return proc_outcome_t::yield;
    }
    if (serv_cfg_fut.is_error()) {
      Error("Configuring serving cell\n");
      return proc_outcome_t::error;
    }

    rrc_ptr->t300.run();

	//调用send_con_request接口发送MSG3
    // Send connectionRequest message to lower layers
    rrc_ptr->send_con_request(cause);

    // Save dedicatedInfoNAS SDU, if needed
    if (dedicated_info_nas.get()) {
      if (rrc_ptr->dedicated_info_nas.get()) {
        Warning("Received a new dedicatedInfoNAS SDU but there was one still in queue. Removing it.\n");
        rrc_ptr->dedicated_info_nas.reset();
      }

      Debug("Updating dedicatedInfoNAS in RRC\n");
      rrc_ptr->dedicated_info_nas = std::move(dedicated_info_nas);
    } else {
      Debug("dedicatedInfoNAS has already been provided to RRC.\n");
    }

    Info("Waiting for RRCConnectionSetup/Reject or expiry\n");
    state = state_t::wait_t300;
    return step();

  } else if (state == state_t::wait_t300) {
    // Wait until t300 stops due to RRCConnectionSetup/Reject or expiry
    if (rrc_ptr->t300.is_running()) {
      return proc_outcome_t::yield;
    }

    if (rrc_ptr->state == RRC_STATE_CONNECTED) {
      // Received ConnectionSetup
      return proc_outcome_t::success;
    } else if (rrc_ptr->t300.is_expired()) {
      // T300 is expired: 5.3.3.6
      Warning("Timer T300 expired: ConnectionRequest timed out\n");
      rrc_ptr->mac->reset();
      rrc_ptr->set_mac_default();
      rrc_ptr->rlc->reestablish();
    } else {
      // T300 is stopped but RRC not Connected is because received Reject: Section 5.3.3.8
      Warning("Timer T300 stopped: Received ConnectionReject\n");
      rrc_ptr->mac->reset();
      rrc_ptr->set_mac_default();
    }
  }

  return proc_outcome_t::error;
}

void rrc::send_con_request(srslte::establishment_cause_t cause)
{
  rrc_log->debug("Preparing RRC Connection Request\n");

  // Prepare ConnectionRequest packet
  ul_ccch_msg_s              ul_ccch_msg;
  rrc_conn_request_r8_ies_s* rrc_conn_req =
      &ul_ccch_msg.msg.set_c1().set_rrc_conn_request().crit_exts.set_rrc_conn_request_r8();

  //NAS配置了ue_id就用s_tmsi,没有配置就用随机数
  if (ue_identity_configured) {
    rrc_conn_req->ue_id.set_s_tmsi();
    srslte::to_asn1(&rrc_conn_req->ue_id.s_tmsi(), ue_identity);
  } else {
    rrc_conn_req->ue_id.set_random_value();
    // TODO use proper RNG
    uint64_t random_id = 0;
    for (uint i = 0; i < 5; i++) { // fill random ID bytewise, 40 bits = 5 bytes
      random_id |= ((uint64_t)rand() & 0xFF) << i * 8;
    }
    //产生随机数
    rrc_conn_req->ue_id.random_value().from_number(random_id);
  }
  rrc_conn_req->establishment_cause = (establishment_cause_opts::options)cause;
 // MSG3 通过CCCH信道发送,SRB0,此时SRB1还未建立,无加密,无完整性保护
  send_ul_ccch_msg(ul_ccch_msg);
}

void rrc::send_ul_ccch_msg(const ul_ccch_msg_s& msg)
{
  // Reset and reuse sdu buffer if provided
  unique_byte_buffer_t pdcp_buf = srslte::allocate_unique_buffer(*pool, true);
  if (not pdcp_buf.get()) {
    rrc_log->error("Fatal Error: Couldn't allocate PDU in byte_align_and_pack().\n");
    return;
  }
  //MSG3是RRC信令,需要先经过ASN.1编码
  asn1::bit_ref bref(pdcp_buf->msg, pdcp_buf->get_tailroom());
  msg.pack(bref);
  bref.align_bytes_zero();
  pdcp_buf->N_bytes = (uint32_t)bref.distance_bytes(pdcp_buf->msg);
  pdcp_buf->set_timestamp();

  // Set UE contention resolution ID in MAC
  uint64_t uecri      = 0;
  uint8_t* ue_cri_ptr = (uint8_t*)&uecri;
  uint32_t nbytes     = 6;
  for (uint32_t i = 0; i < nbytes; i++) {
    ue_cri_ptr[nbytes - i - 1] = pdcp_buf->msg[i];
  }

  rrc_log->debug("Setting UE contention resolution ID: %" PRIu64 "\n", uecri);
  //将ue_id发送给MAC层,MAC层在收到MSG4之后,会和这个进行比较,判断是否随机接入成功
  mac->set_contention_id(uecri);

  uint32_t lcid = RB_ID_SRB0;
  log_rrc_message(get_rb_name(lcid).c_str(), Tx, pdcp_buf.get(), msg, msg.msg.c1().type().to_string());
  //将MSG3发送给RLC,lcid是RB_ID_SRB0,这个是UE启动后会默认建立的
  rlc->write_sdu(lcid, std::move(pdcp_buf));
}

MAC层的主入口

run_tti函数是MAC层的主入口,在MAC层内部分了很多的子模块:

  • mux 逻辑信道复用
  • bsr 负责BSR的产生和上报
  • phr 负责PHR(功率余量)的上报
  • sr 负责SR,调度请求的发送
  • ra 负责随机接入模块
    每个模块都有一个对应的step函数,作为子模块的入口,step函数内部完成各自子模块的内部逻辑维护。
    run_tti函数每个TTI执行一次,由物理层周期触发,MAC层的处理必须要在1ms时间内处理完成,如果发生堆积,会对后续的时序产生影响。因此在实际的产品实现中,MAC层通常有指定的线程,并且绑定一个核,且在实际开发过程中要避免一些低速或阻塞接口的调用,如串口打印等。本篇不是主要介绍MAC层,后续会有专门的篇幅对MAC的每个模块进行详述。
void mac::run_tti(const uint32_t tti)
{
 log_h->step(tti);

 /* Warning: Here order of invocation of procedures is important!! */

 // Step all procedures
 Debug("Running MAC tti=%d\n", tti);
 mux_unit.step();
 bsr_procedure.step(tti);
 phr_procedure.step();

 // Check if BSR procedure need to start SR
 if (bsr_procedure.need_to_send_sr(tti)) {
   Debug("Starting SR procedure by BSR request, PHY TTI=%d\n", tti);
   sr_procedure.start();
 }
 if (bsr_procedure.need_to_reset_sr()) {
   Debug("Resetting SR procedure by BSR request\n");
   sr_procedure.reset();
 }
 sr_procedure.step(tti);

 // Check SR if we need to start RA
 if (sr_procedure.need_random_access()) {
   ra_procedure.start_mac_order();
 }

 ra_procedure.step(tti);
 ra_procedure.update_rar_window(ra_window_start, ra_window_length);

 // Count TTI for metrics
 for (uint32_t i = 0; i < SRSLTE_MAX_CARRIERS; i++) {
   metrics[i].nof_tti++;
 }
}

MAC层的BSR模块

BSR的主要作用是UE向基站报告上行的数据缓存情况,基站根据情况进行合理的调度,这也是基站上行调度的依据之一,BSR上报是以逻辑信道组(LCG)的方式上报,可以上报4(LCG0~LCG3)个。

BSR的种类主要可以分为:

  • 周期性BSR, 周期性产生
  • 常规BSR,逻辑信道上有新数据或者更高优先的逻辑信道有新数据到达会触发,常规BSR会进一步触发SR
  • padding BSR,如果上行调度有多余的字节,会用padding BSR填充;

从BSR的格式上分:

  • long BSR 上报4个逻辑信道组
  • short BSR/ trunc BSR 上报一个逻辑信道组
// Checks if Regular BSR must be assembled, as defined in 5.4.5
// Padding BSR is assembled when called by mux_unit when UL dci is received
// Periodic BSR is triggered by the expiration of the timers
void bsr_proc::step(uint32_t tti)
{
  if (!initiated) {
    return;
  }

  pthread_mutex_lock(&mutex);

  current_tti = tti;

  update_new_data();

  // Regular BSR triggered if new data arrives or channel with high priority has new data
  if (check_new_data() || check_highest_channel()) {
    //触发常规BSR
    set_trigger(REGULAR);
  }

  update_buffer_state();

  pthread_mutex_unlock(&mutex);
}

MAC层的SR模块

SR是调度请求,用于UE有数据要发送,但是没有上行资源,UE可以通过SR通知基站进行上行调度。通常基站的SR会调用一个较少的上行资源,供UE上报BSR,后面根据BSR的情况再分配合理的上行资源,避免资源的浪费,毕竟空口资源还是很宝贵的。当然只有接入的UE,并且是激活的UE才有PUCCH资源,这个是一个动态的过程,基站要对PUCCH的资源进行维护。

BSR触发了常规BSR,is_pending_sr会置1,但是这个时候没有PUCCH资源发送SR,因此,就需要进行随机接入,do_ra会被置为true。后续会RA模块会进入随机接入过程。

void sr_proc::step(uint32_t tti)
{
  if (initiated) {
    if (is_pending_sr) {
      if (sr_cfg.enabled) {
        if (sr_counter < sr_cfg.dsr_transmax) {
          if (sr_counter == 0 || need_tx(tti)) {
            sr_counter++;
            Info("SR:    Signalling PHY sr_counter=%d\n", sr_counter);
            phy_h->sr_send();
          }
        } else {
          if (need_tx(tti)) {
            Info("SR:    Releasing PUCCH/SRS resources, sr_counter=%d, dsr_transmax=%d\n",
                 sr_counter,
                 sr_cfg.dsr_transmax);
            log_h->console("Scheduling request failed: releasing RRC connection...\n");
            rrc->release_pucch_srs();
            do_ra         = true;
            is_pending_sr = false;
          }
        }
      } else {
        Info("SR:    PUCCH not configured. Starting RA procedure\n");
        do_ra = true;
        reset();
      }
    }
  }
}

MAC层的RA模块

ra_procedure.start_mac_order()是竞争随机接入的入口,会进行资源选择,最后通知物理层发送prach,代码的执行流程如下:

  • run_tti
  • start_mac_order
  • initialization
  • notify_phy_config_completed
  • resource_selection
  • preamble_transmission
void mac::run_tti(const uint32_t tti)
{
 ......
  // Check SR if we need to start RA
  if (sr_procedure.need_random_access()) {
    ra_procedure.start_mac_order();
  }
}  

//开始随机接入过程
void ra_proc::start_mac_order(uint32_t msg_len_bits, bool is_ho)
{
  if (state == IDLE) {
    ra_is_ho         = is_ho;
    started_by_pdcch = false;
    new_ra_msg_len   = msg_len_bits;
    rInfo("Starting PRACH by MAC order\n");
    initialization();
  } else {
    Warning("Trying to start PRACH by MAC order in invalid state (%s)\n", state_str[state]);
  }
}

/* RA procedure initialization as defined in 5.1.1 */
void ra_proc::initialization()
{
  read_params();
  current_task_id++;
  transmitted_contention_id   = 0;
  preambleTransmissionCounter = 1;
  mux_unit->msg3_flush();
  backoff_param_ms = 0;

  // Instruct phy to configure PRACH
  state            = WAITING_PHY_CONFIG;
  uint32_t task_id = current_task_id;
  stack->enqueue_background_task([this, task_id](uint32_t worker_id) {
    phy_h->configure_prach_params();
    // notify back MAC
    stack->notify_background_task_result([this, task_id]() { notify_phy_config_completed(task_id); });
  });
}

//物理层PRACH参数配置成功的回调函数
void ra_proc::notify_phy_config_completed(uint32_t task_id)
{
  if (current_task_id == task_id) {
    if (state != WAITING_PHY_CONFIG) {
      rError("Received unexpected notification of PHY configuration completed\n");
    } else {
      rDebug("RA waiting PHY configuration completed\n");
    }
    // Jump directly to Resource selection
    //PRACH资源选择
    resource_selection();
  } else {
    rError("Received old notification of PHY configuration (old task_id=%d, current_task_id=%d)\n",
           task_id,
           current_task_id);
  }
}

/* Resource selection as defined in 5.1.2 */
void ra_proc::resource_selection()
{
  ra_group_t sel_group;

  uint32_t nof_groupB_preambles = 0;
  if (rach_cfg.nof_groupA_preambles > 0) {
    nof_groupB_preambles = rach_cfg.nof_preambles - rach_cfg.nof_groupA_preambles;
  }

  if (preambleIndex > 0) {
    // Preamble is chosen by Higher layers (ie Network)
    sel_maskIndex = maskIndex;
    sel_preamble  = (uint32_t)preambleIndex;
  } else {
    // Preamble is chosen by MAC UE
   //根据MSG3的大小选择GROUPA或者GROUPB,根据配置选择不同的preamble index
    if (!mux_unit->msg3_is_transmitted()) {
      if (nof_groupB_preambles &&
          new_ra_msg_len > rach_cfg.messageSizeGroupA) { // Check also pathloss (Pcmax,deltaPreamble and powerOffset)
        sel_group = RA_GROUP_B;
      } else {
        sel_group = RA_GROUP_A;
      }
      last_msg3_group = sel_group;
    } else {
      sel_group = last_msg3_group;
    }
    if (sel_group == RA_GROUP_A) {
      if (rach_cfg.nof_groupA_preambles) {
        // randomly choose preamble from [0 nof_groupA_preambles)
        sel_preamble = rand() % rach_cfg.nof_groupA_preambles;
      } else {
        rError("Selected group preamble A but nof_groupA_preambles=0\n");
        state = IDLE;
        return;
      }
    } else {
      if (nof_groupB_preambles) {
        // randomly choose preamble from [nof_groupA_preambles nof_groupB_preambles)
        sel_preamble = rach_cfg.nof_groupA_preambles + rand() % nof_groupB_preambles;
      } else {
        rError("Selected group preamble B but nof_groupA_preambles=0\n");
        state = IDLE;
        return;
      }
    }
    sel_maskIndex = 0;
  }

  rDebug("Selected preambleIndex=%d maskIndex=%d GroupA=%d, GroupB=%d\n",
         sel_preamble,
         sel_maskIndex,
         rach_cfg.nof_groupA_preambles,
         nof_groupB_preambles);

  // Jump directly to transmission
  preamble_transmission();
}

/* Preamble transmission as defined in 5.1.3 */
void ra_proc::preamble_transmission()
{
   //计算目标功率
  received_target_power_dbm = rach_cfg.iniReceivedTargetPower + delta_preamble_db +
                              (preambleTransmissionCounter - 1) * rach_cfg.powerRampingStep;
   //通知物理层发送PRACH
  phy_h->prach_send(sel_preamble, sel_maskIndex - 1, received_target_power_dbm);
  rntis->rar_rnti        = 0;
  ra_tti                 = 0;
  rar_received           = false;
  backoff_interval_start = -1;

  state = PDCCH_SETUP;
}

PHY层的PRACH发送

物理层会根据PRACH的配置参数,选择PRACH发送的时域和频域位置,这个可以参考物理层过程的协议文档。

//计算PRACH发送的TTI
/* Returns true if current_tti is a valid opportunity for PRACH transmission and the is an allowed subframe,
 * or allowed_subframe == -1
 */
bool srslte_prach_tti_opportunity(srslte_prach_t* p, uint32_t current_tti, int allowed_subframe)
{
  uint32_t config_idx = p->config_idx;
  if (!p->tdd_config.configured) {
    return srslte_prach_tti_opportunity_config_fdd(config_idx, current_tti, allowed_subframe);
  } else {
    return srslte_prach_tti_opportunity_config_tdd(
        config_idx, p->tdd_config.sf_config, current_tti, &p->current_prach_idx);
  }
}

bool srslte_prach_tti_opportunity_config_fdd(uint32_t config_idx, uint32_t current_tti, int allowed_subframe)
{
  // Get SFN and sf_idx from the PRACH configuration index
  srslte_prach_sfn_t prach_sfn = srslte_prach_get_sfn(config_idx);

  // This is the only option which provides always an opportunity for PRACH transmission.
  if (config_idx == 14) {
    return true;
  }

  if ((prach_sfn == SRSLTE_PRACH_SFN_EVEN && ((current_tti / 10) % 2) == 0) || prach_sfn == SRSLTE_PRACH_SFN_ANY) {
    srslte_prach_sf_config_t sf_config;
    srslte_prach_sf_config(config_idx, &sf_config);
    for (int i = 0; i < sf_config.nof_sf; i++) {
      if (((current_tti % 10) == sf_config.sf[i] && allowed_subframe == -1) ||
          ((current_tti % 10) == sf_config.sf[i] && (current_tti % 10) == allowed_subframe)) {
        return true;
      }
    }
  }
  return false;
}

//查表获取频域位置
uint32_t srslte_prach_nof_f_idx_tdd(uint32_t config_idx, uint32_t tdd_ul_dl_config)
{
  if (config_idx < 64 && tdd_ul_dl_config < 7) {
    return prach_tdd_loc_table[config_idx][tdd_ul_dl_config].nof_elems;
  } else {
    ERROR("PRACH: Invalid parmeters config_idx=%d, tdd_ul_config=%d\n", config_idx, tdd_ul_dl_config);
    return 0;
  }
}

``

你可能感兴趣的:(sysLTE,网络协议,c++,linux)