Merge branch 'next' into agpl_next

master
Codebot 3 years ago committed by Your Name
commit 635d0bc6e9

@ -72,6 +72,7 @@ option(ENABLE_GUI "Enable GUI (using srsGUI)" ON)
option(ENABLE_UHD "Enable UHD" ON)
option(ENABLE_BLADERF "Enable BladeRF" ON)
option(ENABLE_SOAPYSDR "Enable SoapySDR" ON)
option(ENABLE_SKIQ "Enable Sidekiq SDK" ON)
option(ENABLE_ZEROMQ "Enable ZeroMQ" ON)
option(ENABLE_HARDSIM "Enable support for SIM cards" ON)
@ -223,6 +224,15 @@ if(ENABLE_UHD)
endif(UHD_FOUND)
endif(ENABLE_UHD)
# SKIQ
if (ENABLE_SKIQ)
find_package(SKIQ)
if(SKIQ_FOUND)
include_directories(${SKIQ_INCLUDE_DIRS})
link_directories(${SKIQ_LIBRARY_DIRS})
endif(SKIQ_FOUND)
endif (ENABLE_SKIQ)
# BladeRF
if(ENABLE_BLADERF)
find_package(bladeRF)
@ -255,12 +265,12 @@ if(ENABLE_TIMEPROF)
add_definitions(-DENABLE_TIMEPROF)
endif(ENABLE_TIMEPROF)
if(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR ZEROMQ_FOUND)
if(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR ZEROMQ_FOUND OR SKIQ_FOUND)
set(RF_FOUND TRUE CACHE INTERNAL "RF frontend found")
else(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR ZEROMQ_FOUND)
else(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR ZEROMQ_FOUND OR SKIQ_FOUND)
set(RF_FOUND FALSE CACHE INTERNAL "RF frontend found")
add_definitions(-DDISABLE_RF)
endif(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR ZEROMQ_FOUND)
endif(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR ZEROMQ_FOUND OR SKIQ_FOUND)
# Boost
if(BUILD_STATIC)

@ -0,0 +1,66 @@
#
# 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(FindPkgConfig)
#PKG_CHECK_MODULES(SKIQ SKIQ)
IF(NOT SKIQ_FOUND)
FIND_PATH(
SKIQ_INCLUDE_DIRS
NAMES sidekiq_api.h
HINTS $ENV{SKIQ_DIR}/inc
$ENV{SKIQ_DIR}/sidekiq_core/inc
PATHS /usr/local/include
/usr/include
)
FIND_LIBRARY(
SKIQ_LIBRARY
NAMES sidekiq__x86_64.gcc
HINTS $ENV{SKIQ_DIR}/lib
PATHS /usr/local/lib
/usr/lib
/usr/lib/x86_64-linux-gnu
/usr/local/lib64
/usr/local/lib32
)
FIND_LIBRARY(
SKIQ_LIBRARY_GLIB
NAMES libglib-2.0.a
HINTS $ENV{SKIQ_DIR}/lib/support/x86_64.gcc/usr/lib/epiq
PATHS /usr/local/lib
/usr/lib
/usr/lib/epiq
/usr/lib/x86_64-linux-gnu
/usr/local/lib64
/usr/local/lib32
)
FIND_LIBRARY(
SKIQ_LIBRARY_USB
NAMES libusb-1.0.a
HINTS $ENV{SKIQ_DIR}/lib/support/x86_64.gcc/usr/lib/epiq
PATHS /usr/local/lib
/usr/lib
/usr/lib/epiq
/usr/lib/x86_64-linux-gnu
/usr/local/lib64
/usr/local/lib32
)
set(SKIQ_LIBRARIES ${SKIQ_LIBRARY} ${SKIQ_LIBRARY_GLIB} ${SKIQ_LIBRARY_USB})
message(STATUS "SKIQ LIBRARIES " ${SKIQ_LIBRARIES})
message(STATUS "SKIQ INCLUDE DIRS " ${SKIQ_INCLUDE_DIRS})
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SKIQ DEFAULT_MSG SKIQ_LIBRARIES SKIQ_INCLUDE_DIRS)
MARK_AS_ADVANCED(SKIQ_LIBRARIES SKIQ_INCLUDE_DIRS)
ENDIF(NOT SKIQ_FOUND)

@ -98,6 +98,9 @@ bool make_phy_max_code_rate(const asn1::rrc_nr::pucch_format_cfg_s& pucch_format
bool make_phy_res_config(const asn1::rrc_nr::pucch_res_s& pucch_res,
uint32_t format_2_max_code_rate,
srsran_pucch_nr_resource_t* srsran_pucch_nr_resource);
bool make_phy_res_config(const srsran_pucch_nr_resource_t& in_pucch_res,
asn1::rrc_nr::pucch_res_s& out_pucch_res,
uint32_t pucch_res_id);
bool make_phy_sr_resource(const asn1::rrc_nr::sched_request_res_cfg_s& sched_request_res_cfg,
srsran_pucch_nr_sr_resource_t* srsran_pucch_nr_sr_resource);
bool make_phy_pusch_alloc_type(const asn1::rrc_nr::pusch_cfg_s& pusch_cfg,

@ -39,14 +39,31 @@ public:
* - SSB: 5ms
*/
R_CARRIER_CUSTOM_10MHZ = 0,
} carrier = R_CARRIER_CUSTOM_10MHZ;
/**
* @brief Carrier reference configuration for 10MHz serving cell bandwidth
* - BW: 20 MHZ (106 PRB)
* - PCI: 500
* - SCS: 15 kHz
* - SSB: 5ms
*/
R_CARRIER_CUSTOM_20MHZ,
R_CARRIER_COUNT
} carrier = R_CARRIER_CUSTOM_10MHZ;
const std::array<std::string, R_CARRIER_COUNT> R_CARRIER_STRING = {{"10MHz", "20MHz"}};
enum {
/**
* @brief TDD custom reference 5 slot DL and 5 slot UL
*/
R_TDD_CUSTOM_6_4 = 0,
} tdd = R_TDD_CUSTOM_6_4;
/**
* @brief TDD pattern FR1.15-1 defined in TS38.101-4 Table A.1.2-1
*/
R_TDD_FR1_15_1,
R_TDD_COUNT,
} tdd = R_TDD_CUSTOM_6_4;
const std::array<std::string, R_TDD_COUNT> R_TDD_STRING = {{"6D+4U", "FR1.15-1"}};
enum {
/**
@ -65,7 +82,19 @@ public:
* - No DMRS dedicated configuration
*/
R_PDSCH_DEFAULT = 0,
} pdsch = R_PDSCH_DEFAULT;
/**
* @brief PDSCH parameters described in TS 38.101-4 Table 5.2.2.2.1-2 for the test described in table 5.2.2.2.1-3
*/
R_PDSCH_TS38101_5_2_1,
/**
* @brief Invalid PDSCH reference channel
*/
R_PDSCH_COUNT
} pdsch = R_PDSCH_DEFAULT;
const std::array<std::string, R_PDSCH_COUNT> R_PDSCH_STRING = {{"default", "ts38101/5.2-1"}};
enum {
/**
@ -107,6 +136,9 @@ public:
*/
R_PRACH_DEFAULT_LTE,
} prach = R_PRACH_DEFAULT_LTE;
reference_cfg_t() = default;
explicit reference_cfg_t(const std::string& args);
};
phy_cfg_nr_default_t(const reference_cfg_t& reference_cfg);
@ -116,11 +148,13 @@ private:
* Carrier make helper methods
*/
static void make_carrier_custom_10MHz(srsran_carrier_nr_t& carrier);
static void make_carrier_custom_20MHz(srsran_carrier_nr_t& carrier);
/**
* TDD make helper methods
*/
static void make_tdd_custom_6_4(srsran_tdd_config_nr_t& tdd);
static void make_tdd_fr1_15_1(srsran_tdd_config_nr_t& tdd);
/**
* PDCCH make helper methods
@ -131,6 +165,7 @@ private:
* PDSCH make helper methods
*/
static void make_pdsch_default(srsran_sch_hl_cfg_nr_t& pdsch);
static void make_pdsch_2_1_1_tdd(const srsran_carrier_nr_t& carrier, srsran_sch_hl_cfg_nr_t& pdsch);
/**
* PUSCH make helper methods

@ -0,0 +1,172 @@
/**
*
* \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_SLOT_POINT_H
#define SRSRAN_SLOT_POINT_H
#include "srsran/adt/interval.h"
#include "srsran/common/srsran_assert.h"
namespace srsran {
class slot_point
{
uint32_t numerology_ : 3;
uint32_t count_ : 29;
const static uint8_t NOF_NUMEROLOGIES = 5;
const static uint16_t NOF_SFNS = 1024;
const static uint8_t NOF_SUBFRAMES_PER_FRAME = 10;
uint32_t nof_slots_per_hf() const { return nof_slots_per_frame() * NOF_SFNS; }
public:
slot_point() : numerology_(NOF_NUMEROLOGIES), count_(0) {}
slot_point(uint8_t numerology, uint32_t count) : numerology_(numerology), count_(count)
{
srsran_assert(numerology < NOF_NUMEROLOGIES, "Invalid numerology idx=%d passed", (int)numerology);
srsran_assert(count < nof_slots_per_hf(), "Invalid slot count=%d passed", (int)count);
}
slot_point(uint8_t numerology, uint16_t sfn_val, uint8_t slot) :
numerology_(numerology), count_(slot + sfn_val * nof_slots_per_frame())
{
srsran_assert(numerology < NOF_NUMEROLOGIES, "Invalid numerology idx=%d passed", (int)numerology);
srsran_assert(sfn_val < NOF_SFNS, "Invalid SFN=%d provided", (int)sfn_val);
srsran_assert(slot < nof_slots_per_frame(),
"Slot index=%d exceeds maximum number of slots=%d",
(int)slot,
(int)nof_slots_per_frame());
}
bool valid() const { return numerology_ < NOF_NUMEROLOGIES; }
uint8_t nof_slots_per_subframe() const { return 1U << numerology_; }
uint8_t nof_slots_per_frame() const { return nof_slots_per_subframe() * NOF_SUBFRAMES_PER_FRAME; }
uint16_t sfn() const { return count_ / nof_slots_per_frame(); }
uint16_t subframe_idx() const { return slot_idx() / nof_slots_per_subframe(); }
uint8_t slot_idx() const { return count_ % nof_slots_per_frame(); }
uint8_t numerology_idx() const { return numerology_; }
uint32_t to_uint() const { return count_; }
explicit operator uint32_t() const { return count_; }
void clear() { numerology_ = NOF_NUMEROLOGIES; }
// operators
bool operator==(const slot_point& other) const { return other.count_ == count_ and other.numerology_ == numerology_; }
bool operator!=(const slot_point& other) const { return not(*this == other); }
bool operator<(const slot_point& other) const
{
srsran_assert(numerology_idx() == other.numerology_idx(), "Comparing slots of different numerologies");
int a = static_cast<int>(other.count_) - static_cast<int>(count_);
if (a > 0) {
return (a < (int)nof_slots_per_hf() / 2);
}
return (a < -(int)nof_slots_per_hf() / 2);
}
bool operator<=(const slot_point& other) const { return (*this == other) or (*this < other); }
bool operator>=(const slot_point& other) const { return not(*this < other); }
bool operator>(const slot_point& other) const { return (*this != other) and *this >= other; }
int32_t operator-(const slot_point& other) const
{
int a = static_cast<int>(count_) - static_cast<int>(other.count_);
if (a >= (int)nof_slots_per_hf() / 2) {
return a - nof_slots_per_hf();
}
if (a < -(int)nof_slots_per_hf() / 2) {
return a + nof_slots_per_hf();
}
return a;
}
slot_point& operator++()
{
count_++;
if (count_ == nof_slots_per_hf()) {
count_ = 0;
}
return *this;
}
slot_point operator++(int)
{
slot_point ret{*this};
this-> operator++();
return ret;
}
slot_point& operator+=(uint32_t jump)
{
count_ = (count_ + jump) % nof_slots_per_hf();
return *this;
}
slot_point& operator-=(uint32_t jump)
{
int a = (static_cast<int>(count_) - static_cast<int>(jump)) % static_cast<int>(nof_slots_per_hf());
if (a < 0) {
a += nof_slots_per_hf();
}
count_ = a;
return *this;
}
bool is_in_interval(slot_point begin, slot_point end) const { return (*this >= begin and *this < end); }
};
inline slot_point operator+(slot_point slot, uint32_t jump)
{
slot += jump;
return slot;
}
inline slot_point operator+(uint32_t jump, slot_point slot)
{
slot += jump;
return slot;
}
inline slot_point operator-(slot_point slot, uint32_t jump)
{
slot -= jump;
return slot;
}
inline slot_point max(slot_point s1, slot_point s2)
{
return s1 > s2 ? s1 : s2;
}
inline slot_point min(slot_point s1, slot_point s2)
{
return s1 < s2 ? s1 : s2;
}
using slot_interval = srsran::interval<slot_point>;
} // namespace srsran
namespace fmt {
template <>
struct formatter<srsran::slot_point> {
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin())
{
return ctx.begin();
}
template <typename FormatContext>
auto format(srsran::slot_point slot, FormatContext& ctx) -> decltype(std::declval<FormatContext>().out())
{
return format_to(ctx.out(), "{}/{}", slot.sfn(), slot.slot_idx());
}
};
} // namespace fmt
namespace srsenb {
using slot_point = srsran::slot_point;
using slot_interval = srsran::slot_interval;
} // namespace srsenb
#endif // SRSRAN_SLOT_POINT_H

@ -22,21 +22,59 @@
#ifndef SRSRAN_TEST_PCAP_H
#define SRSRAN_TEST_PCAP_H
#if HAVE_PCAP
/**
* @brief Helper class for tests that wish to dump RRC, PDCP or MAC SDUs into PCAP files in order to inspect them with
* Wireshark.
*
* Depending on the layer of interest, the class adds the protocol header for the layers below so that Wireshark can
* disect them. For RRC for example, both PDCP and RLC AM dummy headers are added.
*
* There is an EUTRA and NR version for the helper methods.
*
*/
#include "srsran/common/mac_pcap.h"
#include "srsran/mac/mac_sch_pdu_nr.h"
static std::unique_ptr<srsran::mac_pcap> pcap_handle = nullptr;
#define PCAP_CRNTI (0x1001)
#define PCAP_TTI (666)
#endif
namespace srsran {
int write_mac_sdu_nr(const uint32_t lcid, const uint8_t* payload, const uint32_t len);
/**
* @brief Writes a MAC SDU of a gives LCID for NR
*
* @param lcid The logical channel ID of the SDU
* @param payload Pointer to payload
* @param len Length
* @return int
*/
int write_mac_sdu_nr(const uint32_t lcid, const uint8_t* payload, const uint32_t len)
{
if (pcap_handle) {
byte_buffer_t tx_buffer;
srsran::mac_sch_pdu_nr tx_pdu;
tx_pdu.init_tx(&tx_buffer, len + 10);
tx_pdu.add_sdu(lcid, payload, len);
tx_pdu.pack();
pcap_handle->write_dl_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, 0, PCAP_TTI);
return SRSRAN_SUCCESS;
}
return SRSRAN_ERROR;
}
int write_rlc_am_sdu_nr(const uint32_t lcid, const uint8_t* payload, const uint32_t len)
/**
* @brief Writes a PDCP SDU (e.g. RRC DL-DCCH PDU)
*
* Both PDCP and RLC AM header (dummy for SN=0) are added.
*
* @param lcid The logical channel ID of the SDU
* @param payload Pointer to payload
* @param len Length
* @return int
*/
int write_pdcp_sdu_nr(const uint32_t lcid, const uint8_t* payload, const uint32_t len)
{
#if HAVE_PCAP
if (pcap_handle) {
byte_buffer_t mac_sdu;
// Add dummy RLC AM PDU header
@ -52,23 +90,6 @@ int write_rlc_am_sdu_nr(const uint32_t lcid, const uint8_t* payload, const uint3
mac_sdu.N_bytes += len;
return write_mac_sdu_nr(lcid, mac_sdu.msg, mac_sdu.N_bytes);
}
#endif // HAVE_PCAP
return SRSRAN_ERROR;
}
int write_mac_sdu_nr(const uint32_t lcid, const uint8_t* payload, const uint32_t len)
{
#if HAVE_PCAP
if (pcap_handle) {
byte_buffer_t tx_buffer;
srsran::mac_sch_pdu_nr tx_pdu;
tx_pdu.init_tx(&tx_buffer, len + 10);
tx_pdu.add_sdu(lcid, payload, len);
tx_pdu.pack();
pcap_handle->write_dl_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, 0, PCAP_TTI);
return SRSRAN_SUCCESS;
}
#endif // HAVE_PCAP
return SRSRAN_ERROR;
}

@ -63,8 +63,8 @@ public:
virtual void work_imp() = 0;
private:
uint32_t my_id = 0;
thread_pool* my_parent = nullptr;
uint32_t my_id = 0;
thread_pool* my_parent = nullptr;
std::atomic<bool> running = {true};
void run_thread();
@ -73,22 +73,24 @@ public:
bool is_stopped() const;
};
thread_pool(uint32_t nof_workers);
void init_worker(uint32_t id, worker*, uint32_t prio = 0, uint32_t mask = 255);
void stop();
worker* wait_worker_id(uint32_t id);
worker* wait_worker(uint32_t tti);
worker* wait_worker_nb(uint32_t tti);
void start_worker(worker*);
void start_worker(uint32_t id);
worker* get_worker(uint32_t id);
uint32_t get_nof_workers();
thread_pool(uint32_t nof_workers_, std::string id_ = "");
void init_worker(uint32_t id, worker*, uint32_t prio = 0, uint32_t mask = 255);
void stop();
worker* wait_worker_id(uint32_t id);
worker* wait_worker(uint32_t tti);
worker* wait_worker_nb(uint32_t tti);
void start_worker(worker*);
void start_worker(uint32_t id);
worker* get_worker(uint32_t id);
uint32_t get_nof_workers();
std::string get_id();
private:
bool find_finished_worker(uint32_t tti, uint32_t* id);
typedef enum { STOP, IDLE, START_WORK, WORKER_READY, WORKING } worker_status;
std::string id; // id is prepended to every worker
std::vector<worker*> workers = {};
uint32_t nof_workers = 0;
uint32_t max_workers = 0;

@ -30,6 +30,9 @@
#include "srsran/interfaces/rlc_interface_types.h"
#include "srsran/interfaces/rrc_interface_types.h"
#include "srsran/interfaces/sched_interface.h"
// EUTRA interfaces that are used unmodified
#include "srsran/interfaces/enb_mac_interfaces.h"
#include "srsran/interfaces/enb_rrc_interfaces.h"
namespace srsenb {
@ -46,11 +49,9 @@ public:
virtual uint16_t reserve_rnti() = 0;
};
class mac_interface_rlc_nr
{
public:
virtual int rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) = 0;
};
// NR interface is identical to EUTRA interface
class mac_interface_rlc_nr : public mac_interface_rlc
{};
/*****************************
* RLC INTERFACES
@ -161,20 +162,18 @@ public:
/// User management
virtual int add_user(uint16_t rnti) = 0;
};
class rrc_interface_rlc_nr
// NR interface is almost identical to EUTRA version
class rrc_interface_rlc_nr : public rrc_interface_rlc
{
public:
virtual void read_pdu_pcch(uint8_t* payload, uint32_t payload_size) = 0;
virtual void max_retx_attempted(uint16_t rnti) = 0;
virtual void protocol_failure(uint16_t rnti) = 0;
virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0;
virtual const char* get_rb_name(uint32_t lcid) = 0;
virtual void read_pdu_pcch(uint8_t* payload, uint32_t payload_size) = 0;
virtual const char* get_rb_name(uint32_t lcid) = 0;
};
class rrc_interface_pdcp_nr
// NR interface identical to EUTRA version
class rrc_interface_pdcp_nr : public rrc_interface_pdcp
{
public:
virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0;
virtual void notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) = 0;
};
class phy_interface_rrc_nr
@ -232,8 +231,8 @@ public:
};
struct pdsch_t {
srsran_sch_cfg_nr_t sch = {}; ///< PDSCH configuration
std::array<uint8_t*, SRSRAN_MAX_TB> data = {}; ///< Data pointer
srsran_sch_cfg_nr_t sch = {}; ///< PDSCH configuration
std::array<srsran::byte_buffer_t*, SRSRAN_MAX_TB> data = {}; ///< Data pointer
};
struct ssb_t {
@ -249,9 +248,8 @@ public:
};
struct pusch_t {
uint32_t pid = 0; ///< HARQ process ID
srsran_sch_cfg_nr_t sch = {}; ///< PUSCH configuration
std::array<uint8_t*, SRSRAN_MAX_TB> data = {}; ///< Data pointer
uint32_t pid = 0; ///< HARQ process ID
srsran_sch_cfg_nr_t sch = {}; ///< PUSCH configuration
};
/**
@ -281,14 +279,23 @@ public:
};
struct pusch_info_t {
uint16_t rnti;
uint32_t pid = 0; ///< HARQ process ID
// Context
uint16_t rnti; ///< UE temporal identifier
uint32_t pid = 0; ///< HARQ process ID
// SCH and UCI payload information
srsran_pusch_res_nr_t pusch_data;
srsran_uci_cfg_nr_t uci_cfg; ///< Provides UCI configuration, so stack does not need to keep the pending state
// ... add signal measurements here
// Actual SCH PDU
srsran::unique_byte_buffer_t pdu = nullptr;
// PUSCH signal measurements
// ...
};
struct rach_info_t {
uint32_t slot_index;
uint32_t preamble;
uint32_t time_adv;
};
@ -297,21 +304,12 @@ public:
virtual int get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) = 0;
virtual int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) = 0;
virtual int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) = 0;
virtual int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) = 0;
virtual int pusch_info(const srsran_slot_cfg_t& slot_cfg, pusch_info_t& pusch_info) = 0;
virtual void rach_detected(const rach_info_t& rach_info) = 0;
};
class stack_interface_phy_nr : public mac_interface_phy_nr, public srsran::stack_interface_phy_nr
{
public:
struct rx_data_ind_t {
uint32_t tti;
uint16_t rnti;
srsran::unique_byte_buffer_t tb;
};
virtual int rx_data_indication(rx_data_ind_t& grant) = 0;
};
{};
} // namespace srsenb

@ -59,6 +59,7 @@ public:
virtual bool user_release(uint16_t rnti, asn1::ngap_nr::cause_radio_network_e cause_radio) = 0;
virtual bool is_amf_connected() = 0;
virtual void ue_ctxt_setup_complete(uint16_t rnti) = 0;
virtual void ue_notify_rrc_reconf_complete(uint16_t rnti, bool outcome) = 0;
};
} // namespace srsenb

@ -21,11 +21,18 @@
#ifndef SRSRAN_GNB_RRC_NR_INTERFACES_H
#define SRSRAN_GNB_RRC_NR_INTERFACES_H
#include "srsran/asn1/ngap.h"
#include "srsran/common/byte_buffer.h"
namespace srsenb {
class rrc_interface_ngap_nr
{
public:
virtual int ue_set_security_cfg_key(uint16_t rnti, const asn1::fixed_bitstring<256, false, true>& key) = 0;
virtual int ue_set_bitrates(uint16_t rnti, const asn1::ngap_nr::ue_aggregate_maximum_bit_rate_s& rates) = 0;
virtual int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap_nr::ue_security_cap_s& caps) = 0;
virtual int start_security_mode_procedure(uint16_t rnti) = 0;
virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0;
};
} // namespace srsenb

@ -140,6 +140,7 @@ struct rach_nr_cfg_t {
uint32_t powerRampingStep;
uint32_t ra_responseWindow;
uint32_t ra_ContentionResolutionTimer;
bool skip_rar;
rach_nr_cfg_t() { reset(); }
void reset()
@ -149,6 +150,7 @@ struct rach_nr_cfg_t {
powerRampingStep = 0;
preambleTransMax = 0;
ra_responseWindow = 0;
skip_rar = false;
}
};

@ -27,19 +27,69 @@
namespace srsran {
/**
* @brief Descibes a physical layer common interface
*/
class phy_common_interface
{
private:
std::mutex tx_mutex; ///< Protect Tx attributes
std::condition_variable tx_cvar; ///< Tx condition variable
bool tx_hold = false; ///< Hold threads until the signal is transmitted
protected:
/**
* @brief Waits for the last worker to call `last_worker()` to prevent that the current SF worker is released and
* overwrites the transmit signal prior transmission
*/
void wait_last_worker()
{
std::unique_lock<std::mutex> lock(tx_mutex);
tx_hold = true;
while (tx_hold) {
tx_cvar.wait(lock);
}
}
/**
* @brief Notifies the last SF worker transmitted the baseband and all the workers waiting are released
*/
void last_worker()
{
std::unique_lock<std::mutex> lock(tx_mutex);
tx_hold = false;
tx_cvar.notify_all();
}
public:
/**
* @brief Describes a worker context
*/
struct worker_context_t {
uint32_t sf_idx = 0; ///< Subframe index
void* worker_ptr = nullptr; ///< Worker pointer for wait/release semaphore
bool last = false; ///< Indicates this worker is the last one in the sub-frame processing
srsran::rf_timestamp_t tx_time = {}; ///< Transmit time, used only by last worker
void copy(const worker_context_t& other)
{
sf_idx = other.sf_idx;
worker_ptr = other.worker_ptr;
last = other.last;
tx_time.copy(other.tx_time);
}
worker_context_t() = default;
worker_context_t(const worker_context_t& other) { copy(other); }
};
/**
* @brief Common PHY interface for workers to indicate they ended
* @param h Worker pointer used as unique identifier for synchronising Tx
* @param w_ctx Worker context
* @param tx_enable Indicates whether the buffer has baseband samples to transmit
* @param buffer Baseband buffer
* @param tx_time Transmit timestamp
* @param is_nr Indicates whether the worker is NR or not
*/
virtual void
worker_end(void* h, bool tx_enable, srsran::rf_buffer_t& buffer, srsran::rf_timestamp_t& tx_time, bool is_nr) = 0;
virtual void worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) = 0;
};
} // namespace srsran

@ -109,6 +109,24 @@ struct plmn_id_t {
mcc_to_string(mcc_num, &mcc_str);
return mcc_str + mnc_str;
}
std::string to_serving_network_name_string() const
{
char buff[50];
std::string mcc_str, mnc_str;
uint16_t mnc_num, mcc_num;
bytes_to_mnc(&mnc[0], &mnc_num, nof_mnc_digits);
bytes_to_mcc(&mcc[0], &mcc_num);
mnc_to_string(mnc_num, &mnc_str);
mcc_to_string(mcc_num, &mcc_str);
if (mnc_str.size() == 2) {
mnc_str = "0" + mnc_str;
}
snprintf(buff, sizeof(buff), "5G:mnc%s.mcc%s.3gppnetwork.org", mnc_str.c_str(), mcc_str.c_str());
std::string ssn_s = buff;
return ssn_s;
}
bool operator==(const plmn_id_t& other) const
{
return std::equal(&mcc[0], &mcc[3], &other.mcc[0]) and nof_mnc_digits == other.nof_mnc_digits and

@ -175,16 +175,17 @@ public:
};
struct phy_args_nr_t {
uint32_t nof_carriers = 1;
uint32_t max_nof_prb = 106;
uint32_t nof_phy_threads = 3;
uint32_t worker_cpu_mask = 0;
srsran::phy_log_args_t log = {};
srsran_ue_dl_nr_args_t dl = {};
srsran_ue_ul_nr_args_t ul = {};
std::set<uint32_t> fixed_sr = {1};
uint32_t fix_wideband_cqi = 15; // Set to a non-zero value for fixing the wide-band CQI report
bool store_pdsch_ko = false;
uint32_t rf_channel_offset = 0; ///< Specifies the RF channel the NR carrier shall fill
uint32_t nof_carriers = 1;
uint32_t max_nof_prb = 106;
uint32_t nof_phy_threads = 3;
uint32_t worker_cpu_mask = 0;
srsran::phy_log_args_t log = {};
srsran_ue_dl_nr_args_t dl = {};
srsran_ue_ul_nr_args_t ul = {};
std::set<uint32_t> fixed_sr = {1};
uint32_t fix_wideband_cqi = 15; // Set to a non-zero value for fixing the wide-band CQI report
bool store_pdsch_ko = false;
phy_args_nr_t()
{

@ -38,6 +38,12 @@ public:
virtual bool get_imsi_vec(uint8_t* imsi_, uint32_t n) = 0;
virtual bool get_imei_vec(uint8_t* imei_, uint32_t n) = 0;
virtual bool get_home_plmn_id(srsran::plmn_id_t* home_plmn_id) = 0;
// Get the home mcc as bytes array
virtual bool get_home_mcc_bytes(uint8_t* mcc_, uint32_t n) = 0;
// Get the home mnc as byte array
virtual bool get_home_mnc_bytes(uint8_t* mnc_, uint32_t n) = 0;
// Get the home msin in bytes array encoded as bcd
virtual bool get_home_msin_bcd(uint8_t* msin_, uint32_t n) = 0;
virtual auth_result_t generate_authentication_response(uint8_t* rand,
uint8_t* autn_enb,
uint16_t mcc,

@ -56,7 +56,7 @@ extern "C" {
/**
* @brief Defines the symbol duration, including cyclic prefix
*/
#define SRSRAN_SUBC_SPACING_NR(NUM) (15000U << (NUM))
#define SRSRAN_SUBC_SPACING_NR(NUM) (15000U << (uint32_t)(NUM))
/**
* @brief Defines the number of slots per SF. Defined by TS 38.211 v15.8.0 Table 4.3.2-1.

@ -33,7 +33,7 @@
#define SRSRAN_FILESOURCE_H
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include "srsran/config.h"
#include "srsran/phy/io/format.h"

@ -127,6 +127,7 @@ typedef struct SRSRAN_API {
uint32_t scell_index; ///< Indicates the cell/carrier the measurement belongs, use 0 for PCell
uint32_t L;
uint32_t N;
uint32_t sb_idx;
srsran_cqi_type_t type;
uint32_t ri_len;
} srsran_cqi_cfg_t;
@ -163,10 +164,27 @@ SRSRAN_API bool srsran_cqi_periodic_is_subband(const srsran_cqi_report_cfg_t* cf
SRSRAN_API bool
srsran_cqi_periodic_ri_send(const srsran_cqi_report_cfg_t* periodic_cfg, uint32_t tti, srsran_frame_type_t frame_type);
SRSRAN_API uint32_t srsran_cqi_periodic_sb_bw_part_idx(const srsran_cqi_report_cfg_t* cfg,
uint32_t tti,
uint32_t nof_prb,
srsran_frame_type_t frame_type);
SRSRAN_API int srsran_cqi_hl_get_no_subbands(int nof_prb);
/**
* @brief Returns the number of bits to index a bandwidth part (L)
*
* @remark Implemented according to TS 38.213 section 7.2.2 Periodic CSI Reporting using PUCCH, paragraph that refers to
* `L-bit label indexed in the order of increasing frequency, where L = ceil(log2(nof_prb/k/J))`
*
*/
SRSRAN_API int srsran_cqi_hl_get_L(int nof_prb);
SRSRAN_API uint32_t srsran_cqi_get_sb_idx(uint32_t tti,
uint32_t subband_label,
const srsran_cqi_report_cfg_t* cqi_report_cfg,
const srsran_cell_t* cell);
SRSRAN_API uint8_t srsran_cqi_from_snr(float snr);
SRSRAN_API float srsran_cqi_to_coderate(uint32_t cqi, bool use_alt_table);

@ -85,6 +85,8 @@ SRSRAN_API void srsran_bit_unpack_l(uint64_t value, uint8_t** bits, int nof_bits
SRSRAN_API void srsran_bit_unpack(uint32_t value, uint8_t** bits, int nof_bits);
SRSRAN_API void srsran_bit_unpack_lsb(uint32_t value, uint8_t** bits, int nof_bits);
SRSRAN_API void srsran_bit_fprint(FILE* stream, uint8_t* bits, int nof_bits);
SRSRAN_API uint32_t srsran_bit_diff(const uint8_t* x, const uint8_t* y, int nbits);

@ -1177,7 +1177,7 @@ typedef __m128 simd_sel_t;
#else /* LV_HAVE_AVX2 */
#ifdef HAVE_NEON
typedef int32x4_t simd_i_t;
typedef int32x4_t simd_sel_t;
typedef uint32x4_t simd_sel_t;
#endif /* HAVE_NEON */
#endif /* LV_HAVE_SSE */
#endif /* LV_HAVE_AVX2 */
@ -1309,7 +1309,7 @@ static inline simd_sel_t srsran_simd_f_max(simd_f_t a, simd_f_t b)
return (simd_sel_t)_mm_cmpgt_ps(a, b);
#else /* LV_HAVE_SSE */
#ifdef HAVE_NEON
return (simd_sel_t)vcgtq_f32(a, b);
return vcgtq_f32(a, b);
#endif /* HAVE_NEON */
#endif /* LV_HAVE_SSE */
#endif /* LV_HAVE_AVX2 */
@ -1328,7 +1328,7 @@ static inline simd_sel_t srsran_simd_f_min(simd_f_t a, simd_f_t b)
return (simd_sel_t)_mm_cmplt_ps(a, b);
#else /* LV_HAVE_SSE */
#ifdef HAVE_NEON
return (simd_sel_t)vcltq_f32(a, b);
return vcltq_f32(a, b);
#endif /* HAVE_NEON */
#endif /* LV_HAVE_SSE */
#endif /* LV_HAVE_AVX2 */
@ -1346,20 +1346,8 @@ static inline simd_f_t srsran_simd_f_select(simd_f_t a, simd_f_t b, simd_sel_t s
#ifdef LV_HAVE_SSE
return _mm_blendv_ps(a, b, selector);
#else /* LV_HAVE_SSE */
#ifdef HAVE_NEON // CURRENTLY USES GENERIC IMPLEMENTATION FOR NEON
float* a_ptr = (float*)&a;
float* b_ptr = (float*)&b;
simd_f_t ret;
int* sel = (int*)&selector;
float* c_ptr = (float*)&ret;
for (int i = 0; i < 4; i++) {
if (sel[i] == -1) {
c_ptr[i] = b_ptr[i];
} else {
c_ptr[i] = a_ptr[i];
}
}
return ret;
#ifdef HAVE_NEON
return vbslq_f32(selector, b, a);
#endif /* HAVE_NEON */
#endif /* LV_HAVE_SSE */
#endif /* LV_HAVE_AVX2 */
@ -1377,20 +1365,8 @@ static inline simd_i_t srsran_simd_i_select(simd_i_t a, simd_i_t b, simd_sel_t s
#ifdef LV_HAVE_SSE
return (__m128i)_mm_blendv_ps((__m128)a, (__m128)b, selector);
#else /* LV_HAVE_SSE */
#ifdef HAVE_NEON // CURRENTLY USES GENERIC IMPLEMENTATION FOR NEON
int* a_ptr = (int*)&a;
int* b_ptr = (int*)&b;
simd_i_t ret;
int* sel = (int*)&selector;
int* c_ptr = (int*)&ret;
for (int i = 0; i < 4; i++) {
if (sel[i] == -1) {
c_ptr[i] = b_ptr[i];
} else {
c_ptr[i] = a_ptr[i];
}
}
return ret;
#ifdef HAVE_NEON
return vbslq_s32(selector, b, a);
#endif /* HAVE_NEON */
#endif /* LV_HAVE_SSE */
#endif /* LV_HAVE_AVX2 */

@ -52,6 +52,7 @@ extern "C" {
#define SRSRAN_CEIL(NUM, DEN) (((NUM) + ((DEN)-1)) / (DEN))
#define SRSRAN_FLOOR(NUM, DEN) ((NUM) / (DEN))
#define SRSRAN_ROUND(NUM, DEN) ((uint32_t)round((double)(NUM) / (double)(DEN)))
#define SRSRAN_CEIL_LOG2(N) (((N) == 0) ? 0 : ceil(log2((double)(N))))
// Complex squared absolute value
#define SRSRAN_CSQABS(X) (__real__(X) * __real__(X) + __imag__(X) * __imag__(X))

@ -121,6 +121,26 @@ public:
{
sample_buffer.at(logical_ch * nof_antennas + port_idx) = ptr;
}
void set_combine(const uint32_t& channel_idx, cf_t* ptr)
{
if (sample_buffer.at(channel_idx) == nullptr) {
sample_buffer.at(channel_idx) = ptr;
} else if (ptr != nullptr) {
srsran_vec_sum_ccc(ptr, sample_buffer.at(channel_idx), sample_buffer.at(channel_idx), nof_samples);
}
}
void set_combine(const uint32_t& logical_ch, const uint32_t& port_idx, const uint32_t& nof_antennas, cf_t* ptr)
{
set_combine(logical_ch * nof_antennas + port_idx, ptr);
}
void set_combine(const rf_buffer_interface& other)
{
// Take the other number of samples always
set_nof_samples(other.get_nof_samples());
for (uint32_t ch = 0; ch < SRSRAN_MAX_CHANNELS; ch++) {
set_combine(ch, other.get(ch));
}
}
void** to_void() override { return (void**)sample_buffer.data(); }
cf_t** to_cf_t() override { return sample_buffer.data(); }
uint32_t size() override { return nof_subframes * SRSRAN_SF_LEN_MAX; }

@ -741,6 +741,42 @@ bool make_phy_res_config(const pucch_res_s& pucch_res,
return true;
}
bool make_phy_res_config(const srsran_pucch_nr_resource_t& in_pucch_res,
asn1::rrc_nr::pucch_res_s& out_pucch_res,
uint32_t pucch_res_id)
{
out_pucch_res.pucch_res_id = pucch_res_id;
out_pucch_res.start_prb = in_pucch_res.starting_prb;
switch (in_pucch_res.format) {
case SRSRAN_PUCCH_NR_FORMAT_0:
asn1::log_warning("SRSRAN_PUCCH_NR_FORMAT_0 conversion not supported");
return false;
case SRSRAN_PUCCH_NR_FORMAT_1:
out_pucch_res.format.set_format1();
out_pucch_res.format.format1().init_cyclic_shift = in_pucch_res.initial_cyclic_shift;
out_pucch_res.format.format1().nrof_symbols = in_pucch_res.nof_symbols;
out_pucch_res.format.format1().start_symbol_idx = in_pucch_res.start_symbol_idx;
out_pucch_res.format.format1().time_domain_occ = in_pucch_res.time_domain_occ;
return true;
case SRSRAN_PUCCH_NR_FORMAT_2:
out_pucch_res.format.set_format2();
out_pucch_res.format.format2().nrof_symbols = in_pucch_res.nof_symbols;
out_pucch_res.format.format2().start_symbol_idx = in_pucch_res.start_symbol_idx;
out_pucch_res.format.format2().nrof_prbs = in_pucch_res.nof_prb;
return true;
case SRSRAN_PUCCH_NR_FORMAT_3:
asn1::log_warning("SRSRAN_PUCCH_NR_FORMAT_3 conversion not supported");
return true;
case SRSRAN_PUCCH_NR_FORMAT_4:
asn1::log_warning("SRSRAN_PUCCH_NR_FORMAT_4 conversion not supported");
return false;
default:
asn1::log_warning("Invalid NR PUCCH format");
}
return false;
}
bool make_phy_sr_resource(const sched_request_res_cfg_s& sched_request_res_cfg,
srsran_pucch_nr_sr_resource_t* in_srsran_pucch_nr_sr_resource)
{

@ -39,7 +39,7 @@ uint32_t mac_pcap::open(std::string filename_, uint32_t ue_id_)
return SRSRAN_ERROR;
}
// set DLT for selected RAT
// set UDP DLT
dlt = UDP_DLT;
pcap_file = DLT_PCAP_Open(dlt, filename_.c_str());
if (pcap_file == nullptr) {

@ -20,10 +20,59 @@
*/
#include "srsran/common/phy_cfg_nr_default.h"
#include "srsran/common/string_helpers.h"
#include "srsran/srsran.h"
#include <cctype>
#include <list>
namespace srsran {
template <typename T>
T inc(T v)
{
return static_cast<T>(static_cast<int>(v) + 1);
}
phy_cfg_nr_default_t::reference_cfg_t::reference_cfg_t(const std::string& args)
{
std::list<std::string> param_list = {};
string_parse_list(args, ',', param_list);
for (std::string& e : param_list) {
std::list<std::string> param = {};
string_parse_list(e, '=', param);
// Skip if size is invalid
if (param.size() != 2) {
srsran_terminate("Invalid reference argument '%s'", e.c_str());
}
if (param.front() == "carrier") {
for (carrier = R_CARRIER_CUSTOM_10MHZ; carrier < R_CARRIER_COUNT; carrier = inc(carrier)) {
if (R_CARRIER_STRING[carrier] == param.back()) {
break;
}
}
srsran_assert(carrier != R_CARRIER_COUNT, "Invalid carrier reference configuration '%s'", param.back().c_str());
} else if (param.front() == "tdd") {
for (tdd = R_TDD_CUSTOM_6_4; tdd < R_TDD_COUNT; tdd = inc(tdd)) {
if (R_TDD_STRING[tdd] == param.back()) {
break;
}
}
srsran_assert(tdd != R_TDD_COUNT, "Invalid TDD reference configuration '%s'", param.back().c_str());
} else if (param.front() == "pdsch") {
for (pdsch = R_PDSCH_DEFAULT; pdsch < R_PDSCH_COUNT; pdsch = inc(pdsch)) {
if (R_PDSCH_STRING[pdsch] == param.back()) {
break;
}
}
srsran_assert(pdsch != R_PDSCH_COUNT, "Invalid PDSCH reference configuration '%s'", param.back().c_str());
} else {
srsran_terminate("Invalid %s reference component", param.front().c_str());
}
}
}
void phy_cfg_nr_default_t::make_carrier_custom_10MHz(srsran_carrier_nr_t& carrier)
{
carrier.nof_prb = 52;
@ -35,6 +84,17 @@ void phy_cfg_nr_default_t::make_carrier_custom_10MHz(srsran_carrier_nr_t& carrie
carrier.scs = srsran_subcarrier_spacing_15kHz;
}
void phy_cfg_nr_default_t::make_carrier_custom_20MHz(srsran_carrier_nr_t& carrier)
{
carrier.nof_prb = 106;
carrier.max_mimo_layers = 1;
carrier.pci = 500;
carrier.absolute_frequency_point_a = 633928;
carrier.absolute_frequency_ssb = 634176;
carrier.offset_to_carrier = 0;
carrier.scs = srsran_subcarrier_spacing_15kHz;
}
void phy_cfg_nr_default_t::make_tdd_custom_6_4(srsran_tdd_config_nr_t& tdd)
{
tdd.pattern1.period_ms = 10;
@ -47,6 +107,18 @@ void phy_cfg_nr_default_t::make_tdd_custom_6_4(srsran_tdd_config_nr_t& tdd)
tdd.pattern2.period_ms = 0;
}
void phy_cfg_nr_default_t::make_tdd_fr1_15_1(srsran_tdd_config_nr_t& tdd)
{
tdd.pattern1.period_ms = 5;
tdd.pattern1.nof_dl_slots = 3;
tdd.pattern1.nof_dl_symbols = 10;
tdd.pattern1.nof_ul_slots = 1;
tdd.pattern1.nof_ul_symbols = 2;
// Disable pattern 2
tdd.pattern2.period_ms = 0;
}
void phy_cfg_nr_default_t::make_pdcch_custom_common_ss(srsran_pdcch_cfg_nr_t& pdcch, const srsran_carrier_nr_t& carrier)
{
// Configure CORESET ID 1
@ -90,6 +162,91 @@ void phy_cfg_nr_default_t::make_pdsch_default(srsran_sch_hl_cfg_nr_t& pdsch)
pdsch.typeA_pos = srsran_dmrs_sch_typeA_pos_2;
}
void make_nzp_csi_rs_ts38101_table_5_2_1(const srsran_carrier_nr_t& carrier, srsran_csi_rs_nzp_set_t& trs)
{
// Set defaults
trs = {};
trs.trs_info = true;
trs.count = 4;
srsran_csi_rs_nzp_resource_t& res1 = trs.data[0];
srsran_csi_rs_nzp_resource_t& res2 = trs.data[1];
srsran_csi_rs_nzp_resource_t& res3 = trs.data[2];
srsran_csi_rs_nzp_resource_t& res4 = trs.data[3];
res1.resource_mapping.frequency_domain_alloc[0] = true;
res2.resource_mapping.frequency_domain_alloc[0] = true;
res3.resource_mapping.frequency_domain_alloc[0] = true;
res4.resource_mapping.frequency_domain_alloc[0] = true;
res1.resource_mapping.first_symbol_idx = 6;
res2.resource_mapping.first_symbol_idx = 10;
res3.resource_mapping.first_symbol_idx = 6;
res4.resource_mapping.first_symbol_idx = 10;
res1.resource_mapping.nof_ports = 1;
res2.resource_mapping.nof_ports = 1;
res3.resource_mapping.nof_ports = 1;
res4.resource_mapping.nof_ports = 1;
res1.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm;
res2.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm;
res3.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm;
res4.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm;
res1.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three;
res2.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three;
res3.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three;
res4.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three;
if (carrier.scs == srsran_subcarrier_spacing_15kHz) {
res1.periodicity.period = 20;
res2.periodicity.period = 20;
res3.periodicity.period = 20;
res4.periodicity.period = 20;
res1.periodicity.offset = 10;
res2.periodicity.offset = 10;
res3.periodicity.offset = 11;
res4.periodicity.offset = 11;
} else if (carrier.scs == srsran_subcarrier_spacing_30kHz) {
res1.periodicity.period = 40;
res2.periodicity.period = 40;
res3.periodicity.period = 40;
res4.periodicity.period = 40;
res1.periodicity.offset = 20;
res2.periodicity.offset = 20;
res3.periodicity.offset = 21;
res4.periodicity.offset = 21;
} else {
srsran_terminate("Invalid subcarrier spacing %d kHz", 15U << (uint32_t)carrier.scs);
}
res1.resource_mapping.freq_band = {0, carrier.nof_prb};
res2.resource_mapping.freq_band = {0, carrier.nof_prb};
res3.resource_mapping.freq_band = {0, carrier.nof_prb};
res4.resource_mapping.freq_band = {0, carrier.nof_prb};
}
void phy_cfg_nr_default_t::make_pdsch_2_1_1_tdd(const srsran_carrier_nr_t& carrier, srsran_sch_hl_cfg_nr_t& pdsch)
{
// Select PDSCH time resource allocation
pdsch.common_time_ra[0].mapping_type = srsran_sch_mapping_type_A;
pdsch.common_time_ra[0].k = 0;
pdsch.common_time_ra[0].sliv = srsran_ra_type2_to_riv(SRSRAN_NSYMB_PER_SLOT_NR - 2, 2, SRSRAN_NSYMB_PER_SLOT_NR);
pdsch.nof_common_time_ra = 1;
// Setup PDSCH DMRS
pdsch.typeA_pos = srsran_dmrs_sch_typeA_pos_2;
pdsch.dmrs_typeA.present = true;
pdsch.dmrs_typeA.additional_pos = srsran_dmrs_sch_add_pos_2;
// Make default CSI-RS for tracking from TS38101 Table 5.2.1
make_nzp_csi_rs_ts38101_table_5_2_1(carrier, pdsch.nzp_csi_rs_sets[0]);
}
void phy_cfg_nr_default_t::make_pusch_default(srsran_sch_hl_cfg_nr_t& pusch)
{
// Select PUSCH time resource allocation
@ -124,7 +281,7 @@ void phy_cfg_nr_default_t::make_pucch_custom_one(srsran_pucch_nr_hl_cfg_t& pucch
resource_big.format = SRSRAN_PUCCH_NR_FORMAT_2;
resource_big.nof_prb = 1;
resource_big.nof_symbols = 2;
resource_big.start_symbol_idx = 0;
resource_big.start_symbol_idx = 12;
// Resource for SR
srsran_pucch_nr_resource_t resource_sr = {};
@ -173,11 +330,14 @@ void phy_cfg_nr_default_t::make_harq_auto(srsran_harq_ack_cfg_hl_t& harq,
{
// Generate as many entries as DL slots
harq.nof_dl_data_to_ul_ack = SRSRAN_MIN(tdd_cfg.pattern1.nof_dl_slots, SRSRAN_MAX_NOF_DL_DATA_TO_UL);
if (tdd_cfg.pattern1.nof_dl_symbols > 0) {
harq.nof_dl_data_to_ul_ack++;
}
// Set PDSCH to ACK timing delay to 4 or more
for (uint32_t n = 0; n < harq.nof_dl_data_to_ul_ack; n++) {
// Set the first slots into the first UL slot
if (n < (harq.nof_dl_data_to_ul_ack - 4)) {
if (harq.nof_dl_data_to_ul_ack >= 4 and n < (harq.nof_dl_data_to_ul_ack - 4)) {
harq.dl_data_to_ul_ack[n] = harq.nof_dl_data_to_ul_ack - n;
continue;
}
@ -189,7 +349,7 @@ void phy_cfg_nr_default_t::make_harq_auto(srsran_harq_ack_cfg_hl_t& harq,
}
// Otherwise set delay to the first UL slot of the next TDD period
harq.dl_data_to_ul_ack[n] = 2 * harq.nof_dl_data_to_ul_ack - n;
harq.dl_data_to_ul_ack[n] = (tdd_cfg.pattern1.period_ms + tdd_cfg.pattern1.nof_dl_slots) - n;
}
// Zero the rest
@ -204,7 +364,7 @@ void phy_cfg_nr_default_t::make_harq_auto(srsran_harq_ack_cfg_hl_t& harq,
void phy_cfg_nr_default_t::make_prach_default_lte(srsran_prach_cfg_t& prach)
{
prach.config_idx = 0;
prach.freq_offset = 2;
prach.freq_offset = 4;
prach.root_seq_idx = 0;
prach.is_nr = true;
}
@ -215,12 +375,22 @@ phy_cfg_nr_default_t::phy_cfg_nr_default_t(const reference_cfg_t& reference_cfg)
case reference_cfg_t::R_CARRIER_CUSTOM_10MHZ:
make_carrier_custom_10MHz(carrier);
break;
case reference_cfg_t::R_CARRIER_CUSTOM_20MHZ:
make_carrier_custom_20MHz(carrier);
break;
case reference_cfg_t::R_CARRIER_COUNT:
srsran_terminate("Invalid carrier reference");
}
switch (reference_cfg.tdd) {
case reference_cfg_t::R_TDD_CUSTOM_6_4:
make_tdd_custom_6_4(tdd);
break;
case reference_cfg_t::R_TDD_FR1_15_1:
make_tdd_fr1_15_1(tdd);
break;
case reference_cfg_t::R_TDD_COUNT:
srsran_terminate("Invalid TDD reference");
}
switch (reference_cfg.pdcch) {
@ -233,6 +403,11 @@ phy_cfg_nr_default_t::phy_cfg_nr_default_t(const reference_cfg_t& reference_cfg)
case reference_cfg_t::R_PDSCH_DEFAULT:
make_pdsch_default(pdsch);
break;
case reference_cfg_t::R_PDSCH_TS38101_5_2_1:
make_pdsch_2_1_1_tdd(carrier, pdsch);
break;
case reference_cfg_t::R_PDSCH_COUNT:
srsran_terminate("Invalid PDSCH reference configuration");
}
switch (reference_cfg.pusch) {

@ -50,7 +50,7 @@ void thread_pool::worker::setup(uint32_t id, thread_pool* parent, uint32_t prio,
void thread_pool::worker::run_thread()
{
set_name(std::string("WORKER") + std::to_string(my_id));
set_name(my_parent->get_id() + std::string("WORKER") + std::to_string(my_id));
while (running.load(std::memory_order_relaxed)) {
wait_to_start();
if (running.load(std::memory_order_relaxed)) {
@ -70,9 +70,9 @@ uint32_t thread_pool::worker::get_id()
return my_id;
}
thread_pool::thread_pool(uint32_t max_workers_) : workers(max_workers_), status(max_workers_), cvar_worker(max_workers_)
thread_pool::thread_pool(uint32_t max_workers_, std::string id_) :
workers(max_workers_), max_workers(max_workers_), status(max_workers_), cvar_worker(max_workers_), id(id_)
{
max_workers = max_workers_;
for (uint32_t i = 0; i < max_workers; i++) {
workers[i] = NULL;
status[i] = IDLE;
@ -262,6 +262,11 @@ uint32_t thread_pool::get_nof_workers()
return nof_workers;
}
std::string thread_pool::get_id()
{
return id;
}
/**************************************************************************
* task_thread_pool - uses a queue to enqueue callables, that start
* once a worker is available

@ -168,6 +168,9 @@ srsran_mcs_table_t srsran_mcs_table_from_str(const char* str)
static const uint32_t phy_common_nr_valid_symbol_sz[PHY_COMMON_NR_NOF_VALID_SYMB_SZ] =
{128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096};
static const uint32_t phy_common_nr_valid_std_symbol_sz[PHY_COMMON_NR_NOF_VALID_SYMB_SZ] =
{128, 256, 512, 1024, 1536, 2048, 4096};
uint32_t srsran_min_symbol_sz_rb(uint32_t nof_prb)
{
uint32_t nof_re = nof_prb * SRSRAN_NRE;
@ -176,9 +179,14 @@ uint32_t srsran_min_symbol_sz_rb(uint32_t nof_prb)
return 0;
}
const uint32_t* symbol_table = phy_common_nr_valid_symbol_sz;
if (srsran_symbol_size_is_standard()) {
symbol_table = phy_common_nr_valid_std_symbol_sz;
}
for (uint32_t i = 0; i < PHY_COMMON_NR_NOF_VALID_SYMB_SZ; i++) {
if (phy_common_nr_valid_symbol_sz[i] > nof_re) {
return phy_common_nr_valid_symbol_sz[i];
if (symbol_table[i] > nof_re) {
return symbol_table[i];
}
}

@ -257,7 +257,7 @@ cqi_format2_subband_tostring(srsran_cqi_cfg_t* cfg, srsran_cqi_ue_subband_t* msg
n += snprintf(buff + n, buff_len - n, ", cqi=%d", msg->subband_cqi);
n += snprintf(buff + n, buff_len - n, ", label=%d", msg->subband_label);
n += snprintf(buff + n, buff_len - n, ", sb_idx=%d", cfg->sb_idx);
return n;
}
@ -456,7 +456,6 @@ static bool cqi_get_N_tdd(uint32_t I_cqi_pmi, uint32_t* N_p, uint32_t* N_offset)
static bool cqi_send(uint32_t I_cqi_pmi, uint32_t tti, bool is_fdd, uint32_t H)
{
uint32_t N_p = 0;
uint32_t N_offset = 0;
@ -480,7 +479,6 @@ static bool cqi_send(uint32_t I_cqi_pmi, uint32_t tti, bool is_fdd, uint32_t H)
static bool ri_send(uint32_t I_cqi_pmi, uint32_t I_ri, uint32_t tti, bool is_fdd)
{
uint32_t M_ri = 0;
int N_offset_ri = 0;
uint32_t N_p = 0;
@ -553,23 +551,55 @@ static int cqi_hl_get_bwp_J(int nof_prb)
{
if (nof_prb < 7) {
return 0;
} else if (nof_prb <= 10) {
return 1;
} else if (nof_prb <= 26) {
return 4;
return 2;
} else if (nof_prb <= 63) {
return 6;
return 3;
} else if (nof_prb <= 110) {
return 8;
return 4;
} else {
return -1;
}
}
/* Returns the number of bits to index a bandwidth part (L)
* L = ceil(log2(nof_prb/k/J))
/* Returns the number of subbands in the j-th bandwidth part
*/
static int cqi_sb_get_Nj(uint32_t j, uint32_t nof_prb)
{
uint32_t J = cqi_hl_get_bwp_J(nof_prb);
if (J == 1) {
return (uint32_t)ceil((float)nof_prb / cqi_hl_get_subband_size(nof_prb));
} else {
// all bw parts have the same number of subbands except the last one
uint32_t Nj = (uint32_t)ceil((float)nof_prb / cqi_hl_get_subband_size(nof_prb) / J);
if (j < J - 1) {
return Nj;
} else {
return Nj - 1;
}
}
}
uint32_t srsran_cqi_get_sb_idx(uint32_t tti,
uint32_t subband_label,
const srsran_cqi_report_cfg_t* cqi_report_cfg,
const srsran_cell_t* cell)
{
uint32_t bw_part_idx = srsran_cqi_periodic_sb_bw_part_idx(cqi_report_cfg, tti, cell->nof_prb, cell->frame_type);
return subband_label + bw_part_idx * cqi_sb_get_Nj(bw_part_idx, cell->nof_prb);
}
int srsran_cqi_hl_get_L(int nof_prb)
{
return (int)ceil((float)nof_prb / cqi_hl_get_subband_size(nof_prb) / cqi_hl_get_bwp_J(nof_prb));
int subband_size = cqi_hl_get_subband_size(nof_prb);
int bwp_J = cqi_hl_get_bwp_J(nof_prb);
if (subband_size <= 0 || bwp_J <= 0) {
ERROR("Invalid parameters");
return SRSRAN_ERROR;
}
return (int)SRSRAN_CEIL_LOG2((double)nof_prb / (subband_size * bwp_J));
}
bool srsran_cqi_periodic_ri_send(const srsran_cqi_report_cfg_t* cfg, uint32_t tti, srsran_frame_type_t frame_type)
@ -583,19 +613,51 @@ bool srsran_cqi_periodic_send(const srsran_cqi_report_cfg_t* cfg, uint32_t tti,
return cfg->periodic_configured && cqi_send(cfg->pmi_idx, tti, frame_type == SRSRAN_FDD, 1);
}
uint32_t cqi_sb_get_H(const srsran_cqi_report_cfg_t* cfg, uint32_t nof_prb)
{
uint32_t K = cfg->subband_wideband_ratio;
uint32_t J = cqi_hl_get_bwp_J(nof_prb);
uint32_t H = J * K + 1;
return H;
}
bool srsran_cqi_periodic_is_subband(const srsran_cqi_report_cfg_t* cfg,
uint32_t tti,
uint32_t nof_prb,
srsran_frame_type_t frame_type)
{
uint32_t K = cfg->subband_wideband_ratio;
uint32_t J = cqi_hl_get_bwp_J(nof_prb);
uint32_t H = J * K + 1;
uint32_t H = cqi_sb_get_H(cfg, nof_prb);
// A periodic report is subband if it's a CQI opportunity and is not wideband
return srsran_cqi_periodic_send(cfg, tti, frame_type) && !cqi_send(cfg->pmi_idx, tti, frame_type == SRSRAN_FDD, H);
}
uint32_t srsran_cqi_periodic_sb_bw_part_idx(const srsran_cqi_report_cfg_t* cfg,
uint32_t tti,
uint32_t nof_prb,
srsran_frame_type_t frame_type)
{
uint32_t H = cqi_sb_get_H(cfg, nof_prb);
uint32_t N_p = 0, N_offset = 0;
if (frame_type == SRSRAN_FDD) {
if (!cqi_get_N_fdd(cfg->pmi_idx, &N_p, &N_offset)) {
return false;
}
} else {
if (!cqi_get_N_tdd(cfg->pmi_idx, &N_p, &N_offset)) {
return false;
}
}
uint32_t x = ((tti - N_offset) / N_p) % H;
if (x > 0) {
return (x - 1) % cqi_hl_get_bwp_J(nof_prb);
} else {
return 0;
}
}
// CQI-to-Spectral Efficiency: 36.213 Table 7.2.3-1
static float cqi_to_coderate[16] = {0,
0.1523,

@ -31,15 +31,13 @@
*/
#define DCI_NR_MIN_SIZE 12
#define CEIL_LOG2(N) (((N) == 0) ? 0 : ceil(log2((double)(N))))
static uint32_t dci_nr_freq_resource_size_type1(uint32_t N)
{
if (N == 0) {
return 0;
}
return (int)CEIL_LOG2(N * (N + 1) / 2.0);
return (int)SRSRAN_CEIL_LOG2(N * (N + 1) / 2.0);
}
static uint32_t dci_nr_freq_resource_size(srsran_resource_alloc_t alloc_type, uint32_t N_RBG, uint32_t N_BWP_RB)
@ -66,7 +64,7 @@ static uint32_t dci_nr_bwp_id_size(uint32_t N_BWP_RRC)
N_BWP = N_BWP_RRC + 1;
}
return (int)CEIL_LOG2(N_BWP);
return (int)SRSRAN_CEIL_LOG2(N_BWP);
}
static uint32_t dci_nr_time_res_size(uint32_t nof_time_res)
@ -75,7 +73,7 @@ static uint32_t dci_nr_time_res_size(uint32_t nof_time_res)
// 4 bits are necessary for PUSCH default time resource assigment (TS 38.214 Table 6.1.2.1.1-2)
nof_time_res = SRSRAN_MAX_NOF_TIME_RA;
}
return (uint32_t)CEIL_LOG2(nof_time_res);
return (uint32_t)SRSRAN_CEIL_LOG2(nof_time_res);
}
static uint32_t dci_nr_ptrs_size(const srsran_dci_cfg_nr_t* cfg)
@ -160,9 +158,9 @@ static uint32_t dci_nr_srs_id_size(const srsran_dci_cfg_nr_t* cfg)
for (uint32_t k = 1; k < SRSRAN_MIN(cfg->nof_ul_layers, cfg->nof_srs); k++) {
N += cfg->nof_srs / k;
}
return (uint32_t)CEIL_LOG2(N);
return (uint32_t)SRSRAN_CEIL_LOG2(N);
}
return (uint32_t)CEIL_LOG2(N_srs);
return (uint32_t)SRSRAN_CEIL_LOG2(N_srs);
}
// Determines DCI format 0_0 according to TS 38.212 clause 7.3.1.1.1
@ -1249,7 +1247,7 @@ static uint32_t dci_nr_format_1_1_sizeof(const srsran_dci_cfg_nr_t* cfg, srsran_
}
// ZP CSI-RS trigger - 0, 1, or 2 bits
count += (int)CEIL_LOG2(cfg->nof_aperiodic_zp + 1);
count += (int)SRSRAN_CEIL_LOG2(cfg->nof_aperiodic_zp + 1);
// For transport block 1:
// Modulation and coding scheme 5 bits
@ -1292,7 +1290,7 @@ static uint32_t dci_nr_format_1_1_sizeof(const srsran_dci_cfg_nr_t* cfg, srsran_
count += 3;
// PDSCH-to-HARQ_feedback timing indicator 0, 1, 2, or 3 bits
count += (int)CEIL_LOG2(cfg->nof_dl_to_ul_ack);
count += (int)SRSRAN_CEIL_LOG2(cfg->nof_dl_to_ul_ack);
// Antenna port(s) 4, 5, or 6 bits
count += dci_nr_dl_ports_size(cfg);
@ -1365,7 +1363,7 @@ static int dci_nr_format_1_1_pack(const srsran_dci_nr_t* q, const srsran_dci_dl_
}
// ZP CSI-RS trigger - 0, 1, or 2 bits
srsran_bit_unpack(dci->zp_csi_rs_id, &y, CEIL_LOG2(cfg->nof_aperiodic_zp + 1));
srsran_bit_unpack(dci->zp_csi_rs_id, &y, SRSRAN_CEIL_LOG2(cfg->nof_aperiodic_zp + 1));
// For transport block 1:
// Modulation and coding scheme 5 bits
@ -1408,7 +1406,7 @@ static int dci_nr_format_1_1_pack(const srsran_dci_nr_t* q, const srsran_dci_dl_
srsran_bit_unpack(dci->pucch_resource, &y, 3);
// PDSCH-to-HARQ_feedback timing indicator 0, 1, 2, or 3 bits
srsran_bit_unpack(dci->harq_feedback, &y, (int)CEIL_LOG2(cfg->nof_dl_to_ul_ack));
srsran_bit_unpack(dci->harq_feedback, &y, (int)SRSRAN_CEIL_LOG2(cfg->nof_dl_to_ul_ack));
// Antenna port(s) 4, 5, or 6 bits
srsran_bit_unpack(dci->ports, &y, dci_nr_dl_ports_size(cfg));
@ -1495,7 +1493,7 @@ static int dci_nr_format_1_1_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_
}
// ZP CSI-RS trigger - 0, 1, or 2 bits
dci->zp_csi_rs_id = srsran_bit_pack(&y, CEIL_LOG2(cfg->nof_aperiodic_zp + 1));
dci->zp_csi_rs_id = srsran_bit_pack(&y, SRSRAN_CEIL_LOG2(cfg->nof_aperiodic_zp + 1));
// For transport block 1:
// Modulation and coding scheme 5 bits
@ -1538,7 +1536,7 @@ static int dci_nr_format_1_1_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_
dci->pucch_resource = srsran_bit_pack(&y, 3);
// PDSCH-to-HARQ_feedback timing indicator 0, 1, 2, or 3 bits
dci->harq_feedback = srsran_bit_pack(&y, (int)CEIL_LOG2(cfg->nof_dl_to_ul_ack));
dci->harq_feedback = srsran_bit_pack(&y, (int)SRSRAN_CEIL_LOG2(cfg->nof_dl_to_ul_ack));
// Antenna port(s) 4, 5, or 6 bits
dci->ports = srsran_bit_pack(&y, dci_nr_dl_ports_size(cfg));
@ -1613,7 +1611,7 @@ dci_nr_format_1_1_to_str(const srsran_dci_nr_t* q, const srsran_dci_dl_nr_t* dci
}
// ZP CSI-RS trigger - 0, 1, or 2 bits
if (CEIL_LOG2(cfg->nof_aperiodic_zp + 1) > 0) {
if (SRSRAN_CEIL_LOG2(cfg->nof_aperiodic_zp + 1) > 0) {
len = srsran_print_check(str, str_len, len, "zp_csi_rs_id=%d ", dci->zp_csi_rs_id);
}

@ -299,6 +299,11 @@ int srsran_harq_ack_insert_m(srsran_pdsch_ack_nr_t* ack_info, const srsran_harq_
}
srsran_harq_ack_cc_t* cc = &ack_info->cc[m->resource.scell_idx];
if (cc->M >= SRSRAN_UCI_NR_MAX_M) {
ERROR("Accumulated M HARQ feedback exceeds maximum (%d)", SRSRAN_UCI_NR_MAX_M);
return SRSRAN_ERROR;
}
// Find insertion index
uint32_t idx = cc->M; // Append at the end by default
for (uint32_t i = 0; i < cc->M; i++) {
@ -312,7 +317,7 @@ int srsran_harq_ack_insert_m(srsran_pdsch_ack_nr_t* ack_info, const srsran_harq_
cc->M += 1;
// Make space for insertion
for (uint32_t i = cc->M - 1; i > idx; i--) {
for (uint32_t i = cc->M - 1; i != idx; i--) {
cc->m[i] = cc->m[i - 1];
}

@ -78,12 +78,19 @@ static int srsran_pdcch_nr_get_ncce(const srsran_coreset_t* coreset,
return SRSRAN_ERROR;
}
// Calculate CORESET bandiwth in physical resource blocks
uint32_t coreset_bw = srsran_coreset_get_bw(coreset);
// Every REG is 1PRB wide and a CCE is 6 REG. So, the number of N_CCE is a sixth of the bandwidth times the number of
// symbols
uint32_t N_cce = srsran_coreset_get_bw(coreset) * coreset->duration / 6;
uint32_t N_cce = coreset_bw * coreset->duration / 6;
if (N_cce < L) {
ERROR("Error number of CCE %d is lower than the aggregation level %d", N_cce, L);
ERROR("Error CORESET (total bandwidth of %d RBs and %d CCEs) cannot fit the aggregation level %d (%d)",
coreset_bw,
N_cce,
L,
aggregation_level);
return SRSRAN_ERROR;
}

@ -508,7 +508,7 @@ static int ra_ul_nr_pucch_resource_hl(const srsran_pucch_nr_hl_cfg_t* cfg,
} else if (O_uci <= N3 && cfg->sets[2].nof_resources > 0) {
resource_set_id = 2;
} else if (cfg->sets[3].nof_resources == 0) {
ERROR("Invalid PUCCH resource configuration, N3=%d, O_uci=%d", N3, O_uci);
ERROR("Invalid PUCCH resource configuration, N2=%d, N3=%d, O_uci=%d", N2, N3, O_uci);
return SRSRAN_ERROR;
} else if (O_uci > SRSRAN_UCI_NR_MAX_NOF_BITS) {
ERROR("The number of UCI bits (%d), exceeds the maximum (%d)", O_uci, SRSRAN_UCI_NR_MAX_NOF_BITS);

@ -558,6 +558,18 @@ static int sch_nr_decode(srsran_sch_nr_t* q,
return SRSRAN_ERROR_INVALID_INPUTS;
}
// Protect softbuffer access
if (!tb->softbuffer.rx) {
ERROR("Missing softbuffer!");
return SRSRAN_ERROR;
}
// Protect PDU access
if (!res->payload) {
ERROR("Missing payload pointer!");
return SRSRAN_ERROR;
}
int8_t* input_ptr = e_bits;
uint32_t nof_iter_sum = 0;

@ -124,7 +124,7 @@ target_link_libraries(pssch_pscch_file_test srsran_phy)
# TM2 file tests
add_lte_test(pssch_pscch_file_test_ideal_tm2_p100 pssch_pscch_file_test -p 100 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat)
set_property(TEST pssch_pscch_file_test_ideal_tm2_p100 PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=2 num_decoded_tb=1")
set_property(TEST pssch_pscch_file_test_ideal_tm2_p100 PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=[2,3] num_decoded_tb=1")
# TM4 file tests (first SF is sf_idx = 6 such that the PSSCH sf_idx=0)
add_lte_test(pssch_pscch_file_test_ideal_tm4_p100 pssch_pscch_file_test -p 100 -t 4 -s 10 -n 10 -d -m 6 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p100_c335_size10_num10_cshift0_s30.72e6.dat)

@ -59,6 +59,13 @@ if(RF_FOUND)
list(APPEND SOURCES_RF rf_soapy_imp.c)
endif (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR)
if(SKIQ_FOUND)
add_executable(skiq_pps_test skiq_pps_test.c)
target_link_libraries(skiq_pps_test ${SKIQ_LIBRARIES} rt pthread m)
add_definitions(-DENABLE_SIDEKIQ)
list(APPEND SOURCES_RF rf_skiq_imp.c rf_skiq_imp_card.c rf_skiq_imp_port.c)
endif(SKIQ_FOUND)
if (ZEROMQ_FOUND)
add_definitions(-DENABLE_ZEROMQ)
list(APPEND SOURCES_RF rf_zmq_imp.c rf_zmq_imp_tx.c rf_zmq_imp_rx.c)
@ -80,6 +87,10 @@ if(RF_FOUND)
target_link_libraries(srsran_rf ${SOAPYSDR_LIBRARIES})
endif (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR)
if(SKIQ_FOUND)
target_link_libraries(srsran_rf ${SKIQ_LIBRARIES} rt)
endif(SKIQ_FOUND)
if (ZEROMQ_FOUND)
target_link_libraries(srsran_rf ${ZEROMQ_LIBRARIES})
add_executable(rf_zmq_test rf_zmq_test.c)

@ -225,6 +225,41 @@ static rf_dev_t dev_zmq = {"zmq",
.srsran_rf_send_timed_multi = rf_zmq_send_timed_multi};
#endif
/* Define implementation for Sidekiq */
#ifdef ENABLE_SIDEKIQ
#include "rf_skiq_imp.h"
static rf_dev_t dev_skiq = {.name = "Sidekiq",
.srsran_rf_devname = rf_skiq_devname,
.srsran_rf_start_rx_stream = rf_skiq_start_rx_stream,
.srsran_rf_stop_rx_stream = rf_skiq_stop_rx_stream,
.srsran_rf_flush_buffer = rf_skiq_flush_buffer,
.srsran_rf_has_rssi = rf_skiq_has_rssi,
.srsran_rf_get_rssi = rf_skiq_get_rssi,
.srsran_rf_suppress_stdout = rf_skiq_suppress_stdout,
.srsran_rf_register_error_handler = rf_skiq_register_error_handler,
.srsran_rf_open = rf_skiq_open,
.srsran_rf_open_multi = rf_skiq_open_multi,
.srsran_rf_close = rf_skiq_close,
.srsran_rf_set_rx_srate = rf_skiq_set_rx_srate,
.srsran_rf_set_tx_srate = rf_skiq_set_tx_srate,
.srsran_rf_set_rx_gain = rf_skiq_set_rx_gain,
.srsran_rf_set_tx_gain = rf_skiq_set_tx_gain,
.srsran_rf_set_tx_gain_ch = rf_skiq_set_tx_gain_ch,
.srsran_rf_set_rx_gain_ch = rf_skiq_set_rx_gain_ch,
.srsran_rf_get_rx_gain = rf_skiq_get_rx_gain,
.srsran_rf_get_tx_gain = rf_skiq_get_tx_gain,
.srsran_rf_get_info = rf_skiq_get_info,
.srsran_rf_set_rx_freq = rf_skiq_set_rx_freq,
.srsran_rf_set_tx_freq = rf_skiq_set_tx_freq,
.srsran_rf_get_time = rf_skiq_get_time,
.srsran_rf_recv_with_time = rf_skiq_recv_with_time,
.srsran_rf_recv_with_time_multi = rf_skiq_recv_with_time_multi,
.srsran_rf_send_timed = rf_skiq_send_timed,
.srsran_rf_send_timed_multi = rf_skiq_send_timed_multi};
#endif
//#define ENABLE_DUMMY_DEV
#ifdef ENABLE_DUMMY_DEV
@ -255,6 +290,9 @@ static rf_dev_t* available_devices[] = {
#ifdef ENABLE_ZEROMQ
&dev_zmq,
#endif
#ifdef ENABLE_SIDEKIQ
&dev_skiq,
#endif
#ifdef ENABLE_DUMMY_DEV
&dev_dummy,
#endif

@ -0,0 +1,947 @@
/**
*
* \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 <unistd.h>
#include <sidekiq_api.h>
#include "rf_helper.h"
#include "rf_skiq_imp.h"
#include "rf_skiq_imp_card.h"
/**
* According the document Sidekiq API 4.13.0 @ref AD9361TimestampSlip:
* Functions that will affect the timestamp:
* skiq_write_rx_LO_freq()
* skiq_write_rx_sample_rate_and_bandwidth()
* skiq_write_tx_LO_freq()
* skiq_run_tx_quadcal()
* skiq_write_rx_freq_tune_mode()
* skiq_write_tx_freq_tune_mode()
* Functions that will be affected by the timestamp slip:
* skiq_read_last_1pps_timestamp()
* skiq_receive()
* skiq_transmit()
* skiq_read_curr_rx_timestamp()
* skiq_read_curr_tx_timestamp()
*
* The functions mentioned on the first group above can be divided in two groups. The first group are the ones that
* require restart the tx/rx streams of cards:
* skiq_write_rx_sample_rate_and_bandwidth()
*
* The module assumes:
* - Tx and Rx sampling rates are equal
* - Tx/Rx shall be stopped during the configuration
* - skiq_stop_rx_streaming_multi_immediate can be called while skiq_receive is being executed
* - skiq_receive shall not be called while skiq_stop_rx_streaming_multi_immediate
*
* In order to update the sampling rate, the RF module shall:
* - Stop all cards Rx streams
* - Stop all cards Tx streams
* - Update Tx/Rx sample rates
* - Start Rx stream
* - enable Tx stream on the next transmission
*
* The second group do not require restarting the tx/rx streams. Indeed, they only affect to a single card and there is
* no interest on stalling the rest of cards stream. Because of this, the module shall suspend the affected card.
* skiq_write_rx_LO_freq()
* skiq_write_tx_LO_freq()
*
* The module assumes:
* - The Tx/Rx LO frequency is changed for a single card
* - The Tx/Rx is stalled only in selected card
* - The rest of cards shall keep operating without stalling their streams
*
* In order to update the Tx/Rx LO frequencies, the RF module shall:
* - Suspend the Tx/Rx streams of the card:
* - If receive port ring-buffer has samples, the module shall keep reading from ringbuffer;
* - Otherwise, the module shall not read from ring-buffer and write zeros in the receive buffer
* - Set the Tx/Rx LO frequency
* - Resume the reception
*
*/
uint32_t rf_skiq_logging_level = SKIQ_LOG_INFO;
typedef struct {
uint32_t nof_cards;
uint32_t nof_ports;
rf_skiq_card_t cards[SKIQ_MAX_NUM_CARDS];
float cur_tx_gain;
srsran_rf_info_t info;
uint64_t next_tstamp;
double current_srate_hz;
cf_t dummy_buffer[RF_SKIQ_DUMMY_BUFFER_SIZE];
pthread_mutex_t mutex_rx; ///< Makes sure receive function and sampling rate setter are not running simultaneously
} rf_skiq_handler_t;
void rf_skiq_suppress_stdout(void* h)
{
SKIQ_RF_INFO("Suppressing stdout... lowering logging level to warning\n");
rf_skiq_logging_level = SKIQ_LOG_WARNING;
}
static bool rf_skiq_is_streaming(rf_skiq_handler_t* h)
{
// If a single card is streaming, return true
for (uint32_t i = 0; i < h->nof_cards; i++) {
if (rf_skiq_card_is_streaming(&h->cards[i])) {
return true;
}
}
return false;
}
bool rf_skiq_check_synch(rf_skiq_handler_t* h)
{
// Get first card system timestamp
int64_t ts0_sys = (int64_t)rf_skiq_card_read_sys_timestamp(&h->cards[0]);
int64_t ts0_rf = (int64_t)rf_skiq_card_read_rf_timestamp(&h->cards[0]);
SKIQ_RF_INFO(" ... Card 0 TS(sys/rf)=%ld/%ld\n", ts0_sys, ts0_rf);
bool pass = true;
// Compare all the other card timestamps
for (uint32_t i = 1; i < h->nof_cards; i++) {
int64_t ts2_sys = (int64_t)rf_skiq_card_read_sys_timestamp(&h->cards[i]);
int64_t ts2_rf = (int64_t)rf_skiq_card_read_rf_timestamp(&h->cards[i]);
// Use current sampling rate
double srate = h->current_srate_hz;
// If the current srate was not set (zero, nan or Inf), read it back from the first card
if (!isnormal(srate)) {
uint32_t srate_int = 0;
if (skiq_read_rx_sample_rate(0, skiq_rx_hdl_A1, &srate_int, &srate)) {
ERROR("Error reading sampling rate\n");
}
}
// Make sure all cards system and RF timestamps are inside maximum allowed error window
bool card_pass = labs(ts2_sys - ts0_sys) < SKIQ_CARD_SYNC_MAX_ERROR;
card_pass = card_pass && labs(ts2_rf - ts0_rf) < (int64_t)(srate / 2);
// It is enough that a card does not pass to fail the check
pass = pass && card_pass;
SKIQ_RF_INFO(" ... Card %d TS(sys/rf)=(%ld/%ld) (%.4f/%.4f). %s\n",
i,
ts2_sys,
ts2_rf,
(double)labs(ts2_sys - ts0_sys) / (double)SKIQ_SYS_TIMESTAMP_FREQ,
(double)labs(ts2_rf - ts0_rf) / srate,
card_pass ? "Ok" : "KO");
}
return pass;
}
/**
* Synchronizes single and multiple cards using the PPS signal. This function helper shall be used once at
* initialization.
*
* @param h SKIQ driver handler
* @return SRSRAN_SUCCESS if it is possible to synchronize boards, SRSRAN_ERROR otherwise
*/
static int rf_skiq_synch_cards(rf_skiq_handler_t* h)
{
bool do_1pps = h->nof_cards > 1; //< PPS is required when more than one card is used
uint32_t trials = 0; //< Count PPS synchronization check trials
// Try synchronizing timestamps of all cards up to 10 trials
do {
SKIQ_RF_INFO("Resetting system timestamp trial %d/%d\n", trials + 1, SKIQ_CARD_SYNCH_MAX_TRIALS);
// Reset timestamp in next PPS
for (int i = 0; i < h->nof_cards; i++) {
// Sets the timestamps to the last Rx known time.
if (rf_skiq_card_update_timestamp(
&h->cards[i], do_1pps, h->cards->rx_ports->rb_tstamp_rem + h->current_srate_hz) != SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
}
// It could be that one or more cards PPS reset was issued just after a PPS signal, so only if there is PPS
// (multiple cards), verifies that all cards are synchronised on the same PPS
if (!do_1pps) {
return SRSRAN_SUCCESS;
}
// Wait for a second to pass
SKIQ_RF_INFO(" ... Waiting PPS to pass ...\n");
sleep(1);
SKIQ_RF_INFO(" ... Checking:\n");
// Successful synchronization across boards!
if (rf_skiq_check_synch(h)) {
return SRSRAN_SUCCESS;
}
// Increment trial count
trials++;
} while (trials < SKIQ_CARD_SYNCH_MAX_TRIALS);
// All trials have been consumed without a Successful synchronization
ERROR("Error card synchronization failed\n");
return SRSRAN_ERROR;
}
void rf_skiq_register_error_handler(void* h_, srsran_rf_error_handler_t error_handler, void* arg)
{
rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_;
SKIQ_RF_INFO("Registering error handler...\n");
// Set error handler for each card
for (uint32_t i = 0; i < h->nof_cards; i++) {
rf_skiq_card_set_error_handler(&h->cards[i], error_handler, arg);
}
}
const char* rf_skiq_devname(void* h)
{
return "Sidekiq";
}
int rf_skiq_start_rx_stream(void* h_, bool now)
{
rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_;
rf_skiq_synch_cards(h);
// Get current system timestamp, assume all cards are synchronized
uint64_t ts = rf_skiq_card_read_sys_timestamp(&h->cards[0]);
// Advance a 10th of a second (100ms)
ts += SKIQ_SYS_TIMESTAMP_FREQ / 10;
// Start streams for each card at the indicated timestamp...
for (uint32_t i = 0; i < h->nof_cards; i++) {
if (rf_skiq_card_start_rx_streaming(&h->cards[i], ts)) {
return SRSRAN_ERROR;
}
}
return SRSRAN_SUCCESS;
}
int rf_skiq_stop_rx_stream(void* h_)
{
rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_;
for (int i = 0; i < h->nof_cards; i++) {
rf_skiq_card_stop_rx_streaming(&h->cards[i]);
}
return SRSRAN_SUCCESS;
}
static int rf_skiq_send_end_of_burst(void* h_)
{
rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_;
for (int i = 0; i < h->nof_cards; i++) {
rf_skiq_card_end_of_burst(&h->cards[i]);
}
return SRSRAN_SUCCESS;
}
void rf_skiq_flush_buffer(void* h)
{
SKIQ_RF_INFO("Flushing buffers...\n");
int n;
void* data[SKIQ_MAX_CHANNELS] = {};
do {
n = rf_skiq_recv_with_time_multi(h, data, 1024, 0, NULL, NULL);
} while (n > 0);
}
bool rf_skiq_has_rssi(void* h)
{
return false;
}
float rf_skiq_get_rssi(void* h)
{
return 0.0f;
}
int rf_skiq_open(char* args, void** h)
{
return rf_skiq_open_multi(args, h, 1);
}
void rf_skiq_log_msg(int32_t priority, const char* message)
{
if (priority <= rf_skiq_logging_level) {
printf("%s", message);
}
}
int rf_skiq_open_multi(char* args, void** h_, uint32_t nof_channels)
{
// Check number of antennas bounds
if (nof_channels < SKIQ_MIN_CHANNELS || nof_channels > SKIQ_MAX_CHANNELS) {
ERROR("Number of channels (%d) not supported (%d-%d)\n", nof_channels, SKIQ_MIN_CHANNELS, SKIQ_MAX_CHANNELS);
return SRSRAN_ERROR_OUT_OF_BOUNDS;
}
rf_skiq_handler_t* h = (rf_skiq_handler_t*)calloc(1, sizeof(rf_skiq_handler_t));
if (!h) {
return SRSRAN_ERROR;
}
*h_ = h;
// Parse main parameters
parse_uint32(args, "nof_cards", 0, &h->nof_cards);
parse_uint32(args, "nof_ports", 0, &h->nof_ports);
char log_level[RF_PARAM_LEN] = "info";
parse_string(args, "log_level", 0, log_level);
if (strcmp(log_level, "info") == 0) {
rf_skiq_logging_level = SKIQ_LOG_INFO;
} else if (strcmp(log_level, "debug") == 0) {
rf_skiq_logging_level = SKIQ_LOG_DEBUG;
} else if (strcmp(log_level, "warn") == 0) {
rf_skiq_logging_level = SKIQ_LOG_WARNING;
} else if (strcmp(log_level, "error") == 0) {
rf_skiq_logging_level = SKIQ_LOG_ERROR;
} else {
ERROR("Error log_level %s is undefined. Options: debug, info, warn and error\n", log_level);
return SRSRAN_ERROR;
}
// Register Logger
skiq_register_logging(&rf_skiq_log_msg);
// Get available cards
uint8_t nof_available_cards = 0;
uint8_t available_cards[SKIQ_MAX_NUM_CARDS] = {};
if (skiq_get_cards(skiq_xport_type_auto, &nof_available_cards, available_cards)) {
ERROR("Getting available cards\n");
return SRSRAN_ERROR;
}
//
if (h->nof_cards == 0 && h->nof_ports == 0) {
if (nof_channels <= (uint32_t)nof_available_cards) {
// One channel per card
h->nof_cards = nof_channels;
h->nof_ports = 1;
} else if (nof_channels <= RF_SKIQ_MAX_PORTS_CARD) {
// One channel per port
h->nof_cards = 1;
h->nof_ports = nof_channels;
} else if (nof_channels % RF_SKIQ_MAX_PORTS_CARD == 0) {
// use all ports
h->nof_cards = nof_channels / RF_SKIQ_MAX_PORTS_CARD;
h->nof_ports = RF_SKIQ_MAX_PORTS_CARD;
} else if (nof_channels % nof_available_cards == 0) {
// use all cards
h->nof_cards = nof_available_cards;
h->nof_ports = nof_channels / nof_available_cards;
} else {
ERROR("Error deducing the number of cards and ports");
}
} else if (h->nof_ports == 0 && nof_channels % h->nof_cards == 0) {
h->nof_ports = nof_channels / h->nof_cards;
} else if (h->nof_cards == 0 && nof_channels % h->nof_ports == 0) {
h->nof_cards = nof_channels / h->nof_ports;
}
if (h->nof_cards == 0 || h->nof_cards > nof_available_cards) {
ERROR("Error invalid number of cards %d, available %d\n", h->nof_cards, nof_available_cards);
return SRSRAN_ERROR_OUT_OF_BOUNDS;
}
if (h->nof_ports == 0 || h->nof_ports > RF_SKIQ_MAX_PORTS_CARD) {
ERROR("Error invalid number of cards %d, available %d\n", h->nof_cards, nof_available_cards);
return SRSRAN_ERROR_OUT_OF_BOUNDS;
}
// Create default port options
rf_skiq_port_opts_t port_opts = {};
port_opts.tx_rb_size = 2048;
port_opts.rx_rb_size = 2048;
port_opts.chan_mode = (h->nof_ports > 1) ? skiq_chan_mode_dual : skiq_chan_mode_single;
port_opts.stream_mode = skiq_rx_stream_mode_balanced;
// Parse other options
parse_uint32(args, "tx_rb_size", 0, &port_opts.tx_rb_size);
parse_uint32(args, "rx_rb_size", 0, &port_opts.rx_rb_size);
parse_string(args, "mode", 0, port_opts.stream_mode_str);
if (strlen(port_opts.stream_mode_str) > 0) {
if (strcmp(port_opts.stream_mode_str, "low_latency") == 0) {
port_opts.stream_mode = skiq_rx_stream_mode_low_latency;
} else if (strcmp(port_opts.stream_mode_str, "balanced") == 0) {
port_opts.stream_mode = skiq_rx_stream_mode_balanced;
} else if (strcmp(port_opts.stream_mode_str, "high_tput") == 0) {
port_opts.stream_mode = skiq_rx_stream_mode_high_tput;
} else {
ERROR("Invalid mode: %s; Valid modes are: low_latency, balanced, high_tput\n", port_opts.stream_mode_str);
return SRSRAN_ERROR;
}
}
SKIQ_RF_INFO("Opening %d SKIQ cards with %d ports...\n", h->nof_cards, h->nof_ports);
if (pthread_mutex_init(&h->mutex_rx, NULL)) {
ERROR("Error initialising mutex\n");
return SRSRAN_ERROR;
}
// Initialise driver
if (skiq_init(skiq_xport_type_auto, skiq_xport_init_level_full, available_cards, h->nof_cards)) {
ERROR("Unable to initialise libsidekiq driver\n");
return SRSRAN_ERROR;
}
// Initialise each card
for (uint32_t i = 0; i < h->nof_cards; i++) {
if (rf_skiq_card_init(&h->cards[i], available_cards[i], h->nof_ports, &port_opts)) {
return SRSRAN_ERROR;
}
}
// Parse default frequencies
for (uint32_t i = 0, ch = 0; i < h->nof_cards; i++) {
for (uint32_t j = 0; j < h->nof_ports; j++, ch++) {
double tx_freq = 0.0;
parse_double(args, "tx_freq", ch, &tx_freq);
if (isnormal(tx_freq)) {
rf_skiq_set_tx_freq(h, ch, tx_freq);
}
double rx_freq = 0.0;
parse_double(args, "rx_freq", ch, &rx_freq);
if (isnormal(rx_freq)) {
rf_skiq_set_rx_freq(h, ch, rx_freq);
}
}
}
// Set a default gain
rf_skiq_set_rx_gain(h, SKIQ_RX_GAIN_DEFAULT_dB);
// Parse default sample rate
double srate_hz = 0.0;
parse_double(args, "srate", 0, &srate_hz);
srate_hz = isnormal(srate_hz) ? srate_hz : SKIQ_DEFAULT_SAMPLING_RATE_HZ;
// Set a default sampling rate, default can be too low
rf_skiq_set_tx_srate(h, srate_hz);
rf_skiq_set_rx_srate(h, srate_hz);
return SRSRAN_SUCCESS;
}
int rf_skiq_close(void* h_)
{
rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_;
SKIQ_RF_INFO("Closing...\n");
// Ensure Tx/Rx streaming is stopped
rf_skiq_send_end_of_burst(h);
rf_skiq_stop_rx_stream(h);
// Free all open cards
for (int i = 0; i < h->nof_cards; i++) {
rf_skiq_card_close(&h->cards[i]);
}
// Close sidekiq SDK
skiq_exit();
pthread_mutex_destroy(&h->mutex_rx);
// Deallocate object memory
if (h != NULL) {
free(h);
}
return SRSRAN_SUCCESS;
}
static double rf_skiq_set_srate_hz(rf_skiq_handler_t* h, double srate_hz)
{
// If the sampling rate is not modified dont bother
if (h->current_srate_hz == srate_hz) {
return srate_hz;
}
SKIQ_RF_INFO("Setting sampling rate to %.2f MHz ...\n", srate_hz / 1e6);
// Save streaming state
bool is_streaming = rf_skiq_is_streaming(h);
// Stop streaming
SKIQ_RF_INFO(" ... Stop Tx/Rx streaming\n");
rf_skiq_send_end_of_burst(h);
rf_skiq_stop_rx_stream(h);
// Set sampling
SKIQ_RF_INFO(" ... Setting sampling rates to %.2f MHz\n", srate_hz / 1e6);
pthread_mutex_lock(&h->mutex_rx);
for (uint32_t i = 0; i < h->nof_cards; i++) {
rf_skiq_card_set_srate_hz(&h->cards[i], (uint32_t)srate_hz);
}
pthread_mutex_unlock(&h->mutex_rx);
// Start streaming if it was started
if (is_streaming) {
SKIQ_RF_INFO(" ... Start Rx streaming\n");
rf_skiq_start_rx_stream(h, true);
}
// Update current sampling rate
h->current_srate_hz = srate_hz;
SKIQ_RF_INFO(" ... Done!\n");
return srate_hz;
}
double rf_skiq_set_rx_srate(void* h, double sample_rate)
{
return rf_skiq_set_srate_hz((rf_skiq_handler_t*)h, sample_rate);
}
double rf_skiq_set_tx_srate(void* h, double sample_rate)
{
return rf_skiq_set_srate_hz((rf_skiq_handler_t*)h, sample_rate);
}
int rf_skiq_set_rx_gain(void* h_, double rx_gain)
{
rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_;
for (uint32_t i = 0; i < h->nof_cards; i++) {
rf_skiq_card_set_rx_gain_db(&h->cards[i], h->nof_ports, rx_gain);
}
return SRSRAN_SUCCESS;
}
int rf_skiq_set_tx_gain(void* h_, double tx_gain)
{
rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_;
for (uint32_t i = 0; i < h->nof_cards; i++) {
h->cur_tx_gain = rf_skiq_card_set_tx_gain_db(&h->cards[i], h->nof_ports, tx_gain);
}
return SRSRAN_SUCCESS;
}
int rf_skiq_set_tx_gain_ch(void* h_, uint32_t ch, double tx_gain)
{
rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_;
uint32_t card_idx = ch / h->nof_ports;
uint32_t port_idx = ch % h->nof_ports;
if (card_idx >= h->nof_cards || port_idx >= h->nof_ports) {
return SRSRAN_ERROR_OUT_OF_BOUNDS;
}
tx_gain = rf_skiq_card_set_tx_gain_db(&h->cards[card_idx], port_idx, tx_gain);
if (ch == 0) {
h->cur_tx_gain = tx_gain;
}
return SRSRAN_SUCCESS;
}
int rf_skiq_set_rx_gain_ch(void* h_, uint32_t ch, double rx_gain)
{
rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_;
uint32_t card_idx = ch / h->nof_ports;
uint32_t port_idx = ch % h->nof_ports;
if (card_idx >= h->nof_cards || port_idx >= h->nof_ports) {
return SRSRAN_ERROR_OUT_OF_BOUNDS;
}
rx_gain = rf_skiq_card_set_rx_gain_db(&h->cards[card_idx], port_idx, rx_gain);
if (ch == 0) {
h->cur_tx_gain = rx_gain;
}
return SRSRAN_SUCCESS;
}
double rf_skiq_get_rx_gain(void* h_)
{
rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_;
return h->cards[0].cur_rx_gain_db;
}
double rf_skiq_get_tx_gain(void* h_)
{
rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_;
return h->cur_tx_gain;
}
srsran_rf_info_t* rf_skiq_get_info(void* h_)
{
srsran_rf_info_t* ret = NULL;
rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_;
if (h != NULL) {
ret = &h->info;
rf_skiq_card_update_gain_table(&h->cards[0]);
ret->min_tx_gain = 0.25 * (double)h->cards[0].param.tx_param->atten_quarter_db_max;
ret->max_tx_gain = 0.25 * (double)h->cards[0].param.tx_param->atten_quarter_db_min;
ret->min_rx_gain = h->cards[0].rx_gain_table_db[h->cards[0].param.rx_param[0].gain_index_min];
ret->max_rx_gain = h->cards[0].rx_gain_table_db[h->cards[0].param.rx_param[0].gain_index_max];
}
return ret;
}
double rf_skiq_set_rx_freq(void* h_, uint32_t ch, double freq)
{
rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_;
uint32_t card_idx = ch / h->nof_ports;
uint32_t port_idx = ch % h->nof_ports;
#pragma message "TODO: The Rx stream needs to stop, RF timestamp shall be aligned with other cards and start again"
if (card_idx < h->nof_cards && port_idx < h->nof_ports) {
return rf_skiq_card_set_rx_freq_hz(&h->cards[card_idx], port_idx, freq);
}
return freq;
}
double rf_skiq_set_tx_freq(void* h_, uint32_t ch, double freq)
{
rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_;
uint32_t card_idx = ch / h->nof_ports;
uint32_t port_idx = ch % h->nof_ports;
#pragma message "TODO: The Rx stream needs to stop, RF timestamp shall be aligned with other cards and start again"
if (card_idx < h->nof_cards && port_idx < h->nof_ports) {
return rf_skiq_card_set_tx_freq_hz(&h->cards[card_idx], port_idx, freq);
}
return freq;
}
void tstamp_to_time(rf_skiq_handler_t* h, uint64_t tstamp, time_t* secs, double* frac_secs)
{
uint64_t srate_hz = (uint64_t)h->current_srate_hz;
if (srate_hz == 0) {
ERROR("Warning: Sampling rate has not been set yet.\n");
srate_hz = UINT64_MAX;
}
if (secs) {
*secs = (time_t)tstamp / srate_hz;
}
if (frac_secs) {
uint64_t rem = tstamp % srate_hz;
*frac_secs = (double)rem / h->current_srate_hz;
}
}
uint64_t time_to_tstamp(rf_skiq_handler_t* h, time_t secs, double frac_secs)
{
return secs * h->current_srate_hz + frac_secs * h->current_srate_hz;
}
void rf_skiq_get_time(void* h_, time_t* secs, double* frac_secs)
{
rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_;
uint64_t tstamp = rf_skiq_card_read_rf_timestamp(&h->cards[0]);
tstamp_to_time(h, tstamp, secs, frac_secs);
}
static int
rf_skiq_discard_rx_samples(rf_skiq_handler_t* h, uint32_t card, uint32_t port, uint32_t nsamples, uint64_t* ts_start)
{
*ts_start = 0;
while (nsamples > 0) {
uint64_t ts = 0;
// Receive in dummy buffer
int32_t n = rf_skiq_card_receive(
&h->cards[card], port, h->dummy_buffer, SRSRAN_MIN(nsamples, RF_SKIQ_DUMMY_BUFFER_SIZE), &ts);
// Check for error
if (n < 0) {
ERROR("An error occurred discarding %d Rx samples for channel %d:%d\n", nsamples, card, port);
return n;
}
// Save first timestamp
if (*ts_start == 0) {
*ts_start = ts;
}
// Decrement pending samples
nsamples -= n;
}
return nsamples;
}
static int rf_skiq_synch_rx_ports(rf_skiq_handler_t* h)
{
int64_t tstamp_min = INT64_MAX;
int64_t tstamp_max = 0;
// no need to synchronize
if (h->nof_cards * h->nof_ports < 2) {
return SRSRAN_SUCCESS;
}
// Find minimum and maximum next timestamps
for (uint32_t card = 0; card < h->nof_cards; card++) {
// Iterate for all ports
for (uint32_t port = 0; port < h->nof_ports; port++) {
int64_t ts = (int64_t)rf_skiq_card_get_rx_timestamp(&h->cards[card], port);
// If the card is not streaming or suspended will return TS 0; so skip
if (ts == 0UL) {
continue;
}
// Is minimum?
tstamp_min = SRSRAN_MIN(tstamp_min, ts);
// Is maximum?
tstamp_max = SRSRAN_MAX(tstamp_max, ts);
}
}
// Check if any synchronization is required
if (tstamp_max == tstamp_min) {
return SRSRAN_SUCCESS;
}
// Align all channels to the maximum timestamp
for (uint32_t card = 0; card < h->nof_cards; card++) {
// Iterate for all ports
for (uint32_t port = 0; port < h->nof_ports; port++) {
uint64_t ts = rf_skiq_card_get_rx_timestamp(&h->cards[card], port);
// If the card is not streaming or suspended will return TS 0; so skip
if (ts == 0UL) {
continue;
}
// Calculate number of samples
int nsamples = (int)(tstamp_max - (int64_t)ts);
// Skip port if negative or zero (possible if stream is enabled during this time)
if (nsamples <= 0) {
continue;
}
// Too many samples, sign of some extreme error
if (nsamples > SKIQ_PORT_SYNC_MAX_GAP) {
ERROR("too many samples to align (%d) for channel %d:%d\n", nsamples, card, port);
return SRSRAN_ERROR;
}
ts = 0;
if (rf_skiq_discard_rx_samples(h, card, port, nsamples, &ts) < SRSRAN_SUCCESS) {
ERROR("Error occurred during Rx streams alignment.");
return SRSRAN_ERROR;
}
}
}
return SRSRAN_SUCCESS;
}
int rf_skiq_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs)
{
return rf_skiq_recv_with_time_multi(h, &data, nsamples, blocking, secs, frac_secs);
}
int rf_skiq_recv_with_time_multi(void* h_,
void** data_,
uint32_t nsamples,
bool blocking,
time_t* secs,
double* frac_secs)
{
rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_;
uint64_t ts_start = 0;
cf_t** data = (cf_t**)data_;
pthread_mutex_lock(&h->mutex_rx);
// Perform channel synchronization
rf_skiq_synch_rx_ports(h);
bool completed = false;
uint32_t count[SKIQ_MAX_CHANNELS] = {};
while (!completed) {
// Completion true by default
completed = true;
// Iterate over cards
for (uint32_t card = 0, chan = 0; card < h->nof_cards; card++) {
// Iterate over ports
for (uint32_t port = 0; port < h->nof_ports; port++, chan++) {
// Calculate number of pending samples
uint32_t pending = nsamples - count[chan];
if (pending > 0) {
uint64_t ts = 0;
int n = 0;
// If data is not provided...
if (data[chan] == NULL) {
// ... discard up to RF_SKIQ_DUMMY_BUFFER_SIZE samples
n = rf_skiq_card_receive(
&h->cards[card], port, h->dummy_buffer, SRSRAN_MIN(pending, RF_SKIQ_DUMMY_BUFFER_SIZE), &ts);
} else {
// ... read base-band
n = rf_skiq_card_receive(&h->cards[card], port, &data[chan][count[chan]], pending, &ts);
}
// If error is detected, return it
if (n < SRSRAN_SUCCESS) {
pthread_mutex_unlock(&h->mutex_rx);
return n;
}
// Save first valid timestamp
if (ts_start == 0) {
ts_start = ts;
}
// Increment count for the channel
count[chan] += n;
// Lower completed flag if at least a channel has not reach the target
if (count[chan] < nsamples) {
completed = false;
}
}
}
}
}
pthread_mutex_unlock(&h->mutex_rx);
// Convert u64 to srsran timestamp
tstamp_to_time(h, ts_start, secs, frac_secs);
// No error, return number of received samples
return nsamples;
}
int rf_skiq_send_timed(void* h,
void* data,
int nsamples,
time_t secs,
double frac_secs,
bool has_time_spec,
bool blocking,
bool is_start_of_burst,
bool is_end_of_burst)
{
void* _data[SRSRAN_MAX_PORTS] = {};
_data[0] = data;
return rf_skiq_send_timed_multi(
h, _data, nsamples, secs, frac_secs, has_time_spec, blocking, is_start_of_burst, is_end_of_burst);
}
int rf_skiq_send_timed_multi(void* h_,
void** data_,
int nsamples,
time_t secs,
double frac_secs,
bool has_time_spec,
bool blocking,
bool is_start_of_burst,
bool is_end_of_burst)
{
rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_;
cf_t** data = (cf_t**)data_;
// Force timestamp only if start of burst
if (is_start_of_burst) {
if (has_time_spec) {
h->next_tstamp = time_to_tstamp(h, secs, frac_secs);
SKIQ_RF_DEBUG("[Tx SOB] ts=%ld\n", h->next_tstamp);
} else {
h->next_tstamp = rf_skiq_card_read_rf_timestamp(&h->cards[0]);
h->next_tstamp += (uint64_t)round(h->current_srate_hz / 10); // increment a 10th of a second
}
}
uint32_t rpm = 0;
bool completed = false;
uint32_t count[SKIQ_MAX_CHANNELS] = {};
while (!completed) {
// Completion true by default
completed = true;
// Iterate all cards...
for (uint32_t card = 0, chan = 0; card < h->nof_cards; card++) {
// Iterate all ports...
for (uint32_t port = 0; port < h->nof_ports; port++, chan++) {
// Calculate number of pending samples
uint32_t pending = nsamples - count[chan];
if (pending > 0) {
uint64_t ts = h->next_tstamp + count[chan];
cf_t* ptr = (data[chan] == NULL) ? NULL : &data[chan][count[chan]];
int n = rf_skiq_card_send(&h->cards[card], port, ptr, pending, ts);
if (n > SRSRAN_SUCCESS) {
count[chan] += n;
}
if (count[chan] < nsamples) {
completed = false;
}
}
}
}
}
// Increment timestamp
h->next_tstamp += nsamples;
if (is_end_of_burst) {
rf_skiq_send_end_of_burst(h);
}
return (int)rpm;
}

@ -0,0 +1,95 @@
/**
*
* \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 <stdbool.h>
#include <stdint.h>
#include "srsran/config.h"
#include "srsran/phy/rf/rf.h"
SRSRAN_API int rf_skiq_open(char* args, void** handler);
SRSRAN_API int rf_skiq_open_multi(char* args, void** handler, uint32_t nof_rx_antennas);
SRSRAN_API const char* rf_skiq_devname(void* h);
SRSRAN_API int rf_skiq_close(void* h);
SRSRAN_API int rf_skiq_start_rx_stream(void* h, bool now);
SRSRAN_API int rf_skiq_start_rx_stream_nsamples(void* h, uint32_t nsamples);
SRSRAN_API int rf_skiq_stop_rx_stream(void* h);
SRSRAN_API void rf_skiq_flush_buffer(void* h);
SRSRAN_API bool rf_skiq_has_rssi(void* h);
SRSRAN_API float rf_skiq_get_rssi(void* h);
SRSRAN_API void rf_skiq_set_master_clock_rate(void* h, double rate);
SRSRAN_API bool rf_skiq_is_master_clock_dynamic(void* h);
SRSRAN_API double rf_skiq_set_rx_srate(void* h, double freq);
SRSRAN_API int rf_skiq_set_rx_gain(void* h, double gain);
SRSRAN_API int rf_skiq_set_rx_gain_ch(void* h_, uint32_t ch, double rx_gain);
SRSRAN_API double rf_skiq_get_rx_gain(void* h);
SRSRAN_API double rf_skiq_get_tx_gain(void* h);
SRSRAN_API srsran_rf_info_t* rf_skiq_get_info(void* h);
SRSRAN_API void rf_skiq_suppress_stdout(void* h);
SRSRAN_API void rf_skiq_register_error_handler(void* h, srsran_rf_error_handler_t error_handler, void* arg);
SRSRAN_API double rf_skiq_set_rx_freq(void* h, uint32_t ch, double freq);
SRSRAN_API int
rf_skiq_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs);
SRSRAN_API int
rf_skiq_recv_with_time_multi(void* h, void** data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs);
SRSRAN_API double rf_skiq_set_tx_srate(void* h, double freq);
SRSRAN_API int rf_skiq_set_tx_gain(void* h, double gain);
SRSRAN_API int rf_skiq_set_tx_gain_ch(void* h, uint32_t ch, double gain);
SRSRAN_API double rf_skiq_set_tx_freq(void* h, uint32_t ch, double freq);
SRSRAN_API void rf_skiq_get_time(void* h, time_t* secs, double* frac_secs);
SRSRAN_API int rf_skiq_send_timed(void* h,
void* data,
int nsamples,
time_t secs,
double frac_secs,
bool has_time_spec,
bool blocking,
bool is_start_of_burst,
bool is_end_of_burst);
SRSRAN_API int rf_skiq_send_timed_multi(void* h,
void* data[4],
int nsamples,
time_t secs,
double frac_secs,
bool has_time_spec,
bool blocking,
bool is_start_of_burst,
bool is_end_of_burst);

@ -0,0 +1,580 @@
/**
*
* \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 "rf_skiq_imp_card.h"
static void* reader_thread(void* arg)
{
rf_skiq_card_t* q = (rf_skiq_card_t*)arg;
while (q->state != RF_SKIQ_PORT_STATE_STOP) {
// Wait to leave idle state
if (q->state == RF_SKIQ_PORT_STATE_IDLE) {
SKIQ_RF_INFO("[Rx %d] IDLE\n", q->card);
pthread_mutex_lock(&q->mutex);
while (q->state == RF_SKIQ_PORT_STATE_IDLE) {
pthread_cond_wait(&q->cvar, &q->mutex);
}
pthread_mutex_unlock(&q->mutex);
SKIQ_RF_INFO("[Rx %d] %s\n", q->card, q->state == RF_SKIQ_PORT_STATE_STREAMING ? "STREAMING" : "STOP");
}
// Check exit condition
if (q->state == RF_SKIQ_PORT_STATE_STOP) {
break;
}
skiq_rx_hdl_t curr_rx_hdl = 0;
skiq_rx_block_t* p_rx_block = NULL;
uint32_t len = 0;
skiq_rx_status_t rx_status = skiq_receive(q->card, &curr_rx_hdl, &p_rx_block, &len);
switch (rx_status) {
case skiq_rx_status_success:
// Check Rx index boundary
if (p_rx_block->rfic_control >= q->param.rx_param->gain_index_min &&
p_rx_block->rfic_control <= q->param.rx_param->gain_index_max) {
double new_rx_gain = q->rx_gain_table_db[p_rx_block->rfic_control];
// If the Rx index has changed, update gain
if (new_rx_gain != q->cur_rx_gain_db) {
SKIQ_RF_DEBUG("card %d index=%d; gain=%.2f/%.2f dB;\n",
q->card,
p_rx_block->rfic_control,
q->rx_gain_table_db[p_rx_block->rfic_control],
q->cur_rx_gain_db);
q->cur_rx_gain_db = new_rx_gain;
}
}
if (curr_rx_hdl < q->nof_ports && p_rx_block != NULL && len > SKIQ_RX_HEADER_SIZE_IN_BYTES) {
// Convert number of bytes into samples
uint32_t nsamples = len / 4 - SKIQ_RX_HEADER_SIZE_IN_WORDS;
// Give block to the port
rf_skiq_rx_port_write(&q->rx_ports[curr_rx_hdl], p_rx_block, nsamples);
} else {
ERROR("Card %d received data with corrupted pointers\n", q->card);
}
break;
case skiq_rx_status_no_data:
// Do nothing
break;
case skiq_rx_status_error_generic:
ERROR("Error: Generic error occurred during skiq_receive.\n");
break;
case skiq_rx_status_error_overrun:
ERROR("Error: overrun error occurred during skiq_receive.\n");
break;
case skiq_rx_status_error_packet_malformed:
ERROR("Error: packet malformed error occurred during skiq_receive.\n");
break;
case skiq_rx_status_error_card_not_active:
ERROR("Error: inactive card error occurred during skiq_receive.\n");
break;
case skiq_rx_status_error_not_streaming:
ERROR("Error: not streaming card error occurred during skiq_receive.\n");
break;
default:
ERROR("Error: the impossible happened during skiq_receive.\n");
break;
}
}
SKIQ_RF_INFO("Exiting reader thread!\n");
return NULL;
}
int rf_skiq_card_update_gain_table(rf_skiq_card_t* q)
{
for (uint8_t i = q->param.rx_param->gain_index_min; i <= q->param.rx_param->gain_index_max; i++) {
if (skiq_read_rx_cal_offset_by_gain_index(q->card, skiq_rx_hdl_A1, i, &q->rx_gain_table_db[i])) {
ERROR("Reading calibrated Rx gain index %d", i);
return SRSRAN_ERROR;
}
}
return SRSRAN_SUCCESS;
}
int rf_skiq_card_init(rf_skiq_card_t* q, uint8_t card, uint8_t nof_ports, const rf_skiq_port_opts_t* opts)
{
q->card = card;
q->nof_ports = nof_ports;
// Reprogram FPGA to reset all states
if (skiq_prog_fpga_from_flash(card)) {
ERROR("Error programming card %d from flash\n", q->card);
return SRSRAN_ERROR;
}
// Read card parameters
if (skiq_read_parameters(card, &q->param)) {
ERROR("Reading card %d param", card);
return SRSRAN_ERROR;
}
// Check number of rx channels
if (q->param.rf_param.num_rx_channels < nof_ports) {
ERROR("Card %d does not support %d Rx channels", card, nof_ports);
return SRSRAN_ERROR;
}
// Check number of tx channels
if (q->param.rf_param.num_tx_channels < nof_ports) {
ERROR("Card %d does not support %d Tx channels", card, nof_ports);
return SRSRAN_ERROR;
}
// set a modest rx timeout
if (skiq_set_rx_transfer_timeout(card, 1000)) {
ERROR("Setting Rx transfer timeout");
return SRSRAN_ERROR;
}
// do not pack 12bit samples
if (skiq_write_iq_pack_mode(card, false)) {
ERROR("Setting Rx IQ pack mode");
return SRSRAN_ERROR;
}
// set the control output bits to include the gain
if (skiq_write_rfic_control_output_config(
card, RFIC_CONTROL_OUTPUT_MODE_GAIN_CONTROL_RXA1, RFIC_CONTROL_OUTPUT_MODE_GAIN_BITS) != 0) {
ERROR("Unable to configure card %d the RF IC control output (A1)", card);
return SRSRAN_ERROR;
}
// set RX channel mode
if (skiq_write_chan_mode(card, q->mode)) {
ERROR("Setting card %d channel mode", card);
return SRSRAN_ERROR;
}
// Select Rx streaming mode to low latency if the sampling rate is lower than 5MHz
if (skiq_write_rx_stream_mode(q->card, opts->stream_mode)) {
ERROR("Error setting Rx stream mode\n");
return SRSRAN_ERROR;
}
// initialise tx/rx ports
for (uint8_t i = 0; i < nof_ports; i++) {
if (rf_skiq_tx_port_init(&q->tx_ports[i], card, (skiq_tx_hdl_t)i, opts)) {
ERROR("Initiating card %d, Tx port %d", card, i);
return SRSRAN_ERROR;
}
if (rf_skiq_rx_port_init(&q->rx_ports[i], card, (skiq_rx_hdl_t)i, opts)) {
ERROR("Initiating card %d, Rx port %d", card, i);
return SRSRAN_ERROR;
}
}
if (pthread_mutex_init(&q->mutex, NULL)) {
ERROR("Initiating mutex");
return SRSRAN_ERROR;
}
if (pthread_cond_init(&q->cvar, NULL)) {
ERROR("Initiating cvar");
return SRSRAN_ERROR;
}
// Initialise thread parameters
pthread_attr_t attr;
struct sched_param param;
param.sched_priority = sched_get_priority_max(SCHED_FIFO);
pthread_attr_init(&attr);
if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) {
ERROR("Error not enough privileges to set Scheduling priority\n");
}
if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) {
ERROR("Error not enough privileges to set Scheduling priority\n");
}
if (pthread_attr_setschedparam(&attr, &param)) {
ERROR("Error not enough privileges to set Scheduling priority\n");
}
// Launch thread
if (pthread_create(&q->thread, &attr, reader_thread, q)) {
ERROR("Error creating reader thread with attributes (Did you miss sudo?). Trying without attributes.\n");
return SRSRAN_ERROR;
}
// Rename thread
char thread_name[32] = {};
if (snprintf(thread_name, sizeof(thread_name), "SKIQ Rx %d", q->card) > 0) {
pthread_setname_np(q->thread, thread_name);
}
return SRSRAN_SUCCESS;
}
void rf_skiq_card_set_error_handler(rf_skiq_card_t* q, srsran_rf_error_handler_t error_handler, void* arg)
{
for (uint32_t i = 0; i < q->nof_ports; i++) {
rf_skiq_tx_port_set_error_handler(&q->tx_ports[i], error_handler, arg);
rf_skiq_rx_port_set_error_handler(&q->rx_ports[i], error_handler, arg);
}
}
double rf_skiq_card_set_tx_gain_db(rf_skiq_card_t* q, uint32_t port_idx, double gain_db)
{
double max_atten_dB = 0.25 * q->param.tx_param->atten_quarter_db_max;
double min_atten_dB = 0.25 * q->param.tx_param->atten_quarter_db_min;
// Calculate attenuation:
// - 0dB attenuation -> Maximum gain;
// - 0dB gain -> Maximum attenuation;
double att_dB = max_atten_dB - gain_db;
// Check gain range
if (att_dB < min_atten_dB || att_dB > max_atten_dB) {
ERROR("Error port %d:%d the selected gain (%.2f dB) is out of range (%.2f to %.2f dB).\n",
q->card,
port_idx,
gain_db,
min_atten_dB,
max_atten_dB);
}
// Calculate attenuation index
uint16_t att_index = (uint16_t)floor(att_dB * 4);
// Bound index
att_index = SRSRAN_MIN(SRSRAN_MAX(att_index, q->param.tx_param->atten_quarter_db_min),
q->param.tx_param->atten_quarter_db_max);
// Calculate equivalent gain
double actual_gain_dB = max_atten_dB - att_index * 0.25;
// Set gain per port
if (port_idx >= q->nof_ports) {
for (uint8_t i = 0; i < q->nof_ports; i++) {
if (skiq_write_tx_attenuation(q->card, (skiq_tx_hdl_t)i, att_index)) {
ERROR("Error setting card %d:%d Tx attenuation\n", q->card, i);
}
}
} else {
if (skiq_write_tx_attenuation(q->card, (skiq_tx_hdl_t)port_idx, att_index)) {
ERROR("Error setting card %d:%d Tx attenuation\n", q->card, port_idx);
}
}
return actual_gain_dB;
}
double rf_skiq_card_set_rx_gain_db(rf_skiq_card_t* q, uint32_t port_idx, double gain_db)
{
// Find the nearest gain index in the table
int gain_idx = -1;
double gain_min_diff = INFINITY;
for (int i = q->param.rx_param->gain_index_min; i <= q->param.rx_param->gain_index_max; i++) {
double gain_diff = fabs(q->rx_gain_table_db[i] - gain_db);
if (gain_diff < gain_min_diff) {
gain_min_diff = gain_diff;
gain_idx = i;
}
}
if (gain_idx >= 0) {
gain_db = q->rx_gain_table_db[gain_idx];
if (port_idx < q->nof_ports) {
// Set single port gain
q->issued_rx_gain_db[port_idx] = gain_db;
skiq_write_rx_gain(q->card, (skiq_rx_hdl_t)port_idx, gain_idx);
} else {
// Set all gains
for (int i = 0; i < q->nof_ports; i++) {
q->issued_rx_gain_db[i] = gain_db;
skiq_write_rx_gain(q->card, (skiq_rx_hdl_t)i, gain_idx);
}
}
}
return gain_db;
}
int rf_skiq_card_update_timestamp(rf_skiq_card_t* q, bool use_1pps, uint64_t new_ts)
{
if (use_1pps) {
// Read 1pps source
skiq_1pps_source_t pps_source = skiq_1pps_source_unavailable;
if (skiq_read_1pps_source(q->card, &pps_source)) {
ERROR("Error reading card %d 1PPS source\n", q->card);
return SRSRAN_ERROR;
}
// Make sure the source is external
if (pps_source != skiq_1pps_source_external) {
ERROR("Error card %d is not configured with external 1PPS source\n", q->card);
return SRSRAN_ERROR;
}
// Get last time a PPS was received
uint64_t ts_sys_1pps = 0;
if (skiq_read_last_1pps_timestamp(q->card, NULL, &ts_sys_1pps)) {
ERROR("Reading card %d last 1PPS timestamp", q->card);
return SRSRAN_ERROR;
}
// Read current system time
uint64_t ts = 0;
if (skiq_read_curr_sys_timestamp(q->card, &ts)) {
ERROR("Reading card %d system timestamp", q->card);
return SRSRAN_ERROR;
}
// Make sure a 1PPS was received less than 2 seconds ago
if (ts - ts_sys_1pps > 2 * SKIQ_SYS_TIMESTAMP_FREQ) {
ERROR("Error card %d last PPS was received %.1f seconds ago (%ld - %ld)\n",
q->card,
(double)(ts - ts_sys_1pps) / (double)SKIQ_SYS_TIMESTAMP_FREQ,
ts,
ts_sys_1pps);
return SRSRAN_ERROR;
}
// Set a given time in the future, a 100th of a second (10ms)
ts += SKIQ_SYS_TIMESTAMP_FREQ / 100;
// Order that all timestamps are reseted when next 1PPS signal is received
SKIQ_RF_INFO(" ... Resetting card %d system timestamp on next PPS\n", q->card);
if (skiq_write_timestamp_update_on_1pps(q->card, ts, new_ts)) {
ERROR("Error reseting card %d timestamp on 1 PPS", q->card);
return SRSRAN_ERROR;
}
} else {
// Simply, reset timestamp
SKIQ_RF_INFO(" ... Resetting card %d system timestamp now\n", q->card);
if (skiq_update_timestamps(q->card, new_ts)) {
ERROR("Error resetting card %d timestamp", q->card);
return SRSRAN_ERROR;
}
}
return SRSRAN_SUCCESS;
}
uint64_t rf_skiq_card_read_sys_timestamp(rf_skiq_card_t* q)
{
uint64_t ts = 0UL;
if (skiq_read_curr_sys_timestamp(q->card, &ts)) {
ERROR("Reading card %d system timestamp", q->card);
}
return ts;
}
uint64_t rf_skiq_card_read_rf_timestamp(rf_skiq_card_t* q)
{
uint64_t ts = 0UL;
if (skiq_read_curr_rx_timestamp(q->card, skiq_rx_hdl_A1, &ts)) {
ERROR("Reading card %d system timestamp", q->card);
}
return ts;
}
int rf_skiq_card_start_rx_streaming(rf_skiq_card_t* q, uint64_t timestamp)
{
// Wrong state
if (q->state == RF_SKIQ_PORT_STATE_STOP) {
ERROR("Error starting Rx stream: wrong state (%d)\n", q->state);
return SRSRAN_ERROR;
}
// Already enabled
if (q->state == RF_SKIQ_PORT_STATE_STREAMING) {
SKIQ_RF_INFO("Rx streams in card %d have already started\n", q->card);
return SRSRAN_SUCCESS;
}
// Make a list with Rx handlers
skiq_rx_hdl_t rx_hdl[RF_SKIQ_MAX_PORTS_CARD];
for (uint8_t i = 0; i < RF_SKIQ_MAX_PORTS_CARD; i++) {
rx_hdl[i] = (skiq_rx_hdl_t)i;
}
pthread_mutex_lock(&q->mutex);
// Start all Rx in a row
if (skiq_start_rx_streaming_multi_on_trigger(q->card, rx_hdl, q->nof_ports, skiq_trigger_src_synced, timestamp)) {
ERROR("Failed to start card %d Rx streaming\n", q->card);
pthread_mutex_unlock(&q->mutex);
return SRSRAN_ERROR;
}
// Update state and broadcast condition variable
q->state = RF_SKIQ_PORT_STATE_STREAMING;
pthread_cond_broadcast(&q->cvar);
pthread_mutex_unlock(&q->mutex);
SKIQ_RF_INFO("Rx streams in card %d have started\n", q->card);
return SRSRAN_SUCCESS;
}
int rf_skiq_card_stop_rx_streaming(rf_skiq_card_t* q)
{
if (q->state == RF_SKIQ_PORT_STATE_STOP) {
ERROR("Error stopping Rx stream: wrong state (%d)\n", q->state);
return SRSRAN_ERROR;
}
// Avoid stop streaming if it was not started
if (q->state == RF_SKIQ_PORT_STATE_IDLE) {
return SRSRAN_ERROR;
}
// Make a list with Tx/Rx handlers
skiq_rx_hdl_t rx_hdl[RF_SKIQ_MAX_PORTS_CARD];
for (uint8_t i = 0; i < RF_SKIQ_MAX_PORTS_CARD; i++) {
rx_hdl[i] = (skiq_rx_hdl_t)i;
}
pthread_mutex_lock(&q->mutex);
// Update state and broadcast condition variable first
q->state = RF_SKIQ_PORT_STATE_IDLE;
pthread_cond_broadcast(&q->cvar);
// Stop all Rx in a row
if (skiq_stop_rx_streaming_multi_immediate(q->card, rx_hdl, q->nof_ports)) {
ERROR("Failed to stop card %d Rx streaming\n", q->card);
pthread_mutex_unlock(&q->mutex);
return SRSRAN_ERROR;
}
pthread_mutex_unlock(&q->mutex);
SKIQ_RF_INFO("Rx streams in card %d have stopped\n", q->card);
return SRSRAN_SUCCESS;
}
void rf_skiq_card_end_of_burst(rf_skiq_card_t* q)
{
for (uint32_t i = 0; i < q->nof_ports; i++) {
rf_skiq_tx_port_end_of_burst(&q->tx_ports[i]);
}
}
int rf_skiq_card_set_srate_hz(rf_skiq_card_t* q, uint32_t srate_hz)
{
for (uint8_t i = 0; i < q->nof_ports; i++) {
// Set transmitter sampling rate
if (skiq_write_tx_sample_rate_and_bandwidth(q->card, (skiq_tx_hdl_t)i, srate_hz, srate_hz)) {
ERROR("Setting Tx sampling rate\n");
}
// Set receiver sampling rate
if (skiq_write_rx_sample_rate_and_bandwidth(q->card, (skiq_rx_hdl_t)i, srate_hz, srate_hz)) {
ERROR("Setting Rx sampling rate\n");
}
rf_skiq_rx_port_reset(&q->rx_ports[i]);
}
return SRSRAN_SUCCESS;
}
double rf_skiq_card_set_tx_freq_hz(rf_skiq_card_t* q, uint32_t port_idx, double freq_hz)
{
q->suspend = true;
rf_skiq_tx_port_set_lo(&q->tx_ports[port_idx], (uint64_t)freq_hz);
q->suspend = false;
return freq_hz;
}
double rf_skiq_card_set_rx_freq_hz(rf_skiq_card_t* q, uint32_t port_idx, double freq_hz)
{
q->suspend = true;
rf_skiq_rx_port_set_lo(&q->rx_ports[port_idx], (uint64_t)freq_hz);
q->suspend = false;
// Update gains for only port 0
if (port_idx == 0) {
// Update gain table
rf_skiq_card_update_gain_table(q);
// Set previous issued gain in dB for the new tables
rf_skiq_card_set_rx_gain_db(q, q->nof_ports, q->issued_rx_gain_db[port_idx]);
}
return freq_hz;
}
void rf_skiq_card_close(rf_skiq_card_t* q)
{
SKIQ_RF_INFO("Closing card %d...\n", q->card);
// Post stop state to reader thread
q->state = RF_SKIQ_PORT_STATE_STOP;
pthread_cond_broadcast(&q->cvar);
// Wait for reader thread to finish
pthread_join(q->thread, NULL);
for (uint8_t i = 0; i < q->nof_ports; i++) {
rf_skiq_rx_port_free(&q->rx_ports[i]);
rf_skiq_tx_port_free(&q->tx_ports[i]);
}
pthread_cond_destroy(&q->cvar);
pthread_mutex_destroy(&q->mutex);
// Unlocks all cards
if (skiq_disable_cards(&q->card, 1)) {
ERROR("Unable to disable card %d\n", q->card);
}
}
int rf_skiq_card_receive(rf_skiq_card_t* q, uint32_t port_idx, cf_t* dst, uint32_t nsamples, uint64_t* ts)
{
// If suspended and samples are not available, then set all to zero
if (q->suspend && rf_skiq_rx_port_available(&q->rx_ports[port_idx]) == 0) {
srsran_vec_cf_zero(dst, nsamples);
*ts = 0UL;
return nsamples;
}
return rf_skiq_rx_port_read(&q->rx_ports[port_idx], dst, nsamples, ts);
}
uint64_t rf_skiq_card_get_rx_timestamp(rf_skiq_card_t* q, uint32_t port_idx)
{
if (q->suspend || q->rx_ports[port_idx].rb_overflow) {
return 0UL;
}
return rf_skiq_rx_port_get_timestamp(&q->rx_ports[port_idx]);
}
bool rf_skiq_card_is_streaming(rf_skiq_card_t* q)
{
return q->state == RF_SKIQ_PORT_STATE_STREAMING && !q->suspend;
}
int rf_skiq_card_send(rf_skiq_card_t* q, uint32_t port_idx, const cf_t* data, uint32_t nsamples, uint64_t timestamp)
{
// If suspended, do not bother the transmitter
if (q->suspend) {
return nsamples;
}
return rf_skiq_tx_port_send(&q->tx_ports[port_idx], data, nsamples, timestamp);
}

@ -0,0 +1,77 @@
/**
*
* \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_LIB_SRC_PHY_RF_RF_SKIQ_IMP_CARD_H_
#define SRSRAN_LIB_SRC_PHY_RF_RF_SKIQ_IMP_CARD_H_
#include "rf_skiq_imp_port.h"
typedef struct {
uint8_t card;
uint8_t nof_ports;
skiq_chan_mode_t mode;
skiq_param_t param;
rf_skiq_tx_port_t tx_ports[RF_SKIQ_MAX_PORTS_CARD];
rf_skiq_rx_port_t rx_ports[RF_SKIQ_MAX_PORTS_CARD];
double rx_gain_table_db[UINT8_MAX + 1];
double cur_rx_gain_db;
double issued_rx_gain_db[SRSRAN_MAX_PORTS];
bool suspend;
uint64_t start_rx_stream_ts;
rf_skiq_port_state_t state;
pthread_mutex_t mutex; ///< Protect concurrent access to start/stop rx stream and receive
pthread_cond_t cvar;
pthread_t thread;
} rf_skiq_card_t;
int rf_skiq_card_init(rf_skiq_card_t* q, uint8_t card, uint8_t nof_ports, const rf_skiq_port_opts_t* opts);
void rf_skiq_card_set_error_handler(rf_skiq_card_t* q, srsran_rf_error_handler_t error_handler, void* arg);
int rf_skiq_card_update_gain_table(rf_skiq_card_t* q);
double rf_skiq_card_set_tx_gain_db(rf_skiq_card_t* q, uint32_t port_idx, double gain_db);
double rf_skiq_card_set_rx_gain_db(rf_skiq_card_t* q, uint32_t port_idx, double gain_db);
int rf_skiq_card_update_timestamp(rf_skiq_card_t* q, bool use_1pps, uint64_t new_ts);
uint64_t rf_skiq_card_read_sys_timestamp(rf_skiq_card_t* q);
uint64_t rf_skiq_card_read_rf_timestamp(rf_skiq_card_t* q);
int rf_skiq_card_start_rx_streaming(rf_skiq_card_t* q, uint64_t timestamp);
int rf_skiq_card_stop_rx_streaming(rf_skiq_card_t* q);
void rf_skiq_card_end_of_burst(rf_skiq_card_t* q);
int rf_skiq_card_set_srate_hz(rf_skiq_card_t* q, uint32_t srate_hz);
double rf_skiq_card_set_tx_freq_hz(rf_skiq_card_t* q, uint32_t port_idx, double freq_hz);
double rf_skiq_card_set_rx_freq_hz(rf_skiq_card_t* q, uint32_t port_idx, double freq_hz);
void rf_skiq_card_close(rf_skiq_card_t* q);
int rf_skiq_card_receive(rf_skiq_card_t* q, uint32_t port_idx, cf_t* dst, uint32_t nsamples, uint64_t* ts);
uint64_t rf_skiq_card_get_rx_timestamp(rf_skiq_card_t* q, uint32_t port_idx);
bool rf_skiq_card_is_streaming(rf_skiq_card_t* q);
int rf_skiq_card_send(rf_skiq_card_t* q, uint32_t port_idx, const cf_t* data, uint32_t nsamples, uint64_t timestamp);
#endif // SRSRAN_LIB_SRC_PHY_RF_RF_SKIQ_IMP_CARD_H_

@ -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.
*
*/
#ifndef SRSRAN_RF_SKIQ_IMP_CFG_H
#define SRSRAN_RF_SKIQ_IMP_CFG_H
/**
* RF_SKIQ_MAX_PORTS_CARD sets the maximum number of ports per card
*/
#define RF_SKIQ_MAX_PORTS_CARD 2
/**
* SKIQ_CARD_SYNCH_MAX_TRIALS defines the maximum number of trials to synchronize multiple boards
*/
#define SKIQ_CARD_SYNCH_MAX_TRIALS 10
/**
* SKIQ_CARD_SYNC_MAX_ERROR sets the maximum number of system "ticks" error between boards during synchronization check.
* Consider the communication medium delay between the host and SidekIQ cards.
*/
#define SKIQ_CARD_SYNC_MAX_ERROR (SKIQ_SYS_TIMESTAMP_FREQ / 2)
/**
* Maximum gap allowed in number of samples between ports
*/
#define SKIQ_PORT_SYNC_MAX_GAP (1024 * 1024)
/**
* For checking the number of Tx lattes in the FPGA set the next line to the desired check period in number of blocks.
* Example: set to 1000 for checking it every 1000 blocks.
* WARNING: A low period may cause a reduction of performance in the Host-FPGA communication
*/
#define SKIQ_TX_LATES_CHECK_PERIOD (1920 * 10)
/**
* Minimum number of channels that this RF device can reach
*/
#define SKIQ_MIN_CHANNELS (1)
/**
* Maximum number of channels that this RF device can reach
*/
#define SKIQ_MAX_CHANNELS (SKIQ_MAX_NUM_CARDS * RF_SKIQ_MAX_PORTS_CARD)
/**
* Dummy receive buffer size in samples
*/
#define RF_SKIQ_DUMMY_BUFFER_SIZE (1024)
/**
* Magic word value as a ring buffer check
*/
#define SKIQ_RX_BUFFFER_MAGIC_WORD 0xABCD1234
/**
* Normalization value between fixed and floating point conversion
*/
#define SKIQ_NORM 2048.0
/**
* Default Rx gain in decibels (dB)
*/
#define SKIQ_RX_GAIN_DEFAULT_dB (+50.0f)
/**
* Default sampling rate in samples per second (Hz)
*/
#define SKIQ_DEFAULT_SAMPLING_RATE_HZ (30.72e6)
/**
*
*/
#define SKIQ_TX_PACKET_SIZE(N, MODE) (SKIQ_TX_PACKET_SIZE_INCREMENT_IN_WORDS * (N)-SKIQ_TX_HEADER_SIZE_IN_WORDS)
/**
* SKIQ driver standard output MACRO
*/
extern uint32_t rf_skiq_logging_level;
#define SKIQ_RF_INFO(...) \
do { \
if (rf_skiq_logging_level >= SKIQ_LOG_INFO) { \
fprintf(stdout, "[SKIQ RF INFO] " __VA_ARGS__); \
} \
} while (false)
#define SKIQ_RF_DEBUG(...) \
do { \
if (rf_skiq_logging_level >= SKIQ_LOG_DEBUG) { \
fprintf(stdout, "[SKIQ RF INFO] " __VA_ARGS__); \
} \
} while (false)
#define SKIQ_RF_ERROR(...) \
do { \
if (rf_skiq_logging_level >= SKIQ_LOG_ERROR) { \
fprintf(stdout, "[SKIQ RF ERROR] " __VA_ARGS__); \
} \
} while (false)
#endif // SRSLTE_RF_SKIQ_IMP_CFG_H

@ -0,0 +1,576 @@
/**
*
* \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 "rf_skiq_imp_port.h"
static void rf_skiq_rx_port_handle_overflow(rf_skiq_rx_port_t* q)
{
srsran_rf_error_t error = {};
error.type = SRSRAN_RF_ERROR_OVERFLOW;
if (q->error_handler) {
q->error_handler(q->error_handler_arg, error);
} else {
SKIQ_RF_INFO("Rx overflow detected in %d:%d\n", q->card, (int)q->hdl);
}
}
#if SKIQ_TX_LATES_CHECK_PERIOD
static bool rf_skiq_tx_port_handle_late(rf_skiq_tx_port_t* q)
{
// Get number of lattes from FPGA
uint32_t total_late = 0;
if (skiq_read_tx_num_late_timestamps(q->card, q->hdl, &total_late)) {
ERROR("Error reading lates from port %d:%d\n", q->card, (int)q->hdl);
}
// Calculate number of late timestamps
uint32_t new_late = total_late;
// Remove previous read value
if (new_late >= q->last_total_late) {
new_late = new_late - q->last_total_late;
}
// Update latest value
q->last_total_late = total_late;
// No late, do not report them
if (new_late == 0) {
return false;
}
if (q->error_handler) {
srsran_rf_error_t error = {};
error.type = SRSRAN_RF_ERROR_LATE;
error.opt = new_late;
q->error_handler(q->error_handler_arg, error);
} else {
SKIQ_RF_INFO("Port %d late events detected in %d:%d\n", new_late, q->card, (int)q->hdl);
}
return true;
}
#endif // SKIQ_TX_LATES_CHECK_PERIOD
static void* writer_thread(void* arg)
{
uint64_t last_tx_ts = 0;
rf_skiq_tx_port_t* q = (rf_skiq_tx_port_t*)arg;
skiq_tx_block_t* p_tx_block = NULL;
if (skiq_start_tx_streaming(q->card, q->hdl)) {
ERROR("Error starting Tx stream %d:%d\n", q->card, (int)q->hdl);
return NULL;
}
q->state = RF_SKIQ_PORT_STATE_STREAMING;
while (q->state != RF_SKIQ_PORT_STATE_STOP) {
// Read block from ring-buffer
int n = srsran_ringbuffer_read_block(&q->rb, (void**)&p_tx_block, q->p_block_nbytes, 1000);
// Stop state is detected
if (q->state == RF_SKIQ_PORT_STATE_STOP) {
break;
}
// If the ring buffer read resulted in timeout
if (n == SRSRAN_ERROR_TIMEOUT) {
continue;
}
// Ignore blocks with TS=0
if (p_tx_block->timestamp == 0) {
continue;
}
// Check if the timestamp is the past (this can be caused by sample rate change)
if (last_tx_ts > p_tx_block->timestamp) {
// Get current RF timestamp
uint64_t curr_tx_ts = 0UL;
skiq_read_curr_tx_timestamp(q->card, q->hdl, &curr_tx_ts);
// Avoids giving a block to the FPGA that has already passed, otherwise it could hang forever
if (curr_tx_ts > p_tx_block->timestamp) {
SKIQ_RF_ERROR("[Tx %d:%d block] Tx block (ts=%ld) is in the past (last_tx_ts=%ld, curr_tx_ts=%ld), ignoring\n",
q->card,
(int)q->hdl,
q->p_tx_block->timestamp,
last_tx_ts,
curr_tx_ts);
continue;
}
}
last_tx_ts = p_tx_block->timestamp + q->block_size;
// If the ring-buffer did not return with error code...
if (n > SRSRAN_SUCCESS) {
SKIQ_RF_DEBUG("[Tx %d:%d block] ts=%ld; nsamples=%d; last_tx_ts=%ld;\n",
q->card,
(int)q->hdl,
p_tx_block->timestamp,
q->block_size,
last_tx_ts);
if (skiq_transmit(q->card, q->hdl, p_tx_block, NULL) < 0) {
ERROR("Error transmitting card %d\n", q->card);
q->state = RF_SKIQ_PORT_STATE_STOP;
}
#if SKIQ_TX_LATES_CHECK_PERIOD
if (q->last_check_ts + SKIQ_TX_LATES_CHECK_PERIOD < p_tx_block->timestamp) {
// Handle late timestamps events
rf_skiq_tx_port_handle_late(q);
// Update last check TS
q->last_check_ts = p_tx_block->timestamp;
}
#endif // SKIQ_TX_LATES_CHECK_PERIOD
}
}
if (skiq_stop_tx_streaming(q->card, q->hdl)) {
ERROR("Error stopping Tx stream %d:%d\n", q->card, (int)q->hdl);
}
SKIQ_RF_INFO("Exiting writer thread!\n");
return NULL;
}
int rf_skiq_tx_port_init(rf_skiq_tx_port_t* q, uint8_t card, skiq_tx_hdl_t hdl, const rf_skiq_port_opts_t* opts)
{
// Defines the block size in multiples of 256 words
uint32_t nof_blocks_per_packet = 4;
switch (opts->stream_mode) {
case skiq_rx_stream_mode_high_tput:
nof_blocks_per_packet = 8;
break;
case skiq_rx_stream_mode_low_latency:
nof_blocks_per_packet = 2;
break;
case skiq_rx_stream_mode_balanced:
default:
// Keep default value
break;
}
q->card = card;
q->hdl = hdl;
q->block_size = SKIQ_TX_PACKET_SIZE(nof_blocks_per_packet, opts->chan_mode);
q->p_block_nbytes = q->block_size * 4 + SKIQ_TX_HEADER_SIZE_IN_BYTES;
// configure the data flow mode to use timestamps
if (skiq_write_tx_data_flow_mode(card, hdl, skiq_tx_with_timestamps_data_flow_mode) != 0) {
ERROR("Setting Tx data flow mode");
return SRSRAN_ERROR;
}
// configure the transfer mode to synchronous
if (skiq_write_tx_transfer_mode(card, hdl, skiq_tx_transfer_mode_sync) != 0) {
ERROR("setting tx transfer mode");
return SRSRAN_ERROR;
}
// configure Tx block size
if (skiq_write_tx_block_size(card, hdl, q->block_size) != 0) {
ERROR("configuring Tx block size");
return SRSRAN_ERROR;
}
q->p_tx_block = skiq_tx_block_allocate(q->block_size);
if (q->p_tx_block == NULL) {
ERROR("Allocating Tx block");
return SRSRAN_ERROR;
}
// initialise ring buffer
if (srsran_ringbuffer_init(&q->rb, (int)(opts->tx_rb_size * q->p_block_nbytes))) {
ERROR("Initialising ringbuffer");
return SRSRAN_ERROR;
}
// Initialise thread parameters
pthread_attr_t attr;
struct sched_param param;
param.sched_priority = sched_get_priority_max(SCHED_FIFO);
pthread_attr_init(&attr);
if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) {
ERROR("Error not enough privileges to set Scheduling priority\n");
}
if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) {
ERROR("Error not enough privileges to set Scheduling priority\n");
}
if (pthread_attr_setschedparam(&attr, &param)) {
ERROR("Error not enough privileges to set Scheduling priority\n");
}
// Launch thread
if (pthread_create(&q->thread, &attr, writer_thread, q)) {
ERROR("Error creating writer thread with attributes (Did you miss sudo?). Trying without attributes.\n");
return SRSRAN_ERROR;
}
// Rename thread
char thread_name[32] = {};
if (snprintf(thread_name, sizeof(thread_name), "SKIQ Tx %d:%d", q->card, (int)q->hdl) > 0) {
pthread_setname_np(q->thread, thread_name);
}
return SRSRAN_SUCCESS;
}
void rf_skiq_tx_port_free(rf_skiq_tx_port_t* q)
{
// Stop thread
q->state = RF_SKIQ_PORT_STATE_STOP;
// Unlock ringbuffer
srsran_ringbuffer_write(&q->rb, (void*)q->p_tx_block, q->p_block_nbytes);
// Wait thread to return
pthread_join(q->thread, NULL);
if (q->p_tx_block) {
skiq_tx_block_free(q->p_tx_block);
}
srsran_ringbuffer_free(&q->rb);
}
void rf_skiq_tx_port_end_of_burst(rf_skiq_tx_port_t* q)
{
pthread_mutex_lock(&q->mutex);
// Fill pending block if any, otherwise push a block with zeros
if (q->next_offset > 0) {
// Calculate pending samples to fill the block
uint32_t pending = q->block_size - q->next_offset;
// Zero pending samples in the block
srsran_vec_i16_zero(&q->p_tx_block->data[q->next_offset * 2], 2 * pending);
// Write block into the ring-buffer
srsran_ringbuffer_write_block(&q->rb, q->p_tx_block, q->p_block_nbytes);
SKIQ_RF_DEBUG("[Tx EOB %d:%d] Padding offset=%d; n=%d; ts=%ld\n",
q->card,
(int)q->hdl,
q->next_offset,
pending,
q->p_tx_block->timestamp);
// Reset next offset, so next transmission uses a new block
q->next_offset = 0;
}
pthread_mutex_unlock(&q->mutex);
}
int rf_skiq_tx_port_send(rf_skiq_tx_port_t* q, const cf_t* buffer, uint32_t nsamples, uint64_t ts)
{
// Ignore transmission if the stream is not enabled
if (q->state != RF_SKIQ_PORT_STATE_STREAMING) {
return nsamples;
}
// If no data and no block has started, early return
if (buffer == NULL && q->next_offset == 0) {
return nsamples;
}
pthread_mutex_lock(&q->mutex);
// Calculate destination where IQ shall be stored
int16_t* p_tx_iq = &q->p_tx_block->data[q->next_offset * 2];
// Calculate number of samples to take from buffer
nsamples = SRSRAN_MIN(nsamples, q->block_size - q->next_offset);
// Set time stamp only if no offset
if (q->next_offset == 0) {
skiq_tx_set_block_timestamp(q->p_tx_block, ts);
}
SKIQ_RF_DEBUG(
"[Tx %d:%d] Write offset=%d; nsamples=%d; ts=%ld\n", q->card, (int)q->hdl, q->next_offset, nsamples, ts);
// Fill data ...
if (buffer == NULL) {
// ... with zeros
srsran_vec_i16_zero(p_tx_iq, 2 * nsamples);
} else {
// ... with samples, after conversion
srsran_vec_convert_conj_cs(buffer, SKIQ_NORM, p_tx_iq, nsamples);
}
q->next_offset += nsamples;
// If the number of samples does not fill the block, return early
if (q->next_offset < q->block_size) {
pthread_mutex_unlock(&q->mutex);
return nsamples;
}
if (srsran_ringbuffer_space(&q->rb) < q->p_block_nbytes * 2) {
ERROR("Tx buffer overflow\n");
pthread_mutex_unlock(&q->mutex);
return nsamples;
}
// Actual write in ring buffer
int n = srsran_ringbuffer_write_timed_block(&q->rb, q->p_tx_block, q->p_block_nbytes, 5);
pthread_mutex_unlock(&q->mutex);
// In case of error (e.g. timeout) return code
if (n < SRSRAN_SUCCESS) {
return n;
}
// In case of number of bytes mismatch return error
if (n != q->p_block_nbytes) {
ERROR("Error writing in Tx buffer %d:%d\n", q->card, (int)q->hdl);
return SRSRAN_ERROR;
}
// Reset offset only if the block was successfully written into the ring-buffer
q->next_offset = 0;
// Return the number of samples writen in the buffer
return nsamples;
}
void rf_skiq_tx_port_set_error_handler(rf_skiq_tx_port_t* q, srsran_rf_error_handler_t error_handler, void* arg)
{
q->error_handler = error_handler;
q->error_handler_arg = arg;
}
int rf_skiq_tx_port_set_lo(rf_skiq_tx_port_t* q, uint64_t lo_freq)
{
// Skip setting LO frequency if it is not required
if (q->current_lo == lo_freq) {
return SRSRAN_SUCCESS;
}
skiq_filt_t filt = (lo_freq < 3000000000UL) ? skiq_filt_0_to_3000_MHz : skiq_filt_3000_to_6000_MHz;
if (skiq_write_tx_LO_freq(q->card, q->hdl, lo_freq)) {
ERROR("Setting card %d:%d Tx Lo frequency", q->card, (int)q->hdl);
return SRSRAN_ERROR;
}
if (skiq_write_tx_filter_path(q->card, q->hdl, filt)) {
ERROR("Setting card %d:%d Tx filter", q->card, q->hdl);
return SRSRAN_ERROR;
}
q->current_lo = lo_freq;
return SRSRAN_SUCCESS;
}
int rf_skiq_rx_port_init(rf_skiq_rx_port_t* q, uint8_t card, skiq_rx_hdl_t hdl, const rf_skiq_port_opts_t* opts)
{
q->card = card;
q->hdl = hdl;
// enabling DC offset correction can cause an IQ impairment
if (skiq_write_rx_dc_offset_corr(card, hdl, false)) {
ERROR("Setting RX DC offset correction");
return SRSRAN_ERROR;
}
// set rx gain mode
if (skiq_write_rx_gain_mode(card, hdl, skiq_rx_gain_manual)) {
ERROR("Setting RX gain mode");
return SRSRAN_ERROR;
}
// Rx block size in bytes
int32_t rx_block_size = skiq_read_rx_block_size(q->card, opts->stream_mode) - SKIQ_RX_HEADER_SIZE_IN_BYTES;
// initialise ring buffer
if (srsran_ringbuffer_init(&q->rb, (int)(opts->rx_rb_size * rx_block_size))) {
ERROR("Initialising ringbuffer");
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
void rf_skiq_rx_port_free(rf_skiq_rx_port_t* q)
{
srsran_ringbuffer_free(&q->rb);
}
int rf_skiq_rx_port_available(rf_skiq_rx_port_t* q)
{
return srsran_ringbuffer_status(&q->rb);
}
int rf_skiq_rx_port_read(rf_skiq_rx_port_t* q, cf_t* dest, uint32_t nsamples, uint64_t* ts_start)
{
// Detect start of new block
if (q->rb_read_rem == 0) {
skiq_header_t header = {};
// If ring-buffer overflow was detected...
if (q->rb_overflow) {
// Reset ring buffer
srsran_ringbuffer_reset(&q->rb);
// Clear overflow flag
q->rb_overflow = false;
// Set samples to zero
srsran_vec_cf_zero(dest, nsamples);
// Set default timestamp
*ts_start = 0;
// Since the buffer is empty, return the full amount of samples so it does not delay reception of other channels
return nsamples;
}
// Read a packet. First the header
if (srsran_ringbuffer_read(&q->rb, &header, sizeof(skiq_header_t)) != sizeof(skiq_header_t)) {
ERROR("Error reading header from ring-buffer %d:%d corrupted\n", q->card, (int)q->hdl);
return SRSRAN_ERROR;
}
// Check header magic word
if (header.magic != SKIQ_RX_BUFFFER_MAGIC_WORD) {
ERROR("Error ring-buffer %d:%d corrupted\n", q->card, (int)q->hdl);
return SRSRAN_ERROR;
}
// Successful read
q->rb_read_rem = header.nsamples;
q->rb_tstamp_rem = header.tstamp;
}
// Limit number of samples to the remainder of the stored packet
nsamples = SRSRAN_MIN(q->rb_read_rem, nsamples);
// Read any remainder of a packet from the ring buffer
int n = srsran_ringbuffer_read_convert_conj(&q->rb, dest, SKIQ_NORM, nsamples);
// Detect error in read
if (n < SRSRAN_SUCCESS) {
ERROR("Error reading packet remainder from %d:%d\n", q->card, (int)q->hdl);
return SRSRAN_ERROR;
}
SKIQ_RF_DEBUG("[Rx %d:%d] Read nsamples=%d/%d; ts=%ld\n", q->card, (int)q->hdl, n, nsamples, q->rb_tstamp_rem);
// Update timestamp
*ts_start = q->rb_tstamp_rem;
// Update reminder
q->rb_read_rem -= n;
q->rb_tstamp_rem += n;
// Return number of read samples
return n;
}
uint64_t rf_skiq_rx_port_get_timestamp(rf_skiq_rx_port_t* q)
{
return q->rb_tstamp_rem;
}
int rf_skiq_rx_port_write(rf_skiq_rx_port_t* q, const skiq_rx_block_t* p_rx_block, uint32_t nsamples)
{
// Prepare header
skiq_header_t header = {};
header.magic = SKIQ_RX_BUFFFER_MAGIC_WORD;
header.tstamp = p_rx_block->rf_timestamp;
header.nsamples = nsamples;
// Ignore block if the overflow flag has risen
if (q->rb_overflow) {
return nsamples;
}
SKIQ_RF_DEBUG("[Rx %d:%d block] ts=%ld; nsamples=%d;\n", q->card, (int)q->hdl, header.tstamp, header.nsamples);
// Check space in the ring-buffer prior to writing
if (srsran_ringbuffer_space(&q->rb) >= sizeof(skiq_header_t) + nsamples * 4) {
// Write header
if (srsran_ringbuffer_write_block(&q->rb, &header, sizeof(skiq_header_t)) != sizeof(skiq_header_t)) {
ERROR("Writing header in Rx buffer %d:%d!\n", q->card, (int)q->hdl);
return SRSRAN_ERROR;
}
// Write IQ samples
if (srsran_ringbuffer_write_block(&q->rb, (uint8_t*)p_rx_block->data, (int)nsamples * 4) != nsamples * 4) {
ERROR("Writing base-band in Rx buffer %d:%d!\n", q->card, (int)q->hdl);
return SRSRAN_ERROR;
}
} else {
SKIQ_RF_INFO("Rx %d:%d ring-buffer overflow!\n", q->card, (int)q->hdl);
q->rb_overflow = true;
rf_skiq_rx_port_handle_overflow(q);
}
// Process overload, call handle only for rising-edges
if (!q->rf_overflow && p_rx_block->overload) {
rf_skiq_rx_port_handle_overflow(q);
}
q->rf_overflow = p_rx_block->overload;
return nsamples;
}
void rf_skiq_rx_port_set_error_handler(rf_skiq_rx_port_t* q, srsran_rf_error_handler_t error_handler, void* arg)
{
q->error_handler = error_handler;
q->error_handler_arg = arg;
}
void rf_skiq_rx_port_reset(rf_skiq_rx_port_t* q)
{
SKIQ_RF_INFO("Rx port %d:%d reset\n", q->card, (int)q->hdl);
q->rb_read_rem = 0;
q->rb_tstamp_rem = 0;
srsran_ringbuffer_reset(&q->rb);
}
int rf_skiq_rx_port_set_lo(rf_skiq_rx_port_t* q, uint64_t lo_freq)
{
// Skip setting LO frequency if it is not required
if (q->current_lo == lo_freq) {
return SRSRAN_SUCCESS;
}
skiq_filt_t filt = (lo_freq < 3000000000UL) ? skiq_filt_0_to_3000_MHz : skiq_filt_3000_to_6000_MHz;
if (skiq_write_rx_LO_freq(q->card, q->hdl, lo_freq)) {
ERROR("Setting card %d:%d Rx Lo frequency", q->card, (int)q->hdl);
return SRSRAN_ERROR;
}
if (skiq_write_rx_preselect_filter_path(q->card, q->hdl, filt)) {
ERROR("Setting card %d:%d Rx filter", q->card, q->hdl);
return SRSRAN_ERROR;
}
q->current_lo = lo_freq;
return SRSRAN_SUCCESS;
}

@ -0,0 +1,100 @@
/**
*
* \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_RF_SKIQ_IMP_PORT_H
#define SRSRAN_RF_SKIQ_IMP_PORT_H
#include <sidekiq_api.h>
#include <srsran/phy/rf/rf.h>
#include "rf_helper.h"
#include "rf_skiq_imp_cfg.h"
#include "rf_skiq_imp_port.h"
#include "srsran/srsran.h"
typedef struct {
uint32_t magic;
uint64_t tstamp;
uint32_t nsamples;
} skiq_header_t;
typedef enum {
RF_SKIQ_PORT_STATE_IDLE = 0,
RF_SKIQ_PORT_STATE_STREAMING,
RF_SKIQ_PORT_STATE_STOP
} rf_skiq_port_state_t;
typedef struct {
uint32_t tx_rb_size;
uint32_t rx_rb_size;
skiq_chan_mode_t chan_mode;
char stream_mode_str[RF_PARAM_LEN];
skiq_rx_stream_mode_t stream_mode;
} rf_skiq_port_opts_t;
typedef struct {
uint8_t card;
skiq_tx_hdl_t hdl;
skiq_tx_block_t* p_tx_block;
uint32_t p_block_nbytes; // Size in bytes including header
uint32_t block_size; // Size in words (samples)
uint32_t next_offset; // Number of samples remainder
srsran_ringbuffer_t rb;
rf_skiq_port_state_t state;
pthread_t thread;
pthread_mutex_t mutex; // Protects p_tx_block
uint64_t current_lo;
srsran_rf_error_handler_t error_handler;
void* error_handler_arg;
#if SKIQ_TX_LATES_CHECK_PERIOD
uint64_t last_check_ts;
uint32_t last_total_late;
uint32_t last_total_underruns;
#endif // SKIQ_TX_LATES_CHECK_PERIOD
} rf_skiq_tx_port_t;
int rf_skiq_tx_port_init(rf_skiq_tx_port_t* q, uint8_t card, skiq_tx_hdl_t hdl, const rf_skiq_port_opts_t* opts);
void rf_skiq_tx_port_free(rf_skiq_tx_port_t* q);
void rf_skiq_tx_port_end_of_burst(rf_skiq_tx_port_t* q);
int rf_skiq_tx_port_send(rf_skiq_tx_port_t* q, const cf_t* buffer, uint32_t nsamples, uint64_t ts);
void rf_skiq_tx_port_set_error_handler(rf_skiq_tx_port_t* q, srsran_rf_error_handler_t error_handler, void* arg);
int rf_skiq_tx_port_set_lo(rf_skiq_tx_port_t* q, uint64_t lo_freq);
typedef struct {
uint8_t card;
skiq_rx_hdl_t hdl;
srsran_ringbuffer_t rb;
uint32_t rb_read_rem;
uint64_t rb_tstamp_rem;
bool rf_overflow; ///< Indicates an RF message was flagged with overflow
bool rb_overflow; ///< Indicates that ring-buffer is full and it needs to be flushed
uint64_t current_lo;
srsran_rf_error_handler_t error_handler;
void* error_handler_arg;
} rf_skiq_rx_port_t;
int rf_skiq_rx_port_init(rf_skiq_rx_port_t* q, uint8_t card, skiq_rx_hdl_t hdl, const rf_skiq_port_opts_t* opts);
void rf_skiq_rx_port_free(rf_skiq_rx_port_t* q);
int rf_skiq_rx_port_available(rf_skiq_rx_port_t* q);
int rf_skiq_rx_port_read(rf_skiq_rx_port_t* q, cf_t* dest, uint32_t nsamples, uint64_t* ts_start);
uint64_t rf_skiq_rx_port_get_timestamp(rf_skiq_rx_port_t* q);
int rf_skiq_rx_port_write(rf_skiq_rx_port_t* q, const skiq_rx_block_t* p_rx_block, uint32_t nbytes);
void rf_skiq_rx_port_set_error_handler(rf_skiq_rx_port_t* q, srsran_rf_error_handler_t error_handler, void* arg);
void rf_skiq_rx_port_reset(rf_skiq_rx_port_t* q);
int rf_skiq_rx_port_set_lo(rf_skiq_rx_port_t* q, uint64_t lo_freq);
#endif // SRSRAN_RF_SKIQ_IMP_PORT_H

@ -204,10 +204,10 @@ int rf_zmq_open_multi(char* args, void** h, uint32_t nof_channels)
return SRSRAN_ERROR;
}
bzero(handler, sizeof(rf_zmq_handler_t));
*h = handler;
handler->base_srate = ZMQ_BASERATE_DEFAULT_HZ; // Sample rate for 100 PRB cell
*h = handler;
handler->base_srate = ZMQ_BASERATE_DEFAULT_HZ; // Sample rate for 100 PRB cell
pthread_mutex_lock(&handler->rx_gain_mutex);
handler->rx_gain = 0.0;
handler->rx_gain = 0.0;
pthread_mutex_unlock(&handler->rx_gain_mutex);
handler->info.max_rx_gain = ZMQ_MAX_GAIN_DB;
handler->info.min_rx_gain = ZMQ_MIN_GAIN_DB;
@ -480,7 +480,7 @@ int rf_zmq_set_rx_gain(void* h, double gain)
if (h) {
rf_zmq_handler_t* handler = (rf_zmq_handler_t*)h;
pthread_mutex_lock(&handler->rx_gain_mutex);
handler->rx_gain = gain;
handler->rx_gain = gain;
pthread_mutex_unlock(&handler->rx_gain_mutex);
}
return SRSRAN_SUCCESS;
@ -507,7 +507,7 @@ double rf_zmq_get_rx_gain(void* h)
if (h) {
rf_zmq_handler_t* handler = (rf_zmq_handler_t*)h;
pthread_mutex_lock(&handler->rx_gain_mutex);
ret = handler->rx_gain;
ret = handler->rx_gain;
pthread_mutex_unlock(&handler->rx_gain_mutex);
}
return ret;
@ -626,23 +626,28 @@ int rf_zmq_recv_with_time_multi(void* h, void** data, uint32_t nsamples, bool bl
// Map ports to data buffers according to the selected frequencies
pthread_mutex_lock(&handler->rx_config_mutex);
bool mapped[SRSRAN_MAX_CHANNELS] = {}; // Mapped mask, set to true when the physical channel is used
cf_t* buffers[SRSRAN_MAX_CHANNELS] = {}; // Buffer pointers, NULL if unmatched
for (uint32_t i = 0; i < handler->nof_channels; i++) {
bool mapped = false;
// Find first matching frequency
for (uint32_t j = 0; j < handler->nof_channels && !mapped; j++) {
// Traverse all channels, break if mapped
if (buffers[j] == NULL && rf_zmq_rx_match_freq(&handler->receiver[j], handler->rx_freq_mhz[i])) {
// Available buffer and matched frequency with receiver
buffers[j] = (cf_t*)data[i];
mapped = true;
// For each logical channel...
for (uint32_t logical = 0; logical < handler->nof_channels; logical++) {
bool unmatched = true;
// For each physical channel...
for (uint32_t physical = 0; physical < handler->nof_channels; physical++) {
// Consider a match if the physical channel is NOT mapped and the frequency match
if (!mapped[physical] && rf_zmq_rx_match_freq(&handler->receiver[physical], handler->rx_freq_mhz[logical])) {
// Not mapped and matched frequency with receiver
buffers[physical] = (cf_t*)data[logical];
mapped[physical] = true;
unmatched = false;
break;
}
}
// If no matching frequency found; set data to zeros
if (!mapped && data[i]) {
memset(data[i], 0, sizeof(cf_t) * nsamples);
if (unmatched) {
srsran_vec_zero(data[logical], nsamples);
}
}
pthread_mutex_unlock(&handler->rx_config_mutex);
@ -840,17 +845,19 @@ int rf_zmq_send_timed_multi(void* h,
// Map ports to data buffers according to the selected frequencies
pthread_mutex_lock(&handler->tx_config_mutex);
cf_t* buffers[SRSRAN_MAX_CHANNELS] = {}; // Buffer pointers, NULL if unmatched
for (uint32_t i = 0; i < handler->nof_channels; i++) {
bool mapped = false;
// Find first matching frequency
for (uint32_t j = 0; j < handler->nof_channels && !mapped; j++) {
// Traverse all channels, break if mapped
if (buffers[j] == NULL && rf_zmq_tx_match_freq(&handler->transmitter[j], handler->tx_freq_mhz[i])) {
// Available buffer and matched frequency with receiver
buffers[j] = (cf_t*)data[i];
mapped = true;
bool mapped[SRSRAN_MAX_CHANNELS] = {}; // Mapped mask, set to true when the physical channel is used
cf_t* buffers[SRSRAN_MAX_CHANNELS] = {}; // Buffer pointers, NULL if unmatched or zero transmission
// For each logical channel...
for (uint32_t logical = 0; logical < handler->nof_channels; logical++) {
// For each physical channel...
for (uint32_t physical = 0; physical < handler->nof_channels; physical++) {
// Consider a match if the physical channel is NOT mapped and the frequency match
if (!mapped[physical] && rf_zmq_tx_match_freq(&handler->transmitter[physical], handler->tx_freq_mhz[logical])) {
// Not mapped and matched frequency with receiver
buffers[physical] = (cf_t*)data[logical];
mapped[physical] = true;
break;
}
}
}

@ -0,0 +1,133 @@
/**
*
* \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 <sidekiq_api.h>
#include <stdio.h>
#include <unistd.h>
#define ERROR(...) fprintf(stderr, __VA_ARGS__);
const static uint8_t nof_cards = 1;
int main(int argc, char* argv[])
{
int ret = -1;
uint8_t card = 0;
if (argc > 1) {
card = (uint8_t)strtol(argv[1], NULL, 10);
}
uint8_t nof_available_cards = 0;
uint8_t available_cards[SKIQ_MAX_NUM_CARDS] = {};
if (skiq_get_cards(skiq_xport_type_auto, &nof_available_cards, available_cards)) {
ERROR("Getting available cards\n");
goto clean_exit;
}
// Check number of cards bounds
if (nof_cards > nof_available_cards) {
ERROR("The number of cards (%d) exceeds available cards (%d)\n", nof_cards, nof_available_cards);
goto clean_exit;
}
// Initialise driver
if (skiq_init(skiq_xport_type_auto, skiq_xport_init_level_full, &card, 1)) {
ERROR("Unable to initialise libsidekiq driver\n");
goto clean_exit;
}
// Programming FPGA from flash ensures the FPGA and transceiver are completely reseted
if (skiq_prog_fpga_from_flash(card)) {
ERROR("Error programming FPGA from flash\n");
goto clean_exit;
}
// Read 1pps source
skiq_1pps_source_t pps_source = skiq_1pps_source_unavailable;
if (skiq_read_1pps_source(card, &pps_source)) {
ERROR("Error reading card %d 1PPS source\n", card);
goto clean_exit;
}
// Make sure the source is external
if (pps_source != skiq_1pps_source_external) {
ERROR("Error card %d is not configured with external 1PPS source\n", card);
goto clean_exit;
}
// Sleeping 5 seconds
sleep(5);
// Get last time a PPS was received
uint64_t ts_sys_1pps = 0;
if (skiq_read_last_1pps_timestamp(card, NULL, &ts_sys_1pps)) {
ERROR("Reading card %d last 1PPS timestamp", card);
goto clean_exit;
}
// Read current system time
uint64_t ts = 0;
if (skiq_read_curr_sys_timestamp(card, &ts)) {
ERROR("Reading card %d system timestamp", card);
goto clean_exit;
}
// Make sure a 1PPS was received less than 2 seconds ago
if (ts - ts_sys_1pps > 2 * SKIQ_SYS_TIMESTAMP_FREQ) {
ERROR("Error card %d last PPS was received %.1f seconds ago (ts=%ld)\n",
card,
(double)(ts - ts_sys_1pps) / (double)SKIQ_SYS_TIMESTAMP_FREQ,
ts_sys_1pps);
goto clean_exit;
}
// Set a given time in the future, a 100th of a second (10ms)
ts += SKIQ_SYS_TIMESTAMP_FREQ / 100;
// Reset timestamp in a near future on next PPS signal
if (skiq_write_timestamp_reset_on_1pps(card, ts)) {
ERROR("Error reseting card %d timestamp on 1 PPS", card);
goto clean_exit;
}
// Give time to pass 1PPS
sleep(1);
// Read current system time
if (skiq_read_curr_sys_timestamp(card, &ts)) {
ERROR("Reading card %d system timestamp", card);
goto clean_exit;
}
// The current system timestamp should be below 2s
if (ts > 2 * SKIQ_SYS_TIMESTAMP_FREQ) {
ERROR("Timestamp of card %d is greater than 2 seconds (%.1fs)!\n",
card,
(double)ts / (double)SKIQ_SYS_TIMESTAMP_FREQ);
goto clean_exit;
}
// Success
printf("Success!\n");
ret = 0;
clean_exit:
if (skiq_disable_cards(&card, 1)) {
ERROR("Unable to disable cards\n");
}
// Close sidekiq SDK
skiq_exit();
return ret;
}

@ -54,4 +54,16 @@ if(RF_FOUND)
else(SRSGUI_FOUND)
add_definitions(-DDISABLE_GRAPHICS)
endif(SRSGUI_FOUND)
endif(RF_FOUND)
endif(RF_FOUND)
add_executable(ue_dl_nr_file_test ue_dl_nr_file_test.c)
target_link_libraries(ue_dl_nr_file_test srsran_phy pthread)
foreach (n RANGE 0 9)
add_test(ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0.dat -i 1 -P 25 -n ${n} -R 1234)
add_test(ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0.dat -i 1 -P 25 -n ${n} -R 1234)
add_test(ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0.dat -i 1 -P 25 -n ${n} -R 1234)
add_test(ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0.dat -i 1 -P 25 -n ${n} -R 1234)
add_test(ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0.dat -i 1 -P 25 -n ${n} -R 1234)
add_test(ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0.dat -i 1 -P 25 -n ${n} -R 1234)
endforeach ()
add_test(ue_dl_nr_pci500_rb52_n4_ra_L2_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci500_rb52_n4_ra_L2_ncce0.dat -i 1 -P 52 -n 4 -R 7f)

@ -0,0 +1,286 @@
/**
*
* \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 "srsran/phy/io/filesource.h"
#include "srsran/phy/phch/ra_nr.h"
#include "srsran/phy/ue/ue_dl_nr.h"
#include "srsran/phy/utils/debug.h"
#include <getopt.h>
static srsran_carrier_nr_t carrier = {
501, // pci
0, // absolute_frequency_ssb
0, // absolute_frequency_point_a
0, // offset_to_carrier
srsran_subcarrier_spacing_15kHz, // scs
52, // nof_prb
0, // start
1 // max_mimo_layers
};
static char* filename = NULL;
static srsran_pdcch_cfg_nr_t pdcch_cfg = {};
static srsran_sch_hl_cfg_nr_t pdsch_hl_cfg = {};
static uint16_t rnti = 0x1234;
static srsran_rnti_type_t rnti_type = srsran_rnti_type_c;
static srsran_slot_cfg_t slot_cfg = {};
static srsran_softbuffer_rx_t softbuffer = {};
static uint8_t* data = NULL;
static void usage(char* prog)
{
printf("Usage: %s [pTLR] \n", prog);
printf("\t-f File name [Default none]\n");
printf("\t-P Number of BWP (Carrier) PRB [Default %d]\n", carrier.nof_prb);
printf("\t-i Physical cell identifier [Default %d]\n", carrier.pci);
printf("\t-n Slot index [Default %d]\n", slot_cfg.idx);
printf("\t-R RNTI in hexadecimal [Default 0x%x]\n", rnti);
printf("\t-T RNTI type (c, ra) [Default %s]\n", srsran_rnti_type_str(rnti_type));
printf("\t-S Use standard rates [Default %s]\n", srsran_symbol_size_is_standard() ? "yes" : "no");
printf("\t-v [set srsran_verbose to debug, default none]\n");
}
static int parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "fPivnSRT")) != -1) {
switch (opt) {
case 'f':
filename = argv[optind];
break;
case 'P':
carrier.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'i':
carrier.pci = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'v':
srsran_verbose++;
break;
case 'n':
slot_cfg.idx = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'R':
rnti = (uint16_t)strtol(argv[optind], NULL, 16);
break;
case 'T':
if (strcmp(argv[optind], "c") == 0) {
rnti_type = srsran_rnti_type_c;
} else if (strcmp(argv[optind], "ra") == 0) {
rnti_type = srsran_rnti_type_ra;
} else {
printf("Invalid RNTI type '%s'\n", argv[optind]);
usage(argv[0]);
return SRSRAN_ERROR;
}
break;
case 'S':
srsran_use_standard_symbol_size(true);
break;
default:
usage(argv[0]);
return SRSRAN_ERROR;
}
}
return SRSRAN_SUCCESS;
}
static int work_ue_dl(srsran_ue_dl_nr_t* ue_dl, srsran_slot_cfg_t* slot)
{
// Run FFT
srsran_ue_dl_nr_estimate_fft(ue_dl, slot);
// Blind search
srsran_dci_dl_nr_t dci_dl_rx = {};
int nof_found_dci = srsran_ue_dl_nr_find_dl_dci(ue_dl, slot, rnti, rnti_type, &dci_dl_rx, 1);
if (nof_found_dci < SRSRAN_SUCCESS) {
ERROR("Error in blind search");
return SRSRAN_ERROR;
}
// Print PDCCH blind search candidates
for (uint32_t i = 0; i < ue_dl->pdcch_info_count; i++) {
const srsran_ue_dl_nr_pdcch_info_t* info = &ue_dl->pdcch_info[i];
INFO("PDCCH: rnti=0x%x, crst_id=%d, ss_type=%d, ncce=%d, al=%d, EPRE=%+.2f, RSRP=%+.2f, corr=%.3f; "
"nof_bits=%d; crc=%s;",
info->dci_ctx.rnti,
info->dci_ctx.coreset_id,
info->dci_ctx.ss_type,
info->dci_ctx.location.ncce,
info->dci_ctx.location.L,
info->measure.epre_dBfs,
info->measure.rsrp_dBfs,
info->measure.norm_corr,
info->nof_bits,
info->result.crc ? "OK" : "KO");
}
if (nof_found_dci < 1) {
printf("No DCI found :'(\n");
return SRSRAN_SUCCESS;
}
char str[1024] = {};
srsran_dci_dl_nr_to_str(&ue_dl->dci, &dci_dl_rx, str, (uint32_t)sizeof(str));
printf("Found DCI: %s\n", str);
// Convert DCI to PDSCH transmission
srsran_sch_cfg_nr_t pdsch_cfg = {};
if (srsran_ra_dl_dci_to_grant_nr(&carrier, slot, &pdsch_hl_cfg, &dci_dl_rx, &pdsch_cfg, &pdsch_cfg.grant) <
SRSRAN_SUCCESS) {
ERROR("Error decoding PDSCH search");
return SRSRAN_ERROR;
}
srsran_sch_cfg_nr_info(&pdsch_cfg, str, (uint32_t)sizeof(str));
printf("PDSCH: %s\n", str);
// Set softbuffer
pdsch_cfg.grant.tb[0].softbuffer.rx = &softbuffer;
// Prepare PDSCH result
srsran_pdsch_res_nr_t pdsch_res = {};
pdsch_res.tb[0].payload = data;
// Decode PDSCH
if (srsran_ue_dl_nr_decode_pdsch(ue_dl, slot, &pdsch_cfg, &pdsch_res) < SRSRAN_SUCCESS) {
ERROR("Error decoding PDSCH search");
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
int main(int argc, char** argv)
{
int ret = SRSRAN_ERROR;
srsran_ue_dl_nr_t ue_dl = {};
cf_t* buffer[SRSRAN_MAX_PORTS] = {};
uint32_t sf_len = SRSRAN_SF_LEN_PRB(carrier.nof_prb);
buffer[0] = srsran_vec_cf_malloc(sf_len);
if (buffer[0] == NULL) {
ERROR("Error malloc");
goto clean_exit;
}
if (srsran_softbuffer_rx_init_guru(&softbuffer, SRSRAN_SCH_NR_MAX_NOF_CB_LDPC, SRSRAN_LDPC_MAX_LEN_ENCODED_CB) <
SRSRAN_SUCCESS) {
ERROR("Error init soft-buffer");
goto clean_exit;
}
data = srsran_vec_u8_malloc(SRSRAN_SLOT_MAX_NOF_BITS_NR);
if (data == NULL) {
ERROR("Error malloc");
goto clean_exit;
}
srsran_ue_dl_nr_args_t ue_dl_args = {};
ue_dl_args.nof_rx_antennas = 1;
ue_dl_args.pdsch.sch.disable_simd = false;
ue_dl_args.pdsch.sch.decoder_use_flooded = false;
ue_dl_args.pdsch.measure_evm = true;
ue_dl_args.pdcch.disable_simd = false;
ue_dl_args.pdcch.measure_evm = true;
ue_dl_args.nof_max_prb = carrier.nof_prb;
// Set default PDSCH configuration
if (parse_args(argc, argv) < SRSRAN_SUCCESS) {
goto clean_exit;
}
// Check for filename
if (filename == NULL) {
ERROR("Filename was not provided");
goto clean_exit;
}
// Open filesource
srsran_filesource_t filesource = {};
if (srsran_filesource_init(&filesource, filename, SRSRAN_COMPLEX_FLOAT_BIN) < SRSRAN_SUCCESS) {
ERROR("Error opening filesource");
goto clean_exit;
}
// Configure CORESET
srsran_coreset_t* coreset = &pdcch_cfg.coreset[1];
pdcch_cfg.coreset_present[1] = true;
coreset->duration = 2;
for (uint32_t i = 0; i < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; i++) {
coreset->freq_resources[i] = i < carrier.nof_prb / 6;
}
// Configure Search Space
srsran_search_space_t* search_space = &pdcch_cfg.search_space[0];
pdcch_cfg.search_space_present[0] = true;
search_space->id = 0;
search_space->coreset_id = 1;
search_space->type = srsran_search_space_type_common_3;
search_space->formats[0] = srsran_dci_format_nr_0_0;
search_space->formats[1] = srsran_dci_format_nr_1_0;
search_space->nof_formats = 2;
for (uint32_t L = 0; L < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR; L++) {
search_space->nof_candidates[L] = srsran_pdcch_nr_max_candidates_coreset(coreset, L);
}
// Configure RA search space
pdcch_cfg.ra_search_space_present = true;
pdcch_cfg.ra_search_space = *search_space;
pdcch_cfg.ra_search_space.type = srsran_search_space_type_common_1;
if (srsran_ue_dl_nr_init(&ue_dl, buffer, &ue_dl_args)) {
ERROR("Error UE DL");
goto clean_exit;
}
if (srsran_ue_dl_nr_set_carrier(&ue_dl, &carrier)) {
ERROR("Error setting SCH NR carrier");
goto clean_exit;
}
// Read baseband from file
if (srsran_filesource_read(&filesource, buffer[0], (int)ue_dl.fft->sf_sz) < SRSRAN_SUCCESS) {
ERROR("Error reading baseband");
goto clean_exit;
}
srsran_dci_cfg_nr_t dci_cfg = {};
dci_cfg.bwp_dl_initial_bw = carrier.nof_prb;
dci_cfg.bwp_ul_initial_bw = carrier.nof_prb;
dci_cfg.monitor_common_0_0 = true;
if (srsran_ue_dl_nr_set_pdcch_config(&ue_dl, &pdcch_cfg, &dci_cfg)) {
ERROR("Error setting CORESET");
goto clean_exit;
}
// Actual decode
work_ue_dl(&ue_dl, &slot_cfg);
ret = SRSRAN_SUCCESS;
clean_exit:
if (buffer[0] != NULL) {
free(buffer[0]);
}
if (data != NULL) {
free(data);
}
srsran_ue_dl_nr_free(&ue_dl);
srsran_filesource_free(&filesource);
srsran_softbuffer_rx_free(&softbuffer);
return ret;
}

@ -862,7 +862,8 @@ void srsran_ue_dl_gen_cqi_periodic(srsran_ue_dl_t* q,
uci_data->cfg.cqi.ri_len = 1;
uci_data->value.ri = cfg->last_ri;
} else if (srsran_cqi_periodic_send(&cfg->cfg.cqi_report, tti, q->cell.frame_type)) {
if (cfg->cfg.cqi_report.format_is_subband) {
if (cfg->cfg.cqi_report.format_is_subband &&
srsran_cqi_periodic_is_subband(&cfg->cfg.cqi_report, tti, q->cell.nof_prb, q->cell.frame_type)) {
// TODO: Implement subband periodic reports
uci_data->cfg.cqi.type = SRSRAN_CQI_TYPE_SUBBAND_UE;
uci_data->value.cqi.subband_ue.subband_cqi = wideband_value;

@ -736,6 +736,22 @@ void srsran_bit_unpack(uint32_t value, uint8_t** bits, int nof_bits)
*bits += nof_bits;
}
/**
* Unpacks nof_bits from LSBs of value in LSB order to *bits. Advances pointer past unpacked bits.
*
* @param[in] value nof_bits lowest order bits will be unpacked in MSB order
* @param[in] nof_bits Number of bits to unpack
* @param[out] bits Points to buffer pointer. The buffer pointer will be advanced by nof_bits
*/
void srsran_bit_unpack_lsb(uint32_t value, uint8_t** bits, int nof_bits)
{
int i;
for (i = 0; i < nof_bits; i++) {
(*bits)[nof_bits - i - 1] = (value >> (nof_bits - i - 1)) & 0x1;
}
*bits += nof_bits;
}
void srsran_bit_pack_vector(uint8_t* unpacked, uint8_t* packed, int nof_bits)
{
uint32_t i, nbytes;

@ -1386,7 +1386,7 @@ uint32_t srsran_vec_max_fi_simd(const float* x, const int len)
simd_f_t a = srsran_simd_f_load(&x[i]);
simd_sel_t res = srsran_simd_f_max(a, simd_max_values);
simd_max_indexes = srsran_simd_i_select(simd_max_indexes, simd_indexes, res);
simd_max_values = (simd_f_t)srsran_simd_i_select((simd_i_t)simd_max_values, (simd_i_t)a, res);
simd_max_values = srsran_simd_f_select(simd_max_values, a, res);
simd_indexes = srsran_simd_i_add(simd_indexes, simd_inc);
}
} else {
@ -1394,7 +1394,7 @@ uint32_t srsran_vec_max_fi_simd(const float* x, const int len)
simd_f_t a = srsran_simd_f_loadu(&x[i]);
simd_sel_t res = srsran_simd_f_max(a, simd_max_values);
simd_max_indexes = srsran_simd_i_select(simd_max_indexes, simd_indexes, res);
simd_max_values = (simd_f_t)srsran_simd_i_select((simd_i_t)simd_max_values, (simd_i_t)a, res);
simd_max_values = srsran_simd_f_select(simd_max_values, a, res);
simd_indexes = srsran_simd_i_add(simd_indexes, simd_inc);
}
}
@ -1444,7 +1444,7 @@ uint32_t srsran_vec_max_abs_fi_simd(const float* x, const int len)
simd_f_t a = srsran_simd_f_abs(srsran_simd_f_load(&x[i]));
simd_sel_t res = srsran_simd_f_max(a, simd_max_values);
simd_max_indexes = srsran_simd_i_select(simd_max_indexes, simd_indexes, res);
simd_max_values = (simd_f_t)srsran_simd_i_select((simd_i_t)simd_max_values, (simd_i_t)a, res);
simd_max_values = srsran_simd_f_select(simd_max_values, a, res);
simd_indexes = srsran_simd_i_add(simd_indexes, simd_inc);
}
} else {
@ -1452,7 +1452,7 @@ uint32_t srsran_vec_max_abs_fi_simd(const float* x, const int len)
simd_f_t a = srsran_simd_f_abs(srsran_simd_f_loadu(&x[i]));
simd_sel_t res = srsran_simd_f_max(a, simd_max_values);
simd_max_indexes = srsran_simd_i_select(simd_max_indexes, simd_indexes, res);
simd_max_values = (simd_f_t)srsran_simd_i_select((simd_i_t)simd_max_values, (simd_i_t)a, res);
simd_max_values = srsran_simd_f_select(simd_max_values, a, res);
simd_indexes = srsran_simd_i_add(simd_indexes, simd_inc);
}
}
@ -1511,7 +1511,7 @@ uint32_t srsran_vec_max_ci_simd(const cf_t* x, const int len)
simd_sel_t res = srsran_simd_f_max(z1, simd_max_values);
simd_max_indexes = srsran_simd_i_select(simd_max_indexes, simd_indexes, res);
simd_max_values = (simd_f_t)srsran_simd_i_select((simd_i_t)simd_max_values, (simd_i_t)z1, res);
simd_max_values = srsran_simd_f_select(simd_max_values, z1, res);
simd_indexes = srsran_simd_i_add(simd_indexes, simd_inc);
}
} else {
@ -1527,7 +1527,7 @@ uint32_t srsran_vec_max_ci_simd(const cf_t* x, const int len)
simd_sel_t res = srsran_simd_f_max(z1, simd_max_values);
simd_max_indexes = srsran_simd_i_select(simd_max_indexes, simd_indexes, res);
simd_max_values = (simd_f_t)srsran_simd_i_select((simd_i_t)simd_max_values, (simd_i_t)z1, res);
simd_max_values = srsran_simd_f_select(simd_max_values, z1, res);
simd_indexes = srsran_simd_i_add(simd_indexes, simd_inc);
}
}

@ -539,10 +539,9 @@ bool radio::tx_dev(const uint32_t& device_idx, rf_buffer_interface& buffer, cons
nof_samples = nof_samples - past_nsamples; // Subtracts the number of trimmed samples
// Prints discarded samples
logger.debug("Detected RF overlap of %.1f us. Discarding %d samples. Power=%+.1f dBfs",
logger.debug("Detected RF overlap of %.1f us. Discarding %d samples.",
srsran_timestamp_real(&ts_overlap) * 1.0e6,
past_nsamples,
srsran_convert_power_to_dB(srsran_vec_avg_power_cf(&buffer.get(0)[nof_samples], past_nsamples)));
past_nsamples);
} else if (past_nsamples < 0 and not is_start_of_burst) {
// if the gap is bigger than TX_MAX_GAP_ZEROS, stop burst

@ -617,6 +617,11 @@ void rlc_am_lte::rlc_am_lte_tx::retransmit_pdu(uint32_t sn)
// select first PDU in tx window for retransmission
rlc_amd_tx_pdu& pdu = tx_window[sn];
// increment retx counter and inform upper layers
pdu.retx_count++;
check_sn_reached_max_retx(sn);
logger.info("%s Schedule SN=%d for reTx", RB_NAME, pdu.rlc_sn);
rlc_amd_retx_t& retx = retx_queue.push();
retx.is_segment = false;
@ -760,8 +765,6 @@ int rlc_am_lte::rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_byt
memcpy(ptr, tx_window[retx.sn].buf->msg, tx_window[retx.sn].buf->N_bytes);
retx_queue.pop();
tx_window[retx.sn].retx_count++;
check_sn_reached_max_retx(retx.sn);
logger.info(payload,
tx_window[retx.sn].buf->N_bytes,
@ -920,9 +923,6 @@ int rlc_am_lte::rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_byte
retx_queue.front().so_start = retx.so_end;
}
tx_window[retx.sn].retx_count++;
check_sn_reached_max_retx(retx.sn);
// Write header and pdu
uint8_t* ptr = payload;
rlc_am_write_data_pdu_header(&new_header, &ptr);
@ -1228,7 +1228,13 @@ void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t no
std::lock_guard<std::mutex> lock(mutex);
if (tx_window.has_sn(i)) {
auto& pdu = tx_window[i];
// add to retx queue if it's not already there
if (not retx_queue.has_sn(i)) {
// increment Retx counter and inform upper layers if needed
pdu.retx_count++;
check_sn_reached_max_retx(i);
rlc_amd_retx_t& retx = retx_queue.push();
srsran_expect(tx_window[i].rlc_sn == i, "Incorrect RLC SN=%d!=%d being accessed", tx_window[i].rlc_sn, i);
retx.sn = i;

@ -26,7 +26,9 @@
#define JSON_OUTPUT 0
#define HAVE_PCAP 0
#if HAVE_PCAP
#include "srsran/common/test_pcap.h"
#endif
using namespace asn1;
using namespace asn1::rrc_nr;
@ -375,12 +377,12 @@ int test_cell_group_config()
// pack it again
cell_group_cfg_s cell_group_cfg_pack;
cell_group_cfg_pack.sp_cell_cfg_present = true;
cell_group_cfg_pack.sp_cell_cfg.serv_cell_idx_present = true;
cell_group_cfg_pack.sp_cell_cfg_present = true;
cell_group_cfg_pack.sp_cell_cfg.serv_cell_idx_present = true;
// SP Cell Dedicated config
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded_present = true;
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp_present = true;
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded_present = true;
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp_present = true;
// PDCCH config
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg_present = true;
@ -474,33 +476,180 @@ int test_cell_group_config()
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.first_active_dl_bwp_id = 1;
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg_present = true;
// UL config dedicated
// PUCCH
auto& ul_config = cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg;
ul_config.init_ul_bwp_present = true;
ul_config.init_ul_bwp.pucch_cfg_present = true;
ul_config.init_ul_bwp.pucch_cfg.set_setup();
ul_config.init_ul_bwp.pucch_cfg.setup().format2_present = true;
ul_config.init_ul_bwp.pucch_cfg.setup().format2.set_setup();
ul_config.init_ul_bwp.pucch_cfg.setup().format2.setup().max_code_rate_present = true;
ul_config.init_ul_bwp.pucch_cfg.setup().format2.setup().max_code_rate = pucch_max_code_rate_opts::zero_dot25;
// SR resources
ul_config.init_ul_bwp.pucch_cfg.setup().sched_request_res_to_add_mod_list_present = true;
ul_config.init_ul_bwp.pucch_cfg.setup().sched_request_res_to_add_mod_list.resize(1);
auto& sr_res1 = ul_config.init_ul_bwp.pucch_cfg.setup().sched_request_res_to_add_mod_list[0];
sr_res1.sched_request_res_id = 1;
sr_res1.sched_request_id = 0;
sr_res1.periodicity_and_offset_present = true;
sr_res1.periodicity_and_offset.set_sl40();
sr_res1.periodicity_and_offset.sl40() = 7;
sr_res1.res_present = true;
sr_res1.res = 0; // only PUCCH resource we have defined so far
// DL data
ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack_present = true;
ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack.resize(5);
ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[0] = 8;
ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[1] = 7;
ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[2] = 6;
ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[3] = 5;
ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[4] = 4;
// PUCCH resources (only one format1 for the moment)
ul_config.init_ul_bwp.pucch_cfg.setup().res_to_add_mod_list_present = true;
ul_config.init_ul_bwp.pucch_cfg.setup().res_to_add_mod_list.resize(1);
auto& pucch_res1 = ul_config.init_ul_bwp.pucch_cfg.setup().res_to_add_mod_list[0];
pucch_res1.pucch_res_id = 0;
pucch_res1.start_prb = 0;
pucch_res1.format.set_format1();
pucch_res1.format.format1().init_cyclic_shift = 0;
pucch_res1.format.format1().nrof_symbols = 14;
pucch_res1.format.format1().start_symbol_idx = 0;
pucch_res1.format.format1().time_domain_occ = 0;
// PUSCH config
ul_config.init_ul_bwp.pusch_cfg_present = true;
ul_config.init_ul_bwp.pusch_cfg.set_setup();
auto& pusch_cfg_ded = ul_config.init_ul_bwp.pusch_cfg.setup();
pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a_present = true;
pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a.set_setup();
pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a.setup().dmrs_add_position_present = true;
pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a.setup().dmrs_add_position = dmrs_ul_cfg_s::dmrs_add_position_opts::pos1;
// PUSH power control skipped
pusch_cfg_ded.res_alloc = pusch_cfg_s::res_alloc_opts::res_alloc_type1;
// UCI
pusch_cfg_ded.uci_on_pusch_present = true;
pusch_cfg_ded.uci_on_pusch.set_setup();
pusch_cfg_ded.uci_on_pusch.setup().beta_offsets_present = true;
pusch_cfg_ded.uci_on_pusch.setup().beta_offsets.set_semi_static();
auto& beta_offset_semi_static = pusch_cfg_ded.uci_on_pusch.setup().beta_offsets.semi_static();
beta_offset_semi_static.beta_offset_ack_idx1_present = true;
beta_offset_semi_static.beta_offset_ack_idx1 = 9;
beta_offset_semi_static.beta_offset_ack_idx2_present = true;
beta_offset_semi_static.beta_offset_ack_idx2 = 9;
beta_offset_semi_static.beta_offset_ack_idx3_present = true;
beta_offset_semi_static.beta_offset_ack_idx3 = 9;
beta_offset_semi_static.beta_offset_csi_part1_idx1_present = true;
beta_offset_semi_static.beta_offset_csi_part1_idx2_present = true;
beta_offset_semi_static.beta_offset_csi_part1_idx1 = 6;
beta_offset_semi_static.beta_offset_csi_part1_idx2 = 6;
beta_offset_semi_static.beta_offset_csi_part2_idx1_present = true;
beta_offset_semi_static.beta_offset_csi_part2_idx1 = 6;
beta_offset_semi_static.beta_offset_csi_part2_idx2_present = true;
beta_offset_semi_static.beta_offset_csi_part2_idx2 = 6;
pusch_cfg_ded.uci_on_pusch.setup().scaling = uci_on_pusch_s::scaling_opts::f1;
ul_config.first_active_ul_bwp_id_present = true;
ul_config.first_active_ul_bwp_id = 0;
// Serving cell config (only to setup)
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdcch_serving_cell_cfg_present = true;
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdcch_serving_cell_cfg.set_setup();
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg_present = true;
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.set_setup();
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.setup().nrof_harq_processes_for_pdsch_present =
true;
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.setup().nrof_harq_processes_for_pdsch =
pdsch_serving_cell_cfg_s::nrof_harq_processes_for_pdsch_opts::n16;
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg_present = true;
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.set_setup();
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync_present = true;
// nzp-CSI-RS Resource
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_to_add_mod_list_present = true;
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_to_add_mod_list.resize(1);
auto& nzp_csi_res =
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_to_add_mod_list[0];
nzp_csi_res.nzp_csi_rs_res_id = 0;
nzp_csi_res.res_map.freq_domain_alloc.set_row2();
nzp_csi_res.res_map.freq_domain_alloc.row2().from_number(0b100000000000);
nzp_csi_res.res_map.nrof_ports = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1;
nzp_csi_res.res_map.first_ofdm_symbol_in_time_domain = 4;
nzp_csi_res.res_map.cdm_type = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm;
nzp_csi_res.res_map.density.set_one();
nzp_csi_res.res_map.freq_band.start_rb = 0;
nzp_csi_res.res_map.freq_band.nrof_rbs = 52;
nzp_csi_res.pwr_ctrl_offset = 0;
// Skip pwr_ctrl_offset_ss_present
nzp_csi_res.scrambling_id = 0;
nzp_csi_res.periodicity_and_offset_present = true;
nzp_csi_res.periodicity_and_offset.set_slots80();
nzp_csi_res.periodicity_and_offset.slots80() = 0;
// optional
nzp_csi_res.qcl_info_periodic_csi_rs_present = true;
nzp_csi_res.qcl_info_periodic_csi_rs = 0;
// nzp-CSI-RS ResourceSet
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_set_to_add_mod_list_present =
true;
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_set_to_add_mod_list.resize(1);
auto& nzp_csi_res_set =
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_set_to_add_mod_list[0];
nzp_csi_res_set.nzp_csi_res_set_id = 0;
nzp_csi_res_set.nzp_csi_rs_res.resize(1);
nzp_csi_res_set.nzp_csi_rs_res[0] = 0;
// Skip TRS info
// CSI report config
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().csi_report_cfg_to_add_mod_list_present = true;
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().csi_report_cfg_to_add_mod_list.resize(1);
auto& csi_report =
cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().csi_report_cfg_to_add_mod_list[0];
csi_report.report_cfg_id = 0;
csi_report.res_for_ch_meas = 0;
csi_report.csi_im_res_for_interference_present = true;
csi_report.csi_im_res_for_interference = 1;
csi_report.report_cfg_type.set_periodic();
csi_report.report_cfg_type.periodic().report_slot_cfg.set_slots80();
csi_report.report_cfg_type.periodic().report_slot_cfg.slots80() = 8;
csi_report.report_cfg_type.periodic().pucch_csi_res_list.resize(1);
csi_report.report_cfg_type.periodic().pucch_csi_res_list[0].ul_bw_part_id = 0;
csi_report.report_cfg_type.periodic().pucch_csi_res_list[0].pucch_res = 17;
csi_report.report_quant.set_cri_ri_pmi_cqi();
csi_report.report_freq_cfg_present = true;
csi_report.report_freq_cfg.cqi_format_ind_present = true;
csi_report.report_freq_cfg.cqi_format_ind =
asn1::rrc_nr::csi_report_cfg_s::report_freq_cfg_s_::cqi_format_ind_opts::wideband_cqi;
csi_report.time_restrict_for_ch_meass = asn1::rrc_nr::csi_report_cfg_s::time_restrict_for_ch_meass_opts::not_cfgured;
csi_report.time_restrict_for_interference_meass =
asn1::rrc_nr::csi_report_cfg_s::time_restrict_for_interference_meass_opts::not_cfgured;
csi_report.group_based_beam_report.set_disabled();
csi_report.cqi_table = asn1::rrc_nr::csi_report_cfg_s::cqi_table_opts::table2;
csi_report.subband_size = asn1::rrc_nr::csi_report_cfg_s::subband_size_opts::value1;
// Reconfig with Sync
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync_present = true;
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.new_ue_id = 17943;
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.smtc.release();
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.t304 = recfg_with_sync_s::t304_opts::ms1000;
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common_present = true;
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common_present = true;
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ss_pbch_block_pwr = 0;
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dmrs_type_a_position =
asn1::rrc_nr::serving_cell_cfg_common_s::dmrs_type_a_position_opts::pos2;
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci_present = true;
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci = 500;
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci_present = true;
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci = 500;
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_subcarrier_spacing_present = true;
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_subcarrier_spacing =
subcarrier_spacing_opts::khz30;
// DL config
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common_present = true;
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common_present = true;
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl_present = true;
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl
.absolute_freq_ssb_present = true;
@ -600,7 +749,8 @@ int test_cell_group_config()
.subcarrier_spacing = subcarrier_spacing_opts::khz15;
// RACH config
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common_present=true;
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common_present =
true;
auto& rach_cfg_common_pack =
cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common;
@ -714,7 +864,7 @@ int test_cell_group_config()
packed_dcch.size(),
json_writer3.to_string().c_str());
srsran::write_rlc_am_sdu_nr(1, packed_dcch.data(), packed_dcch.size());
srsran::write_pdcp_sdu_nr(1, packed_dcch.data(), packed_dcch.size());
#endif
return SRSRAN_SUCCESS;

@ -19,6 +19,7 @@
*
*/
#include "srsran/common/slot_point.h"
#include "srsran/common/test_common.h"
#include "srsran/common/tti_point.h"
@ -75,9 +76,53 @@ int test_tti_type()
return SRSRAN_SUCCESS;
}
void test_nr_slot_type()
{
// TEST: constructors
srsran::slot_point slot1;
TESTASSERT(not slot1.valid());
srsran::slot_point slot2{0, 1, 5};
TESTASSERT(slot2.valid() and slot2.numerology_idx() == 0 and slot2.slot_idx() == 5 and slot2.slot_idx() == 5 and
slot2.sfn() == 1);
srsran::slot_point slot3{slot2};
TESTASSERT(slot3 == slot2);
// TEST: comparison and difference operators
slot1 = srsran::slot_point{0, 1, 5};
slot2 = srsran::slot_point{0, 1, 5};
TESTASSERT(slot1 == slot2 and slot1 <= slot2 and slot1 >= slot2);
slot1++;
TESTASSERT(slot1 != slot2 and slot1 >= slot2 and slot1 > slot2 and slot2 < slot1 and slot2 <= slot1);
TESTASSERT(slot1 - slot2 == 1 and slot2 - slot1 == -1);
slot1 = srsran::slot_point{0, 2, 5};
TESTASSERT(slot1 != slot2 and slot1 >= slot2 and slot1 > slot2 and slot2 < slot1 and slot2 <= slot1);
TESTASSERT(slot1 - slot2 == 10 and slot2 - slot1 == -10);
slot1 = srsran::slot_point{0, 1023, 5};
TESTASSERT(slot1 != slot2 and slot1 <= slot2 and slot1 < slot2 and slot2 > slot1 and slot2 >= slot1);
TESTASSERT(slot1 - slot2 == -20 and slot2 - slot1 == 20);
// TEST: increment/decrement operators
slot1 = srsran::slot_point{0, 1, 5};
slot2 = srsran::slot_point{0, 1, 5};
TESTASSERT(slot1++ == slot2);
TESTASSERT(slot2 + 1 == slot1);
TESTASSERT(++slot2 == slot1);
slot1 = srsran::slot_point{0, 1, 5};
slot2 = srsran::slot_point{0, 1, 5};
TESTASSERT(slot1 - 100 == slot2 - 100);
TESTASSERT(((slot1 - 100000) + 100000) == slot1);
TESTASSERT((slot1 - 10240) == slot1);
TESTASSERT((slot1 - 100).slot_idx() == 5 and (slot1 - 100).sfn() == 1015);
TESTASSERT(((slot1 - 100) + 100) == slot1);
TESTASSERT(((slot1 - 1) + 1) == slot1);
fmt::print("[ {}]", slot1);
}
int main()
{
srslog::init();
TESTASSERT(test_tti_type() == SRSRAN_SUCCESS);
test_tti_type();
test_nr_slot_type();
return 0;
}

@ -72,5 +72,5 @@ add_nr_test(phy_dl_nr_test_rvd phy_dl_nr_test -P 52 -p 52 -m 0
-R 0 52 1 100100100100 00000010000000)
add_nr_test(phy_dl_nr_test_cfo_delay phy_dl_nr_test -P 52 -p 52 -m 27 -C 100.0 -D 4 -n 10)
add_nr_test(phy_dl_nr_test_52prb phy_dl_nr_test -P 52 -p 52 -m 27 -T 256qam -v -d 1 1 -n 10)
add_nr_test(phy_dl_nr_test_270prb phy_dl_nr_test -P 270 -p 270 -m 27 -T 256qam -v -d 1 1 -n 10)
add_nr_test(phy_dl_nr_test_52prb phy_dl_nr_test -P 52 -p 52 -m 27 -T 256qam -d 1 1 -n 10)
add_nr_test(phy_dl_nr_test_270prb phy_dl_nr_test -P 270 -p 270 -m 27 -T 256qam -d 1 1 -n 10)

@ -396,10 +396,7 @@ int main(int argc, char** argv)
if (data_tx[tb] == NULL) {
continue;
}
for (uint32_t i = 0; i < pdsch_cfg.grant.tb[tb].tbs; i++) {
data_tx[tb][i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, UINT8_MAX);
}
srsran_random_byte_vector(rand_gen, data_tx[tb], pdsch_cfg.grant.tb[tb].tbs/8);
pdsch_cfg.grant.tb[tb].softbuffer.tx = &softbuffer_tx;
}

@ -3588,7 +3588,7 @@ bool reestablish_test()
}
}
TESTASSERT(tester.sdus.size() == 17);
TESTASSERT(tester.sdus.size() == 18);
srslog::fetch_basic_logger("TEST").info("Received %zd SDUs", tester.sdus.size());

@ -41,7 +41,7 @@ public:
void init(phy_common* phy);
cf_t* get_buffer_rx(uint32_t cc_idx, uint32_t antenna_idx);
void set_time(uint32_t tti_, const srsran::rf_timestamp_t& tx_time_);
void set_context(const srsran::phy_common_interface::worker_context_t& w_ctx);
int add_rnti(uint16_t rnti, uint32_t cc_idx);
void rem_rnti(uint16_t rnti);
@ -68,10 +68,9 @@ private:
bool running = false;
std::mutex work_mutex;
uint32_t tti_rx = 0, tti_tx_dl = 0, tti_tx_ul = 0;
srsran::rf_timestamp_t tx_time = {};
std::vector<std::unique_ptr<cc_worker> > cc_workers;
uint32_t tti_rx = 0, tti_tx_dl = 0, tti_tx_ul = 0;
std::vector<std::unique_ptr<cc_worker> > cc_workers;
srsran::phy_common_interface::worker_context_t context = {};
srsran_softbuffer_tx_t temp_mbsfn_softbuffer = {};
};

@ -45,6 +45,7 @@ public:
uint32_t nof_max_prb = SRSRAN_MAX_PRB_NR;
uint32_t nof_tx_ports = 1;
uint32_t nof_rx_ports = 1;
uint32_t rf_port = 0;
uint32_t pusch_max_nof_iter = 10;
};
@ -59,7 +60,7 @@ public:
cf_t* get_buffer_rx(uint32_t antenna_idx);
cf_t* get_buffer_tx(uint32_t antenna_idx);
uint32_t get_buffer_len();
void set_time(const uint32_t& tti, const srsran::rf_timestamp_t& timestamp);
void set_context(const srsran::phy_common_interface::worker_context_t& w_ctx);
private:
/**
@ -83,16 +84,18 @@ private:
stack_interface_phy_nr& stack;
srslog::basic_logger& logger;
uint32_t sf_len = 0;
uint32_t cell_index = 0;
srsran_slot_cfg_t dl_slot_cfg = {};
srsran_slot_cfg_t ul_slot_cfg = {};
srsran_pdcch_cfg_nr_t pdcch_cfg = {};
srsran::rf_timestamp_t tx_time = {};
srsran_gnb_dl_t gnb_dl = {};
srsran_gnb_ul_t gnb_ul = {};
std::vector<cf_t*> tx_buffer; ///< Baseband transmit buffers
std::vector<cf_t*> rx_buffer; ///< Baseband receive buffers
uint32_t sf_len = 0;
uint32_t cell_index = 0;
uint32_t rf_port = 0;
srsran_slot_cfg_t dl_slot_cfg = {};
srsran_slot_cfg_t ul_slot_cfg = {};
srsran::phy_common_interface::worker_context_t context = {};
srsran_pdcch_cfg_nr_t pdcch_cfg = {};
srsran_gnb_dl_t gnb_dl = {};
srsran_gnb_ul_t gnb_ul = {};
std::vector<cf_t*> tx_buffer; ///< Baseband transmit buffers
std::vector<cf_t*> rx_buffer; ///< Baseband receive buffers
std::mutex mutex; ///< Protect concurrent access from workers (and main process that inits the class)
};
} // namespace nr

@ -50,6 +50,7 @@ private:
void rach_detected(uint32_t tti, uint32_t primary_cc_idx, uint32_t preamble_idx, uint32_t time_adv) override
{
stack_interface_phy_nr::rach_info_t rach_info = {};
rach_info.slot_index = tti;
rach_info.preamble = preamble_idx;
rach_info.time_adv = time_adv;
@ -91,6 +92,7 @@ private:
uint32_t current_tti = 0; ///< Current TTI, read and write from same thread
srslog::basic_logger& logger;
prach_stack_adaptor_t prach_stack_adaptor;
uint32_t nof_prach_workers = 0;
// Current configuration
std::mutex common_cfg_mutex;
@ -100,6 +102,7 @@ private:
public:
struct args_t {
uint32_t nof_phy_threads = 3;
uint32_t nof_prach_workers = 0;
uint32_t prio = 52;
uint32_t pusch_max_nof_iter = 10;
srsran::phy_log_args_t log = {};

@ -34,7 +34,10 @@
namespace srsenb {
class phy final : public enb_phy_base, public phy_interface_stack_lte, public srsran::phy_interface_radio
class phy final : public enb_phy_base,
public phy_interface_stack_lte,
public phy_interface_stack_nr,
public srsran::phy_interface_radio
{
public:
phy(srslog::sink& log_sink);
@ -44,6 +47,11 @@ public:
const phy_cfg_t& cfg,
srsran::radio_interface_phy* radio_,
stack_interface_phy_lte* stack_);
int init(const phy_args_t& args,
const phy_cfg_t& cfg,
srsran::radio_interface_phy* radio_,
stack_interface_phy_lte* stack_lte_,
stack_interface_phy_nr& stack_nr_);
void stop() override;
std::string get_type() override { return "lte"; };
@ -70,6 +78,9 @@ public:
void srsran_phy_logger(phy_logger_level_t log_level, char* str);
int init_nr(const phy_args_t& args, const phy_cfg_t& cfg, stack_interface_phy_nr& stack);
int set_common_cfg(const common_cfg_t& common_cfg) override;
private:
srsran::phy_cfg_mbsfn_t mbsfn_config = {};
uint32_t nof_workers = 0;

@ -66,11 +66,7 @@ public:
* @param tx_time timestamp to transmit samples
* @param is_nr flag is true if it is called from NR
*/
void worker_end(void* tx_sem_id,
bool tx_enable,
srsran::rf_buffer_t& buffer,
srsran::rf_timestamp_t& tx_time,
bool is_nr) override;
void worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) override;
// Common objects
phy_args_t params = {};
@ -84,6 +80,9 @@ public:
if (cc_idx < cell_list_lte.size()) {
ret = cell_list_lte[cc_idx].cell.nof_prb;
} else if (cc_idx == 1 && !cell_list_nr.empty()) {
// for basic NSA config return width of first NR carrier
ret = cell_list_nr[0].carrier.nof_prb;
}
return ret;
@ -94,6 +93,9 @@ public:
if (cc_idx < cell_list_lte.size()) {
ret = cell_list_lte[cc_idx].cell.nof_ports;
} else if (cc_idx == 1 && !cell_list_nr.empty()) {
// one RF port for basic NSA config
ret = 1;
}
return ret;
@ -245,10 +247,9 @@ private:
uint8_t mch_table[40] = {};
uint8_t mcch_table[10] = {};
uint32_t mch_period_stop = 0;
srsran::rf_buffer_t tx_buffer = {};
bool is_mch_subframe(srsran_mbsfn_cfg_t* cfg, uint32_t phy_tti);
bool is_mcch_subframe(srsran_mbsfn_cfg_t* cfg, uint32_t phy_tti);
srsran::rf_buffer_t nr_tx_buffer;
bool nr_tx_buffer_ready = false;
};
} // namespace srsenb

@ -392,6 +392,15 @@ public:
const srsran_uci_cfg_t& uci_cfg,
const srsran_uci_value_t& uci_value);
static void send_cqi_data(uint32_t tti,
uint16_t rnti,
uint32_t cqi_cc_idx,
const srsran_cqi_cfg_t& cqi_cfg,
const srsran_cqi_value_t& cqi_value,
const srsran_cqi_report_cfg_t& cqi_report_cfg,
const srsran_cell_t& cell,
stack_interface_phy_lte* stack);
/**
* Set the latest UL Transport Block resource allocation for a given RNTI, eNb cell/carrier and UL HARQ process
* identifier.

@ -40,10 +40,10 @@ public:
bool init(stack_interface_phy_lte* stack_,
srsran::radio_interface_phy* radio_handler,
lte::worker_pool* lte_workers_,
nr::worker_pool* nr_workers_,
phy_common* worker_com,
prach_worker_pool* prach_,
uint32_t prio);
bool set_nr_workers(nr::worker_pool* nr_workers_);
void stop();
private:

@ -44,13 +44,20 @@
namespace srsenb {
class enb_stack_lte final : public enb_stack_base, public stack_interface_phy_lte, public srsran::thread
class enb_stack_lte final : public enb_stack_base,
public stack_interface_phy_lte,
public stack_interface_phy_nr,
public srsran::thread
{
public:
enb_stack_lte(srslog::sink& log_sink);
~enb_stack_lte() final;
// eNB stack base interface
int init(const stack_args_t& args_,
const rrc_cfg_t& rrc_cfg_,
phy_interface_stack_lte* phy_,
phy_interface_stack_nr* phy_nr_);
int init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_, phy_interface_stack_lte* phy_);
int init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_);
void stop() final;
@ -114,6 +121,26 @@ public:
void toggle_padding() override { mac.toggle_padding(); }
void tti_clock() override;
// mac_interface_phy_nr
int slot_indication(const srsran_slot_cfg_t& slot_cfg) override { return mac_nr.slot_indication(slot_cfg); }
int get_dl_sched(const srsran_slot_cfg_t& slot_cfg, srsenb::mac_interface_phy_nr::dl_sched_t& dl_sched) override
{
return mac_nr.get_dl_sched(slot_cfg, dl_sched);
}
int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, srsenb::mac_interface_phy_nr::ul_sched_t& ul_sched) override
{
return mac_nr.get_ul_sched(slot_cfg, ul_sched);
}
int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) override
{
return mac_nr.pucch_info(slot_cfg, pucch_info);
}
int pusch_info(const srsran_slot_cfg_t& slot_cfg, pusch_info_t& pusch_info) override
{
return mac_nr.pusch_info(slot_cfg, pusch_info);
}
void rach_detected(const rach_info_t& rach_info) override { mac_nr.rach_detected(rach_info); }
private:
static const int STACK_MAIN_THREAD_PRIO = 4;
// thread loop
@ -163,6 +190,7 @@ private:
// RAT-specific interfaces
phy_interface_stack_lte* phy = nullptr;
phy_interface_stack_nr* phy_nr = nullptr;
// state
std::atomic<bool> started{false};

@ -67,9 +67,6 @@ public:
bool is_registered() override { return true; };
bool start_service_request() override { return true; };
// PHY->MAC interface
int rx_data_indication(rx_data_ind_t& grant) override;
// Temporary GW interface
void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) override;
bool has_active_radio_bearer(uint32_t eps_bearer_id) override;
@ -85,7 +82,7 @@ public:
int get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) override;
int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) override;
int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) override;
int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) override;
int pusch_info(const srsran_slot_cfg_t& slot_cfg, pusch_info_t& pusch_info) override;
void rach_detected(const rach_info_t& rach_info) override;
private:

@ -27,6 +27,7 @@
#include "srsenb/hdr/common/rnti_pool.h"
#include "srsenb/hdr/stack/enb_stack_base.h"
#include "srsenb/hdr/stack/mac/nr/sched_nr.h"
#include "srsenb/hdr/stack/mac/nr/ue_nr.h"
#include "srsran/common/task_scheduler.h"
#include "srsran/interfaces/enb_metrics_interface.h"
@ -36,6 +37,9 @@
namespace srsenb {
struct mac_nr_args_t {
srsran::phy_cfg_nr_t phy_base_cfg = {};
int fixed_dl_mcs = -1;
int fixed_ul_mcs = -1;
srsenb::pcap_args_t pcap;
};
@ -64,16 +68,12 @@ public:
int rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) override { return 0; }
// Interface for PHY
int sf_indication(const uint32_t tti);
int rx_data_indication(stack_interface_phy_nr::rx_data_ind_t& grant);
void process_pdus();
void rach_detected(const srsran_slot_cfg_t& slot_cfg, uint32_t enb_cc_idx, uint32_t preamble_idx, uint32_t time_adv);
int slot_indication(const srsran_slot_cfg_t& slot_cfg) override;
int get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) override;
int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) override;
int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) override;
int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) override;
int pusch_info(const srsran_slot_cfg_t& slot_cfg, pusch_info_t& pusch_info) override;
void rach_detected(const rach_info_t& rach_info) override;
private:
@ -84,6 +84,9 @@ private:
bool is_rnti_valid_unsafe(uint16_t rnti);
bool is_rnti_active_unsafe(uint16_t rnti);
// handle UCI data from either PUCCH or PUSCH
bool handle_uci_data(const uint16_t rnti, const srsran_uci_cfg_nr_t& cfg, const srsran_uci_value_nr_t& value);
// PDU processing
int handle_pdu(srsran::unique_byte_buffer_t pdu);
@ -103,6 +106,9 @@ private:
std::atomic<bool> started = {false};
const static uint32_t NUMEROLOGY_IDX = 0; /// only 15kHz supported at this stage
srsran::slot_point pdsch_slot, pusch_slot;
srsenb::sched_nr sched;
srsenb::sched_interface::cell_cfg_t cfg = {};
// Map of active UEs

@ -26,7 +26,7 @@
#include "sched_nr_interface.h"
#include "sched_nr_ue.h"
#include "srsran/adt/pool/cached_alloc.h"
#include "srsran/common/tti_point.h"
#include "srsran/common/slot_point.h"
#include <array>
extern "C" {
#include "srsran/config.h"
@ -50,15 +50,17 @@ public:
int cell_cfg(srsran::const_span<cell_cfg_t> cell_list) override;
void ue_cfg(uint16_t rnti, const ue_cfg_t& cfg) override;
int dl_rach_info(uint32_t cc, const dl_sched_rar_info_t& rar_info);
void dl_ack_info(uint16_t rnti, uint32_t cc, uint32_t pid, uint32_t tb_idx, bool ack) override;
void ul_crc_info(uint16_t rnti, uint32_t cc, uint32_t pid, bool crc) override;
void ul_sr_info(tti_point tti_rx, uint16_t rnti) override;
void ul_sr_info(slot_point slot_rx, uint16_t rnti) override;
int get_dl_sched(tti_point pdsch_tti, uint32_t cc, dl_sched_t& result) override;
int get_ul_sched(tti_point pusch_tti, uint32_t cc, ul_sched_t& result) override;
int get_dl_sched(slot_point pdsch_tti, uint32_t cc, dl_sched_t& result) override;
int get_ul_sched(slot_point pusch_tti, uint32_t cc, ul_sched_t& result) override;
private:
int generate_slot_result(tti_point pdcch_tti, uint32_t cc);
int generate_slot_result(slot_point pdcch_tti, uint32_t cc);
void ue_cfg_impl(uint16_t rnti, const ue_cfg_t& cfg);
// args

@ -33,7 +33,7 @@ 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;
slot_point prach_slot;
srsran::bounded_vector<dl_sched_rar_info_t, sched_interface::MAX_RAR_LIST> msg3_grant;
};

@ -45,6 +45,15 @@ 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;
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);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct bwp_params {
const uint32_t bwp_id;
const uint32_t cc;
@ -65,6 +74,8 @@ struct bwp_params {
std::vector<pusch_ra_time_cfg> pusch_ra_list;
bwp_params(const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_, uint32_t cc, uint32_t bwp_id);
bwp_cce_pos_list rar_cce_list;
};
struct sched_cell_params {
@ -88,17 +99,9 @@ struct sched_params {
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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;

@ -24,7 +24,7 @@
#include "sched_nr_cfg.h"
#include "srsenb/hdr/stack/mac/nr/harq_softbuffer.h"
#include "srsran/common/tti_point.h"
#include "srsran/common/slot_point.h"
#include <array>
namespace srsenb {
@ -40,29 +40,37 @@ public:
return std::all_of(tb.begin(), tb.end(), [](const tb_t& t) { return not t.active; });
}
bool empty(uint32_t tb_idx) const { return not tb[tb_idx].active; }
bool has_pending_retx(tti_point tti_rx) const { return not empty() and not tb[0].ack_state and tti_ack <= tti_rx; }
bool has_pending_retx(slot_point slot_rx) const
{
return not empty() and not tb[0].ack_state and slot_ack <= slot_rx;
}
uint32_t nof_retx() const { return tb[0].n_rtx; }
uint32_t max_nof_retx() const { return max_retx; }
uint32_t tbs() const { return tb[0].tbs; }
uint32_t ndi() const { return tb[0].ndi; }
uint32_t mcs() const { return tb[0].mcs; }
const prb_grant& prbs() const { return prbs_; }
tti_point harq_tti_ack() const { return tti_ack; }
slot_point harq_slot_ack() const { return slot_ack; }
bool ack_info(uint32_t tb_idx, bool ack);
void new_tti(tti_point tti_rx);
void new_slot(slot_point slot_rx);
void reset();
bool
new_tx(tti_point tti_tx, tti_point tti_ack, const prb_grant& grant, uint32_t mcs, uint32_t tbs, uint32_t max_retx);
bool new_retx(tti_point tti_tx, tti_point tti_ack, const prb_grant& grant);
bool new_retx(tti_point tti_tx, tti_point tti_ack);
bool new_tx(slot_point slot_tx,
slot_point slot_ack,
const prb_grant& grant,
uint32_t mcs,
uint32_t tbs,
uint32_t max_retx);
bool new_retx(slot_point slot_tx, slot_point slot_ack, const prb_grant& grant);
bool new_retx(slot_point slot_tx, slot_point slot_ack);
const uint32_t pid;
protected:
// NOTE: Has to be used before first tx is dispatched
bool set_tbs(uint32_t tbs);
const uint32_t pid;
private:
struct tb_t {
bool active = false;
@ -74,8 +82,8 @@ private:
};
uint32_t max_retx = 1;
tti_point tti_tx;
tti_point tti_ack;
slot_point slot_tx;
slot_point slot_ack;
prb_grant prbs_;
std::array<tb_t, SCHED_NR_MAX_TB> tb;
};
@ -83,20 +91,22 @@ private:
class dl_harq_proc : public harq_proc
{
public:
dl_harq_proc(uint32_t id_, uint32_t nprb) :
harq_proc(id_), softbuffer(harq_softbuffer_pool::get_instance().get_tx(nprb))
{}
dl_harq_proc(uint32_t id_, uint32_t nprb);
tx_harq_softbuffer& get_softbuffer() { return *softbuffer; }
srsran::unique_byte_buffer_t* get_tx_pdu() { return &pdu; }
// clear and reset softbuffer and PDU for new tx
bool set_tbs(uint32_t tbs)
{
softbuffer->reset();
pdu->clear();
return harq_proc::set_tbs(tbs);
}
private:
srsran::unique_pool_ptr<tx_harq_softbuffer> softbuffer;
srsran::unique_byte_buffer_t pdu;
};
class ul_harq_proc : public harq_proc
@ -122,18 +132,18 @@ class harq_entity
{
public:
explicit harq_entity(uint32_t nprb, uint32_t nof_harq_procs = SCHED_NR_MAX_HARQ);
void new_tti(tti_point tti_rx_);
void new_slot(slot_point slot_rx_);
void dl_ack_info(uint32_t pid, uint32_t tb_idx, bool ack) { dl_harqs[pid].ack_info(tb_idx, ack); }
void ul_crc_info(uint32_t pid, bool ack) { ul_harqs[pid].ack_info(0, ack); }
dl_harq_proc* find_pending_dl_retx()
{
return find_dl([this](const dl_harq_proc& h) { return h.has_pending_retx(tti_rx); });
return find_dl([this](const dl_harq_proc& h) { return h.has_pending_retx(slot_rx); });
}
ul_harq_proc* find_pending_ul_retx()
{
return find_ul([this](const ul_harq_proc& h) { return h.has_pending_retx(tti_rx); });
return find_ul([this](const ul_harq_proc& h) { return h.has_pending_retx(slot_rx); });
}
dl_harq_proc* find_empty_dl_harq()
{
@ -158,7 +168,7 @@ private:
return (it == ul_harqs.end()) ? nullptr : &(*it);
}
tti_point tti_rx;
slot_point slot_rx;
std::vector<dl_harq_proc> dl_harqs;
std::vector<ul_harq_proc> ul_harqs;
};

@ -27,11 +27,13 @@
namespace srsenb {
namespace sched_nr_impl {
class slot_ue;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool fill_dci_rar(prb_interval interv, const bwp_params& bwp_cfg, srsran_dci_dl_nr_t& dci);
bool fill_dci_rar(prb_interval interv, uint16_t ra_rnti, const bwp_params& bwp_cfg, srsran_dci_dl_nr_t& dci);
class slot_ue;
bool fill_dci_msg3(const slot_ue& ue, const bwp_params& bwp_cfg, srsran_dci_ul_nr_t& dci);
/// Generate PDCCH DL DCI fields
void fill_dl_dci_ue_fields(const slot_ue& ue,

@ -27,7 +27,7 @@
#include "srsran/adt/optional.h"
#include "srsran/adt/span.h"
#include "srsran/common/phy_cfg_nr.h"
#include "srsran/common/tti_point.h"
#include "srsran/common/slot_point.h"
#include "srsran/interfaces/gnb_interfaces.h"
#include "srsran/phy/phch/dci_nr.h"
@ -72,7 +72,10 @@ public:
srsran::bounded_vector<bwp_cfg_t, SCHED_NR_MAX_BWP_PER_CELL> bwps{1}; // idx0 for BWP-common
};
struct sched_cfg_t {};
struct sched_cfg_t {
bool pdsch_enabled = true;
bool pusch_enabled = true;
};
struct ue_cc_cfg_t {
bool active = false;
@ -90,11 +93,13 @@ public:
////// RACH //////
struct dl_sched_rar_info_t {
uint32_t preamble_idx;
uint32_t ta_cmd;
uint16_t temp_crnti;
uint32_t msg3_size;
uint32_t prach_tti;
uint32_t preamble_idx;
uint32_t ofdm_symbol_idx;
uint32_t freq_idx;
uint32_t ta_cmd;
uint16_t temp_crnti;
uint32_t msg3_size;
slot_point prach_slot;
};
///// Sched Result /////
@ -105,12 +110,12 @@ public:
virtual ~sched_nr_interface() = default;
virtual int cell_cfg(srsran::const_span<sched_nr_interface::cell_cfg_t> ue_cfg) = 0;
virtual void ue_cfg(uint16_t rnti, const ue_cfg_t& ue_cfg) = 0;
virtual int get_dl_sched(tti_point tti_rx, uint32_t cc, dl_sched_t& result) = 0;
virtual int get_ul_sched(tti_point tti_rx, uint32_t cc, ul_sched_t& result) = 0;
virtual int get_dl_sched(slot_point slot_rx, uint32_t cc, dl_sched_t& result) = 0;
virtual int get_ul_sched(slot_point slot_rx, uint32_t cc, ul_sched_t& result) = 0;
virtual void dl_ack_info(uint16_t rnti, uint32_t cc, uint32_t pid, uint32_t tb_idx, bool ack) = 0;
virtual void ul_crc_info(uint16_t rnti, uint32_t cc, uint32_t pid, bool crc) = 0;
virtual void ul_sr_info(tti_point, uint16_t rnti) = 0;
virtual void ul_sr_info(slot_point, uint16_t rnti) = 0;
};
} // namespace srsenb

@ -75,6 +75,7 @@ private:
uint32_t coreset_id;
uint32_t slot_idx;
uint32_t nof_freq_res = 0;
const bwp_cce_pos_list& rar_cce_list;
// List of PDCCH grants
struct alloc_record {

@ -68,7 +68,7 @@ struct prb_grant {
alloc.rbgs.~rbg_bitmap();
new (&alloc.interv) prb_interval(prbs);
} else {
alloc.interv = alloc.interv;
alloc.interv = prbs;
}
return *this;
}

@ -70,8 +70,8 @@ struct bwp_slot_grid {
struct bwp_res_grid {
bwp_res_grid(const bwp_params& bwp_cfg_);
bwp_slot_grid& operator[](tti_point tti) { return slots[tti.to_uint() % slots.capacity()]; };
const bwp_slot_grid& operator[](tti_point tti) const { return slots[tti.to_uint() % slots.capacity()]; };
bwp_slot_grid& operator[](slot_point tti) { return slots[tti.to_uint() % slots.capacity()]; };
const bwp_slot_grid& operator[](slot_point tti) const { return slots[tti.to_uint() % slots.capacity()]; };
uint32_t id() const { return cfg->bwp_id; }
uint32_t nof_prbs() const { return cfg->cfg.rb_width; }
@ -90,7 +90,7 @@ class bwp_slot_allocator
public:
explicit bwp_slot_allocator(bwp_res_grid& bwp_grid_);
void new_slot(tti_point pdcch_tti_) { pdcch_tti = pdcch_tti_; }
void new_slot(slot_point pdcch_slot_) { pdcch_slot = pdcch_slot_; }
alloc_result alloc_rar_and_msg3(uint32_t aggr_idx,
const pending_rar_t& rar,
@ -98,9 +98,9 @@ public:
slot_ue_map_t& ues,
uint32_t max_nof_grants);
alloc_result alloc_pdsch(slot_ue& ue, const prb_grant& dl_grant);
alloc_result alloc_pusch(slot_ue& ue, const rbgmask_t& dl_mask);
alloc_result alloc_pusch(slot_ue& ue, const prb_grant& dl_mask);
tti_point get_pdcch_tti() const { return pdcch_tti; }
slot_point get_pdcch_tti() const { return pdcch_slot; }
const bwp_res_grid& res_grid() const { return bwp_grid; }
const bwp_params& cfg;
@ -111,7 +111,7 @@ private:
srslog::basic_logger& logger;
bwp_res_grid& bwp_grid;
tti_point pdcch_tti;
slot_point pdcch_slot;
};
} // namespace sched_nr_impl

@ -39,27 +39,27 @@ class slot_ue
{
public:
slot_ue() = default;
explicit slot_ue(uint16_t rnti_, tti_point tti_rx_, uint32_t cc);
explicit slot_ue(uint16_t rnti_, slot_point slot_rx_, uint32_t cc);
slot_ue(slot_ue&&) noexcept = default;
slot_ue& operator=(slot_ue&&) noexcept = default;
bool empty() const { return rnti == SCHED_NR_INVALID_RNTI; }
void release() { rnti = SCHED_NR_INVALID_RNTI; }
uint16_t rnti = SCHED_NR_INVALID_RNTI;
tti_point tti_rx;
uint32_t cc = SCHED_NR_MAX_CARRIERS;
uint16_t rnti = SCHED_NR_INVALID_RNTI;
slot_point slot_rx;
uint32_t cc = SCHED_NR_MAX_CARRIERS;
// UE parameters common to all sectors
bool pending_sr;
bool pending_sr = false;
// UE parameters that are sector specific
const bwp_ue_cfg* cfg = nullptr;
tti_point pdcch_tti;
tti_point pdsch_tti;
tti_point pusch_tti;
tti_point uci_tti;
uint32_t dl_cqi;
uint32_t ul_cqi;
slot_point pdcch_slot;
slot_point pdsch_slot;
slot_point pusch_slot;
slot_point uci_slot;
uint32_t dl_cqi = 0;
uint32_t ul_cqi = 0;
dl_harq_proc* h_dl = nullptr;
ul_harq_proc* h_ul = nullptr;
};
@ -68,8 +68,8 @@ class ue_carrier
{
public:
ue_carrier(uint16_t rnti, const ue_cfg_t& cfg, const sched_cell_params& cell_params_);
void new_tti(tti_point pdcch_tti, const ue_cfg_t& uecfg_);
slot_ue try_reserve(tti_point pdcch_tti);
void new_slot(slot_point pdcch_slot, const ue_cfg_t& uecfg_);
slot_ue try_reserve(slot_point pdcch_slot);
const uint16_t rnti;
const uint32_t cc;
@ -90,12 +90,12 @@ class ue
public:
ue(uint16_t rnti, const ue_cfg_t& cfg, const sched_params& sched_cfg_);
slot_ue try_reserve(tti_point pdcch_tti, uint32_t cc);
slot_ue try_reserve(slot_point pdcch_slot, uint32_t cc);
void set_cfg(const ue_cfg_t& cfg);
const ue_cfg_t& cfg() const { return ue_cfg; }
void ul_sr_info(tti_point tti_rx) { pending_sr = true; }
void ul_sr_info(slot_point slot_rx) { pending_sr = true; }
bool has_ca() const { return ue_cfg.carriers.size() > 1; }
uint32_t pcell_cc() const { return ue_cfg.carriers[0].cc; }

@ -46,10 +46,12 @@ public:
explicit slot_cc_worker(serv_cell_manager& sched);
void start(tti_point pdcch_tti, ue_map_t& ue_db_);
void start(slot_point pdcch_slot, ue_map_t& ue_db_);
void run();
void finish();
bool running() const { return tti_rx.is_valid(); }
bool running() const { return slot_rx.valid(); }
void enqueue_cc_event(srsran::move_callback<void()> ev);
/// Enqueue feedback directed at a given UE in a given cell
void enqueue_cc_feedback(uint16_t rnti, feedback_callback_t fdbk);
@ -66,7 +68,7 @@ private:
serv_cell_manager& cell;
srslog::basic_logger& logger;
tti_point tti_rx;
slot_point slot_rx;
bwp_slot_allocator bwp_alloc;
// Process of UE cell-specific feedback
@ -74,8 +76,9 @@ private:
uint16_t rnti;
feedback_callback_t fdbk;
};
std::mutex feedback_mutex;
srsran::deque<feedback_t> pending_feedback, tmp_feedback_to_run;
std::mutex feedback_mutex;
srsran::deque<feedback_t> pending_feedback, tmp_feedback_to_run;
srsran::deque<srsran::move_callback<void()> > pending_events, tmp_events_to_run;
srsran::static_circular_map<uint16_t, slot_ue, SCHED_NR_MAX_USERS> slot_ues;
};
@ -85,7 +88,7 @@ class sched_worker_manager
struct slot_worker_ctxt {
std::mutex slot_mutex; // lock of all workers of the same slot.
std::condition_variable cvar;
tti_point tti_rx;
slot_point slot_rx;
int nof_workers_waiting = 0;
std::atomic<int> worker_count{0}; // variable shared across slot_cc_workers
std::vector<slot_cc_worker> workers;
@ -99,16 +102,17 @@ public:
sched_worker_manager(sched_worker_manager&&) = delete;
~sched_worker_manager();
void run_slot(tti_point tti_tx, uint32_t cc, dl_sched_t& dl_res, ul_sched_t& ul_res);
void run_slot(slot_point slot_tx, uint32_t cc, dl_sched_t& dl_res, ul_sched_t& ul_res);
void enqueue_event(uint16_t rnti, srsran::move_callback<void()> ev);
void enqueue_cc_event(uint32_t cc, srsran::move_callback<void()> ev);
void enqueue_cc_feedback(uint16_t rnti, uint32_t cc, slot_cc_worker::feedback_callback_t fdbk)
{
cc_worker_list[cc]->worker.enqueue_cc_feedback(rnti, std::move(fdbk));
}
private:
bool save_sched_result(tti_point pdcch_tti, uint32_t cc, dl_sched_t& dl_res, ul_sched_t& ul_res);
bool save_sched_result(slot_point pdcch_slot, uint32_t cc, dl_sched_t& dl_res, ul_sched_t& ul_res);
const sched_params& cfg;
ue_map_t& ue_db;
@ -125,7 +129,7 @@ private:
std::vector<std::unique_ptr<slot_worker_ctxt> > slot_worker_ctxts;
struct cc_context {
std::condition_variable cvar;
bool waiting = false;
int waiting = 0;
slot_cc_worker worker;
cc_context(serv_cell_manager& sched) : worker(sched) {}
@ -133,7 +137,7 @@ private:
std::mutex slot_mutex;
std::condition_variable cvar;
tti_point current_tti;
slot_point current_slot;
std::atomic<int> worker_count{0}; // variable shared across slot_cc_workers
std::vector<std::unique_ptr<cc_context> > cc_worker_list;
};

@ -57,13 +57,8 @@ public:
void set_active(bool active) { active_state.store(active, std::memory_order_relaxed); }
bool is_active() const { return active_state.load(std::memory_order_relaxed); }
uint8_t* generate_pdu(uint32_t enb_cc_idx,
uint32_t harq_pid,
uint32_t tb_idx,
const sched_interface::dl_sched_pdu_t pdu[sched_interface::MAX_RLC_PDU_LIST],
uint32_t nof_pdu_elems,
uint32_t grant_size);
int process_pdu(srsran::unique_byte_buffer_t pdu);
int generate_pdu(srsran::byte_buffer_t* pdu, uint32_t grant_size);
int process_pdu(srsran::unique_byte_buffer_t pdu);
std::mutex metrics_mutex = {};
void metrics_read(mac_ue_metrics_t* metrics_);

@ -177,7 +177,7 @@ public:
/// Get CQI of given subband index
int get_subband_cqi(uint32_t subband_index) const
{
if (subband_cqi_enabled()) {
if (not subband_cqi_enabled()) {
return get_wb_cqi_info();
}
return bp_list[get_bp_index(subband_index)].last_feedback_tti.is_valid() ? subband_cqi[subband_index] : wb_cqi_avg;
@ -235,14 +235,11 @@ private:
srsran::bounded_vector<float, max_nof_subbands> subband_cqi;
};
/// Get {RBG index, CQI} tuple which correspond to the set RBG with the lowest CQI
std::tuple<uint32_t, int> find_min_cqi_rbg(const rbgmask_t& mask, const sched_dl_cqi& dl_cqi);
/// Returns the same RBG mask, but with the RBGs of the subband with the lowest CQI reset
rbgmask_t remove_min_cqi_subband(const rbgmask_t& rbgmask, const sched_dl_cqi& dl_cqi);
/// Get {RBG indexs, CQI} tuple which correspond to the set RBG with the lowest CQI
rbgmask_t find_min_cqi_rbgs(const rbgmask_t& mask, const sched_dl_cqi& dl_cqi, int& cqi);
/// Returns the same RBG mask, but with the RBG with the lowest CQI reset
rbgmask_t remove_min_cqi_rbg(const rbgmask_t& rbgmask, const sched_dl_cqi& dl_cqi);
/// Returns the same RBG mask, but with the RBGs with the lowest CQI reset
rbgmask_t remove_min_cqi_rbgs(const rbgmask_t& rbgmask, const sched_dl_cqi& dl_cqi);
} // namespace srsenb

@ -46,9 +46,11 @@ namespace srsenb {
class ngap : public ngap_interface_rrc_nr
{
public:
class ue;
ngap(srsran::task_sched_handle task_sched_,
srslog::basic_logger& logger,
srsran::socket_manager_itf* rx_socket_handler);
~ngap();
int init(const ngap_args_t& args_, rrc_interface_ngap_nr* rrc_);
void stop();
@ -63,7 +65,7 @@ public:
srsran::unique_byte_buffer_t pdu,
uint32_t s_tmsi);
void write_pdu(uint16_t rnti, srsran::unique_byte_buffer_t pdu){};
void write_pdu(uint16_t rnti, srsran::unique_byte_buffer_t pdu);
bool user_exists(uint16_t rnti) { return true; };
void user_mod(uint16_t old_rnti, uint16_t new_rnti){};
bool user_release(uint16_t rnti, asn1::ngap_nr::cause_radio_network_e cause_radio) { return true; };
@ -72,6 +74,8 @@ public:
srsran::optional<uint32_t> ran_ue_ngap_id = {},
srsran::optional<uint32_t> amf_ue_ngap_id = {});
void ue_ctxt_setup_complete(uint16_t rnti);
void ue_notify_rrc_reconf_complete(uint16_t rnti, bool outcome);
bool send_pdu_session_resource_setup_response();
// Stack interface
bool
@ -88,7 +92,7 @@ private:
static const int NONUE_STREAM_ID = 0;
// args
rrc_interface_ngap_nr* rrc = nullptr;
rrc_interface_ngap_nr* rrc = nullptr;
ngap_args_t args = {};
srslog::basic_logger& logger;
srsran::task_sched_handle task_sched;
@ -107,22 +111,6 @@ private:
asn1::ngap_nr::tai_s tai;
asn1::ngap_nr::nr_cgi_s nr_cgi;
// Moved into NGAP class to avoid redifinition (Introduce new namespace?)
struct ue_ctxt_t {
static const uint32_t invalid_gnb_id = std::numeric_limits<uint32_t>::max();
uint16_t rnti = SRSRAN_INVALID_RNTI;
uint32_t ran_ue_ngap_id = invalid_gnb_id;
srsran::optional<uint32_t> amf_ue_ngap_id;
uint32_t gnb_cc_idx = 0;
struct timeval init_timestamp = {};
// AMF identifier
uint16_t amf_set_id;
uint8_t amf_pointer;
uint8_t amf_region_id;
};
asn1::ngap_nr::ng_setup_resp_s ngsetupresponse;
int build_tai_cgi();
@ -131,34 +119,23 @@ private:
bool sctp_send_ngap_pdu(const asn1::ngap_nr::ngap_pdu_c& tx_pdu, uint32_t rnti, const char* procedure_name);
bool handle_ngap_rx_pdu(srsran::byte_buffer_t* pdu);
bool handle_successfuloutcome(const asn1::ngap_nr::successful_outcome_s& msg);
bool handle_unsuccessfuloutcome(const asn1::ngap_nr::unsuccessful_outcome_s& msg);
bool handle_initiatingmessage(const asn1::ngap_nr::init_msg_s& msg);
bool handle_dlnastransport(const asn1::ngap_nr::dl_nas_transport_s& msg);
bool handle_ngsetupresponse(const asn1::ngap_nr::ng_setup_resp_s& msg);
bool handle_ngsetupfailure(const asn1::ngap_nr::ng_setup_fail_s& msg);
bool handle_initialctxtsetuprequest(const asn1::ngap_nr::init_context_setup_request_s& msg);
struct ue {
explicit ue(ngap* ngap_ptr_);
bool send_initialuemessage(asn1::ngap_nr::rrcestablishment_cause_e cause,
srsran::unique_byte_buffer_t pdu,
bool has_tmsi,
uint32_t s_tmsi = 0);
bool send_ulnastransport(srsran::unique_byte_buffer_t pdu);
bool was_uectxtrelease_requested() const { return release_requested; }
void ue_ctxt_setup_complete();
ue_ctxt_t ctxt = {};
uint16_t stream_id = 1;
bool handle_successful_outcome(const asn1::ngap_nr::successful_outcome_s& msg);
bool handle_unsuccessful_outcome(const asn1::ngap_nr::unsuccessful_outcome_s& msg);
bool handle_initiating_message(const asn1::ngap_nr::init_msg_s& msg);
// TS 38.413 - Section 8.6.2 - Downlink NAS Transport
bool handle_dl_nas_transport(const asn1::ngap_nr::dl_nas_transport_s& msg);
// TS 38.413 - Section 9.2.6.2 - NG Setup Response
bool handle_ng_setup_response(const asn1::ngap_nr::ng_setup_resp_s& msg);
// TS 38.413 - Section 9.2.6.3 - NG Setup Failure
bool handle_ng_setup_failure(const asn1::ngap_nr::ng_setup_fail_s& msg);
// TS 38.413 - Section 9.2.2.5 - UE Context Release Command
bool handle_ue_ctxt_release_cmd(const asn1::ngap_nr::ue_context_release_cmd_s& msg);
// TS 38.413 - Section 9.2.2.1 - Initial Context Setup Request
bool handle_initial_ctxt_setup_request(const asn1::ngap_nr::init_context_setup_request_s& msg);
// TS 38.413 - Section 9.2.1.1 - PDU Session Resource Setup Request
bool handle_pdu_session_resource_setup_request(const asn1::ngap_nr::pdu_session_res_setup_request_s& msg);
private:
// args
ngap* ngap_ptr;
// state
bool release_requested = false;
};
class user_list
{
public:

@ -0,0 +1,24 @@
/**
*
* \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 SRSENB_NGAP_INTERFACES_H
#define SRSENB_NGAP_INTERFACES_H
namespace srsenb {
class ngap_interface_ngap_proc
{
public:
virtual bool send_initial_ctxt_setup_response() = 0;
};
} // namespace srsenb
#endif

@ -0,0 +1,66 @@
/**
*
* \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 SRSENB_NGAP_UE_H
#define SRSENB_NGAP_UE_H
#include "ngap.h"
#include "ngap_ue_proc.h"
#include "ngap_ue_utils.h"
#include "srsran/asn1/asn1_utils.h"
#include "srsran/asn1/ngap.h"
namespace srsenb {
class ngap::ue : public ngap_interface_ngap_proc
{
public:
explicit ue(ngap* ngap_ptr_, rrc_interface_ngap_nr* rrc_ptr_, srslog::basic_logger& logger_);
virtual ~ue();
// TS 38.413 - Section 9.2.5.1 - Initial UE Message
bool send_initial_ue_message(asn1::ngap_nr::rrcestablishment_cause_e cause,
srsran::unique_byte_buffer_t pdu,
bool has_tmsi,
uint32_t s_tmsi = 0);
// TS 38.413 - Section 9.2.5.3 - Uplink NAS Transport
bool send_ul_nas_transport(srsran::unique_byte_buffer_t pdu);
// TS 38.413 - Section 9.2.2.2 - Initial Context Setup Response
bool send_initial_ctxt_setup_response();
// TS 38.413 - Section 9.2.2.3 - Initial Context Setup Failure
bool send_initial_ctxt_setup_failure(asn1::ngap_nr::cause_c cause);
// TS 38.413 - Section 9.2.2.1 - Initial Context Setup Request
bool handle_initial_ctxt_setup_request(const asn1::ngap_nr::init_context_setup_request_s& msg);
// TS 38.413 - Section 9.2.2.5 - UE Context Release Command
bool handle_ue_ctxt_release_cmd(const asn1::ngap_nr::ue_context_release_cmd_s& msg);
bool was_uectxtrelease_requested() const { return release_requested; }
void ue_ctxt_setup_complete();
void notify_rrc_reconf_complete(const bool reconf_complete_outcome);
srsran::proc_t<ngap_ue_initial_context_setup_proc> initial_context_setup_proc;
srsran::proc_t<ngap_ue_ue_context_release_proc> ue_context_release_proc;
ngap_ue_ctxt_t ctxt = {};
uint16_t stream_id = 1;
private:
// args
ngap* ngap_ptr;
rrc_interface_ngap_nr* rrc_ptr;
// state
bool release_requested = false;
// logger
srslog::basic_logger& logger;
};
} // namespace srsenb
#endif

@ -0,0 +1,84 @@
/**
*
* \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 SRSENB_NGAP_UE_PROC_H
#define SRSENB_NGAP_UE_PROC_H
#include "ngap_interfaces.h"
#include "ngap_ue_utils.h"
#include "srsran/asn1/asn1_utils.h"
#include "srsran/asn1/ngap.h"
#include "srsran/common/buffer_pool.h"
#include "srsran/common/stack_procedure.h"
#include "srsran/interfaces/gnb_rrc_nr_interfaces.h"
#include <map>
#include <string>
namespace srsenb {
// TS 38.413 - Section 8.3 - UE Context Management Procedures
// TS 38.413 - Section 8.3.1 - Initial Context Setup
class ngap_ue_initial_context_setup_proc
{
public:
explicit ngap_ue_initial_context_setup_proc(ngap_interface_ngap_proc* parent_,
rrc_interface_ngap_nr* rrc_,
ngap_ue_ctxt_t* ue_ctxt);
srsran::proc_outcome_t init(const asn1::ngap_nr::init_context_setup_request_s& msg);
srsran::proc_outcome_t react(const bool security_mode_command_outcome);
srsran::proc_outcome_t step();
static const char* name() { return "Initial Context Setup"; }
private:
ngap_ue_ctxt_t* ue_ctxt;
ngap_interface_ngap_proc* parent = nullptr;
rrc_interface_ngap_nr* rrc = nullptr;
srslog::basic_logger& logger;
};
// TS 38.413 - Section 8.3.2 - UE Context Release Request (NG-RAN node initiated)
class ngap_ue_ue_context_release_proc
{
public:
explicit ngap_ue_ue_context_release_proc(ngap_interface_ngap_proc* parent_,
rrc_interface_ngap_nr* rrc_,
ngap_ue_ctxt_t* ue_ctxt);
srsran::proc_outcome_t init(const asn1::ngap_nr::ue_context_release_cmd_s& msg);
srsran::proc_outcome_t step();
static const char* name() { return "UE Context Release"; }
private:
ngap_ue_ctxt_t* ue_ctxt;
ngap_interface_ngap_proc* parent = nullptr;
rrc_interface_ngap_nr* rrc = nullptr;
srslog::basic_logger& logger;
};
// TS 38.413 - Section 8.3.4 - UE Context Modification
class ngap_ue_ue_context_modification_proc
{
public:
explicit ngap_ue_ue_context_modification_proc(ngap_interface_ngap_proc* parent_);
srsran::proc_outcome_t init();
srsran::proc_outcome_t step();
static const char* name() { return "UE Context Modification"; }
private:
ngap_interface_ngap_proc* parent;
};
} // namespace srsenb
#endif

@ -0,0 +1,38 @@
/**
*
* \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 SRSENB_NGAP_UE_UTILS_H
#define SRSENB_NGAP_UE_UTILS_H
#include "srsenb/hdr/common/common_enb.h"
#include "srsran/adt/optional.h"
#include "srsran/common/common.h"
#include "srsran/phy/common/phy_common.h"
namespace srsenb {
struct ngap_ue_ctxt_t {
static const uint32_t invalid_gnb_id = std::numeric_limits<uint32_t>::max();
uint16_t rnti = SRSRAN_INVALID_RNTI;
uint32_t ran_ue_ngap_id = invalid_gnb_id;
srsran::optional<uint32_t> amf_ue_ngap_id;
uint32_t gnb_cc_idx = 0;
struct timeval init_timestamp = {};
// AMF identifier
uint16_t amf_set_id;
uint8_t amf_pointer;
uint8_t amf_region_id;
};
} // namespace srsenb
#endif

@ -52,12 +52,13 @@ public:
rrc_endc(srsenb::rrc::ue* outer_ue);
bool fill_conn_recfg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn_recfg);
void handle_ue_capabilities(const asn1::rrc::ue_eutra_cap_s& eutra_caps);
void handle_eutra_capabilities(const asn1::rrc::ue_eutra_cap_s& eutra_caps);
void handle_ue_meas_report(const asn1::rrc::meas_report_s& msg);
void handle_sgnb_addition_ack(const asn1::dyn_octstring& nr_secondary_cell_group_cfg_r15,
const asn1::dyn_octstring& nr_radio_bearer_cfg1_r15);
void handle_sgnb_addition_reject();
void handle_sgnb_addition_complete();
bool is_endc_supported();
private:
// Send SgNB addition request to gNB

@ -33,6 +33,7 @@ typedef enum {
RRC_STATE_WAIT_FOR_CON_REEST_COMPLETE,
RRC_STATE_WAIT_FOR_SECURITY_MODE_COMPLETE,
RRC_STATE_WAIT_FOR_UE_CAP_INFO,
RRC_STATE_WAIT_FOR_UE_CAP_INFO_ENDC, /* only entered for UEs with NSA support */
RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE,
RRC_STATE_REESTABLISHMENT_COMPLETE,
RRC_STATE_REGISTERED,

@ -71,6 +71,7 @@ public:
rrc_nr_cfg_t update_default_cfg(const rrc_nr_cfg_t& rrc_cfg);
int add_user(uint16_t rnti);
int update_user(uint16_t new_rnti, uint16_t old_rnti);
void config_phy();
void config_mac();
int32_t generate_sibs();
int read_pdu_bcch_bch(const uint32_t tti, srsran::unique_byte_buffer_t& buffer) final;
@ -91,6 +92,13 @@ public:
int sgnb_addition_request(uint16_t rnti);
int sgnb_reconfiguration_complete(uint16_t rnti, asn1::dyn_octstring reconfig_response);
// Interfaces for NGAP
int ue_set_security_cfg_key(uint16_t rnti, const asn1::fixed_bitstring<256, false, true>& key);
int ue_set_bitrates(uint16_t rnti, const asn1::ngap_nr::ue_aggregate_maximum_bit_rate_s& rates);
int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap_nr::ue_security_cap_s& caps);
int start_security_mode_procedure(uint16_t rnti);
void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu);
class ue
{
public:

@ -25,6 +25,7 @@
#include "mac_controller.h"
#include "rrc.h"
#include "srsran/adt/pool/batch_mem_pool.h"
#include "srsran/asn1/rrc/uecap.h"
#include "srsran/interfaces/enb_phy_interfaces.h"
#include "srsran/interfaces/pdcp_interface_types.h"
@ -86,7 +87,7 @@ public:
bool phy_cfg_updated = true,
srsran::const_byte_span nas_pdu = {});
void send_security_mode_command();
void send_ue_cap_enquiry();
void send_ue_cap_enquiry(const std::vector<asn1::rrc::rat_type_opts::options>& rats);
void send_ue_info_req();
void parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu);
@ -113,7 +114,7 @@ public:
void handle_rrc_reconf_complete(asn1::rrc::rrc_conn_recfg_complete_s* msg, srsran::unique_byte_buffer_t pdu);
void handle_security_mode_complete(asn1::rrc::security_mode_complete_s* msg);
void handle_security_mode_failure(asn1::rrc::security_mode_fail_s* msg);
bool handle_ue_cap_info(asn1::rrc::ue_cap_info_s* msg);
int handle_ue_cap_info(asn1::rrc::ue_cap_info_s* msg);
void handle_ue_init_ctxt_setup_req(const asn1::s1ap::init_context_setup_request_s& msg);
bool handle_ue_ctxt_mod_req(const asn1::s1ap::ue_context_mod_request_s& msg);
void handle_ue_info_resp(const asn1::rrc::ue_info_resp_r9_s& msg, srsran::unique_byte_buffer_t pdu);

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save