mirror of https://github.com/pvnis/srsRAN_4G.git
Merge branch 'next' into agpl_next
# Conflicts: # lib/include/srsran/common/basic_pnf.h # lib/include/srsran/common/basic_vnf.h # lib/include/srsran/common/basic_vnf_api.h # lib/src/common/basic_vnf.cc # lib/test/common/pnf_bridge.cc # lib/test/common/pnf_dummy.cc # srsenb/hdr/phy/vnf_phy_nr.h # srsenb/hdr/stack/mac/nr/sched_nr_common.h # srsenb/hdr/stack/mac/nr/sched_nr_phy_helpers.h # srsenb/src/phy/vnf_phy_nr.cc # srsenb/src/stack/mac/nr/sched_nr_common.cc # srsenb/src/stack/mac/nr/sched_nr_phy_helpers.cc # srsue/hdr/phy/vnf_phy_nr.h # srsue/src/phy/vnf_phy_nr.ccmaster
commit
ccf8cfb65f
@ -1,558 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2013-2021 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* This file is part of srsRAN.
|
|
||||||
*
|
|
||||||
* srsRAN is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsRAN is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SRSRAN_BASIC_PNF_H
|
|
||||||
#define SRSRAN_BASIC_PNF_H
|
|
||||||
|
|
||||||
#include "basic_vnf_api.h"
|
|
||||||
#include "common.h"
|
|
||||||
#include "srsran/adt/choice_type.h"
|
|
||||||
#include "srsran/common/block_queue.h"
|
|
||||||
#include "srsran/common/buffer_pool.h"
|
|
||||||
#include "srsran/srslog/srslog.h"
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <atomic>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <mutex>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <poll.h>
|
|
||||||
#include <random>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <thread>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#define RAND_SEED (12384)
|
|
||||||
#define RX_TIMEOUT_MS (500)
|
|
||||||
|
|
||||||
#define MIN_TB_LEN (100) // MAX_TB_LEN defined in api.h
|
|
||||||
|
|
||||||
#define PING_REQUEST_PDU 1
|
|
||||||
|
|
||||||
namespace srsran {
|
|
||||||
|
|
||||||
struct pnf_metrics_t {
|
|
||||||
uint32_t avg_rtt_us;
|
|
||||||
uint32_t num_timing_errors;
|
|
||||||
uint32_t num_pdus;
|
|
||||||
uint32_t tb_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
class srsran_basic_pnf
|
|
||||||
{
|
|
||||||
using msg_header_t = basic_vnf_api::msg_header_t;
|
|
||||||
const static size_t buffer_size =
|
|
||||||
srsran::static_max<sizeof(basic_vnf_api::dl_conf_msg_t), sizeof(basic_vnf_api::tx_request_msg_t)>::value;
|
|
||||||
using msg_buffer_t = std::array<uint8_t, buffer_size>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
srsran_basic_pnf(const std::string& type_,
|
|
||||||
const std::string& vnf_p5_addr,
|
|
||||||
const uint16_t& vnf_p5_port,
|
|
||||||
const uint32_t& sf_interval,
|
|
||||||
const int32_t& num_sf_,
|
|
||||||
const uint32_t& tb_len_) :
|
|
||||||
running(false),
|
|
||||||
type(type_),
|
|
||||||
tti(100), ///< Random start TTI
|
|
||||||
vnf_addr(vnf_p5_addr),
|
|
||||||
vnf_port(vnf_p5_port),
|
|
||||||
sf_interval_us(sf_interval),
|
|
||||||
num_sf(num_sf_),
|
|
||||||
tb_len(tb_len_),
|
|
||||||
rand_gen(RAND_SEED),
|
|
||||||
rand_dist(MIN_TB_LEN, MAX_TB_LEN)
|
|
||||||
{
|
|
||||||
logger.set_level(srslog::basic_levels::warning);
|
|
||||||
logger.set_hex_dump_max_size(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
~srsran_basic_pnf() { stop(); };
|
|
||||||
|
|
||||||
bool start()
|
|
||||||
{
|
|
||||||
// create socket
|
|
||||||
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
||||||
if (sockfd < 0) {
|
|
||||||
perror("socket");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int enable = 1;
|
|
||||||
#if defined(SO_REUSEADDR)
|
|
||||||
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) {
|
|
||||||
perror("setsockopt(SO_REUSEADDR) failed");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if defined(SO_REUSEPORT)
|
|
||||||
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) {
|
|
||||||
perror("setsockopt(SO_REUSEPORT) failed");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bzero(&servaddr, sizeof(servaddr));
|
|
||||||
servaddr.sin_family = AF_INET;
|
|
||||||
servaddr.sin_addr.s_addr = inet_addr(vnf_addr.c_str());
|
|
||||||
servaddr.sin_port = htons(vnf_port);
|
|
||||||
|
|
||||||
// start main thread
|
|
||||||
running = true;
|
|
||||||
|
|
||||||
if (type == "gnb") {
|
|
||||||
rx_thread = std::unique_ptr<std::thread>(new std::thread(&srsran_basic_pnf::dl_handler_thread, this));
|
|
||||||
tx_thread = std::unique_ptr<std::thread>(new std::thread(&srsran_basic_pnf::ul_handler_thread, this));
|
|
||||||
} else {
|
|
||||||
tx_thread = std::unique_ptr<std::thread>(new std::thread(&srsran_basic_pnf::ue_dl_handler_thread, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool stop()
|
|
||||||
{
|
|
||||||
running = false;
|
|
||||||
|
|
||||||
if (rx_thread) {
|
|
||||||
if (rx_thread->joinable()) {
|
|
||||||
rx_thread->join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tx_thread) {
|
|
||||||
if (tx_thread->joinable()) {
|
|
||||||
tx_thread->join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
pnf_metrics_t get_metrics()
|
|
||||||
{
|
|
||||||
pnf_metrics_t tmp = metrics;
|
|
||||||
metrics = {};
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void connect_out_rf_queue(srsran::block_queue<srsran::unique_byte_buffer_t>* rf_queue_)
|
|
||||||
{
|
|
||||||
rf_out_queue = rf_queue_;
|
|
||||||
policy = bridge;
|
|
||||||
}
|
|
||||||
srsran::block_queue<srsran::unique_byte_buffer_t>* get_in_rf_queue()
|
|
||||||
{
|
|
||||||
policy = bridge;
|
|
||||||
return &rf_in_queue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
//! Waits for DL Config or Tx Request Msg from VNF and forwards to RF
|
|
||||||
void dl_handler_thread()
|
|
||||||
{
|
|
||||||
pthread_setname_np(pthread_self(), rx_thread_name.c_str());
|
|
||||||
|
|
||||||
// set_rt_prio();
|
|
||||||
|
|
||||||
struct pollfd fd;
|
|
||||||
fd.fd = sockfd;
|
|
||||||
fd.events = POLLIN;
|
|
||||||
|
|
||||||
std::unique_ptr<msg_buffer_t> rx_buffer{new msg_buffer_t{}};
|
|
||||||
|
|
||||||
while (running) {
|
|
||||||
// receive response
|
|
||||||
int ret = poll(&fd, 1, RX_TIMEOUT_MS);
|
|
||||||
switch (ret) {
|
|
||||||
case -1:
|
|
||||||
printf("Error occurred.\n");
|
|
||||||
running = false;
|
|
||||||
break;
|
|
||||||
case 0:
|
|
||||||
// Timeout
|
|
||||||
printf("Error: Didn't receive response after %dms\n", RX_TIMEOUT_MS);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
int recv_ret = recv(sockfd, rx_buffer->data(), sizeof(*rx_buffer), 0);
|
|
||||||
handle_msg(rx_buffer->data(), recv_ret);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
|
||||||
auto rtt =
|
|
||||||
std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - tti_start_time)
|
|
||||||
.count();
|
|
||||||
|
|
||||||
// TODO: add averaging
|
|
||||||
metrics.avg_rtt_us = rtt;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void ul_handler_thread()
|
|
||||||
{
|
|
||||||
pthread_setname_np(pthread_self(), tx_thread_name.c_str());
|
|
||||||
|
|
||||||
// set_rt_prio();
|
|
||||||
|
|
||||||
struct pollfd fd;
|
|
||||||
fd.fd = sockfd;
|
|
||||||
fd.events = POLLIN;
|
|
||||||
|
|
||||||
const uint32_t max_basic_api_pdu = sizeof(basic_vnf_api::dl_conf_msg_t) + 32; // larger than biggest message
|
|
||||||
std::unique_ptr<std::array<uint8_t, max_basic_api_pdu> > rx_buffer =
|
|
||||||
std::unique_ptr<std::array<uint8_t, max_basic_api_pdu> >(new std::array<uint8_t, max_basic_api_pdu>);
|
|
||||||
|
|
||||||
int32_t sf_counter = 0;
|
|
||||||
while (running && (num_sf > 0 ? sf_counter < num_sf : true)) {
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
|
||||||
|
|
||||||
// Increase TTI
|
|
||||||
tti = (tti + 1) % 10240;
|
|
||||||
|
|
||||||
// Take time before sending the SF indication
|
|
||||||
tti_start_time = std::chrono::steady_clock::now();
|
|
||||||
|
|
||||||
// Send request
|
|
||||||
send_sf_ind(tti);
|
|
||||||
|
|
||||||
if (policy == bridge) {
|
|
||||||
// send_rx_data_ind(tti);
|
|
||||||
} else {
|
|
||||||
// provide UL data every 2nd TTI
|
|
||||||
if (tti % 2 == 0) {
|
|
||||||
send_rx_data_ind(tti);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sf_counter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(sf_interval_us));
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Leaving Tx thread after %d subframes\n", sf_counter);
|
|
||||||
};
|
|
||||||
|
|
||||||
void ue_dl_handler_thread()
|
|
||||||
{
|
|
||||||
pthread_setname_np(pthread_self(), tx_thread_name.c_str());
|
|
||||||
|
|
||||||
// set_rt_prio();
|
|
||||||
|
|
||||||
struct pollfd fd;
|
|
||||||
fd.fd = sockfd;
|
|
||||||
fd.events = POLLIN;
|
|
||||||
|
|
||||||
const uint32_t max_basic_api_pdu = sizeof(basic_vnf_api::dl_conf_msg_t) + 32; // larger than biggest message
|
|
||||||
std::unique_ptr<std::array<uint8_t, max_basic_api_pdu> > rx_buffer =
|
|
||||||
std::unique_ptr<std::array<uint8_t, max_basic_api_pdu> >(new std::array<uint8_t, max_basic_api_pdu>);
|
|
||||||
|
|
||||||
int32_t sf_counter = 0;
|
|
||||||
while (running && (num_sf > 0 ? sf_counter < num_sf : true)) {
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
|
||||||
|
|
||||||
// Increase TTI
|
|
||||||
tti = (tti + 1) % 10240;
|
|
||||||
|
|
||||||
// Take time before sending the SF indication
|
|
||||||
tti_start_time = std::chrono::steady_clock::now();
|
|
||||||
|
|
||||||
// Send SF indication
|
|
||||||
send_sf_ind(tti);
|
|
||||||
|
|
||||||
if (policy == bridge) {
|
|
||||||
srsran::unique_byte_buffer_t tb;
|
|
||||||
if (rf_in_queue.try_pop(&tb)) {
|
|
||||||
send_dl_ind(tti, std::move(tb));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// provide DL grant every even TTI, and UL grant every odd
|
|
||||||
if (tti % 2 == 0) {
|
|
||||||
send_dl_ind(tti);
|
|
||||||
} else {
|
|
||||||
send_ul_ind(tti);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sf_counter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(sf_interval_us));
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Leaving Tx thread after %d subframes\n", sf_counter);
|
|
||||||
};
|
|
||||||
|
|
||||||
void send_sf_ind(uint32_t tti_)
|
|
||||||
{
|
|
||||||
basic_vnf_api::sf_ind_msg_t sf_ind;
|
|
||||||
bzero(&sf_ind, sizeof(sf_ind));
|
|
||||||
sf_ind.header.type = basic_vnf_api::SF_IND;
|
|
||||||
sf_ind.header.msg_len = sizeof(sf_ind) - sizeof(basic_vnf_api::msg_header_t);
|
|
||||||
sf_ind.tti = tti_;
|
|
||||||
sf_ind.t1 = 0;
|
|
||||||
sf_ind.tb_len = tb_len > 0 ? tb_len : rand_dist(rand_gen);
|
|
||||||
|
|
||||||
int n = 0;
|
|
||||||
if ((n = sendto(sockfd, &sf_ind, sizeof(sf_ind), 0, (struct sockaddr*)&servaddr, sizeof(servaddr))) < 0) {
|
|
||||||
printf("sendto failed, ret=%d\n", n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int handle_msg(const uint8_t* buffer, const uint32_t len)
|
|
||||||
{
|
|
||||||
basic_vnf_api::msg_header_t* header = (basic_vnf_api::msg_header_t*)buffer;
|
|
||||||
|
|
||||||
logger.debug("Received %s (%d B) in TTI", basic_vnf_api::msg_type_text[header->type], len);
|
|
||||||
|
|
||||||
switch (header->type) {
|
|
||||||
case basic_vnf_api::SF_IND:
|
|
||||||
printf("Error: %s not handled by VNF\n", basic_vnf_api::msg_type_text[header->type]);
|
|
||||||
break;
|
|
||||||
case basic_vnf_api::DL_CONFIG:
|
|
||||||
handle_dl_config((basic_vnf_api::dl_conf_msg_t*)header);
|
|
||||||
break;
|
|
||||||
case basic_vnf_api::TX_REQUEST:
|
|
||||||
handle_tx_request((basic_vnf_api::tx_request_msg_t*)header);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf("Unknown msg type.\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int handle_dl_config(basic_vnf_api::dl_conf_msg_t* msg)
|
|
||||||
{
|
|
||||||
// printf("Received DL config for TTI=%d\n", msg->tti);
|
|
||||||
|
|
||||||
if (msg->tti != tti) {
|
|
||||||
metrics.num_timing_errors++;
|
|
||||||
// printf("Received DL config for TTI=%d but current TTI is %d\n", msg->tti, tti.load());
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int handle_tx_request(basic_vnf_api::tx_request_msg_t* msg)
|
|
||||||
{
|
|
||||||
if (msg->tti != tti) {
|
|
||||||
metrics.num_timing_errors++;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < msg->nof_pdus; ++i) {
|
|
||||||
metrics.tb_size += msg->pdus[i].length;
|
|
||||||
}
|
|
||||||
|
|
||||||
metrics.num_pdus += msg->nof_pdus;
|
|
||||||
|
|
||||||
if (rf_out_queue != nullptr) {
|
|
||||||
uint32_t len = sizeof(*msg) - sizeof(msg->pdus->data) + msg->pdus->length;
|
|
||||||
|
|
||||||
srsran::unique_byte_buffer_t tx = srsran::make_byte_buffer();
|
|
||||||
if (tx == nullptr) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
memcpy(tx->msg, msg, len);
|
|
||||||
rf_out_queue->push(std::move(tx));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void send_rx_data_ind(const uint32_t tti_)
|
|
||||||
{
|
|
||||||
// MAC PDU for UL-SCH with IPv6 router solicitation
|
|
||||||
static uint8_t tv[] = {0x04, 0x38, 0x00, 0x80, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a, 0xff, 0xfe,
|
|
||||||
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x44, 0x4b, 0x0f, 0x2c, 0x33, 0x98, 0xf2,
|
|
||||||
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x02, 0x85, 0x00, 0x4b, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x7f, 0x00, 0x00, 0x3f, 0x00};
|
|
||||||
|
|
||||||
basic_vnf_api::rx_data_ind_msg_t rx_ind = {};
|
|
||||||
|
|
||||||
rx_ind.header.type = basic_vnf_api::RX_DATA_IND;
|
|
||||||
rx_ind.header.msg_len = sizeof(rx_ind) - sizeof(basic_vnf_api::msg_header_t);
|
|
||||||
rx_ind.sfn = tti_;
|
|
||||||
rx_ind.t1 = 0;
|
|
||||||
|
|
||||||
rx_ind.nof_pdus = 1;
|
|
||||||
rx_ind.pdus[0].type = basic_vnf_api::PUSCH;
|
|
||||||
rx_ind.pdus[0].length = tb_len > 0 ? tb_len : rand_dist(rand_gen);
|
|
||||||
|
|
||||||
if (rx_ind.pdus[0].length >= sizeof(tv)) {
|
|
||||||
// copy TV
|
|
||||||
memcpy(rx_ind.pdus[0].data, tv, sizeof(tv));
|
|
||||||
// set remaining bytes to zero
|
|
||||||
memset(rx_ind.pdus[0].data + sizeof(tv), 0xaa, rx_ind.pdus[0].length - sizeof(tv));
|
|
||||||
} else {
|
|
||||||
// just fill with dummy bytes
|
|
||||||
memset(rx_ind.pdus[0].data, 0xab, rx_ind.pdus[0].length);
|
|
||||||
}
|
|
||||||
|
|
||||||
int n = 0;
|
|
||||||
if ((n = sendto(sockfd, &rx_ind, sizeof(rx_ind), 0, (struct sockaddr*)&servaddr, sizeof(servaddr))) < 0) {
|
|
||||||
printf("sendto failed, ret=%d\n", n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void send_dl_ind(uint32_t tti_, srsran::unique_byte_buffer_t tb = {})
|
|
||||||
{
|
|
||||||
#if PING_REQUEST_PDU
|
|
||||||
static uint8_t tv[] = {
|
|
||||||
0x04, 0x5c, 0x00, 0x80, 0x00, 0x00, 0x45, 0x00, 0x00, 0x54, 0x15, 0x02, 0x40, 0x00, 0x40, 0x01, 0xa2, 0x52,
|
|
||||||
0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8, 0x01, 0x03, 0x08, 0x00, 0x26, 0x40, 0x5e, 0x8f, 0x00, 0xb3, 0x04, 0x55,
|
|
||||||
0xc4, 0x5d, 0x00, 0x00, 0x00, 0x00, 0xe4, 0xf7, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13,
|
|
||||||
0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
|
|
||||||
0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
|
||||||
0x4f, 0x7f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
||||||
#else
|
|
||||||
// MAC PDU with a single LCID with padding only
|
|
||||||
static uint8_t tv[] = {
|
|
||||||
0x01,
|
|
||||||
0x08,
|
|
||||||
0x11,
|
|
||||||
0x22,
|
|
||||||
0x33,
|
|
||||||
0x44,
|
|
||||||
0x55,
|
|
||||||
0x66,
|
|
||||||
0x77,
|
|
||||||
0x88,
|
|
||||||
0x3f,
|
|
||||||
};
|
|
||||||
#endif // PING_REQUEST_PDU
|
|
||||||
basic_vnf_api::dl_ind_msg_t dl_ind = {};
|
|
||||||
|
|
||||||
dl_ind.header.type = basic_vnf_api::DL_IND;
|
|
||||||
dl_ind.header.msg_len = sizeof(dl_ind) - sizeof(basic_vnf_api::msg_header_t);
|
|
||||||
dl_ind.tti = tti_;
|
|
||||||
dl_ind.t1 = 0;
|
|
||||||
|
|
||||||
uint32_t tot_bytes = 0;
|
|
||||||
uint32_t tb_size = tb_len > 0 ? tb_len : rand_dist(rand_gen);
|
|
||||||
|
|
||||||
if (tb != nullptr) {
|
|
||||||
auto* header = (basic_vnf_api::msg_header_t*)tb->msg;
|
|
||||||
|
|
||||||
if (header->type == basic_vnf_api::TX_REQUEST) {
|
|
||||||
auto* tx_req = (basic_vnf_api::tx_request_msg_t*)tb->msg;
|
|
||||||
dl_ind.nof_pdus = tx_req->nof_pdus;
|
|
||||||
// dl_ind.tti = tx_req->tti;
|
|
||||||
for (uint32_t i = 0; i < dl_ind.nof_pdus; ++i) {
|
|
||||||
dl_ind.pdus[i].length = tx_req->pdus[i].length;
|
|
||||||
dl_ind.pdus[i].type = tx_req->pdus[i].type;
|
|
||||||
memcpy(dl_ind.pdus[i].data, tx_req->pdus[i].data, dl_ind.pdus[i].length);
|
|
||||||
tot_bytes += dl_ind.pdus[i].length;
|
|
||||||
logger.info(
|
|
||||||
dl_ind.pdus[i].data, dl_ind.pdus[i].length, "Sending to UE a PDU (%d bytes)", dl_ind.pdus[i].length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
uint32_t N_bytes = sizeof(tv);
|
|
||||||
|
|
||||||
// Create default
|
|
||||||
dl_ind.nof_pdus = 1;
|
|
||||||
dl_ind.pdus[0].type = basic_vnf_api::PDSCH;
|
|
||||||
dl_ind.pdus[0].length = tb_size;
|
|
||||||
|
|
||||||
if (dl_ind.pdus[0].length >= N_bytes) {
|
|
||||||
// copy TV
|
|
||||||
memcpy(dl_ind.pdus[0].data, tv, N_bytes);
|
|
||||||
tot_bytes = N_bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(dl_ind.pdus[0].data, N_bytes, "Sending to UE a TB (%d bytes)", N_bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tot_bytes > 0 and tot_bytes < tb_size) {
|
|
||||||
uint8_t* offset = &dl_ind.pdus[dl_ind.nof_pdus - 1].data[dl_ind.pdus[dl_ind.nof_pdus - 1].length];
|
|
||||||
memset(offset, 0xaa, tb_size - tot_bytes);
|
|
||||||
} else if (tot_bytes == 0) {
|
|
||||||
// just fill with dummy bytes
|
|
||||||
dl_ind.nof_pdus = 1;
|
|
||||||
memset(dl_ind.pdus[0].data, 0xab, tb_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
int n = 0;
|
|
||||||
if ((n = sendto(sockfd, &dl_ind, sizeof(dl_ind), 0, (struct sockaddr*)&servaddr, sizeof(servaddr))) < 0) {
|
|
||||||
printf("sendto failed, ret=%d\n", n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void send_ul_ind(uint32_t tti_)
|
|
||||||
{
|
|
||||||
basic_vnf_api::ul_ind_msg_t ul_ind = {};
|
|
||||||
|
|
||||||
ul_ind.header.type = basic_vnf_api::UL_IND;
|
|
||||||
ul_ind.header.msg_len = sizeof(ul_ind) - sizeof(basic_vnf_api::msg_header_t);
|
|
||||||
ul_ind.tti = tti_;
|
|
||||||
ul_ind.t1 = 0;
|
|
||||||
|
|
||||||
ul_ind.pdus.type = basic_vnf_api::PUSCH;
|
|
||||||
ul_ind.pdus.length = tb_len > 0 ? tb_len : rand_dist(rand_gen);
|
|
||||||
|
|
||||||
int n = 0;
|
|
||||||
if ((n = sendto(sockfd, &ul_ind, sizeof(ul_ind), 0, (struct sockaddr*)&servaddr, sizeof(servaddr))) < 0) {
|
|
||||||
printf("sendto failed, ret=%d\n", n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<std::thread> tx_thread, rx_thread;
|
|
||||||
std::string tx_thread_name = "TX_PNF", rx_thread_name = "RX_PNF";
|
|
||||||
bool running = false;
|
|
||||||
srslog::basic_logger& logger = srslog::fetch_basic_logger("PNF", false);
|
|
||||||
|
|
||||||
std::mutex mutex;
|
|
||||||
std::atomic<std::uint32_t> tti;
|
|
||||||
std::chrono::steady_clock::time_point tti_start_time;
|
|
||||||
|
|
||||||
std::string type;
|
|
||||||
|
|
||||||
std::string vnf_addr;
|
|
||||||
uint16_t vnf_port = 3333;
|
|
||||||
|
|
||||||
uint32_t sf_interval_us = 1000;
|
|
||||||
int32_t num_sf = -1;
|
|
||||||
uint32_t tb_len = 100;
|
|
||||||
|
|
||||||
pnf_metrics_t metrics = {};
|
|
||||||
|
|
||||||
int sockfd = 0;
|
|
||||||
struct sockaddr_in servaddr = {};
|
|
||||||
|
|
||||||
// For random number generation
|
|
||||||
std::mt19937 rand_gen;
|
|
||||||
std::uniform_int_distribution<uint16_t> rand_dist;
|
|
||||||
|
|
||||||
// two policies possible: dummy packets generated by the PNF class, or bridge between UE and gNB PNFs with TBs
|
|
||||||
// entering/exiting each PNF via the rf_in_queue/rf_out_queue.
|
|
||||||
srsran::block_queue<srsran::unique_byte_buffer_t>* rf_out_queue = nullptr;
|
|
||||||
srsran::block_queue<srsran::unique_byte_buffer_t> rf_in_queue;
|
|
||||||
enum data_policy_t { self_gen, bridge } policy = self_gen;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace srsran
|
|
||||||
|
|
||||||
#endif // SRSRAN_BASIC_PNF_H
|
|
@ -1,90 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2013-2021 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* This file is part of srsRAN.
|
|
||||||
*
|
|
||||||
* srsRAN is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsRAN is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SRSRAN_BASIC_VNF_H
|
|
||||||
#define SRSRAN_BASIC_VNF_H
|
|
||||||
|
|
||||||
#include "basic_vnf_api.h"
|
|
||||||
#include "common.h"
|
|
||||||
#include "srsran/common/threads.h"
|
|
||||||
#include "srsran/interfaces/gnb_interfaces.h"
|
|
||||||
#include "srsran/interfaces/ue_nr_interfaces.h"
|
|
||||||
#include "srsran/srslog/srslog.h"
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <random>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <thread>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
namespace srsran {
|
|
||||||
|
|
||||||
class srsran_basic_vnf : public thread
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
srsran_basic_vnf(const vnf_args_t& args_, stack_interface_phy_nr* stack_);
|
|
||||||
~srsran_basic_vnf();
|
|
||||||
|
|
||||||
bool stop();
|
|
||||||
|
|
||||||
int dl_config_request(const srsenb::phy_interface_stack_nr::dl_config_request_t& request);
|
|
||||||
int tx_request(const srsenb::phy_interface_stack_nr::tx_request_t& request);
|
|
||||||
int tx_request(const srsue::phy_interface_stack_nr::tx_request_t& request);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void run_thread();
|
|
||||||
|
|
||||||
// handlers
|
|
||||||
int handle_msg(const uint8_t* buffer, const uint32_t len);
|
|
||||||
int handle_sf_ind(basic_vnf_api::sf_ind_msg_t* msg);
|
|
||||||
int handle_dl_ind(basic_vnf_api::dl_ind_msg_t* msg);
|
|
||||||
int handle_ul_ind(basic_vnf_api::ul_ind_msg_t* msg);
|
|
||||||
int handle_rx_data_ind(basic_vnf_api::rx_data_ind_msg_t* msg);
|
|
||||||
|
|
||||||
// senders
|
|
||||||
int send_dl_config_request();
|
|
||||||
|
|
||||||
// helpers
|
|
||||||
uint32_t calc_full_msg_len(const basic_vnf_api::tx_request_msg_t& msg);
|
|
||||||
|
|
||||||
srslog::basic_logger& logger = srslog::fetch_basic_logger("VNF", false);
|
|
||||||
srsenb::stack_interface_phy_nr* m_gnb_stack = nullptr;
|
|
||||||
srsue::stack_interface_phy_nr* m_ue_stack = nullptr;
|
|
||||||
|
|
||||||
std::unique_ptr<basic_vnf_api::tx_request_msg_t> m_tx_req_msg;
|
|
||||||
|
|
||||||
bool running = false;
|
|
||||||
|
|
||||||
vnf_args_t m_args = {};
|
|
||||||
|
|
||||||
int sockfd = 0;
|
|
||||||
struct sockaddr_in servaddr = {}, client_addr = {};
|
|
||||||
|
|
||||||
uint32_t last_sf_indication_time = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace srsran
|
|
||||||
|
|
||||||
#endif // SRSRAN_BASIC_VNF_H
|
|
@ -1,168 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2013-2021 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* This file is part of srsRAN.
|
|
||||||
*
|
|
||||||
* srsRAN is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsRAN is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SRSRAN_BASIC_VNF_API_H
|
|
||||||
#define SRSRAN_BASIC_VNF_API_H
|
|
||||||
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
namespace basic_vnf_api {
|
|
||||||
|
|
||||||
// PNF (the PHY) VNF (the L2/L3)
|
|
||||||
// | |
|
|
||||||
// | |
|
|
||||||
// | - |
|
|
||||||
// | \ sf_ind_msg_t
|
|
||||||
// | \ |
|
|
||||||
// | \ |
|
|
||||||
// | \ |
|
|
||||||
// | \ |
|
|
||||||
// | \ |
|
|
||||||
// | \|
|
|
||||||
// | |
|
|
||||||
// | | DL_CONFIG.request
|
|
||||||
// | /|
|
|
||||||
// | / |
|
|
||||||
// | / |
|
|
||||||
// | / |
|
|
||||||
// | / |
|
|
||||||
// | / dl_conf_msg_t
|
|
||||||
// | / |
|
|
||||||
// |/ | TX.request
|
|
||||||
// | /|
|
|
||||||
// | / |
|
|
||||||
// | / |
|
|
||||||
// | / |
|
|
||||||
// | / |
|
|
||||||
// | / tx_request_msg_t
|
|
||||||
// | / |
|
|
||||||
// |/ |
|
|
||||||
// | |
|
|
||||||
|
|
||||||
// Primitive API messages for basic testing basic VNF/PNF interaction
|
|
||||||
enum msg_type_t {
|
|
||||||
SF_IND, ///< To signal start of new subframe (later slot) for both UE and gNB
|
|
||||||
DL_CONFIG, ///< To configure the DL for gNB
|
|
||||||
TX_REQUEST, ///< For DL data for gNB
|
|
||||||
RX_DATA_IND, ///< For UL Data for gNB
|
|
||||||
DL_IND, ///< For the UE for DL data
|
|
||||||
UL_IND, ///< For the UE for UL Data
|
|
||||||
MSG_TYPE_NITEMS
|
|
||||||
};
|
|
||||||
static const char* msg_type_text[MSG_TYPE_NITEMS] =
|
|
||||||
{"SF Indication", "DL_CONFIG.Request", "TX.Request", "RX_Data.indication", "DL_Indication", "UL_Indication"};
|
|
||||||
enum pdu_type_t { MAC_PBCH, PHY_PBCH, PDCCH, PDSCH, PUSCH };
|
|
||||||
|
|
||||||
struct msg_header_t {
|
|
||||||
msg_type_t type;
|
|
||||||
uint32_t msg_len;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sf_ind_msg_t {
|
|
||||||
msg_header_t header;
|
|
||||||
uint32_t t1; // Timestamp taken at PNF
|
|
||||||
uint32_t tti; // TTI of requested subframe
|
|
||||||
uint32_t tb_len; // Length of the TB
|
|
||||||
};
|
|
||||||
|
|
||||||
#define MAX_TB_LEN (16 * 1024)
|
|
||||||
#define MAX_PDU_SIZE (16 * 1024)
|
|
||||||
#define MAX_NUM_PDUS (1)
|
|
||||||
|
|
||||||
struct phy_pbch_pdu_t {
|
|
||||||
uint16_t phy_cell_id; // 0 - 1007
|
|
||||||
uint8_t ss_block_index; // 0-63
|
|
||||||
uint8_t ssb_sc_offset; // 0-15
|
|
||||||
uint8_t dmrs_pos; // 0-1
|
|
||||||
uint8_t pdcch_config; // 0-255
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dl_conf_msg_t {
|
|
||||||
msg_header_t header;
|
|
||||||
uint32_t t1; // Replayed timestamp
|
|
||||||
uint32_t t2; // Timestamp taken at VNF
|
|
||||||
uint32_t tti; // TTI
|
|
||||||
uint16_t beam_id; // tx beam id for the slot
|
|
||||||
};
|
|
||||||
|
|
||||||
struct tx_request_pdu_t {
|
|
||||||
uint16_t length;
|
|
||||||
uint16_t index; // index indicated in dl_config
|
|
||||||
pdu_type_t type; // physical chan of pdu/tb
|
|
||||||
uint8_t data[MAX_PDU_SIZE];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct tx_request_msg_t {
|
|
||||||
msg_header_t header;
|
|
||||||
uint32_t tti; // TTI
|
|
||||||
uint32_t tb_len; // actual TB len
|
|
||||||
uint32_t nof_pdus;
|
|
||||||
tx_request_pdu_t pdus[MAX_NUM_PDUS];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rx_data_ind_pdu_t {
|
|
||||||
uint16_t length;
|
|
||||||
pdu_type_t type; // physical chan of pdu/tb
|
|
||||||
uint8_t data[MAX_PDU_SIZE];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rx_data_ind_msg_t {
|
|
||||||
msg_header_t header;
|
|
||||||
uint32_t t1; // Timestamp taken at PNF
|
|
||||||
uint32_t sfn; ///< SFN (0-1023)
|
|
||||||
uint32_t slot; ///< Slot (0-319)
|
|
||||||
uint32_t tb_len; ///< actual TB len
|
|
||||||
uint32_t nof_pdus; //
|
|
||||||
rx_data_ind_pdu_t pdus[MAX_NUM_PDUS];
|
|
||||||
};
|
|
||||||
|
|
||||||
// UE specific messages
|
|
||||||
struct dl_ind_pdu_t {
|
|
||||||
pdu_type_t type; // physical chan of pdu/tb
|
|
||||||
uint16_t length;
|
|
||||||
uint8_t data[MAX_PDU_SIZE];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dl_ind_msg_t {
|
|
||||||
msg_header_t header;
|
|
||||||
uint32_t t1; // Timestamp taken at PNF
|
|
||||||
uint32_t tti; // tti or slot?
|
|
||||||
uint32_t nof_pdus;
|
|
||||||
dl_ind_pdu_t pdus[MAX_NUM_PDUS];
|
|
||||||
};
|
|
||||||
|
|
||||||
///< Messages for UL (only one PDU)
|
|
||||||
struct ul_ind_pdu_t {
|
|
||||||
pdu_type_t type; // physical chan of pdu/tb
|
|
||||||
uint16_t length;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ul_ind_msg_t {
|
|
||||||
msg_header_t header;
|
|
||||||
uint32_t t1; // Timestamp taken at PNF
|
|
||||||
uint32_t tti; // tti or slot?
|
|
||||||
uint32_t rnti; ///< RNTI of this grant
|
|
||||||
ul_ind_pdu_t pdus;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace basic_vnf_api
|
|
||||||
|
|
||||||
#endif // SRSRAN_BASIC_VNF_API_H
|
|
@ -1,408 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2013-2021 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* This file is part of srsRAN.
|
|
||||||
*
|
|
||||||
* srsRAN is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsRAN is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "srsran/common/basic_vnf.h"
|
|
||||||
#include "srsran/common/buffer_pool.h"
|
|
||||||
#include "srsran/interfaces/ue_nr_interfaces.h"
|
|
||||||
#include <algorithm>
|
|
||||||
#include <chrono>
|
|
||||||
#include <poll.h>
|
|
||||||
|
|
||||||
#define RAND_SEED (12314)
|
|
||||||
#define RX_TIMEOUT_MS (1000)
|
|
||||||
|
|
||||||
namespace srsran {
|
|
||||||
|
|
||||||
struct srsran_pnf_info_t {
|
|
||||||
// TODO: fill when needed
|
|
||||||
};
|
|
||||||
|
|
||||||
struct srsran_vnf_info_t {};
|
|
||||||
|
|
||||||
srsran_basic_vnf::srsran_basic_vnf(const vnf_args_t& args_, stack_interface_phy_nr* stack_) :
|
|
||||||
m_args(args_), thread("BASIC_VNF_P7"), m_tx_req_msg(new basic_vnf_api::tx_request_msg_t)
|
|
||||||
{
|
|
||||||
logger.set_level(srslog::str_to_basic_level(m_args.log_level));
|
|
||||||
logger.set_hex_dump_max_size(m_args.log_hex_limit);
|
|
||||||
|
|
||||||
if (m_args.type == "gnb" || m_args.type == "ue") {
|
|
||||||
if (m_args.type == "gnb") {
|
|
||||||
m_gnb_stack = (srsenb::stack_interface_phy_nr*)stack_;
|
|
||||||
} else {
|
|
||||||
m_ue_stack = (srsue::stack_interface_phy_nr*)stack_;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("Initializing VNF for gNB");
|
|
||||||
start();
|
|
||||||
} else {
|
|
||||||
logger.error("Unknown VNF type. Exiting.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
srsran_basic_vnf::~srsran_basic_vnf()
|
|
||||||
{
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void srsran_basic_vnf::run_thread()
|
|
||||||
{
|
|
||||||
// Bind to UDP socket
|
|
||||||
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
||||||
if (sockfd < 0) {
|
|
||||||
perror("socket");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sockets reusable
|
|
||||||
int enable = 1;
|
|
||||||
#if defined(SO_REUSEADDR)
|
|
||||||
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) {
|
|
||||||
perror("setsockopt(SO_REUSEADDR) failed");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if defined(SO_REUSEPORT)
|
|
||||||
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) {
|
|
||||||
perror("setsockopt(SO_REUSEPORT) failed");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
servaddr.sin_family = AF_INET;
|
|
||||||
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
||||||
servaddr.sin_port = htons(m_args.bind_port);
|
|
||||||
|
|
||||||
if (bind(sockfd, (struct sockaddr*)&servaddr, sizeof(struct sockaddr_in))) {
|
|
||||||
perror("bind");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct pollfd fd;
|
|
||||||
fd.fd = sockfd;
|
|
||||||
fd.events = POLLIN;
|
|
||||||
|
|
||||||
const uint32_t max_basic_api_pdu = sizeof(basic_vnf_api::dl_ind_msg_t) + 32; // larger than biggest message
|
|
||||||
std::unique_ptr<std::array<uint8_t, max_basic_api_pdu> > rx_buffer =
|
|
||||||
std::unique_ptr<std::array<uint8_t, max_basic_api_pdu> >(new std::array<uint8_t, max_basic_api_pdu>);
|
|
||||||
|
|
||||||
running = true;
|
|
||||||
|
|
||||||
logger.info("Started VNF handler listening on %s:%d", m_args.bind_addr.c_str(), m_args.bind_port);
|
|
||||||
|
|
||||||
while (running) {
|
|
||||||
int ret = poll(&fd, 1, RX_TIMEOUT_MS);
|
|
||||||
switch (ret) {
|
|
||||||
case -1:
|
|
||||||
printf("Error occured.\n");
|
|
||||||
break;
|
|
||||||
case 0:
|
|
||||||
// Timeout
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
|
|
||||||
socklen_t len = sizeof(client_addr);
|
|
||||||
ret = recvfrom(sockfd, rx_buffer->data(), rx_buffer->size(), MSG_WAITALL, (struct sockaddr*)&client_addr, &len);
|
|
||||||
|
|
||||||
handle_msg(rx_buffer->data(), ret);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.info("VNF thread stopped");
|
|
||||||
}
|
|
||||||
|
|
||||||
int srsran_basic_vnf::handle_msg(const uint8_t* buffer, const uint32_t len)
|
|
||||||
{
|
|
||||||
basic_vnf_api::msg_header_t* header = (basic_vnf_api::msg_header_t*)buffer;
|
|
||||||
|
|
||||||
logger.info("Received %s (%d B)", basic_vnf_api::msg_type_text[header->type], len);
|
|
||||||
|
|
||||||
switch (header->type) {
|
|
||||||
case basic_vnf_api::SF_IND:
|
|
||||||
handle_sf_ind((basic_vnf_api::sf_ind_msg_t*)header);
|
|
||||||
break;
|
|
||||||
case basic_vnf_api::DL_CONFIG:
|
|
||||||
printf("Error: %s not handled by VNF\n", basic_vnf_api::msg_type_text[header->type]);
|
|
||||||
break;
|
|
||||||
case basic_vnf_api::DL_IND:
|
|
||||||
handle_dl_ind((basic_vnf_api::dl_ind_msg_t*)header);
|
|
||||||
break;
|
|
||||||
case basic_vnf_api::UL_IND:
|
|
||||||
handle_ul_ind((basic_vnf_api::ul_ind_msg_t*)header);
|
|
||||||
break;
|
|
||||||
case basic_vnf_api::RX_DATA_IND:
|
|
||||||
handle_rx_data_ind((basic_vnf_api::rx_data_ind_msg_t*)header);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf("Unknown msg type.\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int srsran_basic_vnf::handle_sf_ind(basic_vnf_api::sf_ind_msg_t* msg)
|
|
||||||
{
|
|
||||||
int ret = SRSRAN_SUCCESS;
|
|
||||||
logger.info("Received %s for TTI=%d", basic_vnf_api::msg_type_text[msg->header.type], msg->tti);
|
|
||||||
|
|
||||||
// store Rx timestamp
|
|
||||||
last_sf_indication_time = msg->t1;
|
|
||||||
|
|
||||||
if (m_gnb_stack != nullptr) {
|
|
||||||
srsran_slot_cfg_t slot_cfg = {};
|
|
||||||
slot_cfg.idx = msg->tti;
|
|
||||||
m_gnb_stack->slot_indication(slot_cfg);
|
|
||||||
} else if (m_ue_stack != nullptr) {
|
|
||||||
m_ue_stack->sf_indication(msg->tti);
|
|
||||||
} else {
|
|
||||||
ret = SRSRAN_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int srsran_basic_vnf::handle_dl_ind(basic_vnf_api::dl_ind_msg_t* msg)
|
|
||||||
{
|
|
||||||
int ret = SRSRAN_ERROR;
|
|
||||||
logger.info("Received %s for TTI=%d", basic_vnf_api::msg_type_text[msg->header.type], msg->tti);
|
|
||||||
|
|
||||||
uint32_t cc_idx = 0;
|
|
||||||
|
|
||||||
// fill DL struct
|
|
||||||
srsue::stack_interface_phy_nr::mac_nr_grant_dl_t dl_grant = {};
|
|
||||||
dl_grant.tti = msg->tti;
|
|
||||||
|
|
||||||
if (msg->nof_pdus > SRSRAN_MAX_TB) {
|
|
||||||
logger.error("Too many TBs (%d > %d)", msg->nof_pdus, SRSRAN_MAX_TB);
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < msg->nof_pdus; ++i) {
|
|
||||||
srsue::stack_interface_phy_nr::tb_action_dl_result_t result = {};
|
|
||||||
result.payload = srsran::make_byte_buffer();
|
|
||||||
if (result.payload != nullptr && result.payload->get_tailroom() >= msg->pdus[i].length) {
|
|
||||||
result.ack = true;
|
|
||||||
memcpy(result.payload->msg, msg->pdus[i].data, msg->pdus[i].length);
|
|
||||||
result.payload->N_bytes = msg->pdus[i].length;
|
|
||||||
if (msg->pdus[i].type == basic_vnf_api::PDSCH) {
|
|
||||||
m_ue_stack->tb_decoded(cc_idx, dl_grant, std::move(result));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.error("TB too big to fit into buffer (%d)", msg->pdus[i].length);
|
|
||||||
return SRSRAN_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = SRSRAN_SUCCESS;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int srsran_basic_vnf::handle_ul_ind(basic_vnf_api::ul_ind_msg_t* msg)
|
|
||||||
{
|
|
||||||
logger.info("Received %s for TTI=%d", basic_vnf_api::msg_type_text[msg->header.type], msg->tti);
|
|
||||||
|
|
||||||
if (msg->pdus.type != basic_vnf_api::PUSCH) {
|
|
||||||
logger.error("Received UL indication for wrong PDU type");
|
|
||||||
return SRSRAN_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t cc_idx = 0;
|
|
||||||
|
|
||||||
// fill DL struct
|
|
||||||
srsue::stack_interface_phy_nr::mac_nr_grant_ul_t ul_grant = {};
|
|
||||||
ul_grant.tti = msg->tti;
|
|
||||||
ul_grant.tbs = msg->pdus.length;
|
|
||||||
ul_grant.rnti = msg->rnti;
|
|
||||||
|
|
||||||
srsue::stack_interface_phy_nr::tb_action_ul_t ul_action = {};
|
|
||||||
m_ue_stack->new_grant_ul(cc_idx, ul_grant, &ul_action);
|
|
||||||
|
|
||||||
return SRSRAN_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int srsran_basic_vnf::handle_rx_data_ind(basic_vnf_api::rx_data_ind_msg_t* msg)
|
|
||||||
{
|
|
||||||
logger.info("Received %s for TTI=%d", basic_vnf_api::msg_type_text[msg->header.type], msg->sfn);
|
|
||||||
|
|
||||||
if (msg->nof_pdus != 1 || msg->pdus[0].type != basic_vnf_api::PUSCH) {
|
|
||||||
logger.error("Received UL indication for wrong PDU type");
|
|
||||||
return SRSRAN_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill struct
|
|
||||||
srsenb::stack_interface_phy_nr::rx_data_ind_t rx_data = {};
|
|
||||||
rx_data.tti = msg->sfn;
|
|
||||||
rx_data.tb = srsran::make_byte_buffer();
|
|
||||||
if (rx_data.tb->get_tailroom() >= msg->pdus[0].length) {
|
|
||||||
// copy actual data
|
|
||||||
memcpy(rx_data.tb->msg, msg->pdus[0].data, msg->pdus[0].length);
|
|
||||||
rx_data.tb->N_bytes = msg->pdus[0].length;
|
|
||||||
if (msg->pdus[0].type == basic_vnf_api::PUSCH) {
|
|
||||||
m_gnb_stack->rx_data_indication(rx_data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return SRSRAN_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int srsran_basic_vnf::dl_config_request(const srsenb::phy_interface_stack_nr::dl_config_request_t& request)
|
|
||||||
{
|
|
||||||
// Generate DL Config
|
|
||||||
basic_vnf_api::dl_conf_msg_t dl_conf = {};
|
|
||||||
dl_conf.header.type = basic_vnf_api::DL_CONFIG;
|
|
||||||
dl_conf.header.msg_len = sizeof(dl_conf) - sizeof(basic_vnf_api::msg_header_t);
|
|
||||||
|
|
||||||
dl_conf.t1 = last_sf_indication_time; // play back the time
|
|
||||||
dl_conf.t2 = 0xaa; // TODO: add timestamp
|
|
||||||
dl_conf.tti = request.tti;
|
|
||||||
dl_conf.beam_id = request.beam_id;
|
|
||||||
|
|
||||||
// Send entire struct
|
|
||||||
uint32_t len = sizeof(dl_conf);
|
|
||||||
|
|
||||||
// Send it to PNF
|
|
||||||
logger.info("Sending %s (%d B)", basic_vnf_api::msg_type_text[dl_conf.header.type], len);
|
|
||||||
int n = 0;
|
|
||||||
if ((n = sendto(sockfd, &dl_conf, len, MSG_CONFIRM, (struct sockaddr*)&client_addr, sizeof(client_addr))) < 0) {
|
|
||||||
logger.error("sendto failed, ret=%d", n);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tx request from UE, i.e. UL transmission
|
|
||||||
int srsran_basic_vnf::tx_request(const srsue::phy_interface_stack_nr::tx_request_t& request)
|
|
||||||
{
|
|
||||||
// Generate Tx request
|
|
||||||
m_tx_req_msg->header.type = basic_vnf_api::TX_REQUEST;
|
|
||||||
m_tx_req_msg->header.msg_len = 0; // set further down
|
|
||||||
|
|
||||||
m_tx_req_msg->tti = request.tti;
|
|
||||||
|
|
||||||
m_tx_req_msg->nof_pdus = 1;
|
|
||||||
m_tx_req_msg->pdus[0].index = 0;
|
|
||||||
m_tx_req_msg->pdus[0].type = basic_vnf_api::PUSCH;
|
|
||||||
m_tx_req_msg->pdus[0].length = request.tb_len;
|
|
||||||
|
|
||||||
if (request.tb_len <= MAX_PDU_SIZE) {
|
|
||||||
// copy data from TB0
|
|
||||||
memcpy(m_tx_req_msg->pdus[0].data, request.data, request.tb_len);
|
|
||||||
} else {
|
|
||||||
logger.error("Trying to send %d B PDU. Maximum size is %d B", request.tb_len, MAX_PDU_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate actual length of
|
|
||||||
uint32_t len = calc_full_msg_len(*m_tx_req_msg.get());
|
|
||||||
|
|
||||||
// update msg header length field
|
|
||||||
m_tx_req_msg->header.msg_len = len - sizeof(basic_vnf_api::msg_header_t);
|
|
||||||
|
|
||||||
// Send it to PNF
|
|
||||||
logger.info("Sending %s (%d B)", basic_vnf_api::msg_type_text[m_tx_req_msg->header.type], len);
|
|
||||||
int n = 0;
|
|
||||||
if ((n = sendto(sockfd, m_tx_req_msg.get(), len, MSG_CONFIRM, (struct sockaddr*)&client_addr, sizeof(client_addr))) <
|
|
||||||
0) {
|
|
||||||
logger.error("sendto failed, ret=%d", n);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int srsran_basic_vnf::tx_request(const srsenb::phy_interface_stack_nr::tx_request_t& request)
|
|
||||||
{
|
|
||||||
if (request.nof_pdus > MAX_NUM_PDUS) {
|
|
||||||
logger.error("Trying to send %d PDUs but only %d supported", request.nof_pdus, MAX_NUM_PDUS);
|
|
||||||
return SRSRAN_ERROR;
|
|
||||||
}
|
|
||||||
if (request.nof_pdus == 0) {
|
|
||||||
return SRSRAN_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate Tx request
|
|
||||||
m_tx_req_msg->header.type = basic_vnf_api::TX_REQUEST;
|
|
||||||
m_tx_req_msg->header.msg_len = 0; // set further down
|
|
||||||
|
|
||||||
m_tx_req_msg->nof_pdus = request.nof_pdus;
|
|
||||||
m_tx_req_msg->tti = request.tti;
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < m_tx_req_msg->nof_pdus; ++i) {
|
|
||||||
if (request.pdus[i].length <= MAX_PDU_SIZE) {
|
|
||||||
m_tx_req_msg->pdus[i].index = i;
|
|
||||||
m_tx_req_msg->pdus[i].type = request.pdus[i].pbch.mib_present ? basic_vnf_api::MAC_PBCH : basic_vnf_api::PDSCH;
|
|
||||||
m_tx_req_msg->pdus[i].length = request.pdus[i].length;
|
|
||||||
// copy data from TB0
|
|
||||||
memcpy(m_tx_req_msg->pdus[i].data, request.pdus[i].data[0], m_tx_req_msg->pdus[i].length);
|
|
||||||
} else {
|
|
||||||
logger.error("Trying to send %d B PDU. Maximum size is %d B", request.pdus[i].length, MAX_PDU_SIZE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate actual length of message
|
|
||||||
uint32_t len = calc_full_msg_len(*m_tx_req_msg.get());
|
|
||||||
|
|
||||||
// update msg header length field
|
|
||||||
m_tx_req_msg->header.msg_len = len - sizeof(basic_vnf_api::msg_header_t);
|
|
||||||
|
|
||||||
// Send it to PNF
|
|
||||||
logger.info("Sending %s (%d B)", basic_vnf_api::msg_type_text[m_tx_req_msg->header.type], len);
|
|
||||||
if (logger.debug.enabled()) {
|
|
||||||
for (uint32_t i = 0; i < m_tx_req_msg->nof_pdus; ++i) {
|
|
||||||
logger.debug(m_tx_req_msg->pdus[i].data,
|
|
||||||
m_tx_req_msg->pdus[i].length,
|
|
||||||
"Sending PDU %s:%d (%d bytes)",
|
|
||||||
basic_vnf_api::msg_type_text[m_tx_req_msg->header.type],
|
|
||||||
m_tx_req_msg->pdus[i].index,
|
|
||||||
m_tx_req_msg->pdus[i].length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int n = 0;
|
|
||||||
if ((n = sendto(sockfd, m_tx_req_msg.get(), len, MSG_CONFIRM, (struct sockaddr*)&client_addr, sizeof(client_addr))) <
|
|
||||||
0) {
|
|
||||||
logger.error("sendto failed, ret=%d", n);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t srsran_basic_vnf::calc_full_msg_len(const basic_vnf_api::tx_request_msg_t& msg)
|
|
||||||
{
|
|
||||||
// start with mandatory part
|
|
||||||
uint32_t len = sizeof(basic_vnf_api::msg_header_t) + 3 * sizeof(uint32_t);
|
|
||||||
|
|
||||||
// add all PDUs
|
|
||||||
for (uint32_t i = 0; i < msg.nof_pdus; ++i) {
|
|
||||||
len += 2 * sizeof(uint16_t) + sizeof(basic_vnf_api::pdu_type_t) + msg.pdus[i].length;
|
|
||||||
}
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool srsran_basic_vnf::stop()
|
|
||||||
{
|
|
||||||
if (running) {
|
|
||||||
running = false;
|
|
||||||
wait_thread_finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace srsran
|
|
@ -0,0 +1,97 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 SRSLOG_SYSLOG_SINK_H
|
||||||
|
#define SRSLOG_SYSLOG_SINK_H
|
||||||
|
|
||||||
|
#include "srsran/srslog/shared_types.h"
|
||||||
|
#include "srsran/srslog/sink.h"
|
||||||
|
#include <syslog.h>
|
||||||
|
|
||||||
|
namespace srslog {
|
||||||
|
|
||||||
|
/// This sink implementation writes to syslog.
|
||||||
|
class syslog_sink : public sink
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
syslog_sink(std::unique_ptr<log_formatter> f,
|
||||||
|
std::string preamble_ = "",
|
||||||
|
syslog_local_type log_local_ = syslog_local_type::local0) :
|
||||||
|
sink(std::move(f))
|
||||||
|
{
|
||||||
|
create_syslog(preamble_, syslog_translate(log_local_));
|
||||||
|
}
|
||||||
|
|
||||||
|
syslog_sink(const syslog_sink& other) = delete;
|
||||||
|
syslog_sink& operator=(const syslog_sink& other) = delete;
|
||||||
|
|
||||||
|
detail::error_string write(detail::memory_buffer buffer) override
|
||||||
|
{
|
||||||
|
std::string entry(buffer.data(), buffer.size());
|
||||||
|
if (entry.find("[E]") != std::string::npos) {
|
||||||
|
syslog(LOG_ERR, "%s", buffer.data());
|
||||||
|
} else if (entry.find("[W]") != std::string::npos) {
|
||||||
|
syslog(LOG_WARNING, "%s", buffer.data());
|
||||||
|
} else if (entry.find("[I]") != std::string::npos) {
|
||||||
|
syslog(LOG_INFO, "%s", buffer.data());
|
||||||
|
} else if (entry.find("[D]") != std::string::npos) {
|
||||||
|
syslog(LOG_DEBUG, "%s", buffer.data());
|
||||||
|
} else {
|
||||||
|
syslog(LOG_ERR, "%s", buffer.data());
|
||||||
|
}
|
||||||
|
// openlog syslog does not return any value.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
detail::error_string flush() override { return {}; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Creates a new syslog
|
||||||
|
detail::error_string create_syslog(std::string preamble, int log_local)
|
||||||
|
{
|
||||||
|
if (preamble == "") {
|
||||||
|
openlog(NULL, LOG_CONS | LOG_PID | LOG_NDELAY, log_local);
|
||||||
|
} else {
|
||||||
|
openlog(preamble.c_str(), LOG_CONS | LOG_PID | LOG_NDELAY, log_local);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static int syslog_translate(syslog_local_type log_local)
|
||||||
|
{
|
||||||
|
switch (log_local) {
|
||||||
|
case syslog_local_type::local0:
|
||||||
|
return LOG_LOCAL0;
|
||||||
|
case syslog_local_type::local1:
|
||||||
|
return LOG_LOCAL1;
|
||||||
|
case syslog_local_type::local2:
|
||||||
|
return LOG_LOCAL2;
|
||||||
|
case syslog_local_type::local3:
|
||||||
|
return LOG_LOCAL3;
|
||||||
|
case syslog_local_type::local4:
|
||||||
|
return LOG_LOCAL4;
|
||||||
|
case syslog_local_type::local5:
|
||||||
|
return LOG_LOCAL5;
|
||||||
|
case syslog_local_type::local6:
|
||||||
|
return LOG_LOCAL6;
|
||||||
|
case syslog_local_type::local7:
|
||||||
|
return LOG_LOCAL7;
|
||||||
|
default:
|
||||||
|
return LOG_LOCAL0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace srslog
|
||||||
|
|
||||||
|
#endif // SRSLOG_SYSLOG_SINK_H
|
@ -1,127 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2013-2021 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* This file is part of srsRAN.
|
|
||||||
*
|
|
||||||
* srsRAN is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsRAN is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <boost/program_options.hpp>
|
|
||||||
#include <boost/program_options/parsers.hpp>
|
|
||||||
#include <chrono>
|
|
||||||
#include <iostream>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include "srsran/common/basic_pnf.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
namespace bpo = boost::program_options;
|
|
||||||
|
|
||||||
struct pnf_args_t {
|
|
||||||
std::string gnb_vnf_addr;
|
|
||||||
std::string ue_vnf_addr;
|
|
||||||
uint16_t gnb_vnf_port;
|
|
||||||
uint16_t ue_vnf_port;
|
|
||||||
uint32_t sf_interval;
|
|
||||||
int32_t num_sf;
|
|
||||||
uint32_t tb_len;
|
|
||||||
};
|
|
||||||
|
|
||||||
void parse_args(pnf_args_t* args, int argc, char* argv[])
|
|
||||||
{
|
|
||||||
// Command line only options
|
|
||||||
bpo::options_description general("General options");
|
|
||||||
|
|
||||||
general.add_options()("help,h", "Produce help message")("version,v", "Print version information and exit");
|
|
||||||
|
|
||||||
// Command line or config file options
|
|
||||||
bpo::options_description common("Configuration options");
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
common.add_options()
|
|
||||||
("vnf.gnb_addr", bpo::value<string>(&args->gnb_vnf_addr)->default_value("127.0.0.1"), "VNF address")
|
|
||||||
("vnf.ue_addr", bpo::value<string>(&args->ue_vnf_addr)->default_value("127.0.0.1"), "VNF address")
|
|
||||||
("vnf.gnb_port", bpo::value<uint16_t>(&args->gnb_vnf_port)->default_value(3333), "gNB VNF port")
|
|
||||||
("vnf.ue_port", bpo::value<uint16_t>(&args->ue_vnf_port)->default_value(3334), "UE VNF port")
|
|
||||||
("sf_interval", bpo::value<uint32_t>(&args->sf_interval)->default_value(1000), "Interval between subframes in us")
|
|
||||||
("num_sf", bpo::value<int32_t>(&args->num_sf)->default_value(-1), "Number of subframes to signal (-1 infinity)")
|
|
||||||
("tb_len", bpo::value<uint32_t>(&args->tb_len)->default_value(1600), "TB lenth (0 for random size)");
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
// these options are allowed on the command line
|
|
||||||
bpo::options_description cmdline_options;
|
|
||||||
cmdline_options.add(common).add(general);
|
|
||||||
|
|
||||||
// parse the command line and store result in vm
|
|
||||||
bpo::variables_map vm;
|
|
||||||
bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).run(), vm);
|
|
||||||
bpo::notify(vm);
|
|
||||||
|
|
||||||
// help option was given - print usage and exit
|
|
||||||
if (vm.count("help")) {
|
|
||||||
cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl;
|
|
||||||
cout << common << endl << general << endl;
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool running = true;
|
|
||||||
void sig_int_handler(int signo)
|
|
||||||
{
|
|
||||||
printf("SIGINT received. Exiting...\n");
|
|
||||||
if (signo == SIGINT) {
|
|
||||||
running = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
signal(SIGINT, sig_int_handler);
|
|
||||||
|
|
||||||
pnf_args_t args;
|
|
||||||
parse_args(&args, argc, argv);
|
|
||||||
|
|
||||||
srsran::srsran_basic_pnf ue_pnf("ue", args.ue_vnf_addr, args.ue_vnf_port, args.sf_interval, args.num_sf, args.tb_len);
|
|
||||||
srsran::srsran_basic_pnf gnb_pnf(
|
|
||||||
"gnb", args.gnb_vnf_addr, args.gnb_vnf_port, args.sf_interval, args.num_sf, args.tb_len);
|
|
||||||
|
|
||||||
gnb_pnf.connect_out_rf_queue(ue_pnf.get_in_rf_queue());
|
|
||||||
|
|
||||||
ue_pnf.start();
|
|
||||||
gnb_pnf.start();
|
|
||||||
|
|
||||||
while (running) {
|
|
||||||
for (uint32_t i = 0; i < 2; ++i) {
|
|
||||||
srsran::pnf_metrics_t metrics = (i == 0) ? ue_pnf.get_metrics() : gnb_pnf.get_metrics();
|
|
||||||
printf("%s: RTT=%d, #Error=%d, #PDUs=%d, Total TB size=%d, Rate=%.2f Mbit/s\n",
|
|
||||||
i == 0 ? "UE" : "gNB",
|
|
||||||
metrics.avg_rtt_us,
|
|
||||||
metrics.num_timing_errors,
|
|
||||||
metrics.num_pdus,
|
|
||||||
metrics.tb_size,
|
|
||||||
metrics.tb_size * 8 / 1.0e6);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
ue_pnf.stop();
|
|
||||||
gnb_pnf.stop();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,117 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2013-2021 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* This file is part of srsRAN.
|
|
||||||
*
|
|
||||||
* srsRAN is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsRAN is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <boost/program_options.hpp>
|
|
||||||
#include <boost/program_options/parsers.hpp>
|
|
||||||
#include <chrono>
|
|
||||||
#include <iostream>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include "srsran/common/basic_pnf.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
namespace bpo = boost::program_options;
|
|
||||||
|
|
||||||
struct pnf_args_t {
|
|
||||||
std::string type;
|
|
||||||
std::string vnf_addr;
|
|
||||||
uint16_t vnf_port;
|
|
||||||
uint32_t sf_interval;
|
|
||||||
int32_t num_sf;
|
|
||||||
uint32_t tb_len;
|
|
||||||
};
|
|
||||||
|
|
||||||
void parse_args(pnf_args_t* args, int argc, char* argv[])
|
|
||||||
{
|
|
||||||
// Command line only options
|
|
||||||
bpo::options_description general("General options");
|
|
||||||
|
|
||||||
general.add_options()("help,h", "Produce help message")("version,v", "Print version information and exit");
|
|
||||||
|
|
||||||
// Command line or config file options
|
|
||||||
bpo::options_description common("Configuration options");
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
common.add_options()
|
|
||||||
("vnf.type", bpo::value<string>(&args->type)->default_value("gnb"), "VNF instance type [gnb,ue]")
|
|
||||||
("vnf.addr", bpo::value<string>(&args->vnf_addr)->default_value("127.0.0.1"), "VNF address")
|
|
||||||
("vnf.port", bpo::value<uint16_t>(&args->vnf_port)->default_value(3333), "VNF port")
|
|
||||||
("sf_interval", bpo::value<uint32_t>(&args->sf_interval)->default_value(1000), "Interval between subframes in us")
|
|
||||||
("num_sf", bpo::value<int32_t>(&args->num_sf)->default_value(-1), "Number of subframes to signal (-1 infinity)")
|
|
||||||
("tb_len", bpo::value<uint32_t>(&args->tb_len)->default_value(0), "TB lenth (0 for random size)");
|
|
||||||
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
// these options are allowed on the command line
|
|
||||||
bpo::options_description cmdline_options;
|
|
||||||
cmdline_options.add(common).add(general);
|
|
||||||
|
|
||||||
// parse the command line and store result in vm
|
|
||||||
bpo::variables_map vm;
|
|
||||||
bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).run(), vm);
|
|
||||||
bpo::notify(vm);
|
|
||||||
|
|
||||||
// help option was given - print usage and exit
|
|
||||||
if (vm.count("help")) {
|
|
||||||
cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl;
|
|
||||||
cout << common << endl << general << endl;
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool running = true;
|
|
||||||
void sig_int_handler(int signo)
|
|
||||||
{
|
|
||||||
printf("SIGINT received. Exiting...\n");
|
|
||||||
if (signo == SIGINT) {
|
|
||||||
running = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
signal(SIGINT, sig_int_handler);
|
|
||||||
|
|
||||||
pnf_args_t args;
|
|
||||||
parse_args(&args, argc, argv);
|
|
||||||
|
|
||||||
srsran::srsran_basic_pnf pnf(args.type, args.vnf_addr, args.vnf_port, args.sf_interval, args.num_sf, args.tb_len);
|
|
||||||
|
|
||||||
pnf.start();
|
|
||||||
|
|
||||||
while (running) {
|
|
||||||
srsran::pnf_metrics_t metrics = pnf.get_metrics();
|
|
||||||
printf("RTT=%d, #Error=%d, #PDUs=%d, Total TB size=%d, Rate=%.2f Mbit/s\n",
|
|
||||||
metrics.avg_rtt_us,
|
|
||||||
metrics.num_timing_errors,
|
|
||||||
metrics.num_pdus,
|
|
||||||
metrics.tb_size,
|
|
||||||
metrics.tb_size * 8 / 1e6);
|
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
pnf.stop();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 "src/srslog/sinks/syslog_sink.h"
|
||||||
|
#include "srsran/srslog/srslog.h"
|
||||||
|
#include "test_dummies.h"
|
||||||
|
#include "testing_helpers.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
using namespace srslog;
|
||||||
|
|
||||||
|
/// Syslog sink name.
|
||||||
|
static constexpr char sink_name[] = "srslog_syslog_sink";
|
||||||
|
|
||||||
|
static bool find_string_infile(std::string filename, std::string pattern)
|
||||||
|
{
|
||||||
|
std::ifstream file(filename);
|
||||||
|
std::string line;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
if (file.is_open()) {
|
||||||
|
while (std::getline(file, line)) {
|
||||||
|
if (line.find(pattern) != std::string::npos) { // WILL SEARCH 2015-1113 in file
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("WARNING: Could not open file %s", filename.c_str());
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool syslog_basic_test()
|
||||||
|
{
|
||||||
|
syslog_sink syslog_sink(get_default_log_formatter());
|
||||||
|
|
||||||
|
// Build a 1000 byte entry.
|
||||||
|
std::string entry(1000, 'a');
|
||||||
|
|
||||||
|
syslog_sink.write(detail::memory_buffer(entry));
|
||||||
|
|
||||||
|
syslog_sink.flush();
|
||||||
|
|
||||||
|
ASSERT_EQ(find_string_infile("/var/log/syslog", entry), true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
TEST_FUNCTION(syslog_basic_test);
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,72 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2013-2021 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* This file is part of srsRAN.
|
|
||||||
*
|
|
||||||
* srsRAN is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsRAN is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SRSGNB_NR_VNF_PHY_H
|
|
||||||
#define SRSGNB_NR_VNF_PHY_H
|
|
||||||
|
|
||||||
#include "srsenb/hdr/phy/enb_phy_base.h"
|
|
||||||
#include "srsenb/hdr/phy/phy_common.h"
|
|
||||||
#include "srsran/common/basic_vnf.h"
|
|
||||||
#include "srsran/interfaces/enb_metrics_interface.h"
|
|
||||||
#include "srsran/interfaces/gnb_interfaces.h"
|
|
||||||
#include "srsran/interfaces/radio_interfaces.h"
|
|
||||||
|
|
||||||
namespace srsenb {
|
|
||||||
|
|
||||||
struct nr_phy_cfg_t {
|
|
||||||
// TODO: add cell and RRC configs
|
|
||||||
};
|
|
||||||
|
|
||||||
class vnf_phy_nr : public srsenb::enb_phy_base, public srsenb::phy_interface_stack_nr
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
vnf_phy_nr() = default;
|
|
||||||
~vnf_phy_nr();
|
|
||||||
|
|
||||||
int init(const srsenb::phy_args_t& args, const nr_phy_cfg_t& cfg, srsenb::stack_interface_phy_nr* stack_);
|
|
||||||
void stop() override;
|
|
||||||
|
|
||||||
std::string get_type() override { return "vnf"; };
|
|
||||||
|
|
||||||
void start_plot() override;
|
|
||||||
|
|
||||||
void get_metrics(std::vector<srsenb::phy_metrics_t>& metrics) override;
|
|
||||||
|
|
||||||
// MAC interface
|
|
||||||
int dl_config_request(const dl_config_request_t& request) override;
|
|
||||||
int tx_request(const tx_request_t& request) override;
|
|
||||||
|
|
||||||
void cmd_cell_gain(uint32_t cell_idx, float gain_db) override
|
|
||||||
{
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<srsran::srsran_basic_vnf> vnf = nullptr;
|
|
||||||
|
|
||||||
bool initialized = false;
|
|
||||||
|
|
||||||
void parse_config(const nr_phy_cfg_t& cfg);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace srsenb
|
|
||||||
|
|
||||||
#endif // SRSGNB_NR_VNF_PHY_H
|
|
@ -0,0 +1,132 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 SRSRAN_HARQ_SOFTBUFFER_H
|
||||||
|
#define SRSRAN_HARQ_SOFTBUFFER_H
|
||||||
|
|
||||||
|
#include "srsran/adt/pool/pool_interface.h"
|
||||||
|
#include "srsran/adt/span.h"
|
||||||
|
extern "C" {
|
||||||
|
#include "srsran/phy/common/phy_common_nr.h"
|
||||||
|
#include "srsran/phy/fec/softbuffer.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
|
||||||
|
class tx_harq_softbuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
tx_harq_softbuffer() { bzero(&buffer, sizeof(buffer)); }
|
||||||
|
explicit tx_harq_softbuffer(uint32_t nof_prb_) { srsran_softbuffer_tx_init(&buffer, nof_prb_); }
|
||||||
|
tx_harq_softbuffer(const tx_harq_softbuffer&) = delete;
|
||||||
|
tx_harq_softbuffer(tx_harq_softbuffer&& other) noexcept
|
||||||
|
{
|
||||||
|
memcpy(&buffer, &other.buffer, sizeof(other.buffer));
|
||||||
|
bzero(&other.buffer, sizeof(other.buffer));
|
||||||
|
}
|
||||||
|
tx_harq_softbuffer& operator=(const tx_harq_softbuffer&) = delete;
|
||||||
|
tx_harq_softbuffer& operator =(tx_harq_softbuffer&& other) noexcept
|
||||||
|
{
|
||||||
|
if (this != &other) {
|
||||||
|
destroy();
|
||||||
|
memcpy(&buffer, &other.buffer, sizeof(other.buffer));
|
||||||
|
bzero(&other.buffer, sizeof(other.buffer));
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
~tx_harq_softbuffer() { destroy(); }
|
||||||
|
|
||||||
|
void reset() { srsran_softbuffer_tx_reset(&buffer); }
|
||||||
|
|
||||||
|
srsran_softbuffer_tx_t& operator*() { return buffer; }
|
||||||
|
const srsran_softbuffer_tx_t& operator*() const { return buffer; }
|
||||||
|
srsran_softbuffer_tx_t* operator->() { return &buffer; }
|
||||||
|
const srsran_softbuffer_tx_t* operator->() const { return &buffer; }
|
||||||
|
srsran_softbuffer_tx_t* get() { return &buffer; }
|
||||||
|
const srsran_softbuffer_tx_t* get() const { return &buffer; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void destroy() { srsran_softbuffer_tx_free(&buffer); }
|
||||||
|
|
||||||
|
srsran_softbuffer_tx_t buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
class rx_harq_softbuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
rx_harq_softbuffer() { bzero(&buffer, sizeof(buffer)); }
|
||||||
|
explicit rx_harq_softbuffer(uint32_t nof_prb_) { srsran_softbuffer_rx_init(&buffer, nof_prb_); }
|
||||||
|
rx_harq_softbuffer(const rx_harq_softbuffer&) = delete;
|
||||||
|
rx_harq_softbuffer(rx_harq_softbuffer&& other) noexcept
|
||||||
|
{
|
||||||
|
memcpy(&buffer, &other.buffer, sizeof(other.buffer));
|
||||||
|
bzero(&other.buffer, sizeof(other.buffer));
|
||||||
|
}
|
||||||
|
rx_harq_softbuffer& operator=(const rx_harq_softbuffer&) = delete;
|
||||||
|
rx_harq_softbuffer& operator =(rx_harq_softbuffer&& other) noexcept
|
||||||
|
{
|
||||||
|
if (this != &other) {
|
||||||
|
destroy();
|
||||||
|
memcpy(&buffer, &other.buffer, sizeof(other.buffer));
|
||||||
|
bzero(&other.buffer, sizeof(other.buffer));
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
~rx_harq_softbuffer() { destroy(); }
|
||||||
|
|
||||||
|
void reset() { srsran_softbuffer_rx_reset(&buffer); }
|
||||||
|
void reset(uint32_t tbs_bits) { srsran_softbuffer_rx_reset_tbs(&buffer, tbs_bits); }
|
||||||
|
|
||||||
|
srsran_softbuffer_rx_t& operator*() { return buffer; }
|
||||||
|
const srsran_softbuffer_rx_t& operator*() const { return buffer; }
|
||||||
|
srsran_softbuffer_rx_t* operator->() { return &buffer; }
|
||||||
|
const srsran_softbuffer_rx_t* operator->() const { return &buffer; }
|
||||||
|
srsran_softbuffer_rx_t* get() { return &buffer; }
|
||||||
|
const srsran_softbuffer_rx_t* get() const { return &buffer; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void destroy() { srsran_softbuffer_rx_free(&buffer); }
|
||||||
|
|
||||||
|
srsran_softbuffer_rx_t buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
class harq_softbuffer_pool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
harq_softbuffer_pool(const harq_softbuffer_pool&) = delete;
|
||||||
|
harq_softbuffer_pool(harq_softbuffer_pool&&) = delete;
|
||||||
|
harq_softbuffer_pool& operator=(const harq_softbuffer_pool&) = delete;
|
||||||
|
harq_softbuffer_pool& operator=(harq_softbuffer_pool&&) = delete;
|
||||||
|
|
||||||
|
void init_pool(uint32_t nof_prb, uint32_t batch_size = MAX_HARQ * 4, uint32_t thres = 0, uint32_t init_size = 0);
|
||||||
|
|
||||||
|
srsran::unique_pool_ptr<tx_harq_softbuffer> get_tx(uint32_t nof_prb);
|
||||||
|
srsran::unique_pool_ptr<rx_harq_softbuffer> get_rx(uint32_t nof_prb);
|
||||||
|
|
||||||
|
static harq_softbuffer_pool& get_instance()
|
||||||
|
{
|
||||||
|
static harq_softbuffer_pool pool;
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const static uint32_t MAX_HARQ = 16;
|
||||||
|
|
||||||
|
harq_softbuffer_pool() = default;
|
||||||
|
|
||||||
|
std::array<std::unique_ptr<srsran::obj_pool_itf<tx_harq_softbuffer> >, SRSRAN_MAX_PRB_NR> tx_pool;
|
||||||
|
std::array<std::unique_ptr<srsran::obj_pool_itf<rx_harq_softbuffer> >, SRSRAN_MAX_PRB_NR> rx_pool;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace srsenb
|
||||||
|
|
||||||
|
#endif // SRSRAN_HARQ_SOFTBUFFER_H
|
@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 SRSRAN_SCHED_NR_BWP_H
|
||||||
|
#define SRSRAN_SCHED_NR_BWP_H
|
||||||
|
|
||||||
|
#include "sched_nr_cfg.h"
|
||||||
|
#include "sched_nr_rb_grid.h"
|
||||||
|
#include "srsran/adt/pool/cached_alloc.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
namespace sched_nr_impl {
|
||||||
|
|
||||||
|
using dl_sched_rar_info_t = sched_nr_interface::dl_sched_rar_info_t;
|
||||||
|
|
||||||
|
struct pending_rar_t {
|
||||||
|
uint16_t ra_rnti = 0;
|
||||||
|
tti_point prach_tti;
|
||||||
|
srsran::bounded_vector<dl_sched_rar_info_t, sched_interface::MAX_RAR_LIST> msg3_grant;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// RAR/Msg3 scheduler
|
||||||
|
class ra_sched
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ra_sched(const bwp_params& bwp_cfg_);
|
||||||
|
|
||||||
|
int dl_rach_info(const dl_sched_rar_info_t& rar_info);
|
||||||
|
void run_slot(bwp_slot_allocator& slot_grid);
|
||||||
|
size_t empty() const { return pending_rars.empty(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
alloc_result
|
||||||
|
allocate_pending_rar(bwp_slot_allocator& slot_grid, const pending_rar_t& rar, uint32_t& nof_grants_alloc);
|
||||||
|
|
||||||
|
const bwp_params* bwp_cfg = nullptr;
|
||||||
|
srslog::basic_logger& logger;
|
||||||
|
|
||||||
|
srsran::deque<pending_rar_t> pending_rars;
|
||||||
|
};
|
||||||
|
|
||||||
|
class bwp_ctxt
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit bwp_ctxt(const bwp_params& bwp_cfg);
|
||||||
|
|
||||||
|
const bwp_params* cfg;
|
||||||
|
|
||||||
|
// channel-specific schedulers
|
||||||
|
ra_sched ra;
|
||||||
|
|
||||||
|
// Stores pending allocations and PRB bitmaps
|
||||||
|
bwp_res_grid grid;
|
||||||
|
};
|
||||||
|
|
||||||
|
class serv_cell_ctxt
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
srsran::bounded_vector<bwp_ctxt, SCHED_NR_MAX_BWP_PER_CELL> bwps;
|
||||||
|
|
||||||
|
explicit serv_cell_ctxt(const sched_cell_params& cell_cfg_);
|
||||||
|
|
||||||
|
const sched_cell_params* cfg;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sched_nr_impl
|
||||||
|
} // namespace srsenb
|
||||||
|
|
||||||
|
#endif // SRSRAN_SCHED_NR_BWP_H
|
@ -0,0 +1,190 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 SRSRAN_SCHED_NR_CFG_H
|
||||||
|
#define SRSRAN_SCHED_NR_CFG_H
|
||||||
|
|
||||||
|
#include "sched_nr_interface.h"
|
||||||
|
#include "sched_nr_rb.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
|
||||||
|
const static size_t SCHED_NR_MAX_USERS = 4;
|
||||||
|
const static size_t SCHED_NR_NOF_SUBFRAMES = 10;
|
||||||
|
const static size_t SCHED_NR_NOF_HARQS = 16;
|
||||||
|
static const size_t MAX_NOF_AGGR_LEVELS = 5;
|
||||||
|
|
||||||
|
namespace sched_nr_impl {
|
||||||
|
|
||||||
|
const static size_t MAX_GRANTS = sched_nr_interface::MAX_GRANTS;
|
||||||
|
|
||||||
|
using pucch_t = mac_interface_phy_nr::pucch_t;
|
||||||
|
using pucch_list_t = srsran::bounded_vector<pucch_t, MAX_GRANTS>;
|
||||||
|
using pusch_t = mac_interface_phy_nr::pusch_t;
|
||||||
|
using pusch_list_t = srsran::bounded_vector<pusch_t, MAX_GRANTS>;
|
||||||
|
|
||||||
|
using sched_cfg_t = sched_nr_interface::sched_cfg_t;
|
||||||
|
using cell_cfg_t = sched_nr_interface::cell_cfg_t;
|
||||||
|
using bwp_cfg_t = sched_nr_interface::bwp_cfg_t;
|
||||||
|
|
||||||
|
struct bwp_params {
|
||||||
|
const uint32_t bwp_id;
|
||||||
|
const uint32_t cc;
|
||||||
|
const bwp_cfg_t& cfg;
|
||||||
|
const cell_cfg_t& cell_cfg;
|
||||||
|
const sched_cfg_t& sched_cfg;
|
||||||
|
|
||||||
|
// derived params
|
||||||
|
uint32_t P;
|
||||||
|
uint32_t N_rbg;
|
||||||
|
|
||||||
|
bwp_params(const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_, uint32_t cc, uint32_t bwp_id);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sched_cell_params {
|
||||||
|
const uint32_t cc;
|
||||||
|
const cell_cfg_t cell_cfg;
|
||||||
|
const sched_cfg_t& sched_cfg;
|
||||||
|
std::vector<bwp_params> bwps;
|
||||||
|
|
||||||
|
sched_cell_params(uint32_t cc_, const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_);
|
||||||
|
|
||||||
|
uint32_t nof_prb() const { return cell_cfg.carrier.nof_prb; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sched_params {
|
||||||
|
const sched_cfg_t sched_cfg;
|
||||||
|
std::vector<sched_cell_params> cells;
|
||||||
|
|
||||||
|
explicit sched_params(const sched_cfg_t& sched_cfg_);
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
using prb_bitmap = srsran::bounded_bitset<SRSRAN_MAX_PRB_NR, true>;
|
||||||
|
using rbgmask_t = srsran::bounded_bitset<SCHED_NR_MAX_NOF_RBGS, true>;
|
||||||
|
|
||||||
|
using pdcchmask_t = srsran::bounded_bitset<SCHED_NR_MAX_NOF_RBGS, true>;
|
||||||
|
|
||||||
|
using pdcch_cce_pos_list = srsran::bounded_vector<uint32_t, SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR>;
|
||||||
|
using bwp_cce_pos_list = std::array<std::array<pdcch_cce_pos_list, MAX_NOF_AGGR_LEVELS>, SRSRAN_NOF_SF_X_FRAME>;
|
||||||
|
void get_dci_locs(const srsran_coreset_t& coreset,
|
||||||
|
const srsran_search_space_t& search_space,
|
||||||
|
uint16_t rnti,
|
||||||
|
bwp_cce_pos_list& cce_locs);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
using ue_cfg_t = sched_nr_interface::ue_cfg_t;
|
||||||
|
using ue_cc_cfg_t = sched_nr_interface::ue_cc_cfg_t;
|
||||||
|
|
||||||
|
class bwp_ue_cfg
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bwp_ue_cfg() = default;
|
||||||
|
explicit bwp_ue_cfg(uint16_t rnti, const bwp_params& bwp_cfg, const ue_cfg_t& uecfg_);
|
||||||
|
|
||||||
|
const ue_cfg_t* ue_cfg() const { return cfg_; }
|
||||||
|
const srsran::phy_cfg_nr_t& phy() const { return cfg_->phy_cfg; }
|
||||||
|
const bwp_params& active_bwp() const { return *bwp_cfg; }
|
||||||
|
const bwp_cce_pos_list& cce_pos_list(uint32_t search_id) const
|
||||||
|
{
|
||||||
|
return cce_positions_list[ss_id_to_cce_idx[search_id]];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint16_t rnti = SRSRAN_INVALID_RNTI;
|
||||||
|
const ue_cfg_t* cfg_ = nullptr;
|
||||||
|
const bwp_params* bwp_cfg = nullptr;
|
||||||
|
|
||||||
|
std::vector<bwp_cce_pos_list> cce_positions_list;
|
||||||
|
std::array<uint32_t, SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE> ss_id_to_cce_idx;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ue_cfg_extended : public ue_cfg_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct search_space_params {
|
||||||
|
const srsran_search_space_t* cfg;
|
||||||
|
bwp_cce_pos_list cce_positions;
|
||||||
|
};
|
||||||
|
struct coreset_params {
|
||||||
|
srsran_coreset_t* cfg = nullptr;
|
||||||
|
std::vector<uint32_t> ss_list;
|
||||||
|
};
|
||||||
|
struct bwp_params {
|
||||||
|
std::array<srsran::optional<search_space_params>, SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE> ss_list;
|
||||||
|
std::vector<coreset_params> coresets;
|
||||||
|
};
|
||||||
|
struct cc_params {
|
||||||
|
srsran::bounded_vector<bwp_params, SCHED_NR_MAX_BWP_PER_CELL> bwps;
|
||||||
|
};
|
||||||
|
|
||||||
|
ue_cfg_extended() = default;
|
||||||
|
explicit ue_cfg_extended(uint16_t rnti, const ue_cfg_t& uecfg);
|
||||||
|
|
||||||
|
const bwp_cce_pos_list& get_dci_pos_list(uint32_t cc, uint32_t bwp_id, uint32_t search_space_id) const
|
||||||
|
{
|
||||||
|
return cc_params[cc].bwps[bwp_id].ss_list[search_space_id]->cce_positions;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t rnti;
|
||||||
|
std::vector<cc_params> cc_params;
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct resource_guard {
|
||||||
|
public:
|
||||||
|
resource_guard() = default;
|
||||||
|
resource_guard(const resource_guard& other) = delete;
|
||||||
|
resource_guard(resource_guard&& other) = delete;
|
||||||
|
resource_guard& operator=(const resource_guard& other) = delete;
|
||||||
|
resource_guard& operator=(resource_guard&& other) = delete;
|
||||||
|
bool busy() const { return flag; }
|
||||||
|
|
||||||
|
struct token {
|
||||||
|
token() = default;
|
||||||
|
token(resource_guard& parent) : flag(parent.busy() ? nullptr : &parent.flag)
|
||||||
|
{
|
||||||
|
if (flag != nullptr) {
|
||||||
|
*flag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
token(token&&) noexcept = default;
|
||||||
|
token& operator=(token&&) noexcept = default;
|
||||||
|
void release() { flag.reset(); }
|
||||||
|
bool owns_token() const { return flag != nullptr; }
|
||||||
|
bool empty() const { return flag == nullptr; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct release_deleter {
|
||||||
|
void operator()(bool* ptr)
|
||||||
|
{
|
||||||
|
if (ptr != nullptr) {
|
||||||
|
srsran_assert(*ptr == true, "resource token: detected inconsistency token state");
|
||||||
|
*ptr = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
std::unique_ptr<bool, release_deleter> flag;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool flag = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sched_nr_impl
|
||||||
|
|
||||||
|
} // namespace srsenb
|
||||||
|
|
||||||
|
#endif // SRSRAN_SCHED_NR_CFG_H
|
@ -1,111 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2013-2021 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* This file is part of srsRAN.
|
|
||||||
*
|
|
||||||
* srsRAN is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsRAN is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SRSRAN_SCHED_NR_COMMON_H
|
|
||||||
#define SRSRAN_SCHED_NR_COMMON_H
|
|
||||||
|
|
||||||
#include "sched_nr_interface.h"
|
|
||||||
#include "srsran/adt/bounded_bitset.h"
|
|
||||||
|
|
||||||
namespace srsenb {
|
|
||||||
|
|
||||||
const static size_t SCHED_NR_MAX_USERS = 4;
|
|
||||||
const static size_t SCHED_NR_NOF_SUBFRAMES = 10;
|
|
||||||
const static size_t SCHED_NR_NOF_HARQS = 16;
|
|
||||||
static const size_t MAX_NOF_AGGR_LEVELS = 5;
|
|
||||||
|
|
||||||
namespace sched_nr_impl {
|
|
||||||
|
|
||||||
const static size_t MAX_GRANTS = sched_nr_interface::MAX_GRANTS;
|
|
||||||
|
|
||||||
using sched_cfg_t = sched_nr_interface::sched_cfg_t;
|
|
||||||
using cell_cfg_t = sched_nr_interface::cell_cfg_t;
|
|
||||||
|
|
||||||
struct sched_cell_params {
|
|
||||||
const uint32_t cc;
|
|
||||||
const cell_cfg_t cell_cfg;
|
|
||||||
const sched_cfg_t& sched_cfg;
|
|
||||||
|
|
||||||
sched_cell_params(uint32_t cc_, const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sched_params {
|
|
||||||
const sched_cfg_t sched_cfg;
|
|
||||||
std::vector<sched_cell_params> cells;
|
|
||||||
|
|
||||||
explicit sched_params(const sched_cfg_t& sched_cfg_);
|
|
||||||
};
|
|
||||||
|
|
||||||
using pdcchmask_t = srsran::bounded_bitset<SCHED_NR_MAX_NOF_RBGS, true>;
|
|
||||||
using rbgmask_t = srsran::bounded_bitset<SCHED_NR_MAX_NOF_RBGS, true>;
|
|
||||||
|
|
||||||
struct resource_guard {
|
|
||||||
public:
|
|
||||||
resource_guard() = default;
|
|
||||||
resource_guard(const resource_guard& other) = delete;
|
|
||||||
resource_guard(resource_guard&& other) = delete;
|
|
||||||
resource_guard& operator=(const resource_guard& other) = delete;
|
|
||||||
resource_guard& operator=(resource_guard&& other) = delete;
|
|
||||||
bool busy() const { return flag; }
|
|
||||||
|
|
||||||
struct token {
|
|
||||||
token() = default;
|
|
||||||
token(resource_guard& parent) : flag(parent.busy() ? nullptr : &parent.flag)
|
|
||||||
{
|
|
||||||
if (flag != nullptr) {
|
|
||||||
*flag = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
token(token&&) noexcept = default;
|
|
||||||
token& operator=(token&&) noexcept = default;
|
|
||||||
void release() { flag.reset(); }
|
|
||||||
bool owns_token() const { return flag != nullptr; }
|
|
||||||
bool empty() const { return flag == nullptr; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct release_deleter {
|
|
||||||
void operator()(bool* ptr)
|
|
||||||
{
|
|
||||||
if (ptr != nullptr) {
|
|
||||||
srsran_assert(*ptr == true, "resource token: detected inconsistency token state");
|
|
||||||
*ptr = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
std::unique_ptr<bool, release_deleter> flag;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool flag = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
using pdcch_cce_pos_list = srsran::bounded_vector<uint32_t, SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR>;
|
|
||||||
using bwp_cce_pos_list = std::array<std::array<pdcch_cce_pos_list, MAX_NOF_AGGR_LEVELS>, SRSRAN_NOF_SF_X_FRAME>;
|
|
||||||
void get_dci_locs(const srsran_coreset_t& coreset,
|
|
||||||
const srsran_search_space_t& search_space,
|
|
||||||
uint16_t rnti,
|
|
||||||
bwp_cce_pos_list& cce_locs);
|
|
||||||
|
|
||||||
} // namespace sched_nr_impl
|
|
||||||
|
|
||||||
} // namespace srsenb
|
|
||||||
|
|
||||||
#endif // SRSRAN_SCHED_NR_COMMON_H
|
|
@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 SRSRAN_SCHED_NR_HELPERS_H
|
||||||
|
#define SRSRAN_SCHED_NR_HELPERS_H
|
||||||
|
|
||||||
|
#include "sched_nr_cfg.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
namespace sched_nr_impl {
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool fill_dci_rar(prb_interval interv, const bwp_params& bwp_cfg, srsran_dci_dl_nr_t& dci);
|
||||||
|
|
||||||
|
class slot_ue;
|
||||||
|
|
||||||
|
/// Generate PDCCH DL DCI fields
|
||||||
|
void fill_dl_dci_ue_fields(const slot_ue& ue,
|
||||||
|
const bwp_params& bwp_cfg,
|
||||||
|
uint32_t ss_id,
|
||||||
|
srsran_dci_location_t dci_pos,
|
||||||
|
srsran_dci_dl_nr_t& dci);
|
||||||
|
|
||||||
|
/// Generate PDCCH UL DCI fields
|
||||||
|
void fill_ul_dci_ue_fields(const slot_ue& ue,
|
||||||
|
const bwp_params& bwp_cfg,
|
||||||
|
uint32_t ss_id,
|
||||||
|
srsran_dci_location_t dci_pos,
|
||||||
|
srsran_dci_ul_nr_t& dci);
|
||||||
|
|
||||||
|
} // namespace sched_nr_impl
|
||||||
|
} // namespace srsenb
|
||||||
|
|
||||||
|
#endif // SRSRAN_SCHED_NR_HELPERS_H
|
@ -1,42 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2013-2021 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* This file is part of srsRAN.
|
|
||||||
*
|
|
||||||
* srsRAN is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsRAN is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SRSRAN_SCHED_NR_PHY_HELPERS_H
|
|
||||||
#define SRSRAN_SCHED_NR_PHY_HELPERS_H
|
|
||||||
|
|
||||||
#include "sched_nr_common.h"
|
|
||||||
|
|
||||||
namespace srsenb {
|
|
||||||
namespace sched_nr_impl {
|
|
||||||
|
|
||||||
uint32_t get_P(uint32_t bwp_nof_prb, bool config_1_or_2);
|
|
||||||
uint32_t get_nof_rbgs(uint32_t bwp_nof_prb, uint32_t bwp_start, bool config1_or_2);
|
|
||||||
|
|
||||||
void bitmap_to_prb_array(const rbgmask_t& bitmap, uint32_t bwp_nof_prb, srsran_sch_grant_nr_t& grant);
|
|
||||||
|
|
||||||
class slot_ue;
|
|
||||||
void fill_dci_ue_cfg(const slot_ue& ue, srsran_dci_dl_nr_t& dci);
|
|
||||||
void fill_dci_ue_cfg(const slot_ue& ue, srsran_dci_ul_nr_t& dci);
|
|
||||||
|
|
||||||
} // namespace sched_nr_impl
|
|
||||||
} // namespace srsenb
|
|
||||||
|
|
||||||
#endif // SRSRAN_SCHED_NR_PHY_HELPERS_H
|
|
@ -0,0 +1,237 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 SRSRAN_SCHED_NR_RB_H
|
||||||
|
#define SRSRAN_SCHED_NR_RB_H
|
||||||
|
|
||||||
|
#include "srsenb/hdr/stack/mac/nr/sched_nr_interface.h"
|
||||||
|
#include "srsran/adt/bounded_bitset.h"
|
||||||
|
#include "srsran/phy/common/phy_common_nr.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
namespace sched_nr_impl {
|
||||||
|
|
||||||
|
using prb_bitmap = srsran::bounded_bitset<SRSRAN_MAX_PRB_NR, true>;
|
||||||
|
using rbg_bitmap = srsran::bounded_bitset<SCHED_NR_MAX_NOF_RBGS, true>;
|
||||||
|
|
||||||
|
/// TS 38.214, Table 6.1.2.2.1-1 - Nominal RBG size P
|
||||||
|
uint32_t get_P(uint32_t bwp_nof_prb, bool config_1_or_2);
|
||||||
|
|
||||||
|
/// TS 38.214 - total number of RBGs for a uplink bandwidth part of size "bwp_nof_prb" PRBs
|
||||||
|
uint32_t get_nof_rbgs(uint32_t bwp_nof_prb, uint32_t bwp_start, bool config1_or_2);
|
||||||
|
|
||||||
|
/// Struct to express a {min,...,max} range of PRBs
|
||||||
|
struct prb_interval : public srsran::interval<uint32_t> {
|
||||||
|
using interval::interval;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct prb_grant {
|
||||||
|
prb_grant() = default;
|
||||||
|
prb_grant(const prb_interval& other) noexcept : alloc_type_0(false), alloc(other) {}
|
||||||
|
prb_grant(const rbg_bitmap& other) noexcept : alloc_type_0(true), alloc(other) {}
|
||||||
|
prb_grant(const prb_grant& other) noexcept : alloc_type_0(other.alloc_type_0), alloc(other.alloc_type_0, other.alloc)
|
||||||
|
{}
|
||||||
|
prb_grant& operator=(const prb_grant& other) noexcept
|
||||||
|
{
|
||||||
|
if (this == &other) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
if (other.alloc_type_0) {
|
||||||
|
*this = other.rbgs();
|
||||||
|
} else {
|
||||||
|
*this = other.prbs();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
prb_grant& operator=(const prb_interval& prbs)
|
||||||
|
{
|
||||||
|
if (alloc_type_0) {
|
||||||
|
alloc_type_0 = false;
|
||||||
|
alloc.rbgs.~rbg_bitmap();
|
||||||
|
new (&alloc.interv) prb_interval(prbs);
|
||||||
|
} else {
|
||||||
|
alloc.interv = alloc.interv;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
prb_grant& operator=(const rbg_bitmap& rbgs)
|
||||||
|
{
|
||||||
|
if (alloc_type_0) {
|
||||||
|
alloc.rbgs = rbgs;
|
||||||
|
} else {
|
||||||
|
alloc_type_0 = true;
|
||||||
|
alloc.interv.~prb_interval();
|
||||||
|
new (&alloc.rbgs) rbg_bitmap(rbgs);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
~prb_grant()
|
||||||
|
{
|
||||||
|
if (is_alloc_type0()) {
|
||||||
|
alloc.rbgs.~rbg_bitmap();
|
||||||
|
} else {
|
||||||
|
alloc.interv.~prb_interval();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_alloc_type0() const { return alloc_type_0; }
|
||||||
|
bool is_alloc_type1() const { return not is_alloc_type0(); }
|
||||||
|
const rbg_bitmap& rbgs() const
|
||||||
|
{
|
||||||
|
srsran_assert(is_alloc_type0(), "Invalid access to rbgs() field of grant with alloc type 1");
|
||||||
|
return alloc.rbgs;
|
||||||
|
}
|
||||||
|
const prb_interval& prbs() const
|
||||||
|
{
|
||||||
|
srsran_assert(is_alloc_type1(), "Invalid access to prbs() field of grant with alloc type 0");
|
||||||
|
return alloc.interv;
|
||||||
|
}
|
||||||
|
rbg_bitmap& rbgs()
|
||||||
|
{
|
||||||
|
srsran_assert(is_alloc_type0(), "Invalid access to rbgs() field of grant with alloc type 1");
|
||||||
|
return alloc.rbgs;
|
||||||
|
}
|
||||||
|
prb_interval& prbs()
|
||||||
|
{
|
||||||
|
srsran_assert(is_alloc_type1(), "Invalid access to prbs() field of grant with alloc type 0");
|
||||||
|
return alloc.interv;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool alloc_type_0 = false;
|
||||||
|
union alloc_t {
|
||||||
|
rbg_bitmap rbgs;
|
||||||
|
prb_interval interv;
|
||||||
|
|
||||||
|
alloc_t() : interv(0, 0) {}
|
||||||
|
explicit alloc_t(const prb_interval& prbs) : interv(prbs) {}
|
||||||
|
explicit alloc_t(const rbg_bitmap& rbgs_) : rbgs(rbgs_) {}
|
||||||
|
alloc_t(bool type0, const alloc_t& other)
|
||||||
|
{
|
||||||
|
if (type0) {
|
||||||
|
new (&rbgs) rbg_bitmap(other.rbgs);
|
||||||
|
} else {
|
||||||
|
new (&interv) prb_interval(other.interv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} alloc;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bwp_rb_bitmap {
|
||||||
|
public:
|
||||||
|
bwp_rb_bitmap() = default;
|
||||||
|
bwp_rb_bitmap(uint32_t bwp_nof_prbs, uint32_t bwp_prb_start_, bool config1_or_2);
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
prbs_.reset();
|
||||||
|
rbgs_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void operator|=(const T& grant)
|
||||||
|
{
|
||||||
|
add(grant);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(const prb_interval& prbs)
|
||||||
|
{
|
||||||
|
prbs_.fill(prbs.start(), prbs.stop());
|
||||||
|
add_prbs_to_rbgs(prbs);
|
||||||
|
}
|
||||||
|
void add(const prb_bitmap& grant)
|
||||||
|
{
|
||||||
|
prbs_ |= grant;
|
||||||
|
add_prbs_to_rbgs(grant);
|
||||||
|
}
|
||||||
|
void add(const rbg_bitmap& grant)
|
||||||
|
{
|
||||||
|
rbgs_ |= grant;
|
||||||
|
add_rbgs_to_prbs(grant);
|
||||||
|
}
|
||||||
|
void add(const prb_grant& grant)
|
||||||
|
{
|
||||||
|
if (grant.is_alloc_type0()) {
|
||||||
|
add(grant.rbgs());
|
||||||
|
} else {
|
||||||
|
add(grant.prbs());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool collides(const prb_grant& grant) const
|
||||||
|
{
|
||||||
|
if (grant.is_alloc_type0()) {
|
||||||
|
return (rbgs() & grant.rbgs()).any();
|
||||||
|
}
|
||||||
|
return prbs().any(grant.prbs().start(), grant.prbs().stop());
|
||||||
|
}
|
||||||
|
bool test(uint32_t prb_idx) { return prbs().test(prb_idx); }
|
||||||
|
void set(uint32_t prb_idx)
|
||||||
|
{
|
||||||
|
prbs_.set(prb_idx);
|
||||||
|
rbgs_.set(prb_to_rbg_idx(prb_idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
const prb_bitmap& prbs() const { return prbs_; }
|
||||||
|
const rbg_bitmap& rbgs() const { return rbgs_; }
|
||||||
|
uint32_t P() const { return P_; }
|
||||||
|
uint32_t nof_prbs() const { return prbs_.size(); }
|
||||||
|
uint32_t nof_rbgs() const { return rbgs_.size(); }
|
||||||
|
|
||||||
|
uint32_t prb_to_rbg_idx(uint32_t prb_idx) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
prb_bitmap prbs_;
|
||||||
|
rbg_bitmap rbgs_;
|
||||||
|
uint32_t P_ = 0;
|
||||||
|
uint32_t Pnofbits = 0;
|
||||||
|
uint32_t first_rbg_size = 0;
|
||||||
|
|
||||||
|
void add_prbs_to_rbgs(const prb_bitmap& grant);
|
||||||
|
void add_prbs_to_rbgs(const prb_interval& grant);
|
||||||
|
void add_rbgs_to_prbs(const rbg_bitmap& grant);
|
||||||
|
};
|
||||||
|
|
||||||
|
inline prb_interval
|
||||||
|
find_next_empty_interval(const prb_bitmap& mask, size_t start_prb_idx = 0, size_t last_prb_idx = SRSRAN_MAX_PRB_NR)
|
||||||
|
{
|
||||||
|
int rb_start = mask.find_lowest(start_prb_idx, std::min(mask.size(), last_prb_idx), false);
|
||||||
|
if (rb_start != -1) {
|
||||||
|
int rb_end = mask.find_lowest(rb_start + 1, std::min(mask.size(), last_prb_idx), true);
|
||||||
|
return {(uint32_t)rb_start, (uint32_t)(rb_end < 0 ? mask.size() : rb_end)};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline prb_interval find_empty_interval_of_length(const prb_bitmap& mask, size_t nof_prbs, uint32_t start_prb_idx = 0)
|
||||||
|
{
|
||||||
|
prb_interval max_interv;
|
||||||
|
do {
|
||||||
|
prb_interval interv = find_next_empty_interval(mask, start_prb_idx, mask.size());
|
||||||
|
if (interv.empty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (interv.length() >= nof_prbs) {
|
||||||
|
max_interv.set(interv.start(), interv.start() + nof_prbs);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (interv.length() > max_interv.length()) {
|
||||||
|
max_interv = interv;
|
||||||
|
}
|
||||||
|
start_prb_idx = interv.stop() + 1;
|
||||||
|
} while (start_prb_idx < mask.size());
|
||||||
|
return max_interv;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sched_nr_impl
|
||||||
|
} // namespace srsenb
|
||||||
|
|
||||||
|
#endif // SRSRAN_SCHED_NR_RB_H
|
@ -1,76 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2013-2021 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* This file is part of srsRAN.
|
|
||||||
*
|
|
||||||
* srsRAN is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsRAN is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
|
|
||||||
#include "srsenb/hdr/phy/vnf_phy_nr.h"
|
|
||||||
#include "srsran/common/basic_vnf_api.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace srsenb {
|
|
||||||
|
|
||||||
vnf_phy_nr::~vnf_phy_nr()
|
|
||||||
{
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void vnf_phy_nr::parse_config(const nr_phy_cfg_t& cfg) {}
|
|
||||||
|
|
||||||
int vnf_phy_nr::init(const srsenb::phy_args_t& args, const nr_phy_cfg_t& cfg, srsenb::stack_interface_phy_nr* stack_)
|
|
||||||
{
|
|
||||||
mlockall(MCL_CURRENT | MCL_FUTURE);
|
|
||||||
|
|
||||||
// create VNF
|
|
||||||
vnf = std::unique_ptr<srsran::srsran_basic_vnf>(new srsran::srsran_basic_vnf(args.vnf_args, stack_));
|
|
||||||
|
|
||||||
initialized = true;
|
|
||||||
|
|
||||||
return SRSRAN_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void vnf_phy_nr::stop()
|
|
||||||
{
|
|
||||||
if (initialized) {
|
|
||||||
vnf->stop();
|
|
||||||
initialized = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start GUI
|
|
||||||
void vnf_phy_nr::start_plot() {}
|
|
||||||
|
|
||||||
void vnf_phy_nr::get_metrics(std::vector<srsenb::phy_metrics_t>& metrics) {}
|
|
||||||
|
|
||||||
int vnf_phy_nr::dl_config_request(const dl_config_request_t& request)
|
|
||||||
{
|
|
||||||
// prepare DL config request over basic API and send
|
|
||||||
return vnf->dl_config_request(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
int vnf_phy_nr::tx_request(const tx_request_t& request)
|
|
||||||
{
|
|
||||||
// send Tx request over basic API
|
|
||||||
return vnf->tx_request(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace srsenb
|
|
@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 "srsenb/hdr/stack/mac/nr/harq_softbuffer.h"
|
||||||
|
#include "srsran/adt/pool/obj_pool.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
|
||||||
|
void harq_softbuffer_pool::init_pool(uint32_t nof_prb, uint32_t batch_size, uint32_t thres, uint32_t init_size)
|
||||||
|
{
|
||||||
|
srsran_assert(nof_prb <= SRSRAN_MAX_PRB_NR, "Invalid nof prb=%d", nof_prb);
|
||||||
|
size_t idx = nof_prb - 1;
|
||||||
|
if (tx_pool[idx] != nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (thres == 0) {
|
||||||
|
thres = batch_size;
|
||||||
|
}
|
||||||
|
if (init_size == 0) {
|
||||||
|
init_size = batch_size;
|
||||||
|
}
|
||||||
|
auto init_tx_softbuffers = [nof_prb](void* ptr) { new (ptr) tx_harq_softbuffer(nof_prb); };
|
||||||
|
auto recycle_tx_softbuffers = [](tx_harq_softbuffer& softbuffer) { softbuffer.reset(); };
|
||||||
|
tx_pool[idx].reset(new srsran::background_obj_pool<tx_harq_softbuffer>(
|
||||||
|
batch_size, thres, init_size, init_tx_softbuffers, recycle_tx_softbuffers));
|
||||||
|
|
||||||
|
auto init_rx_softbuffers = [nof_prb](void* ptr) { new (ptr) rx_harq_softbuffer(nof_prb); };
|
||||||
|
auto recycle_rx_softbuffers = [](rx_harq_softbuffer& softbuffer) { softbuffer.reset(); };
|
||||||
|
rx_pool[idx].reset(new srsran::background_obj_pool<rx_harq_softbuffer>(
|
||||||
|
batch_size, thres, init_size, init_rx_softbuffers, recycle_rx_softbuffers));
|
||||||
|
}
|
||||||
|
|
||||||
|
srsran::unique_pool_ptr<tx_harq_softbuffer> harq_softbuffer_pool::get_tx(uint32_t nof_prb)
|
||||||
|
{
|
||||||
|
srsran_assert(nof_prb <= SRSRAN_MAX_PRB_NR, "Invalid Nprb=%d", nof_prb);
|
||||||
|
size_t idx = nof_prb - 1;
|
||||||
|
if (tx_pool[idx] == nullptr) {
|
||||||
|
init_pool(nof_prb);
|
||||||
|
}
|
||||||
|
return tx_pool[idx]->make();
|
||||||
|
}
|
||||||
|
|
||||||
|
srsran::unique_pool_ptr<rx_harq_softbuffer> harq_softbuffer_pool::get_rx(uint32_t nof_prb)
|
||||||
|
{
|
||||||
|
srsran_assert(nof_prb <= SRSRAN_MAX_PRB_NR, "Invalid Nprb=%d", nof_prb);
|
||||||
|
size_t idx = nof_prb - 1;
|
||||||
|
if (rx_pool[idx] == nullptr) {
|
||||||
|
init_pool(nof_prb);
|
||||||
|
}
|
||||||
|
return rx_pool[idx]->make();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace srsenb
|
@ -0,0 +1,161 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 "srsenb/hdr/stack/mac/nr/sched_nr_bwp.h"
|
||||||
|
#include "srsran/common/standard_streams.h"
|
||||||
|
#include "srsran/common/string_helpers.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
namespace sched_nr_impl {
|
||||||
|
|
||||||
|
ra_sched::ra_sched(const bwp_params& bwp_cfg_) : bwp_cfg(&bwp_cfg_), logger(srslog::fetch_basic_logger("MAC")) {}
|
||||||
|
|
||||||
|
alloc_result
|
||||||
|
ra_sched::allocate_pending_rar(bwp_slot_allocator& slot_grid, const pending_rar_t& rar, uint32_t& nof_grants_alloc)
|
||||||
|
{
|
||||||
|
const uint32_t rar_aggr_level = 2;
|
||||||
|
const prb_bitmap& prbs = slot_grid.res_grid()[slot_grid.get_pdcch_tti()].dl_prbs.prbs();
|
||||||
|
|
||||||
|
alloc_result ret = alloc_result::other_cause;
|
||||||
|
for (nof_grants_alloc = rar.msg3_grant.size(); nof_grants_alloc > 0; nof_grants_alloc--) {
|
||||||
|
ret = alloc_result::invalid_coderate;
|
||||||
|
uint32_t start_prb_idx = 0;
|
||||||
|
for (uint32_t nprb = 1; nprb < bwp_cfg->cfg.rb_width and ret == alloc_result::invalid_coderate; ++nprb) {
|
||||||
|
prb_interval interv = find_empty_interval_of_length(prbs, nprb, start_prb_idx);
|
||||||
|
start_prb_idx = interv.stop();
|
||||||
|
if (interv.length() == nprb) {
|
||||||
|
ret = slot_grid.alloc_rar(rar_aggr_level, rar, interv, nof_grants_alloc);
|
||||||
|
} else {
|
||||||
|
ret = alloc_result::no_sch_space;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If allocation was not successful because there were not enough RBGs, try allocating fewer Msg3 grants
|
||||||
|
if (ret != alloc_result::invalid_coderate and ret != alloc_result::no_sch_space) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ret != alloc_result::success) {
|
||||||
|
logger.info("SCHED: RAR allocation for L=%d was postponed. Cause=%s", rar_aggr_level, to_string(ret));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ra_sched::run_slot(bwp_slot_allocator& slot_grid)
|
||||||
|
{
|
||||||
|
static const uint32_t PRACH_RAR_OFFSET = 3;
|
||||||
|
tti_point pdcch_tti = slot_grid.get_pdcch_tti();
|
||||||
|
|
||||||
|
for (auto it = pending_rars.begin(); it != pending_rars.end();) {
|
||||||
|
pending_rar_t& rar = *it;
|
||||||
|
|
||||||
|
// In case of RAR outside RAR window:
|
||||||
|
// - if window has passed, discard RAR
|
||||||
|
// - if window hasn't started, stop loop, as RARs are ordered by TTI
|
||||||
|
tti_interval rar_window{rar.prach_tti + PRACH_RAR_OFFSET,
|
||||||
|
rar.prach_tti + PRACH_RAR_OFFSET + bwp_cfg->cfg.rar_window_size};
|
||||||
|
if (not rar_window.contains(pdcch_tti)) {
|
||||||
|
if (pdcch_tti >= rar_window.stop()) {
|
||||||
|
fmt::memory_buffer str_buffer;
|
||||||
|
fmt::format_to(str_buffer,
|
||||||
|
"SCHED: Could not transmit RAR within the window (RA={}, Window={}, RAR={}",
|
||||||
|
rar.prach_tti,
|
||||||
|
rar_window,
|
||||||
|
pdcch_tti);
|
||||||
|
srsran::console("%s\n", srsran::to_c_str(str_buffer));
|
||||||
|
logger.warning("%s", srsran::to_c_str(str_buffer));
|
||||||
|
it = pending_rars.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to schedule DCI + RBGs for RAR Grant
|
||||||
|
uint32_t nof_rar_allocs = 0;
|
||||||
|
alloc_result ret = allocate_pending_rar(slot_grid, rar, nof_rar_allocs);
|
||||||
|
|
||||||
|
if (ret == alloc_result::success) {
|
||||||
|
// If RAR allocation was successful:
|
||||||
|
// - in case all Msg3 grants were allocated, remove pending RAR, and continue with following RAR
|
||||||
|
// - otherwise, erase only Msg3 grants that were allocated, and stop iteration
|
||||||
|
|
||||||
|
if (nof_rar_allocs == rar.msg3_grant.size()) {
|
||||||
|
it = pending_rars.erase(it);
|
||||||
|
} else {
|
||||||
|
std::copy(rar.msg3_grant.begin() + nof_rar_allocs, rar.msg3_grant.end(), rar.msg3_grant.begin());
|
||||||
|
rar.msg3_grant.resize(rar.msg3_grant.size() - nof_rar_allocs);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If RAR allocation was not successful:
|
||||||
|
// - in case of unavailable PDCCH space, try next pending RAR allocation
|
||||||
|
// - otherwise, stop iteration
|
||||||
|
if (ret != alloc_result::no_cch_space) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ra_sched::dl_rach_info(const dl_sched_rar_info_t& rar_info)
|
||||||
|
{
|
||||||
|
logger.info("SCHED: New PRACH tti=%d, preamble=%d, temp_crnti=0x%x, ta_cmd=%d, msg3_size=%d",
|
||||||
|
rar_info.prach_tti,
|
||||||
|
rar_info.preamble_idx,
|
||||||
|
rar_info.temp_crnti,
|
||||||
|
rar_info.ta_cmd,
|
||||||
|
rar_info.msg3_size);
|
||||||
|
|
||||||
|
// RA-RNTI = 1 + t_id + f_id
|
||||||
|
// t_id = index of first subframe specified by PRACH (0<=t_id<10)
|
||||||
|
// f_id = index of the PRACH within subframe, in ascending order of freq domain (0<=f_id<6) (for FDD, f_id=0)
|
||||||
|
uint16_t ra_rnti = 1 + (uint16_t)(rar_info.prach_tti % 10u);
|
||||||
|
|
||||||
|
// find pending rar with same RA-RNTI
|
||||||
|
for (pending_rar_t& r : pending_rars) {
|
||||||
|
if (r.prach_tti.to_uint() == rar_info.prach_tti and ra_rnti == r.ra_rnti) {
|
||||||
|
if (r.msg3_grant.size() >= sched_interface::MAX_RAR_LIST) {
|
||||||
|
logger.warning("PRACH ignored, as the the maximum number of RAR grants per tti has been reached");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
r.msg3_grant.push_back(rar_info);
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new RAR
|
||||||
|
pending_rar_t p;
|
||||||
|
p.ra_rnti = ra_rnti;
|
||||||
|
p.prach_tti = tti_point{rar_info.prach_tti};
|
||||||
|
p.msg3_grant.push_back(rar_info);
|
||||||
|
pending_rars.push_back(p);
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
bwp_ctxt::bwp_ctxt(const bwp_params& bwp_cfg) : cfg(&bwp_cfg), ra(bwp_cfg), grid(bwp_cfg) {}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
serv_cell_ctxt::serv_cell_ctxt(const sched_cell_params& cell_cfg_) : cfg(&cell_cfg_)
|
||||||
|
{
|
||||||
|
for (uint32_t bwp_id = 0; bwp_id < cfg->cell_cfg.bwps.size(); ++bwp_id) {
|
||||||
|
bwps.emplace_back(cell_cfg_.bwps[bwp_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-allocate HARQs in common pool of softbuffers
|
||||||
|
harq_softbuffer_pool::get_instance().init_pool(cfg->nof_prb());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sched_nr_impl
|
||||||
|
} // namespace srsenb
|
@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 "srsenb/hdr/stack/mac/nr/sched_nr_cfg.h"
|
||||||
|
#include "srsenb/hdr/stack/mac/nr/sched_nr_helpers.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
namespace sched_nr_impl {
|
||||||
|
|
||||||
|
bwp_params::bwp_params(const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_, uint32_t cc_, uint32_t bwp_id_) :
|
||||||
|
cell_cfg(cell), sched_cfg(sched_cfg_), cc(cc_), bwp_id(bwp_id_), cfg(cell.bwps[bwp_id_])
|
||||||
|
{
|
||||||
|
P = get_P(cfg.rb_width, cfg.pdsch.rbg_size_cfg_1);
|
||||||
|
N_rbg = get_nof_rbgs(cfg.rb_width, cfg.start_rb, cfg.pdsch.rbg_size_cfg_1);
|
||||||
|
|
||||||
|
srsran_assert(bwp_id != 0 or cfg.pdcch.coreset_present[0], "CORESET#0 has to be active for initial BWP");
|
||||||
|
}
|
||||||
|
|
||||||
|
sched_cell_params::sched_cell_params(uint32_t cc_, const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_) :
|
||||||
|
cc(cc_), cell_cfg(cell), sched_cfg(sched_cfg_)
|
||||||
|
{
|
||||||
|
bwps.reserve(cell.bwps.size());
|
||||||
|
for (uint32_t i = 0; i < cell.bwps.size(); ++i) {
|
||||||
|
bwps.emplace_back(cell, sched_cfg_, cc, i);
|
||||||
|
}
|
||||||
|
srsran_assert(not bwps.empty(), "No BWPs were configured");
|
||||||
|
}
|
||||||
|
|
||||||
|
sched_params::sched_params(const sched_cfg_t& sched_cfg_) : sched_cfg(sched_cfg_) {}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void get_dci_locs(const srsran_coreset_t& coreset,
|
||||||
|
const srsran_search_space_t& search_space,
|
||||||
|
uint16_t rnti,
|
||||||
|
bwp_cce_pos_list& cce_locs)
|
||||||
|
{
|
||||||
|
for (uint32_t sl = 0; sl < SRSRAN_NOF_SF_X_FRAME; ++sl) {
|
||||||
|
for (uint32_t agg_idx = 0; agg_idx < MAX_NOF_AGGR_LEVELS; ++agg_idx) {
|
||||||
|
pdcch_cce_pos_list pdcch_locs;
|
||||||
|
cce_locs[sl][agg_idx].resize(pdcch_locs.capacity());
|
||||||
|
uint32_t n =
|
||||||
|
srsran_pdcch_nr_locations_coreset(&coreset, &search_space, rnti, agg_idx, sl, cce_locs[sl][agg_idx].data());
|
||||||
|
cce_locs[sl][agg_idx].resize(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bwp_ue_cfg::bwp_ue_cfg(uint16_t rnti_, const bwp_params& bwp_cfg_, const ue_cfg_t& uecfg_) :
|
||||||
|
rnti(rnti_), cfg_(&uecfg_), bwp_cfg(&bwp_cfg_)
|
||||||
|
{
|
||||||
|
std::fill(ss_id_to_cce_idx.begin(), ss_id_to_cce_idx.end(), SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE);
|
||||||
|
const auto& pdcch = phy().pdcch;
|
||||||
|
for (uint32_t i = 0; i < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE; ++i) {
|
||||||
|
if (pdcch.search_space_present[i]) {
|
||||||
|
const auto& ss = pdcch.search_space[i];
|
||||||
|
srsran_assert(pdcch.coreset_present[ss.coreset_id],
|
||||||
|
"Invalid mapping search space id=%d to coreset id=%d",
|
||||||
|
ss.id,
|
||||||
|
ss.coreset_id);
|
||||||
|
const auto& coreset = pdcch.coreset[ss.coreset_id];
|
||||||
|
cce_positions_list.emplace_back();
|
||||||
|
get_dci_locs(coreset, ss, rnti, cce_positions_list.back());
|
||||||
|
ss_id_to_cce_idx[ss.id] = cce_positions_list.size() - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ue_cfg_extended::ue_cfg_extended(uint16_t rnti_, const ue_cfg_t& uecfg) : ue_cfg_t(uecfg), rnti(rnti_)
|
||||||
|
{
|
||||||
|
cc_params.resize(carriers.size());
|
||||||
|
for (uint32_t cc = 0; cc < cc_params.size(); ++cc) {
|
||||||
|
cc_params[cc].bwps.resize(1);
|
||||||
|
auto& bwp = cc_params[cc].bwps[0];
|
||||||
|
for (uint32_t ssid = 0; ssid < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE; ++ssid) {
|
||||||
|
if (phy_cfg.pdcch.search_space_present[ssid]) {
|
||||||
|
auto& ss = phy_cfg.pdcch.search_space[ssid];
|
||||||
|
bwp.ss_list[ss.id].emplace();
|
||||||
|
bwp.ss_list[ss.id]->cfg = &ss;
|
||||||
|
get_dci_locs(phy_cfg.pdcch.coreset[ss.coreset_id], ss, rnti, bwp.ss_list[ss.id]->cce_positions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint32_t idx = 0; idx < SRSRAN_UE_DL_NR_MAX_NOF_CORESET; ++idx) {
|
||||||
|
if (phy_cfg.pdcch.coreset_present[idx]) {
|
||||||
|
bwp.coresets.emplace_back();
|
||||||
|
auto& coreset = bwp.coresets.back();
|
||||||
|
coreset.cfg = &phy_cfg.pdcch.coreset[idx];
|
||||||
|
for (auto& ss : bwp.ss_list) {
|
||||||
|
if (ss.has_value() and ss->cfg->coreset_id == coreset.cfg->id) {
|
||||||
|
coreset.ss_list.push_back(ss->cfg->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sched_nr_impl
|
||||||
|
} // namespace srsenb
|
@ -1,52 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2013-2021 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* This file is part of srsRAN.
|
|
||||||
*
|
|
||||||
* srsRAN is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsRAN is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "srsenb/hdr/stack/mac/nr/sched_nr_common.h"
|
|
||||||
|
|
||||||
namespace srsenb {
|
|
||||||
namespace sched_nr_impl {
|
|
||||||
|
|
||||||
sched_cell_params::sched_cell_params(uint32_t cc_, const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_) :
|
|
||||||
cc(cc_), cell_cfg(cell), sched_cfg(sched_cfg_)
|
|
||||||
{}
|
|
||||||
|
|
||||||
sched_params::sched_params(const sched_cfg_t& sched_cfg_) : sched_cfg(sched_cfg_) {}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void get_dci_locs(const srsran_coreset_t& coreset,
|
|
||||||
const srsran_search_space_t& search_space,
|
|
||||||
uint16_t rnti,
|
|
||||||
bwp_cce_pos_list& cce_locs)
|
|
||||||
{
|
|
||||||
for (uint32_t sl = 0; sl < SRSRAN_NOF_SF_X_FRAME; ++sl) {
|
|
||||||
for (uint32_t agg_idx = 0; agg_idx < MAX_NOF_AGGR_LEVELS; ++agg_idx) {
|
|
||||||
pdcch_cce_pos_list pdcch_locs;
|
|
||||||
cce_locs[sl][agg_idx].resize(pdcch_locs.capacity());
|
|
||||||
uint32_t n =
|
|
||||||
srsran_pdcch_nr_locations_coreset(&coreset, &search_space, rnti, agg_idx, sl, cce_locs[sl][agg_idx].data());
|
|
||||||
cce_locs[sl][agg_idx].resize(n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace sched_nr_impl
|
|
||||||
} // namespace srsenb
|
|
@ -0,0 +1,85 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 "srsenb/hdr/stack/mac/nr/sched_nr_helpers.h"
|
||||||
|
#include "srsenb/hdr/stack/mac/nr/sched_nr_harq.h"
|
||||||
|
#include "srsenb/hdr/stack/mac/nr/sched_nr_ue.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
namespace sched_nr_impl {
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool fill_dci_rar(prb_interval interv, const bwp_params& cell, srsran_dci_dl_nr_t& dci)
|
||||||
|
{
|
||||||
|
dci.mcs = 5;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename DciDlOrUl>
|
||||||
|
void fill_dci_common(const slot_ue& ue, const bwp_params& bwp_cfg, DciDlOrUl& dci)
|
||||||
|
{
|
||||||
|
const static uint32_t rv_idx[4] = {0, 2, 3, 1};
|
||||||
|
|
||||||
|
dci.bwp_id = ue.cfg->active_bwp().bwp_id;
|
||||||
|
dci.cc_id = ue.cc;
|
||||||
|
dci.tpc = 1;
|
||||||
|
// harq
|
||||||
|
harq_proc* h = std::is_same<DciDlOrUl, srsran_dci_dl_nr_t>::value ? ue.h_dl : ue.h_ul;
|
||||||
|
dci.pid = h->pid;
|
||||||
|
dci.ndi = h->ndi();
|
||||||
|
dci.mcs = h->mcs();
|
||||||
|
dci.rv = rv_idx[h->nof_retx() % 4];
|
||||||
|
// PRB assignment
|
||||||
|
const prb_grant& grant = h->prbs();
|
||||||
|
if (grant.is_alloc_type0()) {
|
||||||
|
dci.freq_domain_assigment = grant.rbgs().to_uint64();
|
||||||
|
} else {
|
||||||
|
dci.freq_domain_assigment =
|
||||||
|
srsran_ra_nr_type1_riv(bwp_cfg.cfg.rb_width, grant.prbs().start(), grant.prbs().length());
|
||||||
|
}
|
||||||
|
dci.time_domain_assigment = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill_dl_dci_ue_fields(const slot_ue& ue,
|
||||||
|
const bwp_params& bwp_cfg,
|
||||||
|
uint32_t ss_id,
|
||||||
|
srsran_dci_location_t dci_pos,
|
||||||
|
srsran_dci_dl_nr_t& dci)
|
||||||
|
{
|
||||||
|
// Note: DCI location may not be the final one, as scheduler may rellocate the UE PDCCH. However, the remaining DCI
|
||||||
|
// params are independent of the exact DCI location
|
||||||
|
bool ret = ue.cfg->phy().get_dci_ctx_pdsch_rnti_c(ss_id, dci_pos, ue.rnti, dci.ctx);
|
||||||
|
srsran_assert(ret, "Invalid DL DCI format");
|
||||||
|
|
||||||
|
fill_dci_common(ue, bwp_cfg, dci);
|
||||||
|
if (dci.ctx.format == srsran_dci_format_nr_1_0) {
|
||||||
|
dci.harq_feedback = ue.cfg->phy().harq_ack.dl_data_to_ul_ack[ue.pdsch_tti.sf_idx()] - 1;
|
||||||
|
} else {
|
||||||
|
dci.harq_feedback = ue.pdsch_tti.sf_idx();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill_ul_dci_ue_fields(const slot_ue& ue,
|
||||||
|
const bwp_params& bwp_cfg,
|
||||||
|
uint32_t ss_id,
|
||||||
|
srsran_dci_location_t dci_pos,
|
||||||
|
srsran_dci_ul_nr_t& dci)
|
||||||
|
{
|
||||||
|
bool ret = ue.cfg->phy().get_dci_ctx_pdsch_rnti_c(ss_id, dci_pos, ue.rnti, dci.ctx);
|
||||||
|
srsran_assert(ret, "Invalid DL DCI format");
|
||||||
|
|
||||||
|
fill_dci_common(ue, bwp_cfg, dci);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sched_nr_impl
|
||||||
|
} // namespace srsenb
|
@ -1,111 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2013-2021 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* This file is part of srsRAN.
|
|
||||||
*
|
|
||||||
* srsRAN is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsRAN is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "srsenb/hdr/stack/mac/nr/sched_nr_phy_helpers.h"
|
|
||||||
#include "srsenb/hdr/stack/mac/nr/sched_nr_harq.h"
|
|
||||||
#include "srsenb/hdr/stack/mac/nr/sched_nr_ue.h"
|
|
||||||
|
|
||||||
namespace srsenb {
|
|
||||||
namespace sched_nr_impl {
|
|
||||||
|
|
||||||
/// Table 6.1.2.2.1-1 - Nominal RBG size P
|
|
||||||
uint32_t get_P(uint32_t bwp_nof_prb, bool config_1_or_2)
|
|
||||||
{
|
|
||||||
srsran_assert(bwp_nof_prb > 0 and bwp_nof_prb <= 275, "Invalid BWP size");
|
|
||||||
if (bwp_nof_prb <= 36) {
|
|
||||||
return config_1_or_2 ? 2 : 4;
|
|
||||||
}
|
|
||||||
if (bwp_nof_prb <= 72) {
|
|
||||||
return config_1_or_2 ? 4 : 8;
|
|
||||||
}
|
|
||||||
if (bwp_nof_prb <= 144) {
|
|
||||||
return config_1_or_2 ? 8 : 16;
|
|
||||||
}
|
|
||||||
return 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t get_nof_rbgs(uint32_t bwp_nof_prb, uint32_t bwp_start, bool config1_or_2)
|
|
||||||
{
|
|
||||||
uint32_t P = get_P(bwp_nof_prb, config1_or_2);
|
|
||||||
return srsran::ceil_div(bwp_nof_prb + (bwp_start % P), P);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t get_rbg_size(uint32_t bwp_nof_prb, uint32_t bwp_start, bool config1_or_2, uint32_t rbg_idx)
|
|
||||||
{
|
|
||||||
uint32_t P = get_P(bwp_nof_prb, config1_or_2);
|
|
||||||
uint32_t nof_rbgs = get_nof_rbgs(bwp_nof_prb, bwp_start, config1_or_2);
|
|
||||||
if (rbg_idx == 0) {
|
|
||||||
return P - (bwp_start % P);
|
|
||||||
}
|
|
||||||
if (rbg_idx == nof_rbgs - 1) {
|
|
||||||
uint32_t ret = (bwp_start + bwp_nof_prb) % P;
|
|
||||||
return ret > 0 ? ret : P;
|
|
||||||
}
|
|
||||||
return P;
|
|
||||||
}
|
|
||||||
|
|
||||||
void bitmap_to_prb_array(const rbgmask_t& bitmap, uint32_t bwp_nof_prb, srsran_sch_grant_nr_t& grant)
|
|
||||||
{
|
|
||||||
uint32_t count = 0;
|
|
||||||
grant.nof_prb = bwp_nof_prb;
|
|
||||||
for (uint32_t rbg = 0; rbg < bitmap.size(); ++rbg) {
|
|
||||||
bool val = bitmap.test(rbg);
|
|
||||||
uint32_t rbg_size = get_rbg_size(bwp_nof_prb, 0, true, rbg);
|
|
||||||
for (uint32_t prb = count; prb < count + rbg_size; ++prb) {
|
|
||||||
grant.prb_idx[prb] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void fill_dci_harq(const harq_proc& h, srsran_dci_dl_nr_t& dci)
|
|
||||||
{
|
|
||||||
dci.pid = h.pid;
|
|
||||||
dci.ndi = h.ndi();
|
|
||||||
dci.mcs = h.mcs();
|
|
||||||
}
|
|
||||||
|
|
||||||
void fill_dci_ue_cfg(const slot_ue& ue, srsran_dci_dl_nr_t& dci)
|
|
||||||
{
|
|
||||||
dci.bwp_id = ue.bwp_id;
|
|
||||||
dci.cc_id = ue.cc;
|
|
||||||
dci.ctx.rnti = ue.rnti;
|
|
||||||
dci.tpc = 1;
|
|
||||||
fill_dci_harq(*ue.h_dl, dci);
|
|
||||||
}
|
|
||||||
|
|
||||||
void fill_dci_harq(const harq_proc& h, srsran_dci_ul_nr_t& dci)
|
|
||||||
{
|
|
||||||
dci.pid = h.pid;
|
|
||||||
dci.ndi = h.ndi();
|
|
||||||
dci.mcs = h.mcs();
|
|
||||||
}
|
|
||||||
|
|
||||||
void fill_dci_ue_cfg(const slot_ue& ue, srsran_dci_ul_nr_t& dci)
|
|
||||||
{
|
|
||||||
dci.bwp_id = ue.bwp_id;
|
|
||||||
dci.cc_id = ue.cc;
|
|
||||||
dci.ctx.rnti = ue.rnti;
|
|
||||||
dci.tpc = 1;
|
|
||||||
fill_dci_harq(*ue.h_ul, dci);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace sched_nr_impl
|
|
||||||
} // namespace srsenb
|
|
@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 "srsenb/hdr/stack/mac/nr/sched_nr_rb.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
namespace sched_nr_impl {
|
||||||
|
|
||||||
|
/// TS 38.214, Table 6.1.2.2.1-1 - Nominal RBG size P
|
||||||
|
uint32_t get_P(uint32_t bwp_nof_prb, bool config_1_or_2)
|
||||||
|
{
|
||||||
|
srsran_assert(bwp_nof_prb > 0 and bwp_nof_prb <= 275, "Invalid BWP size");
|
||||||
|
if (bwp_nof_prb <= 36) {
|
||||||
|
return config_1_or_2 ? 2 : 4;
|
||||||
|
}
|
||||||
|
if (bwp_nof_prb <= 72) {
|
||||||
|
return config_1_or_2 ? 4 : 8;
|
||||||
|
}
|
||||||
|
if (bwp_nof_prb <= 144) {
|
||||||
|
return config_1_or_2 ? 8 : 16;
|
||||||
|
}
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TS 38.214 - total number of RBGs for a uplink bandwidth part of size "bwp_nof_prb" PRBs
|
||||||
|
uint32_t get_nof_rbgs(uint32_t bwp_nof_prb, uint32_t bwp_start, bool config1_or_2)
|
||||||
|
{
|
||||||
|
uint32_t P = get_P(bwp_nof_prb, config1_or_2);
|
||||||
|
return srsran::ceil_div(bwp_nof_prb + (bwp_start % P), P);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t get_rbg_size(uint32_t bwp_nof_prb, uint32_t bwp_start, bool config1_or_2, uint32_t rbg_idx)
|
||||||
|
{
|
||||||
|
uint32_t P = get_P(bwp_nof_prb, config1_or_2);
|
||||||
|
uint32_t nof_rbgs = get_nof_rbgs(bwp_nof_prb, bwp_start, config1_or_2);
|
||||||
|
if (rbg_idx == 0) {
|
||||||
|
return P - (bwp_start % P);
|
||||||
|
}
|
||||||
|
if (rbg_idx == nof_rbgs - 1) {
|
||||||
|
uint32_t ret = (bwp_start + bwp_nof_prb) % P;
|
||||||
|
return ret > 0 ? ret : P;
|
||||||
|
}
|
||||||
|
return P;
|
||||||
|
}
|
||||||
|
|
||||||
|
bwp_rb_bitmap::bwp_rb_bitmap(uint32_t bwp_nof_prbs, uint32_t bwp_prb_start_, bool config1_or_2) :
|
||||||
|
prbs_(bwp_nof_prbs),
|
||||||
|
rbgs_(get_nof_rbgs(bwp_nof_prbs, bwp_prb_start_, config1_or_2)),
|
||||||
|
P_(get_P(bwp_nof_prbs, config1_or_2)),
|
||||||
|
Pnofbits(log2(P_)),
|
||||||
|
first_rbg_size(get_rbg_size(bwp_nof_prbs, bwp_prb_start_, config1_or_2, 0))
|
||||||
|
{}
|
||||||
|
|
||||||
|
uint32_t bwp_rb_bitmap::prb_to_rbg_idx(uint32_t prb_idx) const
|
||||||
|
{
|
||||||
|
return ((prb_idx + P_ - first_rbg_size) >> Pnofbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bwp_rb_bitmap::add_prbs_to_rbgs(const prb_bitmap& grant)
|
||||||
|
{
|
||||||
|
int idx = 0;
|
||||||
|
do {
|
||||||
|
idx = grant.find_lowest(idx, grant.size(), true);
|
||||||
|
if (idx < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t rbg_idx = prb_to_rbg_idx(idx);
|
||||||
|
rbgs_.set(rbg_idx, true);
|
||||||
|
idx++;
|
||||||
|
} while (idx != (int)prbs_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void bwp_rb_bitmap::add_prbs_to_rbgs(const prb_interval& grant)
|
||||||
|
{
|
||||||
|
uint32_t rbg_start = prb_to_rbg_idx(grant.start());
|
||||||
|
uint32_t rbg_stop = std::min(prb_to_rbg_idx(grant.stop() - 1) + 1u, (uint32_t)rbgs_.size());
|
||||||
|
rbgs_.fill(rbg_start, rbg_stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bwp_rb_bitmap::add_rbgs_to_prbs(const rbg_bitmap& grant)
|
||||||
|
{
|
||||||
|
int idx = 0;
|
||||||
|
do {
|
||||||
|
idx = grant.find_lowest(idx, grant.size(), true);
|
||||||
|
if (idx < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t prb_idx = (idx - 1) * P_ + first_rbg_size;
|
||||||
|
uint32_t prb_end = std::min(prb_idx + ((idx == 0) ? first_rbg_size : P_), (uint32_t)prbs_.size());
|
||||||
|
prbs_.fill(prb_idx, prb_end);
|
||||||
|
idx++;
|
||||||
|
} while (idx != (int)prbs_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sched_nr_impl
|
||||||
|
} // namespace srsenb
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue