Added MAC NR RA procedure with a minimal test case

This commit is contained in:
David Rupprecht 2021-02-07 16:47:09 +01:00 committed by David Rupprecht
parent 3d3c80262c
commit 24123313bf
13 changed files with 715 additions and 41 deletions

View File

@ -116,6 +116,26 @@ struct rach_cfg_t {
}
};
// 38.321 5.1.1 Not complete yet
struct rach_nr_cfg_t {
uint32_t prach_ConfigurationIndex;
int PreambleReceivedTargetPower;
uint32_t preambleTransMax;
uint32_t powerRampingStep;
uint32_t ra_responseWindow;
uint32_t ra_ContentionResolutionTimer;
rach_nr_cfg_t() { reset(); }
void reset()
{
prach_ConfigurationIndex = 0;
PreambleReceivedTargetPower = 0;
powerRampingStep = 0;
preambleTransMax = 0;
ra_responseWindow = 0;
}
};
struct mac_cfg_t {
// Default constructor with default values as in 36.331 9.2.2
mac_cfg_t() { set_defaults(); }

View File

@ -95,11 +95,11 @@ public:
// MAC informs PHY about UL grant included in RAR PDU
virtual int set_ul_grant(std::array<uint8_t, SRSLTE_RAR_UL_GRANT_NBITS>) = 0;
// MAC instructs PHY to send a PRACH at the next given occasion
virtual void send_prach(uint32_t prach_occasion, uint32_t preamble_index, int preamble_received_target_power) = 0;
// MAC instructs PHY to transmit MAC TB at the given TTI
virtual int tx_request(const tx_request_t& request) = 0;
/// Instruct PHY to send PRACH in the next occasion.
virtual void send_prach(const uint32_t prach_occasion, const int preamble_index, const float preamble_received_target_power, const float ta_base_sec = 0.0f) = 0;
};
class phy_interface_rrc_nr

View File

@ -130,7 +130,7 @@ std::string mac_rar_subpdu_nr::to_string()
{
std::stringstream ss;
if (has_rapid()) {
ss << "RAPID: " << rapid << ", Temp C-RNTI: " << temp_crnti << ", TA: " << ta << ", UL Grant: ";
ss << "RAPID: " << rapid << ", Temp C-RNTI: " << std::hex << temp_crnti << ", TA: " << ta << ", UL Grant: ";
} else {
ss << "Backoff Indicator: " << backoff_indicator << " ";
}

View File

@ -47,9 +47,9 @@ public:
void start_plot();
// MAC interface
int tx_request(const tx_request_t& request);
int tx_request(const tx_request_t& request);
int set_ul_grant(std::array<uint8_t, SRSLTE_RAR_UL_GRANT_NBITS>) { return SRSLTE_SUCCESS; };
void send_prach(uint32_t prach_occasion, uint32_t preamble_index, int preamble_received_target_power){};
void send_prach(const uint32_t preamble_idx, const int prach_occasion, const float target_power_dbm, const float ta_base_sec = 0.0f){};
private:
std::unique_ptr<srslte::srslte_basic_vnf> vnf;

View File

@ -13,6 +13,8 @@
#ifndef SRSUE_MAC_NR_H
#define SRSUE_MAC_NR_H
#include "mac_nr_interfaces.h"
#include "proc_ra_nr.h"
#include "srslte/common/block_queue.h"
#include "srslte/common/mac_pcap.h"
#include "srslte/interfaces/mac_interface_types.h"
@ -30,7 +32,7 @@ struct mac_nr_args_t {
uint32_t drb_lcid;
};
class mac_nr final : public mac_interface_phy_nr, public mac_interface_rrc_nr
class mac_nr final : public mac_interface_phy_nr, public mac_interface_rrc_nr, public mac_interface_proc_ra_nr
{
public:
mac_nr(srslte::ext_task_sched_handle task_sched_);
@ -65,12 +67,30 @@ public:
void setup_lcid(const srslte::logical_channel_config_t& config);
void set_config(const srslte::bsr_cfg_t& bsr_cfg);
void set_config(const srslte::sr_cfg_t& sr_cfg);
void set_config(const srslte::rach_nr_cfg_t& rach_cfg);
void set_contention_id(const uint64_t ue_identity);
bool set_crnti(const uint16_t crnti);
void start_ra_procedure();
/// procedure ra nr interface
uint64_t get_contention_id();
uint16_t get_c_rnti();
void set_c_rnti(uint64_t c_rnti_);
bool msg3_is_transmitted() { return true; }
void msg3_flush() {}
void msg3_prepare() {}
bool msg3_is_empty() { return true; }
/// stack interface
void process_pdus();
static bool is_in_window(uint32_t tti, int* start, int* len);
// PHY Interface
void prach_sent(const uint32_t tti);
void tb_decoded_ok(const uint8_t cc_idx, const uint32_t tti);
private:
void write_pcap(const uint32_t cc_idx, mac_nr_grant_dl_t& grant); // If PCAPs are enabled for this MAC
void handle_pdu(srslte::unique_byte_buffer_t pdu);
@ -97,7 +117,7 @@ private:
bool started = false;
uint16_t crnti = 0xdead;
uint16_t c_rnti = SRSLTE_INVALID_RNTI;
uint64_t contention_id = 0;
static constexpr uint32_t MIN_RLC_PDU_LEN =
@ -117,6 +137,9 @@ private:
srslte::unique_byte_buffer_t rlc_buffer = nullptr;
srslte::task_multiqueue::queue_handle stack_task_dispatch_queue;
// MAC Uplink-related procedures
proc_ra_nr proc_ra;
};
} // namespace srsue

View File

@ -0,0 +1,39 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSUE_MAC_NR_INTERFACES_H
#define SRSUE_MAC_NR_INTERFACES_H
#include "srslte/common/interfaces_common.h"
namespace srsue {
/**
* @brief Interface from MAC NR parent class to subclass random access procedure
*/
class mac_interface_proc_ra_nr
{
public:
// Functions for identity handling, e.g., contention id and c-rnti
virtual uint64_t get_contention_id() = 0;
virtual uint16_t get_c_rnti() = 0;
virtual void set_c_rnti(uint64_t c_rnti) = 0;
// Functions for msg3 manipulation which shall be transparent to the procedure
virtual bool msg3_is_transmitted() = 0;
virtual void msg3_flush() = 0;
virtual void msg3_prepare() = 0;
virtual bool msg3_is_empty() = 0;
};
} // namespace srsue
#endif // SRSUE_MAC_NR_INTERFACES_H

View File

@ -0,0 +1,108 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSUE_PROC_RA_NR_H
#define SRSUE_PROC_RA_NR_H
#include <atomic>
#include <mutex>
#include <stdint.h>
#include "mac_nr_interfaces.h"
#include "srslte/common/common.h"
#include "srslte/common/task_scheduler.h"
#include "srslte/interfaces/ue_nr_interfaces.h"
#include "srslte/srslog/srslog.h"
namespace srsue {
class proc_ra_nr
{
public:
proc_ra_nr(srslog::basic_logger& logger_);
~proc_ra_nr(){};
void init(phy_interface_mac_nr* phy_h_, mac_interface_proc_ra_nr* mac_, srslte::ext_task_sched_handle* task_sched_);
void set_config(const srslte::rach_nr_cfg_t& rach_cfg);
bool is_contention_resolution();
bool is_rar_opportunity(uint32_t tti);
uint16_t get_rar_rnti();
// PHY interfaces
void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id);
void handle_rar_pdu(mac_interface_phy_nr::mac_nr_grant_dl_t& grant);
void pdcch_to_crnti();
void start_by_rrc();
void reset();
private:
srslog::basic_logger& logger;
phy_interface_mac_nr* phy = nullptr;
mac_interface_proc_ra_nr* mac = nullptr;
srslte::ext_task_sched_handle* task_sched = nullptr;
srslte::task_multiqueue::queue_handle task_queue;
int ra_window_length = -1, ra_window_start = -1;
uint16_t rar_rnti = SRSLTE_INVALID_RNTI;
uint16_t temp_rnti = SRSLTE_INVALID_RNTI;
srslte::rach_nr_cfg_t rach_cfg = {};
bool configured = false;
enum ra_state_t {
IDLE = 0,
PDCCH_SETUP,
WAITING_FOR_PRACH_SENT,
WAITING_FOR_RESPONSE_RECEPTION,
WAITING_FOR_CONTENTION_RESOLUTION,
WAITING_FOR_COMPLETION,
MAX_RA_STATES,
};
std::atomic<ra_state_t> state = {IDLE};
enum initiators_t { MAC, RRC, initiators_t_NULLTYPE };
std::atomic<initiators_t> started_by = {initiators_t_NULLTYPE};
srslte::timer_handler::unique_timer prach_send_timer;
srslte::timer_handler::unique_timer rar_timeout_timer;
srslte::timer_handler::unique_timer contention_resolution_timer;
// 38.321 5.1.1 Variables
uint32_t preamble_index = 0;
uint32_t preamble_transmission_counter = 0;
uint32_t preamble_power_ramping_step = 0;
int preamble_received_target_power = 0;
uint32_t scaling_factor_bi = 0;
// uint32_t temporary_c_rnti;
uint32_t power_offset_2step_ra = 0;
// not explicty mentioned
uint32_t preambleTransMax = 0;
uint32_t prach_occasion = 0;
uint32_t current_ta = 0;
void timer_expired(uint32_t timer_id);
// 38.321 Steps 5.1.1 - 5.1.6
void ra_procedure_initialization();
void ra_resource_selection();
void ra_preamble_transmission();
void ra_response_reception(const mac_interface_phy_nr::mac_nr_grant_dl_t& grant);
void ra_contention_resolution();
void ra_contention_resolution(uint64_t rx_contention_id);
void ra_completion();
void ra_error();
};
} // namespace srsue
#endif

View File

@ -6,5 +6,6 @@
# the distribution.
#
set(SOURCES mac_nr.cc)
add_library(srsue_mac_nr STATIC ${SOURCES})
set(SOURCES mac_nr.cc proc_ra_nr.cc)
add_library(srsue_mac_nr STATIC ${SOURCES})
target_link_libraries(srsue_mac_nr srslte_mac)

View File

@ -12,11 +12,12 @@
#include "srsue/hdr/stack/mac_nr/mac_nr.h"
#include "srslte/mac/mac_rar_pdu_nr.h"
#include "srsue/hdr/stack/mac_nr/proc_ra_nr.h"
namespace srsue {
mac_nr::mac_nr(srslte::ext_task_sched_handle task_sched_) :
task_sched(task_sched_), logger(srslog::fetch_basic_logger("MAC"))
task_sched(task_sched_), logger(srslog::fetch_basic_logger("MAC")), proc_ra(logger)
{
tx_buffer = srslte::make_byte_buffer();
rlc_buffer = srslte::make_byte_buffer();
@ -72,7 +73,7 @@ void mac_nr::run_tti(const uint32_t tti)
uint16_t mac_nr::get_ul_sched_rnti(const uint32_t tti)
{
return crnti;
return c_rnti;
}
bool mac_nr::is_si_opportunity()
@ -118,12 +119,12 @@ uint16_t mac_nr::get_dl_sched_rnti(const uint32_t tti)
bool mac_nr::has_crnti()
{
return crnti != SRSLTE_INVALID_RNTI;
return c_rnti != SRSLTE_INVALID_RNTI;
}
uint16_t mac_nr::get_crnti()
{
return crnti;
return c_rnti;
}
void mac_nr::bch_decoded_ok(uint32_t tti, srslte::unique_byte_buffer_t payload)
@ -184,7 +185,7 @@ void mac_nr::tb_decoded(const uint32_t cc_idx, mac_nr_grant_dl_t& grant)
// handle PDU
if (SRSLTE_RNTI_ISRAR(grant.rnti)) { // TODO: replace with proc_ra->get_rar_rnti()
// TODO: pass to RA proc
handle_rar_pdu(grant);
// handle_rar_pdu(grant);
} else {
// Push DL PDUs to queue for back-ground processing
for (uint32_t i = 0; i < SRSLTE_MAX_CODEWORDS; ++i) {
@ -198,27 +199,6 @@ void mac_nr::tb_decoded(const uint32_t cc_idx, mac_nr_grant_dl_t& grant)
stack_task_dispatch_queue.push([this]() { process_pdus(); });
}
// Temporary helper until RA proc is complete
void mac_nr::handle_rar_pdu(mac_nr_grant_dl_t& grant)
{
for (uint32_t i = 0; i < SRSLTE_MAX_CODEWORDS; ++i) {
if (grant.tb[i] != nullptr) {
srslte::mac_rar_pdu_nr pdu;
if (!pdu.unpack(grant.tb[i]->msg, grant.tb[i]->N_bytes)) {
logger.warning("Error unpacking RAR PDU");
return;
}
logger.info(pdu.to_string());
for (auto& subpdu : pdu.get_subpdus()) {
if (subpdu.has_rapid() && subpdu.get_rapid() == 0 /* selected preamble */) {
phy->set_ul_grant(subpdu.get_ul_grant());
}
}
}
}
}
void mac_nr::new_grant_ul(const uint32_t cc_idx, const mac_nr_grant_ul_t& grant)
{
phy_interface_stack_nr::tx_request_t tx_request = {};
@ -303,23 +283,32 @@ void mac_nr::set_config(const srslte::sr_cfg_t& sr_cfg)
logger.warning("Not Scheduling Request Config yet");
}
void mac_nr::set_config(const srslte::rach_nr_cfg_t& rach_cfg){
proc_ra.set_config(rach_cfg);
}
void mac_nr::set_contention_id(uint64_t ue_identity)
{
contention_id = ue_identity;
}
bool mac_nr::set_crnti(const uint16_t crnti_)
bool mac_nr::set_crnti(const uint16_t c_rnti_)
{
if (is_valid_crnti(crnti_)) {
logger.info("Setting C-RNTI to 0x%X", crnti_);
crnti = crnti_;
if (is_valid_crnti(c_rnti_)) {
logger.info("Setting C-RNTI to 0x%X", c_rnti_);
c_rnti = c_rnti_;
return true;
} else {
logger.warning("Failed to set C-RNTI, 0x%X is not valid.", crnti_);
logger.warning("Failed to set C-RNTI, 0x%X is not valid.", c_rnti_);
return false;
}
}
void mac_nr::start_ra_procedure()
{
proc_ra.start_by_rrc();
}
bool mac_nr::is_valid_crnti(const uint16_t crnti)
{
// TS 38.321 15.3.0 Table 7.1-1
@ -361,4 +350,47 @@ void mac_nr::handle_pdu(srslte::unique_byte_buffer_t pdu)
}
}
uint64_t mac_nr::get_contention_id()
{
return 0xdeadbeef; // TODO when rebased on PR
}
uint16_t mac_nr::get_c_rnti()
{
return c_rnti;
}
void mac_nr::set_c_rnti(uint64_t c_rnti_)
{
c_rnti = c_rnti_;
}
// TODO same function as for mac_eutra
bool mac_nr::is_in_window(uint32_t tti, int* start, int* len)
{
uint32_t st = (uint32_t)*start;
uint32_t l = (uint32_t)*len;
if (srslte_tti_interval(tti, st) < l + 5) {
if (tti > st) {
if (tti <= st + l) {
return true;
} else {
*start = 0;
*len = 0;
return false;
}
} else {
if (tti <= (st + l) % 10240) {
return true;
} else {
*start = 0;
*len = 0;
return false;
}
}
}
return false;
}
} // namespace srsue

View File

@ -0,0 +1,306 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsue/hdr/stack/mac_nr/proc_ra_nr.h"
#include "srsue/hdr/stack/mac_nr/mac_nr.h"
#include "srslte/mac/mac_rar_pdu_nr.h"
namespace srsue {
const char* state_str_nr[] = {"RA: IDLE: ",
"RA: PDCCH_SETUP: ",
"RA: WAITING_FOR_PRACH_SENT: ",
"RA: WAITING_FOR_RESPONSE_RECEPTION: ",
"RA: WAITING_FOR_COMPLETION: ",
"RA: MAX_RA_STATES: "};
// Table 7.2-1. Backoff Parameter values
uint32_t backoff_table_nr[16] = {0, 10, 20, 30, 40, 60, 80, 120, 160, 240, 320, 480, 960, 1920, 1920, 1920};
// Table 7.6-1: DELTA_PREAMBLE values long
int delta_preamble_db_table_nr[5] = {0, -3, -6, 0};
proc_ra_nr::proc_ra_nr(srslog::basic_logger& logger_) : logger(logger_) {}
void proc_ra_nr::init(phy_interface_mac_nr* phy_,
mac_interface_proc_ra_nr* mac_,
srslte::ext_task_sched_handle* task_sched_)
{
phy = phy_;
mac = mac_;
task_sched = task_sched_;
task_queue = task_sched->make_task_queue();
prach_send_timer = task_sched->get_unique_timer();
rar_timeout_timer = task_sched->get_unique_timer();
contention_resolution_timer = task_sched->get_unique_timer();
}
/* Sets a new configuration. The configuration is applied by initialization() function */
void proc_ra_nr::set_config(const srslte::rach_nr_cfg_t& rach_cfg_)
{
if (state != IDLE) {
logger.warning("Wrong state for ra reponse reception %s (expected state %s)",
srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, state),
srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, IDLE));
return;
}
rach_cfg = rach_cfg_;
configured = true;
logger.info("Set RACH common config (Config Index %d, preambleTransMax %d, Repsonse Window %d)",
rach_cfg.prach_ConfigurationIndex,
rach_cfg.preambleTransMax,
rach_cfg.ra_responseWindow);
}
void proc_ra_nr::start_by_rrc()
{
if (state != IDLE || configured == false) {
logger.warning("Trying to start PRACH by RRC order in invalid state (%s)",
srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, state));
return;
}
started_by = initiators_t::RRC;
logger.info("Starting PRACH by RRC order");
ra_procedure_initialization();
}
bool proc_ra_nr::is_rar_opportunity(uint32_t tti)
{
// TODO replace second "&&"" by rar_timeout_timer.running if timer thread safe and delayed starting (tti+3)
if (state == WAITING_FOR_RESPONSE_RECEPTION && ra_window_start > 0 && ra_window_length > 0 &&
mac_nr::is_in_window(tti, &ra_window_start, &ra_window_length)) {
logger.debug("SCHED: Searching RAR-RNTI=0x%x, tti=%d", rar_rnti, tti);
return true;
}
return false;
}
uint16_t proc_ra_nr::get_rar_rnti()
{
if (rar_rnti == SRSLTE_INVALID_RNTI || state != WAITING_FOR_RESPONSE_RECEPTION) {
logger.error("Requested ra rnti is invalid. Anyway we return an invalid ra rnti\n");
return SRSLTE_INVALID_RNTI;
}
return rar_rnti;
}
void proc_ra_nr::timer_expired(uint32_t timer_id)
{
if (prach_send_timer.id() == timer_id) {
logger.error("PRACH Send timer expired. PRACH was not transmitted within %d ttis by phy. (TODO)",
prach_send_timer.duration());
ra_error();
} else if (rar_timeout_timer.id() == timer_id) {
logger.error("RAR Timer expired. RA response not received within the response window Response Error (TODO)");
ra_error();
} else if (contention_resolution_timer.id() == timer_id) {
logger.error("Contention Resolution Timer expired. Stopping PDCCH Search and going to Response Error (TODO)");
ra_error();
} else {
logger.error("Timer not implemented");
}
}
// 5.1.2 Random Access Resource selection
void proc_ra_nr::ra_procedure_initialization()
{
mac->msg3_flush();
preamble_power_ramping_step = rach_cfg.powerRampingStep;
scaling_factor_bi = 1;
preambleTransMax = rach_cfg.preambleTransMax;
ra_resource_selection();
}
// 5.1.2 Random Access Resource selection (TODO)
void proc_ra_nr::ra_resource_selection()
{
ra_preamble_transmission();
}
// 5.1.3 Random Access Preamble transmission
void proc_ra_nr::ra_preamble_transmission()
{
uint32_t delta_preamble = 0; // TODO calulate the delta preamble based on delta_preamble_db_table_nr
preamble_received_target_power = rach_cfg.PreambleReceivedTargetPower + delta_preamble +
(preamble_transmission_counter - 1) * rach_cfg.powerRampingStep +
power_offset_2step_ra;
preamble_index = 0;
prach_occasion = 0;
// instruct the physical layer to transmit the Random Access Preamble using the selected PRACH occasion, corresponding
// RA-RNTI (if available), PREAMBLE_INDEX, and PREAMBLE_RECEIVED_TARGET_POWER.
phy->send_prach(prach_occasion, preamble_index, preamble_received_target_power);
prach_send_timer.set(100, [this](uint32_t tid) { timer_expired(tid); }); // TODO find a suitable 100?
prach_send_timer.run();
state = WAITING_FOR_PRACH_SENT;
}
// 5.1.4 Random Access Preamble transmission
void proc_ra_nr::ra_response_reception(const mac_interface_phy_nr::mac_nr_grant_dl_t& grant)
{
if (state != WAITING_FOR_RESPONSE_RECEPTION) {
logger.warning(
"Wrong state for ra reponse reception %s (expected state %s)",
srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, state),
srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_RESPONSE_RECEPTION));
return;
}
// Stop rar timer
rar_timeout_timer.stop();
for (uint32_t i = 0; i < SRSLTE_MAX_CODEWORDS; ++i) {
if (grant.tb[i] != nullptr) {
srslte::mac_rar_pdu_nr pdu;
if (!pdu.unpack(grant.tb[i]->msg, grant.tb[i]->N_bytes)) {
logger.warning("Error unpacking RAR PDU");
return;
}
logger.info(pdu.to_string());
for (auto& subpdu : pdu.get_subpdus()) {
if (subpdu.has_rapid() && subpdu.get_rapid() == preamble_index) {
phy->set_ul_grant(subpdu.get_ul_grant());
// reset all parameters that are used before rar
rar_rnti = SRSLTE_INVALID_RNTI;
mac->msg3_prepare();
temp_rnti = subpdu.get_temp_crnti();
current_ta = subpdu.get_ta();
}
}
}
}
contention_resolution_timer.set(rach_cfg.ra_ContentionResolutionTimer, [this](uint32_t tid) { timer_expired(tid); });
contention_resolution_timer.run();
logger.debug("Waiting for Contention Resolution");
state = WAITING_FOR_CONTENTION_RESOLUTION;
}
// TS 38.321 Section 5.1.5 2 ways to resolve contention resolution
// if the C-RNTI MAC CE was included in Msg3: (only this one is implemented)
void proc_ra_nr::ra_contention_resolution()
{
if (state != WAITING_FOR_CONTENTION_RESOLUTION) {
logger.warning(
"Wrong state for ra contention resolution by phy %s (expected state %s)",
srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, state),
srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_CONTENTION_RESOLUTION));
return;
}
if (started_by == initiators_t::RRC || started_by == initiators_t::MAC) {
logger.info("PDCCH to C-RNTI received with a new UL grant of transmission");
contention_resolution_timer.stop();
state = WAITING_FOR_COMPLETION;
ra_completion();
} else {
logger.error("Not started by the correct initiator MAC or RRC");
}
}
// or else if the CCCH SDU was included in Msg3 and the PDCCH transmission is addressed to its TEMPORARY_C-RNTI:
void proc_ra_nr::ra_contention_resolution(uint64_t rx_contention_id)
{
if (state != WAITING_FOR_CONTENTION_RESOLUTION) {
logger.warning(
"Wrong state for ra contention resolution by phy %s (expected state %s)",
srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, state),
srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_CONTENTION_RESOLUTION));
return;
}
// TODO
}
void proc_ra_nr::ra_completion()
{
if (state != WAITING_FOR_COMPLETION) {
logger.warning("Wrong state for ra completion by phy %s (expected state %s)",
srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, state),
srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_COMPLETION));
return;
}
mac->set_c_rnti(temp_rnti);
srslte::console("Random Access Complete. c-rnti=0x%x, ta=%d\n", mac->get_c_rnti(), current_ta);
logger.info("Random Access Complete. c-rnti=0x%x, ta=%d", mac->get_c_rnti(), current_ta);
temp_rnti = 0;
reset();
}
void proc_ra_nr::ra_error()
{
logger.error("NR random access procedure error recovery not implemented yet\n");
}
// Is called by PHY once it has transmitted the prach transmitted, than configure RA-RNTI and wait for RAR reception
void proc_ra_nr::prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id)
{
task_queue.push([this, tti, s_id, t_id, f_id, ul_carrier_id]() {
if (state != WAITING_FOR_PRACH_SENT) {
logger.warning("Wrong state for prach sent notification by phy %s (expected state %s)",
srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, state),
srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_PRACH_SENT));
return;
}
prach_send_timer.stop();
rar_rnti = 1 + s_id + 14 * t_id + 14 * 80 * f_id + 14 * 80 * 8 * ul_carrier_id;
logger.info(
"prach_occasion=%d, preamble_index=%d, ra-rnti=0x%x, ra-tti=%d, s_id=%d, t_id=%d, f_id=%d, ul_carrier_id=%d",
prach_occasion,
preamble_index,
rar_rnti,
tti,
s_id,
t_id,
f_id,
ul_carrier_id);
srslte::console("Random Access Transmission: prach_occasion=%d, preamble_index=%d, ra-rnti=0x%x, tti=%d\n",
prach_occasion,
preamble_index,
rar_rnti,
tti);
uint32_t rar_window_st = TTI_ADD(tti, 3);
// TODO check ra_response window (delayed start)?
rar_timeout_timer.set(rach_cfg.ra_responseWindow + 3, [this](uint32_t tid) { timer_expired(tid); });
rar_timeout_timer.run();
// Wait for RAR reception
ra_window_length = rach_cfg.ra_responseWindow;
ra_window_start = TTI_ADD(tti, 3);
logger.debug("Calculated ra_window_start=%d, ra_window_length=%d", ra_window_start, ra_window_length);
state = WAITING_FOR_RESPONSE_RECEPTION;
});
}
// Called by PHY thread through MAC parent
void proc_ra_nr::handle_rar_pdu(mac_interface_phy_nr::mac_nr_grant_dl_t& grant)
{
// Defer the handling of the grant to main stack thread in ra_response_reception
auto task_handler = [this](const mac_interface_phy_nr::mac_nr_grant_dl_t& t) { ra_response_reception(std::move(t)); };
task_queue.push(std::bind(task_handler, std::move(grant)));
}
// Called from PHY thread, defer actions therefore.
void proc_ra_nr::pdcch_to_crnti()
{
task_queue.push([this]() { ra_contention_resolution(); });
}
bool proc_ra_nr::is_contention_resolution()
{
return state == WAITING_FOR_CONTENTION_RESOLUTION;
}
void proc_ra_nr::reset()
{
state = IDLE;
started_by = initiators_t::initiators_t_NULLTYPE;
prach_send_timer.stop();
rar_timeout_timer.stop();
contention_resolution_timer.stop();
}
} // namespace srsue

View File

@ -9,6 +9,10 @@
add_subdirectory(phy)
add_subdirectory(upper)
if (ENABLE_5GNR)
add_subdirectory(mac_nr)
endif (ENABLE_5GNR)
if (ENABLE_TTCN3)
add_subdirectory(ttcn3)
endif (ENABLE_TTCN3)

View File

@ -0,0 +1,11 @@
#
# Copyright 2013-2021 Software Radio Systems Limited
#
# By using this file, you agree to the terms and conditions set
# forth in the LICENSE file which can be found at the top level of
# the distribution.
#
add_executable(proc_ra_nr_test proc_ra_nr_test.cc)
target_link_libraries(proc_ra_nr_test srsue_mac_nr srslte_common)
add_test(proc_ra_nr_test proc_ra_nr_test)

View File

@ -0,0 +1,130 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2020 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srslte/common/common.h"
#include "srslte/common/log_filter.h"
#include "srslte/common/test_common.h"
#include "srsue/hdr/stack/mac_nr/proc_ra_nr.h"
using namespace srsue;
class dummy_phy : public phy_interface_mac_nr
{
public:
dummy_phy() {}
void send_prach(const uint32_t prach_occasion_, const int preamble_index_, const float preamble_received_target_power_, const float ta_base_sec_ = 0.0f)
{
prach_occasion = prach_occasion_;
preamble_index = preamble_index_;
preamble_received_target_power = preamble_received_target_power_;
}
int tx_request(const tx_request_t& request) {return 0;}
int set_ul_grant(std::array<uint8_t, SRSLTE_RAR_UL_GRANT_NBITS>) { return 0; }
void get_last_send_prach(uint32_t* prach_occasion_, uint32_t* preamble_index_, int* preamble_received_target_power_)
{
*prach_occasion_ = prach_occasion;
*preamble_index_ = preamble_index;
*preamble_received_target_power_ = preamble_received_target_power;
}
private:
uint32_t prach_occasion;
uint32_t preamble_index;
int preamble_received_target_power;
};
class dummy_mac : public mac_interface_proc_ra_nr
{
public:
uint64_t get_contention_id() { return 0xdeadbeaf; }
uint16_t get_c_rnti() { return crnti; }
void set_c_rnti(uint64_t c_rnti) { crnti = c_rnti; }
bool msg3_is_transmitted() { return true; }
void msg3_flush() {}
void msg3_prepare() {}
bool msg3_is_empty() { return true; }
void msga_flush(){};
private:
uint16_t crnti;
};
int main()
{
srslog::init();
auto& mac_logger = srslog::fetch_basic_logger("MAC");
mac_logger.set_level(srslog::basic_levels::debug);
mac_logger.set_hex_dump_max_size(-1);
dummy_phy dummy_phy;
dummy_mac dummy_mac;
srslte::task_scheduler task_sched{5, 2};
srslte::ext_task_sched_handle ext_task_sched_h(&task_sched);
proc_ra_nr proc_ra_nr(mac_logger);
proc_ra_nr.init(&dummy_phy, &dummy_mac, &ext_task_sched_h);
TESTASSERT(proc_ra_nr.is_rar_opportunity(1) == false);
srslte::rach_nr_cfg_t rach_cfg;
rach_cfg.powerRampingStep = 4;
rach_cfg.prach_ConfigurationIndex = 16;
rach_cfg.PreambleReceivedTargetPower = -110;
rach_cfg.preambleTransMax = 7;
rach_cfg.ra_ContentionResolutionTimer = 64;
rach_cfg.ra_responseWindow = 10;
proc_ra_nr.set_config(rach_cfg);
proc_ra_nr.start_by_rrc();
// Test send prach parameters
uint32_t prach_occasion = 0;
uint32_t preamble_index = 0;
int preamble_received_target_power = 0;
dummy_phy.get_last_send_prach(&prach_occasion, &preamble_index, &preamble_received_target_power);
TESTASSERT(prach_occasion == 0);
TESTASSERT(preamble_index == 0);
TESTASSERT(preamble_received_target_power == -114);
// Simulate PHY and call prach_sent (random values)
uint32_t tti_start = 0;
proc_ra_nr.prach_sent(tti_start, 6, 0, 4, 1);
for (uint32_t i = tti_start; i < rach_cfg.ra_responseWindow; i++) {
// update clock and run internal tasks
task_sched.tic();
task_sched.run_pending_tasks();
bool rar_opportunity = proc_ra_nr.is_rar_opportunity(i);
if (i < 3 + tti_start) {
TESTASSERT(rar_opportunity == false);
} else if (3 + tti_start > i && i < 3 + rach_cfg.ra_responseWindow) {
TESTASSERT(rar_opportunity == true);
TESTASSERT(proc_ra_nr.get_rar_rnti() == 0x3487);
}
}
mac_interface_phy_nr::mac_nr_grant_dl_t grant;
grant.rnti = 0x3487;
grant.tti = rach_cfg.ra_responseWindow + tti_start + 3;
grant.pid = 0x0123;
uint8_t mac_dl_rar_pdu[] = {0x40, 0x05, 0xa0, 0x00, 0x11, 0x46, 0x46, 0x16, 0x00, 0x00, 0x00};
grant.tb[0] = srslte::make_byte_buffer();
grant.tb[0].get()->append_bytes(mac_dl_rar_pdu, sizeof(mac_dl_rar_pdu));
proc_ra_nr.handle_rar_pdu(grant);
task_sched.tic();
task_sched.run_pending_tasks();
proc_ra_nr.pdcch_to_crnti();
task_sched.tic();
task_sched.run_pending_tasks();
return SRSLTE_SUCCESS;
}