前一篇文章介绍SIB2相关的内容,PRACH必须要再SIB2之后再进行发送,原因是PRACH相关参数是在SIB2携带。这一篇文章将详细介绍下,PRACH的发送过程,这个过程在代码的流程有些长,如果是对协议和srsLTE代码不太熟悉的读者会有些困难,读完本文如果有一些知识点还不太清楚的读者可以自行翻阅下协议或者在评论区留言。
先整体介绍下过程大致的流程:
这个流程牵扯的模块和流程有些多,所以很多读者在理解上存在一些困难。下面来按照流程逐步的来分析下具体的代码流程。
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)的主要内容。
调用流程代码调用流程如下:
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));
}
run_tti函数是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++;
}
}
BSR的主要作用是UE向基站报告上行的数据缓存情况,基站根据情况进行合理的调度,这也是基站上行调度的依据之一,BSR上报是以逻辑信道组(LCG)的方式上报,可以上报4(LCG0~LCG3)个。
BSR的种类主要可以分为:
从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);
}
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();
}
}
}
}
ra_procedure.start_mac_order()是竞争随机接入的入口,会进行资源选择,最后通知物理层发送prach,代码的执行流程如下:
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;
}
物理层会根据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;
}
}
``