mirror of https://github.com/PentHertz/srsLTE.git
Merge branch 'next' into agpl_next
This commit is contained in:
commit
bfa1215b89
|
@ -339,7 +339,7 @@ int main(int argc, char** argv)
|
|||
parse_args(&prog_args, argc, argv);
|
||||
|
||||
#if HAVE_PCAP
|
||||
FILE* pcap_file = LTE_PCAP_Open(MAC_LTE_DLT, "/tmp/npdsch.pcap");
|
||||
FILE* pcap_file = DLT_PCAP_Open(MAC_LTE_DLT, "/tmp/npdsch.pcap");
|
||||
#endif
|
||||
|
||||
sigset_t sigset;
|
||||
|
@ -865,7 +865,7 @@ int main(int argc, char** argv)
|
|||
|
||||
#if HAVE_PCAP
|
||||
printf("Saving PCAP file\n");
|
||||
LTE_PCAP_Close(pcap_file);
|
||||
DLT_PCAP_Close(pcap_file);
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_RF
|
||||
|
|
|
@ -232,7 +232,7 @@ int main(int argc, char** argv)
|
|||
|
||||
parse_args(&prog_args, argc, argv);
|
||||
|
||||
FILE* pcap_file = LTE_PCAP_Open(MAC_LTE_DLT, PCAP_FILENAME);
|
||||
FILE* pcap_file = DLT_PCAP_Open(MAC_LTE_DLT, PCAP_FILENAME);
|
||||
|
||||
srsran_use_standard_symbol_size(prog_args.use_standard_lte_rates);
|
||||
|
||||
|
@ -546,7 +546,7 @@ clean_exit:
|
|||
|
||||
if (pcap_file != NULL) {
|
||||
printf("Saving PCAP file to %s\n", PCAP_FILENAME);
|
||||
LTE_PCAP_Close(pcap_file);
|
||||
DLT_PCAP_Close(pcap_file);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_GUI
|
||||
|
|
|
@ -39,6 +39,11 @@ struct rolling_average {
|
|||
}
|
||||
T value() const { return count_ == 0 ? 0 : avg_; }
|
||||
uint32_t count() const { return count_; }
|
||||
void reset()
|
||||
{
|
||||
avg_ = 0;
|
||||
count_ = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
T avg_ = 0;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#ifndef SRSRAN_NAS_PCAP_H
|
||||
#define SRSRAN_NAS_PCAP_H
|
||||
|
||||
#include "srsran/common/common.h"
|
||||
#include "srsran/common/pcap.h"
|
||||
#include <string>
|
||||
|
||||
|
@ -37,7 +38,7 @@ public:
|
|||
pcap_file = NULL;
|
||||
}
|
||||
void enable();
|
||||
uint32_t open(std::string filename_, uint32_t ue_id = 0);
|
||||
uint32_t open(std::string filename_, uint32_t ue_id = 0, srsran_rat_t rat_type = srsran_rat_t::lte);
|
||||
void close();
|
||||
void write_nas(uint8_t* pdu, uint32_t pdu_len_bytes);
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ protected:
|
|||
|
||||
namespace net_utils {
|
||||
|
||||
bool sctp_init_client(unique_socket* socket, net_utils::socket_type socktype, const char* bind_addr_str);
|
||||
bool sctp_init_client(unique_socket* socket, net_utils::socket_type socktype, const char* bind_addr_str, int bind_port);
|
||||
bool sctp_init_server(unique_socket* socket, net_utils::socket_type socktype, const char* bind_addr_str, int port);
|
||||
|
||||
} // namespace net_utils
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#define NAS_LTE_DLT 148
|
||||
#define UDP_DLT 149 // UDP needs to be selected as protocol
|
||||
#define S1AP_LTE_DLT 150
|
||||
#define NAS_5G_DLT 151
|
||||
|
||||
/* This structure gets written to the start of the file */
|
||||
typedef struct pcap_hdr_s {
|
||||
|
@ -193,10 +194,10 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
/* Open the file and write file header */
|
||||
FILE* LTE_PCAP_Open(uint32_t DLT, const char* fileName);
|
||||
FILE* DLT_PCAP_Open(uint32_t DLT, const char* fileName);
|
||||
|
||||
/* Close the PCAP file */
|
||||
void LTE_PCAP_Close(FILE* fd);
|
||||
void DLT_PCAP_Close(FILE* fd);
|
||||
|
||||
/* Write an individual MAC PDU (PCAP packet header + mac-context + mac-pdu) */
|
||||
int LTE_PCAP_MAC_WritePDU(FILE* fd, MAC_Context_Info_t* context, const unsigned char* PDU, unsigned int length);
|
||||
|
|
|
@ -37,6 +37,7 @@ struct s1ap_args_t {
|
|||
std::string gtp_bind_addr;
|
||||
std::string gtp_advertise_addr;
|
||||
std::string s1c_bind_addr;
|
||||
uint16_t s1c_bind_port;
|
||||
std::string enb_name;
|
||||
};
|
||||
|
||||
|
|
|
@ -27,16 +27,16 @@
|
|||
namespace srsenb {
|
||||
|
||||
struct ngap_args_t {
|
||||
uint32_t gnb_id; // 20-bit id (lsb bits)
|
||||
uint8_t cell_id; // 8-bit cell id
|
||||
uint16_t tac; // 16-bit tac
|
||||
uint16_t mcc; // BCD-coded with 0xF filler
|
||||
uint16_t mnc; // BCD-coded with 0xF filler
|
||||
std::string amf_addr;
|
||||
std::string gtp_bind_addr;
|
||||
std::string gtp_advertise_addr;
|
||||
std::string ngc_bind_addr;
|
||||
std::string gnb_name;
|
||||
uint32_t gnb_id = 0; // 20-bit id (lsb bits)
|
||||
uint8_t cell_id = 0; // 8-bit cell id
|
||||
uint16_t tac = 0; // 16-bit tac
|
||||
uint16_t mcc = 0; // BCD-coded with 0xF filler
|
||||
uint16_t mnc = 0; // BCD-coded with 0xF filler
|
||||
std::string amf_addr = "";
|
||||
std::string gtp_bind_addr = "";
|
||||
std::string gtp_advertise_addr = "";
|
||||
std::string ngc_bind_addr = "";
|
||||
std::string gnb_name = "";
|
||||
};
|
||||
|
||||
// NGAP interface for RRC
|
||||
|
|
|
@ -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 SRSRAN_PHY_COMMON_INTERFACE_H
|
||||
#define SRSRAN_PHY_COMMON_INTERFACE_H
|
||||
|
||||
#include "../radio/rf_buffer.h"
|
||||
#include "../radio/rf_timestamp.h"
|
||||
|
||||
namespace srsran {
|
||||
|
||||
class phy_common_interface
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Common PHY interface for workers to indicate they ended
|
||||
* @param h Worker pointer used as unique identifier for synchronising Tx
|
||||
* @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;
|
||||
};
|
||||
|
||||
} // namespace srsran
|
||||
|
||||
#endif // SRSRAN_PHY_COMMON_INTERFACE_H
|
|
@ -610,6 +610,14 @@ SRSRAN_API uint32_t srsran_csi_meas_info(const srsran_csi_trs_measurements_t* me
|
|||
*/
|
||||
SRSRAN_API srsran_subcarrier_spacing_t srsran_subcarrier_spacing_from_str(const char* str);
|
||||
|
||||
/**
|
||||
* @brief Combine Channel State Information from Tracking Reference Signals (CSI-TRS)
|
||||
* @param[in] a CSI-RS measurement
|
||||
* @param[in] b CSI-RS measurement
|
||||
* @param[out] dst Destination of the combined
|
||||
*/
|
||||
SRSRAN_API void srsran_combine_csi_trs_measurements(const srsran_csi_trs_measurements_t *a, const srsran_csi_trs_measurements_t *b, srsran_csi_trs_measurements_t *dst);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -73,10 +73,10 @@ typedef struct SRSRAN_API {
|
|||
srsran_ssb_patern_t pattern; ///< SSB pattern as defined in TS 38.313 section 4.1 Cell search
|
||||
srsran_duplex_mode_t duplex_mode; ///< Set to true if the spectrum is paired (FDD)
|
||||
uint32_t periodicity_ms; ///< SSB periodicity in ms
|
||||
float beta_pss; ////< PSS power allocation
|
||||
float beta_sss; ////< SSS power allocation
|
||||
float beta_pbch; ////< PBCH power allocation
|
||||
float beta_pbch_dmrs; ////< PBCH DMRS power allocation
|
||||
float beta_pss; ///< PSS power allocation
|
||||
float beta_sss; ///< SSS power allocation
|
||||
float beta_pbch; ///< PBCH power allocation
|
||||
float beta_pbch_dmrs; ///< PBCH DMRS power allocation
|
||||
} srsran_ssb_cfg_t;
|
||||
|
||||
/**
|
||||
|
@ -88,11 +88,15 @@ typedef struct SRSRAN_API {
|
|||
|
||||
/// Sampling rate dependent parameters
|
||||
float scs_hz; ///< Subcarrier spacing in Hz
|
||||
uint32_t max_sf_sz; ///< Maximum subframe size at the specified sampling rate
|
||||
uint32_t max_symbol_sz; ///< Maximum symbol size given the minimum supported SCS and sampling rate
|
||||
uint32_t max_corr_sz; ///< Maximum correlation size
|
||||
uint32_t max_ssb_sz; ///< Maximum SSB size in samples at the configured sampling rate
|
||||
uint32_t sf_sz; ///< Current subframe size at the specified sampling rate
|
||||
uint32_t symbol_sz; ///< Current SSB symbol size (for the given base-band sampling rate)
|
||||
uint32_t corr_sz; ///< Correlation size
|
||||
uint32_t corr_window; ///< Correlation window length
|
||||
uint32_t ssb_sz; ///< SSB size in samples at the configured sampling rate
|
||||
int32_t f_offset; ///< Current SSB integer frequency offset (multiple of SCS)
|
||||
uint32_t cp_sz; ///< CP length for the given symbol size
|
||||
|
||||
|
@ -111,6 +115,7 @@ typedef struct SRSRAN_API {
|
|||
cf_t* tmp_freq; ///< Temporal frequency domain buffer
|
||||
cf_t* tmp_time; ///< Temporal time domain buffer
|
||||
cf_t* tmp_corr; ///< Temporal correlation frequency domain buffer
|
||||
cf_t* sf_buffer; ///< subframe buffer
|
||||
cf_t* pss_seq[SRSRAN_NOF_NID_2_NR]; ///< Possible frequency domain PSS for find
|
||||
} srsran_ssb_t;
|
||||
|
||||
|
@ -193,7 +198,7 @@ srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg,
|
|||
/**
|
||||
* @brief Perform cell search and measurement
|
||||
* @note This function assumes the SSB transmission is aligned with the input base-band signal
|
||||
* @param q NR PSS object
|
||||
* @param q SSB object
|
||||
* @param in Base-band signal buffer
|
||||
* @param N_id Physical Cell Identifier of the most suitable cell identifier
|
||||
* @param meas SSB-based CSI measurement of the most suitable cell identifier
|
||||
|
@ -207,7 +212,7 @@ SRSRAN_API int srsran_ssb_csi_search(srsran_ssb_t* q,
|
|||
|
||||
/**
|
||||
* @brief Perform Channel State Information (CSI) measurement from the SSB
|
||||
* @param q NR PSS object
|
||||
* @param q SSB object
|
||||
* @param N_id Physical Cell Identifier
|
||||
* @param ssb_idx SSB candidate index
|
||||
* @param in Base-band signal
|
||||
|
@ -220,4 +225,55 @@ SRSRAN_API int srsran_ssb_csi_measure(srsran_ssb_t* q,
|
|||
const cf_t* in,
|
||||
srsran_csi_trs_measurements_t* meas);
|
||||
|
||||
/**
|
||||
* @brief Find SSB signal in a given time domain subframe buffer
|
||||
* @param q SSB object
|
||||
* @param sf_buffer subframe buffer with 1ms worth of samples
|
||||
* @param N_id Physical cell identifier to find
|
||||
* @param meas Measurements performed on the found peak
|
||||
* @param pbch_msg PBCH decoded message
|
||||
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
|
||||
*/
|
||||
SRSRAN_API int srsran_ssb_find(srsran_ssb_t* q,
|
||||
const cf_t* sf_buffer,
|
||||
uint32_t N_id,
|
||||
srsran_csi_trs_measurements_t* meas,
|
||||
srsran_pbch_msg_nr_t* pbch_msg);
|
||||
|
||||
/**
|
||||
* @brief Track SSB by performing measurements and decoding PBCH
|
||||
* @param q SSB object
|
||||
* @param sf_buffer subframe buffer with 1ms worth of samples
|
||||
* @param N_id Physical cell identifier to find
|
||||
* @param ssb_idx SSB candidate index
|
||||
* @param n_hf Number of half frame
|
||||
* @param meas Measurements perform
|
||||
* @param pbch_msg PBCH decoded message
|
||||
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
|
||||
*/
|
||||
SRSRAN_API int srsran_ssb_track(srsran_ssb_t* q,
|
||||
const cf_t* sf_buffer,
|
||||
uint32_t N_id,
|
||||
uint32_t ssb_idx,
|
||||
uint32_t n_hf,
|
||||
srsran_csi_trs_measurements_t* meas,
|
||||
srsran_pbch_msg_nr_t* pbch_msg);
|
||||
|
||||
/**
|
||||
* @brief Calculates the subframe index within the radio frame of a given SSB candidate for the SSB object
|
||||
* @param q SSB object
|
||||
* @param ssb_idx SSB candidate index
|
||||
* @param half_frame Indicates whether it is half frame
|
||||
* @return The subframe index
|
||||
*/
|
||||
SRSRAN_API uint32_t srsran_ssb_candidate_sf_idx(const srsran_ssb_t* q, uint32_t ssb_idx, bool half_frame);
|
||||
|
||||
/**
|
||||
* @brief Calculates the SSB offset within the subframe of a given SSB candidate for the SSB object
|
||||
* @param q SSB object
|
||||
* @param ssb_idx SSB candidate index
|
||||
* @return The sample offset within the subframe
|
||||
*/
|
||||
SRSRAN_API uint32_t srsran_ssb_candidate_sf_offset(const srsran_ssb_t* q, uint32_t ssb_idx);
|
||||
|
||||
#endif // SRSRAN_SSB_H
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/**
|
||||
*
|
||||
* \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_UE_SYNC_NR_H
|
||||
#define SRSRAN_UE_SYNC_NR_H
|
||||
|
||||
#include "srsran/phy/common/timestamp.h"
|
||||
#include "srsran/phy/sync/ssb.h"
|
||||
|
||||
#define SRSRAN_RECV_CALLBACK_TEMPLATE(NAME) int (*NAME)(void*, cf_t**, uint32_t, srsran_timestamp_t*)
|
||||
|
||||
/**
|
||||
* @brief Describes NR UE synchronization object internal states
|
||||
*/
|
||||
typedef enum SRSRAN_API {
|
||||
SRSRAN_UE_SYNC_NR_STATE_IDLE = 0, ///< Initial state, the object has no configuration
|
||||
SRSRAN_UE_SYNC_NR_STATE_FIND, ///< State just after configuring, baseband is not aligned
|
||||
SRSRAN_UE_SYNC_NR_STATE_TRACK ///< Baseband is aligned with subframes
|
||||
} srsran_ue_sync_nr_state_t;
|
||||
|
||||
/**
|
||||
* @brief Describes a UE sync NR object arguments
|
||||
*/
|
||||
typedef struct SRSRAN_API {
|
||||
// Memory allocation constraints
|
||||
double max_srate_hz; ///< Maximum sampling rate in Hz, set to zero to use default
|
||||
srsran_subcarrier_spacing_t min_scs; ///< Minimum subcarrier spacing
|
||||
uint32_t nof_rx_channels; ///< Number of receive channels, set to 0 for 1
|
||||
|
||||
// Enable/Disable features
|
||||
bool disable_cfo; ///< Set to true for disabling the CFO compensation close loop
|
||||
|
||||
// Signal detection thresholds and averaging coefficients
|
||||
float pbch_dmrs_thr; ///< NR-PBCH DMRS threshold for blind decoding, set to 0 for default
|
||||
float cfo_alpha; ///< Exponential Moving Average (EMA) alpha coefficient for CFO
|
||||
|
||||
// Receive callback
|
||||
void* recv_obj; ///< Receive object
|
||||
SRSRAN_RECV_CALLBACK_TEMPLATE(recv_callback); ///< Receive callback
|
||||
} srsran_ue_sync_nr_args_t;
|
||||
|
||||
/**
|
||||
* @brief Describes a UE sync NR object configuration
|
||||
*/
|
||||
typedef struct SRSRAN_API {
|
||||
srsran_ssb_cfg_t ssb; ///< SSB configuration
|
||||
uint32_t N_id; ///< Physicall cell identifier
|
||||
} srsran_ue_sync_nr_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Describes a UE sync NR object
|
||||
*/
|
||||
typedef struct SRSRAN_API {
|
||||
// State
|
||||
srsran_ue_sync_nr_state_t state; ///< Internal state
|
||||
int32_t next_rf_sample_offset; ///< Next RF sample offset
|
||||
uint32_t ssb_idx; ///< Tracking SSB candidate index
|
||||
uint32_t sf_idx; ///< Current subframe index (0-9)
|
||||
uint32_t sfn; ///< Current system frame number (0-1023)
|
||||
srsran_csi_trs_measurements_t feedback; ///< Feedback measurements
|
||||
|
||||
// Components
|
||||
srsran_ssb_t ssb; ///< SSB internal object
|
||||
cf_t** tmp_buffer; ///< Temporal buffer pointers
|
||||
|
||||
// Initialised arguments
|
||||
uint32_t nof_rx_channels; ///< Number of receive channels
|
||||
bool disable_cfo; ///< Set to true for disabling the CFO compensation close loop
|
||||
float cfo_alpha; ///< Exponential Moving Average (EMA) alpha coefficient for CFO
|
||||
void* recv_obj; ///< Receive object
|
||||
SRSRAN_RECV_CALLBACK_TEMPLATE(recv_callback); ///< Receive callback
|
||||
|
||||
// Current configuration
|
||||
uint32_t N_id; ///< Current physical cell identifier
|
||||
double srate_hz; ///< Current sampling rate in Hz
|
||||
uint32_t sf_sz; ///< Current subframe size
|
||||
|
||||
// Metrics
|
||||
float cfo_hz; ///< Current CFO in Hz
|
||||
float avg_delay_us; ///< Current average delay
|
||||
} srsran_ue_sync_nr_t;
|
||||
|
||||
/**
|
||||
* @brief Describes a UE sync NR zerocopy outcome
|
||||
*/
|
||||
typedef struct SRSRAN_API {
|
||||
bool in_sync; ///< Indicates whether the received baseband is synchronized
|
||||
uint32_t sf_idx; ///< Subframe index
|
||||
uint32_t sfn; ///< System Frame Number
|
||||
srsran_timestamp_t timestamp; ///< Last received timestamp
|
||||
float cfo_hz; ///< Current CFO in Hz
|
||||
float delay_us; ///< Current average delay in microseconds
|
||||
} srsran_ue_sync_nr_outcome_t;
|
||||
|
||||
/**
|
||||
* @brief Initialises a UE sync NR object
|
||||
* @param q NR UE synchronization object
|
||||
* @param[in] args NR UE synchronization initialization arguments
|
||||
* @return SRSRAN_SUCCESS if no error occurs, SRSRAN_ERROR code otherwise
|
||||
*/
|
||||
SRSRAN_API int srsran_ue_sync_nr_init(srsran_ue_sync_nr_t* q, const srsran_ue_sync_nr_args_t* args);
|
||||
|
||||
/**
|
||||
* @brief Deallocate an NR UE synchronization object
|
||||
* @param q NR UE synchronization object
|
||||
*/
|
||||
SRSRAN_API void srsran_ue_sync_nr_free(srsran_ue_sync_nr_t* q);
|
||||
|
||||
/**
|
||||
* @brief Configures a UE sync NR object
|
||||
* @param q NR UE synchronization object
|
||||
* @param[in] cfg NR UE synchronization configuration
|
||||
* @return SRSRAN_SUCCESS if no error occurs, SRSRAN_ERROR code otherwise
|
||||
*/
|
||||
SRSRAN_API int srsran_ue_sync_nr_set_cfg(srsran_ue_sync_nr_t* q, const srsran_ue_sync_nr_cfg_t* cfg);
|
||||
|
||||
/**
|
||||
* @brief Runs the NR UE synchronization object, tries to find and track the configured SSB leaving in buffer the
|
||||
* received baseband subframe
|
||||
* @param q NR UE synchronization object
|
||||
* @param buffer 2D complex buffer
|
||||
* @param outcome zerocopy outcome
|
||||
* @return SRSRAN_SUCCESS if no error occurs, SRSRAN_ERROR code otherwise
|
||||
*/
|
||||
SRSRAN_API int srsran_ue_sync_nr_zerocopy(srsran_ue_sync_nr_t* q, cf_t** buffer, srsran_ue_sync_nr_outcome_t* outcome);
|
||||
|
||||
/**
|
||||
* @brief Feedback Channel State Information from Tracking Reference Signals into a UE synchronization object
|
||||
* @param q NR UE synchronization object
|
||||
* @param measurements CSI-TRS feedback measurement
|
||||
* @return SRSRAN_SUCCESS if no error occurs, SRSRAN_ERROR code otherwise
|
||||
*/
|
||||
SRSRAN_API int srsran_ue_sync_nr_feedback(srsran_ue_sync_nr_t* q, const srsran_csi_trs_measurements_t* measurements);
|
||||
|
||||
#endif // SRSRAN_UE_SYNC_NR_H
|
|
@ -41,7 +41,7 @@ uint32_t mac_pcap::open(std::string filename_, uint32_t ue_id_)
|
|||
|
||||
// set DLT for selected RAT
|
||||
dlt = UDP_DLT;
|
||||
pcap_file = LTE_PCAP_Open(dlt, filename_.c_str());
|
||||
pcap_file = DLT_PCAP_Open(dlt, filename_.c_str());
|
||||
if (pcap_file == nullptr) {
|
||||
logger.error("Couldn't open %s to write PCAP", filename_.c_str());
|
||||
return SRSRAN_ERROR;
|
||||
|
@ -77,7 +77,7 @@ uint32_t mac_pcap::close()
|
|||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
srsran::console("Saving MAC PCAP (DLT=%d) to %s\n", dlt, filename.c_str());
|
||||
LTE_PCAP_Close(pcap_file);
|
||||
DLT_PCAP_Close(pcap_file);
|
||||
pcap_file = nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,10 +31,14 @@ void nas_pcap::enable()
|
|||
enable_write = true;
|
||||
}
|
||||
|
||||
uint32_t nas_pcap::open(std::string filename_, uint32_t ue_id_)
|
||||
uint32_t nas_pcap::open(std::string filename_, uint32_t ue_id_, srsran_rat_t rat_type)
|
||||
{
|
||||
filename = filename_;
|
||||
pcap_file = LTE_PCAP_Open(NAS_LTE_DLT, filename.c_str());
|
||||
if (rat_type == srsran_rat_t::nr) {
|
||||
pcap_file = DLT_PCAP_Open(NAS_5G_DLT, filename.c_str());
|
||||
} else {
|
||||
pcap_file = DLT_PCAP_Open(NAS_LTE_DLT, filename.c_str());
|
||||
}
|
||||
if (pcap_file == nullptr) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
@ -46,7 +50,7 @@ uint32_t nas_pcap::open(std::string filename_, uint32_t ue_id_)
|
|||
void nas_pcap::close()
|
||||
{
|
||||
fprintf(stdout, "Saving NAS PCAP file (DLT=%d) to %s \n", NAS_LTE_DLT, filename.c_str());
|
||||
LTE_PCAP_Close(pcap_file);
|
||||
DLT_PCAP_Close(pcap_file);
|
||||
}
|
||||
|
||||
void nas_pcap::write_nas(uint8_t* pdu, uint32_t pdu_len_bytes)
|
||||
|
|
|
@ -318,9 +318,9 @@ bool sctp_init_socket(unique_socket* socket, net_utils::socket_type socktype, co
|
|||
return true;
|
||||
}
|
||||
|
||||
bool sctp_init_client(unique_socket* socket, net_utils::socket_type socktype, const char* bind_addr_str)
|
||||
bool sctp_init_client(unique_socket* socket, net_utils::socket_type socktype, const char* bind_addr_str, int bind_port)
|
||||
{
|
||||
return sctp_init_socket(socket, socktype, bind_addr_str, 0);
|
||||
return sctp_init_socket(socket, socktype, bind_addr_str, bind_port);
|
||||
}
|
||||
|
||||
bool sctp_init_server(unique_socket* socket, net_utils::socket_type socktype, const char* bind_addr_str, int port)
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include <sys/time.h>
|
||||
|
||||
/* Open the file and write file header */
|
||||
FILE* LTE_PCAP_Open(uint32_t DLT, const char* fileName)
|
||||
FILE* DLT_PCAP_Open(uint32_t DLT, const char* fileName)
|
||||
{
|
||||
pcap_hdr_t file_header = {
|
||||
0xa1b2c3d4, /* magic number */
|
||||
|
@ -52,7 +52,7 @@ FILE* LTE_PCAP_Open(uint32_t DLT, const char* fileName)
|
|||
}
|
||||
|
||||
/* Close the PCAP file */
|
||||
void LTE_PCAP_Close(FILE* fd)
|
||||
void DLT_PCAP_Close(FILE* fd)
|
||||
{
|
||||
if (fd) {
|
||||
fclose(fd);
|
||||
|
|
|
@ -34,7 +34,7 @@ void rlc_pcap::enable(bool en)
|
|||
void rlc_pcap::open(const char* filename, rlc_config_t config)
|
||||
{
|
||||
fprintf(stdout, "Opening RLC PCAP with DLT=%d\n", UDP_DLT);
|
||||
pcap_file = LTE_PCAP_Open(UDP_DLT, filename);
|
||||
pcap_file = DLT_PCAP_Open(UDP_DLT, filename);
|
||||
enable_write = true;
|
||||
|
||||
if (config.rlc_mode == rlc_mode_t::am) {
|
||||
|
@ -54,7 +54,7 @@ void rlc_pcap::open(const char* filename, rlc_config_t config)
|
|||
void rlc_pcap::close()
|
||||
{
|
||||
fprintf(stdout, "Saving RLC PCAP file\n");
|
||||
LTE_PCAP_Close(pcap_file);
|
||||
DLT_PCAP_Close(pcap_file);
|
||||
}
|
||||
|
||||
void rlc_pcap::set_ue_id(uint16_t ue_id_)
|
||||
|
|
|
@ -32,13 +32,13 @@ void s1ap_pcap::enable()
|
|||
}
|
||||
void s1ap_pcap::open(const char* filename)
|
||||
{
|
||||
pcap_file = LTE_PCAP_Open(S1AP_LTE_DLT, filename);
|
||||
pcap_file = DLT_PCAP_Open(S1AP_LTE_DLT, filename);
|
||||
enable_write = true;
|
||||
}
|
||||
void s1ap_pcap::close()
|
||||
{
|
||||
fprintf(stdout, "Saving S1AP PCAP file\n");
|
||||
LTE_PCAP_Close(pcap_file);
|
||||
DLT_PCAP_Close(pcap_file);
|
||||
}
|
||||
|
||||
void s1ap_pcap::write_s1ap(uint8_t* pdu, uint32_t pdu_len_bytes)
|
||||
|
|
|
@ -59,7 +59,7 @@ void pdu_queue::deallocate(const uint8_t* pdu)
|
|||
}
|
||||
|
||||
/* Demultiplexing of logical channels and dissassemble of MAC CE
|
||||
* This function enqueues the packet and returns quicly because ACK
|
||||
* This function enqueues the packet and returns quickly because ACK
|
||||
* deadline is important here.
|
||||
*/
|
||||
void pdu_queue::push(const uint8_t* ptr, uint32_t len, channel_t channel, int grant_nof_prbs)
|
||||
|
|
|
@ -360,3 +360,33 @@ srsran_subcarrier_spacing_t srsran_subcarrier_spacing_from_str(const char* str)
|
|||
|
||||
return srsran_subcarrier_spacing_invalid;
|
||||
}
|
||||
|
||||
void srsran_combine_csi_trs_measurements(const srsran_csi_trs_measurements_t* a,
|
||||
const srsran_csi_trs_measurements_t* b,
|
||||
srsran_csi_trs_measurements_t* dst)
|
||||
{
|
||||
// Verify inputs
|
||||
if (a == NULL || b == NULL || dst == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Protect from zero division
|
||||
uint32_t nof_re_sum = a->nof_re + b->nof_re;
|
||||
if (nof_re_sum == 0) {
|
||||
SRSRAN_MEM_ZERO(dst, srsran_csi_trs_measurements_t, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform proportional average
|
||||
dst->rsrp = SRSRAN_VEC_PMA(a->rsrp, a->nof_re, b->rsrp, b->nof_re);
|
||||
dst->rsrp_dB = SRSRAN_VEC_PMA(a->rsrp_dB, a->nof_re, b->rsrp_dB, b->nof_re);
|
||||
dst->epre = SRSRAN_VEC_PMA(a->epre, a->nof_re, b->epre, b->nof_re);
|
||||
dst->epre_dB = SRSRAN_VEC_PMA(a->epre_dB, a->nof_re, b->epre_dB, b->nof_re);
|
||||
dst->n0 = SRSRAN_VEC_PMA(a->n0, a->nof_re, b->n0, b->nof_re);
|
||||
dst->n0_dB = SRSRAN_VEC_PMA(a->n0_dB, a->nof_re, b->n0_dB, b->nof_re);
|
||||
dst->snr_dB = SRSRAN_VEC_PMA(a->snr_dB, a->nof_re, b->snr_dB, b->nof_re);
|
||||
dst->cfo_hz = SRSRAN_VEC_PMA(a->cfo_hz, a->nof_re, b->cfo_hz, b->nof_re);
|
||||
dst->cfo_hz_max = SRSRAN_MAX(a->cfo_hz_max, b->cfo_hz_max);
|
||||
dst->delay_us = SRSRAN_VEC_PMA(a->delay_us, a->nof_re, b->delay_us, b->nof_re);
|
||||
dst->nof_re = nof_re_sum;
|
||||
}
|
||||
|
|
|
@ -65,6 +65,13 @@ static int ssb_init_corr(srsran_ssb_t* q)
|
|||
}
|
||||
}
|
||||
|
||||
q->sf_buffer = srsran_vec_cf_malloc(q->max_ssb_sz + q->max_sf_sz);
|
||||
if (q->sf_buffer == NULL) {
|
||||
ERROR("Malloc");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
srsran_vec_cf_zero(q->sf_buffer, q->max_ssb_sz + q->max_sf_sz);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -102,8 +109,10 @@ int srsran_ssb_init(srsran_ssb_t* q, const srsran_ssb_args_t* args)
|
|||
q->args.pbch_dmrs_thr = (!isnormal(q->args.pbch_dmrs_thr)) ? SSB_PBCH_DMRS_DEFAULT_CORR_THR : q->args.pbch_dmrs_thr;
|
||||
|
||||
q->scs_hz = (float)SRSRAN_SUBC_SPACING_NR(q->args.min_scs);
|
||||
q->max_sf_sz = (uint32_t)round(1e-3 * q->args.max_srate_hz);
|
||||
q->max_symbol_sz = (uint32_t)round(q->args.max_srate_hz / q->scs_hz);
|
||||
q->max_corr_sz = SSB_CORR_SZ(q->max_symbol_sz);
|
||||
q->max_ssb_sz = SRSRAN_SSB_DURATION_NSYMB * (q->max_symbol_sz + (144 * q->max_symbol_sz) / 2048);
|
||||
|
||||
// Allocate temporal data
|
||||
q->tmp_time = srsran_vec_cf_malloc(q->max_corr_sz);
|
||||
|
@ -152,6 +161,10 @@ void srsran_ssb_free(srsran_ssb_t* q)
|
|||
}
|
||||
}
|
||||
|
||||
if (q->sf_buffer != NULL) {
|
||||
free(q->sf_buffer);
|
||||
}
|
||||
|
||||
srsran_dft_plan_free(&q->ifft);
|
||||
srsran_dft_plan_free(&q->fft);
|
||||
srsran_dft_plan_free(&q->fft_corr);
|
||||
|
@ -446,6 +459,7 @@ int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg)
|
|||
// Verify symbol size
|
||||
if (q->max_symbol_sz < symbol_sz) {
|
||||
ERROR("New symbol size (%d) exceeds maximum symbol size (%d)", symbol_sz, q->max_symbol_sz);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Replan iFFT
|
||||
|
@ -477,6 +491,8 @@ int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg)
|
|||
// Finally, copy configuration
|
||||
q->cfg = *cfg;
|
||||
q->symbol_sz = symbol_sz;
|
||||
q->sf_sz = (uint32_t)round(1e-3 * cfg->srate_hz);
|
||||
q->ssb_sz = SRSRAN_SSB_DURATION_NSYMB * (q->symbol_sz + q->cp_sz);
|
||||
|
||||
// Initialise correlation
|
||||
if (ssb_setup_corr(q) < SRSRAN_SUCCESS) {
|
||||
|
@ -1102,3 +1118,213 @@ int srsran_ssb_search(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, srs
|
|||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
static int ssb_pss_find(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, uint32_t N_id_2, uint32_t* found_delay)
|
||||
{
|
||||
// verify it is initialised
|
||||
if (q->corr_sz == 0) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Correlation best sequence
|
||||
float best_corr = 0;
|
||||
uint32_t best_delay = 0;
|
||||
|
||||
// Delay in correlation window
|
||||
uint32_t t_offset = 0;
|
||||
while ((t_offset + q->symbol_sz) < nof_samples) {
|
||||
// Number of samples taken in this iteration
|
||||
uint32_t n = q->corr_sz;
|
||||
|
||||
// Detect if the correlation input exceeds the input length, take the maximum amount of samples
|
||||
if (t_offset + q->corr_sz > nof_samples) {
|
||||
n = nof_samples - t_offset;
|
||||
}
|
||||
|
||||
// Copy the amount of samples
|
||||
srsran_vec_cf_copy(q->tmp_time, &in[t_offset], n);
|
||||
|
||||
// Append zeros if there is space left
|
||||
if (n < q->corr_sz) {
|
||||
srsran_vec_cf_zero(&q->tmp_time[n], q->corr_sz - n);
|
||||
}
|
||||
|
||||
// Convert to frequency domain
|
||||
srsran_dft_run_guru_c(&q->fft_corr);
|
||||
|
||||
// Actual correlation in frequency domain
|
||||
srsran_vec_prod_conj_ccc(q->tmp_freq, q->pss_seq[N_id_2], q->tmp_corr, q->corr_sz);
|
||||
|
||||
// Convert to time domain
|
||||
srsran_dft_run_guru_c(&q->ifft_corr);
|
||||
|
||||
// Find maximum
|
||||
uint32_t peak_idx = srsran_vec_max_abs_ci(q->tmp_time, q->corr_window);
|
||||
|
||||
// Average power, skip window if value is invalid (0.0, nan or inf)
|
||||
float avg_pwr_corr = srsran_vec_avg_power_cf(&q->tmp_time[peak_idx], q->symbol_sz);
|
||||
if (!isnormal(avg_pwr_corr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Normalise correlation
|
||||
float corr = SRSRAN_CSQABS(q->tmp_time[peak_idx]) / avg_pwr_corr / sqrtf(SRSRAN_PSS_NR_LEN);
|
||||
|
||||
// Update if the correlation is better than the current best
|
||||
if (best_corr < corr) {
|
||||
best_corr = corr;
|
||||
best_delay = peak_idx + t_offset;
|
||||
}
|
||||
|
||||
// Advance time
|
||||
t_offset += q->corr_window;
|
||||
}
|
||||
|
||||
// Save findings
|
||||
*found_delay = best_delay;
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int srsran_ssb_find(srsran_ssb_t* q,
|
||||
const cf_t* sf_buffer,
|
||||
uint32_t N_id,
|
||||
srsran_csi_trs_measurements_t* meas,
|
||||
srsran_pbch_msg_nr_t* pbch_msg)
|
||||
{
|
||||
// Verify inputs
|
||||
if (q == NULL || sf_buffer == NULL || meas == NULL || !isnormal(q->scs_hz)) {
|
||||
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
|
||||
if (!q->args.enable_search) {
|
||||
ERROR("SSB is not configured for search");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Copy tail from previous execution into the start of this
|
||||
srsran_vec_cf_copy(q->sf_buffer, &q->sf_buffer[q->sf_sz], q->ssb_sz);
|
||||
|
||||
// Append new samples
|
||||
srsran_vec_cf_copy(&q->sf_buffer[q->ssb_sz], sf_buffer, q->sf_sz);
|
||||
|
||||
// Search for PSS in time domain
|
||||
uint32_t t_offset = 0;
|
||||
if (ssb_pss_find(q, q->sf_buffer, q->sf_sz + q->ssb_sz, SRSRAN_NID_2_NR(N_id), &t_offset) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error searching for N_id_2");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Remove CP offset prior demodulation
|
||||
if (t_offset >= q->cp_sz) {
|
||||
t_offset -= q->cp_sz;
|
||||
} else {
|
||||
t_offset = 0;
|
||||
}
|
||||
|
||||
// Demodulate
|
||||
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
|
||||
if (ssb_demodulate(q, q->sf_buffer, t_offset, ssb_grid) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error demodulating");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Measure selected N_id
|
||||
if (ssb_measure(q, ssb_grid, N_id, meas)) {
|
||||
ERROR("Error measuring");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Select the most suitable SSB candidate
|
||||
uint32_t n_hf = 0;
|
||||
uint32_t ssb_idx = 0; // SSB candidate index
|
||||
if (ssb_select_pbch(q, N_id, ssb_grid, &n_hf, &ssb_idx) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error selecting PBCH");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Calculate the SSB offset in the subframe
|
||||
uint32_t ssb_offset = srsran_ssb_candidate_sf_offset(q, ssb_idx);
|
||||
|
||||
// Compute PBCH channel estimates
|
||||
if (ssb_decode_pbch(q, N_id, n_hf, ssb_idx, ssb_grid, pbch_msg) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error decoding PBCH");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// SSB delay in SF
|
||||
float ssb_delay_us = (float)(1e6 * (((double)t_offset - (double)q->ssb_sz - (double)ssb_offset) / q->cfg.srate_hz));
|
||||
|
||||
// Add delay to measure
|
||||
meas->delay_us += ssb_delay_us;
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int srsran_ssb_track(srsran_ssb_t* q,
|
||||
const cf_t* sf_buffer,
|
||||
uint32_t N_id,
|
||||
uint32_t ssb_idx,
|
||||
uint32_t n_hf,
|
||||
srsran_csi_trs_measurements_t* meas,
|
||||
srsran_pbch_msg_nr_t* pbch_msg)
|
||||
{
|
||||
// Verify inputs
|
||||
if (q == NULL || sf_buffer == NULL || meas == NULL || !isnormal(q->scs_hz)) {
|
||||
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
|
||||
if (!q->args.enable_search) {
|
||||
ERROR("SSB is not configured for search");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Calculate SSB offset
|
||||
uint32_t t_offset = srsran_ssb_candidate_sf_offset(q, ssb_idx);
|
||||
|
||||
// Demodulate
|
||||
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
|
||||
if (ssb_demodulate(q, sf_buffer, t_offset, ssb_grid) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error demodulating");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Measure selected N_id
|
||||
if (ssb_measure(q, ssb_grid, N_id, meas)) {
|
||||
ERROR("Error measuring");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Compute PBCH channel estimates
|
||||
if (ssb_decode_pbch(q, N_id, n_hf, ssb_idx, ssb_grid, pbch_msg) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error decoding PBCH");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t srsran_ssb_candidate_sf_idx(const srsran_ssb_t* q, uint32_t ssb_idx, bool half_frame)
|
||||
{
|
||||
if (q == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nof_symbols_subframe = SRSRAN_NSYMB_PER_SLOT_NR * SRSRAN_NSLOTS_PER_SF_NR(q->cfg.scs);
|
||||
|
||||
return q->l_first[ssb_idx] / nof_symbols_subframe + (half_frame ? (SRSRAN_NOF_SF_X_FRAME / 2) : 0);
|
||||
}
|
||||
|
||||
uint32_t srsran_ssb_candidate_sf_offset(const srsran_ssb_t* q, uint32_t ssb_idx)
|
||||
{
|
||||
if (q == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nof_symbols_subframe = SRSRAN_NSYMB_PER_SLOT_NR * SRSRAN_NSLOTS_PER_SF_NR(q->cfg.scs);
|
||||
uint32_t l = q->l_first[ssb_idx] % nof_symbols_subframe;
|
||||
|
||||
uint32_t cp_sz_0 = (16U * q->symbol_sz) / 2048U;
|
||||
|
||||
return cp_sz_0 + l * (q->symbol_sz + q->cp_sz);
|
||||
}
|
||||
|
|
|
@ -38,6 +38,10 @@ add_executable(ue_dl_nbiot_test ue_dl_nbiot_test.c)
|
|||
target_link_libraries(ue_dl_nbiot_test srsran_phy pthread)
|
||||
add_test(ue_dl_nbiot_test ue_dl_nbiot_test)
|
||||
|
||||
add_executable(ue_sync_nr_test ue_sync_nr_test.c)
|
||||
target_link_libraries(ue_sync_nr_test srsran_phy pthread)
|
||||
add_test(ue_sync_nr_test ue_sync_nr_test)
|
||||
|
||||
if(RF_FOUND)
|
||||
add_executable(ue_mib_sync_test_nbiot_usrp ue_mib_sync_test_nbiot_usrp.c)
|
||||
target_link_libraries(ue_mib_sync_test_nbiot_usrp srsran_phy srsran_rf pthread)
|
||||
|
|
|
@ -0,0 +1,304 @@
|
|||
/**
|
||||
*
|
||||
* \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/common/test_common.h"
|
||||
#include "srsran/phy/channel/ch_awgn.h"
|
||||
#include "srsran/phy/channel/delay.h"
|
||||
#include "srsran/phy/ue/ue_sync_nr.h"
|
||||
#include "srsran/phy/utils/debug.h"
|
||||
#include "srsran/phy/utils/ringbuffer.h"
|
||||
#include "srsran/phy/utils/vector.h"
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// NR parameters
|
||||
static uint32_t pci = 1; // Physical Cell Identifier
|
||||
static uint32_t carrier_nof_prb = 52; // Carrier bandwidth
|
||||
static srsran_subcarrier_spacing_t carrier_scs = srsran_subcarrier_spacing_15kHz;
|
||||
static srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_30kHz;
|
||||
|
||||
// Test and channel parameters
|
||||
static uint32_t nof_sf = 1000; // Number of subframes to test
|
||||
static float cfo_hz = 100.0f; // CFO in Hz
|
||||
static float n0_dB = -10.0f; // Noise floor in dB relative to full-scale
|
||||
static float delay_min_us = 10.0f; // Minimum dynamic delay in microseconds
|
||||
static float delay_max_us = 1000.0f; // Maximum dynamic delay in microseconds
|
||||
static float delay_period_s = 60.0f; // Delay period in seconds
|
||||
|
||||
// Test context
|
||||
static double srate_hz = 0.0f; // Base-band sampling rate
|
||||
static uint32_t sf_len = 0; // Subframe length
|
||||
static cf_t* buffer = NULL; // Base-band buffer
|
||||
static cf_t* buffer2 = NULL; // Base-band buffer
|
||||
|
||||
static void usage(char* prog)
|
||||
{
|
||||
printf("Usage: %s [v]\n", prog);
|
||||
printf("\t-v [set srsran_verbose to debug, default none]\n");
|
||||
}
|
||||
|
||||
static void parse_args(int argc, char** argv)
|
||||
{
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "v")) != -1) {
|
||||
switch (opt) {
|
||||
case 'v':
|
||||
srsran_verbose++;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint32_t sf_idx;
|
||||
uint32_t sfn;
|
||||
srsran_ringbuffer_t ringbuffer;
|
||||
srsran_ssb_t ssb;
|
||||
srsran_timestamp_t timestamp;
|
||||
srsran_channel_awgn_t awgn;
|
||||
srsran_channel_delay_t delay;
|
||||
} test_context_t;
|
||||
|
||||
static void run_channel(test_context_t* ctx)
|
||||
{
|
||||
// Delay
|
||||
srsran_channel_delay_execute(&ctx->delay, buffer, buffer2, sf_len, &ctx->timestamp);
|
||||
|
||||
// CFO
|
||||
srsran_vec_apply_cfo(buffer2, -cfo_hz / srate_hz, buffer, sf_len);
|
||||
|
||||
// AWGN
|
||||
srsran_channel_awgn_run_c(&ctx->awgn, buffer, buffer, sf_len);
|
||||
}
|
||||
|
||||
static int test_context_init(test_context_t* ctx)
|
||||
{
|
||||
SRSRAN_MEM_ZERO(ctx, test_context_t, 1);
|
||||
|
||||
if (ctx == NULL) {
|
||||
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
|
||||
ctx->sfn = 1;
|
||||
|
||||
if (srsran_ringbuffer_init(&ctx->ringbuffer, (int)(10 * sf_len * sizeof(cf_t))) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
srsran_ssb_args_t ssb_args = {};
|
||||
ssb_args.max_srate_hz = srate_hz;
|
||||
ssb_args.min_scs = carrier_scs;
|
||||
ssb_args.enable_encode = true;
|
||||
if (srsran_ssb_init(&ctx->ssb, &ssb_args) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
srsran_ssb_cfg_t ssb_cfg = {};
|
||||
ssb_cfg.srate_hz = srate_hz;
|
||||
ssb_cfg.srate_hz = srate_hz;
|
||||
ssb_cfg.center_freq_hz = 3.5e9;
|
||||
ssb_cfg.ssb_freq_hz = 3.5e9 - 960e3;
|
||||
ssb_cfg.scs = ssb_scs;
|
||||
ssb_cfg.pattern = SRSRAN_SSB_PATTERN_C;
|
||||
if (srsran_ssb_set_cfg(&ctx->ssb, &ssb_cfg) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (srsran_channel_delay_init(&ctx->delay, delay_min_us, delay_max_us, delay_period_s, 0, (uint32_t)srate_hz) <
|
||||
SRSRAN_SUCCESS) {
|
||||
ERROR("Init");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (srsran_channel_awgn_init(&ctx->awgn, 0x0) < SRSRAN_SUCCESS) {
|
||||
ERROR("Init");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
if (srsran_channel_awgn_set_n0(&ctx->awgn, n0_dB) < SRSRAN_SUCCESS) {
|
||||
ERROR("Init");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
static void test_context_free(test_context_t* ctx)
|
||||
{
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
srsran_ringbuffer_free(&ctx->ringbuffer);
|
||||
srsran_ssb_free(&ctx->ssb);
|
||||
srsran_channel_delay_free(&ctx->delay);
|
||||
srsran_channel_awgn_free(&ctx->awgn);
|
||||
}
|
||||
|
||||
static int recv_callback(void* ptr, cf_t** rx_buffer, uint32_t nof_samples, srsran_timestamp_t* timestamp)
|
||||
{
|
||||
test_context_t* ctx = (test_context_t*)ptr;
|
||||
|
||||
// Check inputs
|
||||
if (ctx == NULL || rx_buffer == NULL || rx_buffer[0] == NULL) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Calculate the number of required bytes
|
||||
int required_nbytes = (int)sizeof(cf_t) * nof_samples;
|
||||
|
||||
// Execute subframe until the ringbuffer has data
|
||||
while (srsran_ringbuffer_status(&ctx->ringbuffer) < required_nbytes) {
|
||||
// Reset buffer
|
||||
srsran_vec_cf_zero(buffer, sf_len);
|
||||
|
||||
if (ctx->sf_idx % (SRSRAN_NOF_SF_X_FRAME / 2) == 0) {
|
||||
// Prepare PBCH message
|
||||
srsran_pbch_msg_nr_t pbch_msg = {};
|
||||
pbch_msg.ssb_idx = 0;
|
||||
pbch_msg.hrf = ctx->sf_idx >= (SRSRAN_NOF_SF_X_FRAME / 2);
|
||||
pbch_msg.sfn_4lsb = ctx->sfn & 0b1111U;
|
||||
|
||||
// Encode SSB
|
||||
if (srsran_ssb_add(&ctx->ssb, pci, &pbch_msg, buffer, buffer) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// Run channel
|
||||
run_channel(ctx);
|
||||
|
||||
// Write in the ring buffer
|
||||
if (srsran_ringbuffer_write(&ctx->ringbuffer, buffer, (int)sf_len * sizeof(cf_t)) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Increment subframe index
|
||||
ctx->sf_idx++;
|
||||
|
||||
// Increment SFN if required
|
||||
if (ctx->sf_idx >= SRSRAN_NOF_SF_X_FRAME) {
|
||||
ctx->sfn = (ctx->sfn + 1) % 1024U;
|
||||
ctx->sf_idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
srsran_vec_cf_zero(buffer, sf_len);
|
||||
|
||||
// Read ringbuffer
|
||||
if (srsran_ringbuffer_read(&ctx->ringbuffer, rx_buffer[0], required_nbytes) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Setup timestamp
|
||||
*timestamp = ctx->timestamp;
|
||||
|
||||
// Advance timestamp
|
||||
srsran_timestamp_add(&ctx->timestamp, 0, (float)(nof_samples / srate_hz));
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_case_1(srsran_ue_sync_nr_t* ue_sync)
|
||||
{
|
||||
for (uint32_t sf_idx = 0; sf_idx < nof_sf; sf_idx++) {
|
||||
srsran_ue_sync_nr_outcome_t outcome = {};
|
||||
TESTASSERT(srsran_ue_sync_nr_zerocopy(ue_sync, &buffer, &outcome) == SRSRAN_SUCCESS);
|
||||
|
||||
// Print outcome
|
||||
INFO("measure - zerocpy in-sync=%s sf_idx=%d sfn=%d timestamp=%f cfo_hz=%+.1f delay_us=%+.3f",
|
||||
outcome.in_sync ? "y" : "n",
|
||||
outcome.sf_idx,
|
||||
outcome.sfn,
|
||||
srsran_timestamp_real(&outcome.timestamp),
|
||||
outcome.cfo_hz,
|
||||
outcome.delay_us);
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int ret = SRSRAN_ERROR;
|
||||
parse_args(argc, argv);
|
||||
|
||||
srate_hz = (double)SRSRAN_SUBC_SPACING_NR(carrier_scs) * srsran_min_symbol_sz_rb(carrier_nof_prb);
|
||||
sf_len = (uint32_t)ceil(srate_hz / 1000.0);
|
||||
buffer = srsran_vec_cf_malloc(sf_len);
|
||||
buffer2 = srsran_vec_cf_malloc(sf_len);
|
||||
|
||||
test_context_t ctx = {};
|
||||
srsran_ue_sync_nr_t ue_sync = {};
|
||||
|
||||
if (buffer == NULL) {
|
||||
ERROR("Malloc");
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
if (buffer2 == NULL) {
|
||||
ERROR("Malloc");
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
srsran_ue_sync_nr_args_t ue_sync_args = {};
|
||||
ue_sync_args.max_srate_hz = srate_hz;
|
||||
ue_sync_args.min_scs = carrier_scs;
|
||||
ue_sync_args.recv_obj = &ctx;
|
||||
ue_sync_args.recv_callback = &recv_callback;
|
||||
ue_sync_args.disable_cfo = false;
|
||||
if (srsran_ue_sync_nr_init(&ue_sync, &ue_sync_args) < SRSRAN_SUCCESS) {
|
||||
ERROR("Init");
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
srsran_ue_sync_nr_cfg_t ue_sync_cfg = {};
|
||||
ue_sync_cfg.ssb.srate_hz = srate_hz;
|
||||
ue_sync_cfg.ssb.center_freq_hz = 3.5e9;
|
||||
ue_sync_cfg.ssb.ssb_freq_hz = 3.5e9 - 960e3;
|
||||
ue_sync_cfg.ssb.scs = ssb_scs;
|
||||
ue_sync_cfg.ssb.pattern = SRSRAN_SSB_PATTERN_C;
|
||||
ue_sync_cfg.N_id = pci;
|
||||
if (srsran_ue_sync_nr_set_cfg(&ue_sync, &ue_sync_cfg) < SRSRAN_SUCCESS) {
|
||||
ERROR("Init");
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
if (test_context_init(&ctx) < SRSRAN_SUCCESS) {
|
||||
ERROR("Init");
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
if (test_case_1(&ue_sync) != SRSRAN_SUCCESS) {
|
||||
ERROR("test case failed");
|
||||
}
|
||||
|
||||
ret = SRSRAN_SUCCESS;
|
||||
|
||||
clean_exit:
|
||||
srsran_ue_sync_nr_free(&ue_sync);
|
||||
|
||||
if (buffer) {
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
if (buffer2) {
|
||||
free(buffer2);
|
||||
}
|
||||
|
||||
test_context_free(&ctx);
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,314 @@
|
|||
/**
|
||||
*
|
||||
* \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/ue/ue_sync_nr.h"
|
||||
#include "srsran/phy/utils/vector.h"
|
||||
|
||||
#define UE_SYNC_NR_DEFAULT_CFO_ALPHA 0.1
|
||||
|
||||
int srsran_ue_sync_nr_init(srsran_ue_sync_nr_t* q, const srsran_ue_sync_nr_args_t* args)
|
||||
{
|
||||
// Check inputs
|
||||
if (q == NULL || args == NULL) {
|
||||
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
|
||||
// Copy arguments
|
||||
q->recv_obj = args->recv_obj;
|
||||
q->recv_callback = args->recv_callback;
|
||||
q->nof_rx_channels = args->nof_rx_channels == 0 ? 1 : args->nof_rx_channels;
|
||||
q->disable_cfo = args->disable_cfo;
|
||||
q->cfo_alpha = isnormal(args->cfo_alpha) ? args->cfo_alpha : UE_SYNC_NR_DEFAULT_CFO_ALPHA;
|
||||
|
||||
// Initialise SSB
|
||||
srsran_ssb_args_t ssb_args = {};
|
||||
ssb_args.max_srate_hz = args->max_srate_hz;
|
||||
ssb_args.min_scs = args->min_scs;
|
||||
ssb_args.enable_search = true;
|
||||
ssb_args.enable_decode = true;
|
||||
ssb_args.pbch_dmrs_thr = args->pbch_dmrs_thr;
|
||||
if (srsran_ssb_init(&q->ssb, &ssb_args) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error SSB init");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Allocate temporal buffer pointers
|
||||
q->tmp_buffer = SRSRAN_MEM_ALLOC(cf_t*, q->nof_rx_channels);
|
||||
if (q->tmp_buffer == NULL) {
|
||||
ERROR("Error alloc");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
void srsran_ue_sync_nr_free(srsran_ue_sync_nr_t* q)
|
||||
{
|
||||
// Check inputs
|
||||
if (q == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
srsran_ssb_free(&q->ssb);
|
||||
|
||||
if (q->tmp_buffer) {
|
||||
free(q->tmp_buffer);
|
||||
}
|
||||
|
||||
SRSRAN_MEM_ZERO(q, srsran_ue_sync_nr_t, 1);
|
||||
}
|
||||
|
||||
int srsran_ue_sync_nr_set_cfg(srsran_ue_sync_nr_t* q, const srsran_ue_sync_nr_cfg_t* cfg)
|
||||
{
|
||||
// Check inputs
|
||||
if (q == NULL || cfg == NULL) {
|
||||
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
|
||||
// Copy parameters
|
||||
q->N_id = cfg->N_id;
|
||||
q->srate_hz = cfg->ssb.srate_hz;
|
||||
|
||||
// Calculate new subframe size
|
||||
q->sf_sz = (uint32_t)round(1e-3 * q->srate_hz);
|
||||
|
||||
// Configure SSB
|
||||
if (srsran_ssb_set_cfg(&q->ssb, &cfg->ssb) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error configuring SSB");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Transition to find
|
||||
q->state = SRSRAN_UE_SYNC_NR_STATE_FIND;
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
static void ue_sync_nr_reset_feedback(srsran_ue_sync_nr_t* q)
|
||||
{
|
||||
SRSRAN_MEM_ZERO(&q->feedback, srsran_csi_trs_measurements_t, 1);
|
||||
}
|
||||
|
||||
static void ue_sync_nr_apply_feedback(srsran_ue_sync_nr_t* q)
|
||||
{
|
||||
// Skip any update if there is no feedback available
|
||||
if (q->feedback.nof_re == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update number of samples
|
||||
q->avg_delay_us = q->feedback.delay_us;
|
||||
q->next_rf_sample_offset = (uint32_t)round((double)q->avg_delay_us * (q->srate_hz * 1e-6));
|
||||
|
||||
// Integrate CFO
|
||||
if (q->disable_cfo) {
|
||||
q->cfo_hz = SRSRAN_VEC_SAFE_EMA(q->feedback.cfo_hz, q->cfo_hz, q->cfo_alpha);
|
||||
} else {
|
||||
q->cfo_hz += q->feedback.cfo_hz * q->cfo_alpha;
|
||||
}
|
||||
|
||||
// Reset feedback
|
||||
ue_sync_nr_reset_feedback(q);
|
||||
}
|
||||
|
||||
static int ue_sync_nr_run_find(srsran_ue_sync_nr_t* q, cf_t* buffer)
|
||||
{
|
||||
srsran_csi_trs_measurements_t measurements = {};
|
||||
srsran_pbch_msg_nr_t pbch_msg = {};
|
||||
|
||||
// Find SSB, measure PSS/SSS and decode PBCH
|
||||
if (srsran_ssb_find(&q->ssb, buffer, q->N_id, &measurements, &pbch_msg) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error finding SSB");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// If the PBCH message was NOT decoded, early return
|
||||
if (!pbch_msg.crc) {
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
// Reset feedback to prevent any previous erroneous measurement
|
||||
ue_sync_nr_reset_feedback(q);
|
||||
|
||||
// Set feedback measurement
|
||||
srsran_combine_csi_trs_measurements(&q->feedback, &measurements, &q->feedback);
|
||||
|
||||
// Apply feedback
|
||||
ue_sync_nr_apply_feedback(q);
|
||||
|
||||
// Setup context
|
||||
q->ssb_idx = pbch_msg.ssb_idx;
|
||||
q->sf_idx = srsran_ssb_candidate_sf_idx(&q->ssb, pbch_msg.ssb_idx, pbch_msg.hrf);
|
||||
q->sfn = pbch_msg.sfn_4lsb;
|
||||
|
||||
// Transition to track only if the measured delay is below 2.4 microseconds
|
||||
if (measurements.delay_us < 2.4f) {
|
||||
q->state = SRSRAN_UE_SYNC_NR_STATE_TRACK;
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
static int ue_sync_nr_run_track(srsran_ue_sync_nr_t* q, cf_t* buffer)
|
||||
{
|
||||
srsran_csi_trs_measurements_t measurements = {};
|
||||
srsran_pbch_msg_nr_t pbch_msg = {};
|
||||
uint32_t half_frame = q->sf_idx / (SRSRAN_NOF_SF_X_FRAME / 2);
|
||||
|
||||
// Check if the SSB selected candidate index shall be received in this subframe
|
||||
bool is_ssb_opportunity = (q->sf_idx == srsran_ssb_candidate_sf_idx(&q->ssb, q->ssb_idx, half_frame > 0));
|
||||
|
||||
// If
|
||||
if (is_ssb_opportunity) {
|
||||
// Measure PSS/SSS and decode PBCH
|
||||
if (srsran_ssb_track(&q->ssb, buffer, q->N_id, q->ssb_idx, half_frame, &measurements, &pbch_msg) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error finding SSB");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// If the PBCH message was NOT decoded, transition to track
|
||||
if (!pbch_msg.crc) {
|
||||
q->state = SRSRAN_UE_SYNC_NR_STATE_FIND;
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
// Otherwise feedback measurements and apply
|
||||
srsran_combine_csi_trs_measurements(&q->feedback, &measurements, &q->feedback);
|
||||
}
|
||||
|
||||
// Apply accumulated feedback
|
||||
ue_sync_nr_apply_feedback(q);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
static int ue_sync_nr_recv(srsran_ue_sync_nr_t* q, cf_t** buffer, srsran_timestamp_t* timestamp)
|
||||
{
|
||||
// Verify callback and srate are valid
|
||||
if (q->recv_callback == NULL && !isnormal(q->srate_hz)) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
uint32_t buffer_offset = 0;
|
||||
uint32_t nof_samples = q->sf_sz;
|
||||
|
||||
if (q->next_rf_sample_offset > 0) {
|
||||
// Discard a number of samples from RF
|
||||
if (q->recv_callback(q->recv_obj, buffer, (uint32_t)q->next_rf_sample_offset, timestamp) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
} else {
|
||||
// Adjust receive buffer
|
||||
buffer_offset = (uint32_t)(-q->next_rf_sample_offset);
|
||||
nof_samples = (uint32_t)(q->sf_sz + q->next_rf_sample_offset);
|
||||
}
|
||||
q->next_rf_sample_offset = 0;
|
||||
|
||||
// Select buffer offsets
|
||||
for (uint32_t chan = 0; chan < q->nof_rx_channels; chan++) {
|
||||
// Set buffer to NULL if not present
|
||||
if (buffer[chan] == NULL) {
|
||||
q->tmp_buffer[chan] = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Initialise first offset samples to zero
|
||||
if (buffer_offset > 0) {
|
||||
srsran_vec_cf_zero(buffer[chan], buffer_offset);
|
||||
}
|
||||
|
||||
// Set to sample index
|
||||
q->tmp_buffer[chan] = &buffer[chan][buffer_offset];
|
||||
}
|
||||
|
||||
// Receive
|
||||
if (q->recv_callback(q->recv_obj, q->tmp_buffer, nof_samples, timestamp) < SRSRAN_SUCCESS) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Compensate CFO
|
||||
for (uint32_t chan = 0; chan < q->nof_rx_channels; chan++) {
|
||||
if (buffer[chan] != 0 && !q->disable_cfo) {
|
||||
srsran_vec_apply_cfo(buffer[chan], q->cfo_hz / q->srate_hz, buffer[chan], (int)q->sf_sz);
|
||||
}
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int srsran_ue_sync_nr_zerocopy(srsran_ue_sync_nr_t* q, cf_t** buffer, srsran_ue_sync_nr_outcome_t* outcome)
|
||||
{
|
||||
// Check inputs
|
||||
if (q == NULL || buffer == NULL || outcome == NULL) {
|
||||
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
|
||||
// Verify callback is valid
|
||||
if (q->recv_callback == NULL) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Receive
|
||||
if (ue_sync_nr_recv(q, buffer, &outcome->timestamp) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error receiving baseband");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Run FSM
|
||||
switch (q->state) {
|
||||
case SRSRAN_UE_SYNC_NR_STATE_IDLE:
|
||||
// Do nothing
|
||||
break;
|
||||
case SRSRAN_UE_SYNC_NR_STATE_FIND:
|
||||
if (ue_sync_nr_run_find(q, buffer[0]) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error running find");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
break;
|
||||
case SRSRAN_UE_SYNC_NR_STATE_TRACK:
|
||||
if (ue_sync_nr_run_track(q, buffer[0]) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error running track");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Increment subframe counter
|
||||
q->sf_idx++;
|
||||
|
||||
// Increment SFN
|
||||
if (q->sf_idx >= SRSRAN_NOF_SF_X_FRAME) {
|
||||
q->sfn = (q->sfn + 1) % 1024;
|
||||
q->sf_idx = 0;
|
||||
}
|
||||
|
||||
// Fill outcome
|
||||
outcome->in_sync = (q->state == SRSRAN_UE_SYNC_NR_STATE_TRACK);
|
||||
outcome->sf_idx = q->sf_idx;
|
||||
outcome->sfn = q->sfn;
|
||||
outcome->cfo_hz = q->cfo_hz;
|
||||
outcome->delay_us = q->avg_delay_us;
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int srsran_ue_sync_nr_feedback(srsran_ue_sync_nr_t* q, const srsran_csi_trs_measurements_t* measurements)
|
||||
{
|
||||
if (q == NULL || measurements == NULL) {
|
||||
return SRSRAN_ERROR_INVALID_INPUTS;
|
||||
}
|
||||
|
||||
// Accumulate feedback proportional to the number of elements provided by the measurement
|
||||
srsran_combine_csi_trs_measurements(&q->feedback, measurements, &q->feedback);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
|
@ -114,13 +114,6 @@ bool pdcp_entity_base::integrity_verify(uint8_t* msg, uint32_t msg_len, uint32_t
|
|||
break;
|
||||
}
|
||||
|
||||
logger.debug("Integrity check input: COUNT %" PRIu32 ", Bearer ID %d, Direction %s",
|
||||
count,
|
||||
cfg.bearer_id,
|
||||
cfg.rx_direction == SECURITY_DIRECTION_DOWNLINK ? "Downlink" : "Uplink");
|
||||
logger.debug(k_int, 32, "Integrity check key:");
|
||||
logger.debug(msg, msg_len, "Integrity check input msg:");
|
||||
|
||||
if (sec_cfg.integ_algo != INTEGRITY_ALGORITHM_ID_EIA0) {
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
if (mac[i] != mac_exp[i]) {
|
||||
|
@ -130,9 +123,13 @@ bool pdcp_entity_base::integrity_verify(uint8_t* msg, uint32_t msg_len, uint32_t
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (is_valid) {
|
||||
logger.info(mac_exp, 4, "MAC match");
|
||||
}
|
||||
srslog::log_channel& channel = is_valid ? logger.debug : logger.warning;
|
||||
channel("Integrity check input: COUNT %" PRIu32 ", Bearer ID %d, Direction %s",
|
||||
count,
|
||||
cfg.bearer_id,
|
||||
cfg.rx_direction == SECURITY_DIRECTION_DOWNLINK ? "Downlink" : "Uplink");
|
||||
channel(k_int, 32, "Integrity check key:");
|
||||
channel(msg, msg_len, "Integrity check input msg (Bytes=%" PRIu32 "):", msg_len);
|
||||
}
|
||||
|
||||
return is_valid;
|
||||
|
|
|
@ -1177,11 +1177,16 @@ void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t no
|
|||
return;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
// Local variables for handling Status PDU will be updated with lock
|
||||
rlc_status_pdu_t status = {};
|
||||
uint32_t i = 0;
|
||||
uint32_t vt_s_local = 0;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
logger.debug(payload, nof_bytes, "%s Rx control PDU", RB_NAME);
|
||||
|
||||
rlc_status_pdu_t status;
|
||||
rlc_am_read_status_pdu(payload, nof_bytes, &status);
|
||||
|
||||
log_rlc_am_status_pdu_to_string(logger.info, "%s Rx Status PDU: %s", &status, RB_NAME);
|
||||
|
@ -1209,16 +1214,18 @@ void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t no
|
|||
retx_queue.clear();
|
||||
}
|
||||
|
||||
// Handle ACKs and NACKs
|
||||
bool update_vt_a = true;
|
||||
uint32_t i = vt_a;
|
||||
i = vt_a;
|
||||
vt_s_local = vt_s;
|
||||
}
|
||||
|
||||
while (TX_MOD_BASE(i) < TX_MOD_BASE(status.ack_sn) && TX_MOD_BASE(i) < TX_MOD_BASE(vt_s)) {
|
||||
bool update_vt_a = true;
|
||||
while (TX_MOD_BASE(i) < TX_MOD_BASE(status.ack_sn) && TX_MOD_BASE(i) < TX_MOD_BASE(vt_s_local)) {
|
||||
bool nack = false;
|
||||
for (uint32_t j = 0; j < status.N_nack; j++) {
|
||||
if (status.nacks[j].nack_sn == i) {
|
||||
nack = true;
|
||||
update_vt_a = false;
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
if (tx_window.has_sn(i)) {
|
||||
auto& pdu = tx_window[i];
|
||||
if (not retx_queue.has_sn(i)) {
|
||||
|
@ -1268,6 +1275,7 @@ void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t no
|
|||
|
||||
if (!nack) {
|
||||
// ACKed SNs get marked and removed from tx_window so PDCP get's only notified once
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
if (tx_window.has_sn(i)) {
|
||||
update_notification_ack_info(i);
|
||||
logger.debug("Tx PDU SN=%zd being removed from tx window", i);
|
||||
|
@ -1282,16 +1290,17 @@ void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t no
|
|||
i = (i + 1) % MOD;
|
||||
}
|
||||
|
||||
{
|
||||
// Make sure vt_a points to valid SN
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
if (not tx_window.empty() && not tx_window.has_sn(vt_a)) {
|
||||
logger.error("%s vt_a=%d points to invalid position in Tx window.", RB_NAME, vt_a);
|
||||
parent->rrc->protocol_failure();
|
||||
}
|
||||
}
|
||||
|
||||
debug_state();
|
||||
|
||||
lock.unlock();
|
||||
|
||||
// Notify PDCP without holding Tx mutex
|
||||
if (not notify_info_vec.empty()) {
|
||||
parent->pdcp->notify_delivery(parent->lcid, notify_info_vec);
|
||||
|
|
|
@ -487,8 +487,7 @@ void rlc_um_lte::rlc_um_lte_rx::reassemble_rx_sdus()
|
|||
logger.debug("Reassemble loop for vr_ur=%d", vr_ur);
|
||||
|
||||
if (not pdu_belongs_to_rx_sdu()) {
|
||||
logger.warning(
|
||||
"PDU SN=%d lost, stop reassambling SDU (vr_ur_in_rx_sdu=%d)", vr_ur_in_rx_sdu + 1, vr_ur_in_rx_sdu);
|
||||
logger.info("PDU SN=%d lost, stop reassambling SDU (vr_ur_in_rx_sdu=%d)", vr_ur_in_rx_sdu + 1, vr_ur_in_rx_sdu);
|
||||
pdu_lost = false; // Reset flag to not prevent reassembling of further segments
|
||||
rx_sdu->clear();
|
||||
}
|
||||
|
@ -504,7 +503,7 @@ void rlc_um_lte::rlc_um_lte_rx::reassemble_rx_sdus()
|
|||
rlc_fi_field_text[rx_window[vr_ur].header.fi]);
|
||||
// Check if the first part of the PDU is a middle or end segment
|
||||
if (rx_sdu->N_bytes == 0 && i == 0 && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) {
|
||||
logger.warning(
|
||||
logger.info(
|
||||
rx_window[vr_ur].buf->msg, len, "Dropping first %d B of SN=%d due to lost start segment", len, vr_ur);
|
||||
|
||||
if (rx_window[vr_ur].buf->N_bytes < len) {
|
||||
|
|
|
@ -64,8 +64,8 @@ int test_socket_handler()
|
|||
TESTASSERT(sctp_init_server(&server_socket, socket_type::seqpacket, server_addr, server_port));
|
||||
logger.info("Listening from fd=%d", server_socket.fd());
|
||||
|
||||
TESTASSERT(sctp_init_client(&client_socket, socket_type::seqpacket, "127.0.0.1"));
|
||||
TESTASSERT(sctp_init_client(&client_socket2, socket_type::seqpacket, "127.0.0.2"));
|
||||
TESTASSERT(sctp_init_client(&client_socket, socket_type::seqpacket, "127.0.0.1", 0));
|
||||
TESTASSERT(sctp_init_client(&client_socket2, socket_type::seqpacket, "127.0.0.2", 0));
|
||||
TESTASSERT(client_socket.connect_to(server_addr, server_port));
|
||||
TESTASSERT(client_socket2.connect_to(server_addr, server_port));
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
# gtp_bind_addr: Local IP address to bind for GTP connection
|
||||
# gtp_advertise_addr: IP address of eNB to advertise for DL GTP-U Traffic
|
||||
# s1c_bind_addr: Local IP address to bind for S1AP connection
|
||||
# s1c_bind_port: Source port for S1AP connection (0 means any)
|
||||
# n_prb: Number of Physical Resource Blocks (6,15,25,50,75,100)
|
||||
# tm: Transmission mode 1-4 (TM1 default)
|
||||
# nof_ports: Number of Tx ports (1 port default, set to 2 for TM2/3/4)
|
||||
|
@ -24,6 +25,7 @@ mnc = 01
|
|||
mme_addr = 127.0.1.100
|
||||
gtp_bind_addr = 127.0.1.1
|
||||
s1c_bind_addr = 127.0.1.1
|
||||
s1c_bind_port = 0
|
||||
n_prb = 50
|
||||
#tm = 4
|
||||
#nof_ports = 2
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#ifndef SRSENB_NR_CC_WORKER_H
|
||||
#define SRSENB_NR_CC_WORKER_H
|
||||
|
||||
#include "srsenb/hdr/phy/phy_interfaces.h"
|
||||
#include "srsran/interfaces/gnb_interfaces.h"
|
||||
#include "srsran/interfaces/rrc_nr_interface_types.h"
|
||||
#include "srsran/phy/enb/enb_dl_nr.h"
|
||||
|
@ -33,16 +34,18 @@
|
|||
namespace srsenb {
|
||||
namespace nr {
|
||||
|
||||
typedef struct {
|
||||
uint32_t nof_carriers;
|
||||
srsran_enb_dl_nr_args_t dl;
|
||||
} phy_nr_args_t;
|
||||
struct phy_nr_args_t {
|
||||
uint32_t nof_carriers = 1;
|
||||
uint32_t nof_ports = 1;
|
||||
srsran_enb_dl_nr_args_t dl = {};
|
||||
};
|
||||
|
||||
class phy_nr_state
|
||||
{
|
||||
public:
|
||||
phy_nr_args_t args = {};
|
||||
srsran::phy_cfg_nr_t cfg = {};
|
||||
phy_cell_cfg_list_nr_t cell_list = {};
|
||||
phy_nr_args_t args;
|
||||
srsran::phy_cfg_nr_t cfg;
|
||||
|
||||
phy_nr_state()
|
||||
{
|
||||
|
@ -58,7 +61,7 @@ public:
|
|||
class cc_worker
|
||||
{
|
||||
public:
|
||||
cc_worker(uint32_t cc_idx, srslog::basic_logger& logger, phy_nr_state* phy_state_);
|
||||
cc_worker(uint32_t cc_idx, srslog::basic_logger& logger, phy_nr_state& phy_state_);
|
||||
~cc_worker();
|
||||
|
||||
bool set_carrier(const srsran_carrier_nr_t* carrier);
|
||||
|
@ -79,7 +82,7 @@ private:
|
|||
std::array<cf_t*, SRSRAN_MAX_PORTS> tx_buffer = {};
|
||||
std::array<cf_t*, SRSRAN_MAX_PORTS> rx_buffer = {};
|
||||
uint32_t buffer_sz = 0;
|
||||
phy_nr_state* phy_state;
|
||||
phy_nr_state& phy_state;
|
||||
srsran_enb_dl_nr_t enb_dl = {};
|
||||
srslog::basic_logger& logger;
|
||||
};
|
||||
|
|
|
@ -19,12 +19,12 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#ifndef SRSUE_NR_PHCH_WORKER_H
|
||||
#define SRSUE_NR_PHCH_WORKER_H
|
||||
#ifndef SRSENB_NR_PHCH_WORKER_H
|
||||
#define SRSENB_NR_PHCH_WORKER_H
|
||||
|
||||
#include "cc_worker.h"
|
||||
#include "srsenb/hdr/phy/phy_common.h"
|
||||
#include "srsran/common/thread_pool.h"
|
||||
#include "srsran/interfaces/phy_common_interface.h"
|
||||
#include "srsran/srslog/srslog.h"
|
||||
|
||||
namespace srsenb {
|
||||
|
@ -41,7 +41,7 @@ namespace nr {
|
|||
class sf_worker final : public srsran::thread_pool::worker
|
||||
{
|
||||
public:
|
||||
sf_worker(phy_common* phy_, phy_nr_state* phy_state_, srslog::basic_logger& logger);
|
||||
sf_worker(srsran::phy_common_interface& common_, phy_nr_state& phy_state_, srslog::basic_logger& logger);
|
||||
~sf_worker();
|
||||
|
||||
bool set_carrier_unlocked(uint32_t cc_idx, const srsran_carrier_nr_t* carrier_);
|
||||
|
@ -58,8 +58,8 @@ private:
|
|||
|
||||
std::vector<std::unique_ptr<cc_worker> > cc_workers;
|
||||
|
||||
phy_common* phy = nullptr;
|
||||
phy_nr_state* phy_state = nullptr;
|
||||
srsran::phy_common_interface& common;
|
||||
phy_nr_state& phy_state;
|
||||
srslog::basic_logger& logger;
|
||||
|
||||
// Temporal attributes
|
||||
|
@ -70,4 +70,4 @@ private:
|
|||
} // namespace nr
|
||||
} // namespace srsenb
|
||||
|
||||
#endif // SRSUE_NR_PHCH_WORKER_H
|
||||
#endif // SRSENB_NR_PHCH_WORKER_H
|
||||
|
|
|
@ -19,10 +19,11 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#ifndef SRSUE_NR_WORKER_POOL_H
|
||||
#define SRSUE_NR_WORKER_POOL_H
|
||||
#ifndef SRSENB_NR_WORKER_POOL_H
|
||||
#define SRSENB_NR_WORKER_POOL_H
|
||||
|
||||
#include "sf_worker.h"
|
||||
#include "srsenb/hdr/phy/phy_interfaces.h"
|
||||
#include "srsran/common/thread_pool.h"
|
||||
|
||||
namespace srsenb {
|
||||
|
@ -38,7 +39,11 @@ public:
|
|||
sf_worker* operator[](std::size_t pos) { return workers.at(pos).get(); }
|
||||
|
||||
worker_pool(uint32_t max_workers);
|
||||
bool init(const phy_args_t& args, phy_common* common, srslog::sink& log_sink, int prio);
|
||||
bool init(const phy_cell_cfg_list_nr_t& cell_list,
|
||||
const phy_args_t& args,
|
||||
srsran::phy_common_interface& common,
|
||||
srslog::sink& log_sink,
|
||||
int prio);
|
||||
sf_worker* wait_worker(uint32_t tti);
|
||||
sf_worker* wait_worker_id(uint32_t id);
|
||||
void start_worker(sf_worker* w);
|
||||
|
@ -48,4 +53,4 @@ public:
|
|||
} // namespace nr
|
||||
} // namespace srsenb
|
||||
|
||||
#endif // SRSUE_NR_WORKER_POOL_H
|
||||
#endif // SRSENB_NR_WORKER_POOL_H
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "srsran/common/thread_pool.h"
|
||||
#include "srsran/common/threads.h"
|
||||
#include "srsran/interfaces/enb_metrics_interface.h"
|
||||
#include "srsran/interfaces/phy_common_interface.h"
|
||||
#include "srsran/interfaces/radio_interfaces.h"
|
||||
#include "srsran/phy/channel/channel.h"
|
||||
#include "srsran/radio/radio.h"
|
||||
|
@ -40,7 +41,7 @@
|
|||
|
||||
namespace srsenb {
|
||||
|
||||
class phy_common
|
||||
class phy_common : public srsran::phy_common_interface
|
||||
{
|
||||
public:
|
||||
phy_common() = default;
|
||||
|
@ -65,7 +66,11 @@ 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, srsran::rf_buffer_t& buffer, srsran::rf_timestamp_t& tx_time, bool is_nr = false);
|
||||
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;
|
||||
|
||||
// Common objects
|
||||
phy_args_t params = {};
|
||||
|
@ -160,15 +165,6 @@ public:
|
|||
}
|
||||
return c;
|
||||
};
|
||||
srsran_carrier_nr_t get_cell_nr(uint32_t cc_idx)
|
||||
{
|
||||
srsran_carrier_nr_t c = {};
|
||||
if (cc_idx < cell_list_nr.size()) {
|
||||
c = cell_list_nr[cc_idx].carrier;
|
||||
}
|
||||
|
||||
return c;
|
||||
};
|
||||
|
||||
void set_cell_gain(uint32_t cell_id, float gain_db)
|
||||
{
|
||||
|
|
|
@ -86,7 +86,7 @@ public:
|
|||
{
|
||||
last_phr = phr_;
|
||||
for (auto& ch_snr : snr_estim_list) {
|
||||
ch_snr.phr_flag = false;
|
||||
ch_snr.acc_tpc_phr_values = 0;
|
||||
}
|
||||
|
||||
// compute and cache the max nof UL PRBs that avoids overflowing PHR
|
||||
|
@ -118,6 +118,7 @@ public:
|
|||
if (ch_snr.pending_snr == null_snr) {
|
||||
ch_snr.last_snr_sample_count++;
|
||||
ch_snr.acc_tpc_values += ch_snr.win_tpc_values.oldest();
|
||||
ch_snr.acc_tpc_phr_values += ch_snr.win_tpc_values.oldest();
|
||||
} else {
|
||||
ch_snr.acc_tpc_values = 0;
|
||||
ch_snr.snr_avg.push(ch_snr.pending_snr, ch_snr.last_snr_sample_count);
|
||||
|
@ -179,40 +180,43 @@ private:
|
|||
// undefined target SINR case
|
||||
return encode_tpc_delta(0);
|
||||
}
|
||||
|
||||
// limitation of TPC based on PHR
|
||||
int max_delta = 3;
|
||||
int eff_phr = last_phr;
|
||||
if (cc == PUSCH_CODE and last_phr != undefined_phr) {
|
||||
eff_phr -= ch_snr.win_tpc_values.value() + ch_snr.acc_tpc_phr_values;
|
||||
max_delta = std::min(max_delta, eff_phr);
|
||||
}
|
||||
|
||||
float snr = ch_snr.last_snr_sample;
|
||||
// In case of periodicity of TPCs is > 1 tti, use average SNR to compute SNR diff
|
||||
if (min_tpc_tti_interval > 1) {
|
||||
ch_snr.tpc_snr_avg.push(snr);
|
||||
if ((tti_count - ch_snr.last_tpc_tti_count) < min_tpc_tti_interval) {
|
||||
// more time required before sending next TPC
|
||||
return encode_tpc_delta(0);
|
||||
}
|
||||
if (cc == PUSCH_CODE and last_phr < 0 and not ch_snr.phr_flag) {
|
||||
// if negative PHR and PUSCH
|
||||
logger.info("TPC: rnti=0x%x, PUSCH command=0 due to PHR=%d<0", rnti, last_phr);
|
||||
ch_snr.phr_flag = true;
|
||||
ch_snr.pending_delta = -1;
|
||||
return encode_tpc_delta(ch_snr.pending_delta);
|
||||
snr = ch_snr.tpc_snr_avg.value();
|
||||
}
|
||||
|
||||
// target SINR is finite and there is power headroom
|
||||
float diff = target_snr_dB - ch_snr.last_snr_sample;
|
||||
float diff = target_snr_dB - snr;
|
||||
diff -= ch_snr.win_tpc_values.value() + ch_snr.acc_tpc_values;
|
||||
if (diff >= 1) {
|
||||
ch_snr.pending_delta = diff > 3 ? 3 : 1;
|
||||
if (cc == PUSCH_CODE and static_cast<int>(ch_snr.pending_delta) > last_phr) {
|
||||
// cap PUSCH TPC when PHR is low
|
||||
ch_snr.pending_delta = last_phr > 1 ? 1 : 0;
|
||||
}
|
||||
ch_snr.last_tpc_tti_count = tti_count;
|
||||
} else if (diff <= -1) {
|
||||
ch_snr.pending_delta = -1;
|
||||
ch_snr.last_tpc_tti_count = tti_count;
|
||||
}
|
||||
ch_snr.pending_delta = std::max(std::min((int)floorf(diff), max_delta), -1);
|
||||
ch_snr.pending_delta = (ch_snr.pending_delta == 2) ? 1 : ch_snr.pending_delta;
|
||||
if (ch_snr.pending_delta != 0) {
|
||||
logger.debug("TPC: rnti=0x%x, %s command=%d, last SNR=%d, SNR average=%f, diff_acc=%f",
|
||||
if (min_tpc_tti_interval > 1) {
|
||||
ch_snr.last_tpc_tti_count = tti_count;
|
||||
ch_snr.tpc_snr_avg.reset();
|
||||
}
|
||||
logger.debug("TPC: rnti=0x%x, %s command=%d, last SNR=%d, SNR average=%f, diff_acc=%f, eff_phr=%d",
|
||||
rnti,
|
||||
cc == PUSCH_CODE ? "PUSCH" : "PUCCH",
|
||||
encode_tpc_delta(ch_snr.pending_delta),
|
||||
ch_snr.last_snr_sample,
|
||||
ch_snr.snr_avg.value(),
|
||||
diff);
|
||||
diff,
|
||||
eff_phr);
|
||||
}
|
||||
return encode_tpc_delta(ch_snr.pending_delta);
|
||||
}
|
||||
|
@ -234,8 +238,6 @@ private:
|
|||
|
||||
// SNR estimation
|
||||
struct ul_ch_snr_estim {
|
||||
// flag used in undefined target SINR case
|
||||
bool phr_flag = false;
|
||||
// pending new snr sample
|
||||
float pending_snr = srsran::null_sliding_average<float>::null_value();
|
||||
// SNR average estimation with irregular sample spacing
|
||||
|
@ -244,9 +246,11 @@ private:
|
|||
int last_snr_sample;
|
||||
// Accumulation of past TPC commands
|
||||
srsran::sliding_sum<int> win_tpc_values;
|
||||
int8_t pending_delta = 0;
|
||||
int acc_tpc_values = 0;
|
||||
int acc_tpc_phr_values = 0;
|
||||
int8_t pending_delta = 0;
|
||||
uint32_t last_tpc_tti_count = 0;
|
||||
srsran::rolling_average<float> tpc_snr_avg; // average of SNRs since last TPC != 1
|
||||
|
||||
explicit ul_ch_snr_estim(float exp_avg_alpha, int initial_snr) :
|
||||
snr_avg(exp_avg_alpha, initial_snr),
|
||||
|
|
|
@ -169,7 +169,7 @@ public:
|
|||
srsran_softbuffer_rx_t* get_rx_softbuffer(uint32_t enb_cc_idx, uint32_t tti);
|
||||
|
||||
uint8_t* request_buffer(uint32_t tti, uint32_t enb_cc_idx, uint32_t len);
|
||||
void process_pdu(srsran::unique_byte_buffer_t pdu, uint32_t grant_nof_prbs);
|
||||
void process_pdu(srsran::unique_byte_buffer_t pdu, uint32_t ue_cc_idx, uint32_t grant_nof_prbs);
|
||||
srsran::unique_byte_buffer_t release_pdu(uint32_t tti, uint32_t enb_cc_idx);
|
||||
void clear_old_buffers(uint32_t tti);
|
||||
|
||||
|
@ -224,8 +224,6 @@ private:
|
|||
// Mutexes
|
||||
std::mutex mutex;
|
||||
std::mutex rx_buffers_mutex;
|
||||
|
||||
static const uint8_t UL_CC_IDX = 0; ///< Passed to write CC index in PCAP
|
||||
};
|
||||
|
||||
} // namespace srsenb
|
||||
|
|
|
@ -86,7 +86,7 @@ private:
|
|||
|
||||
// args
|
||||
rrc_interface_ngap_nr* rrc = nullptr;
|
||||
ngap_args_t args;
|
||||
ngap_args_t args = {};
|
||||
srslog::basic_logger& logger;
|
||||
srsran::task_sched_handle task_sched;
|
||||
srsran::task_queue_handle amf_task_queue;
|
||||
|
|
|
@ -83,6 +83,7 @@ void parse_args(all_args_t* args, int argc, char* argv[])
|
|||
("enb.gtp_bind_addr", bpo::value<string>(&args->stack.s1ap.gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection")
|
||||
("enb.gtp_advertise_addr", bpo::value<string>(&args->stack.s1ap.gtp_advertise_addr)->default_value(""), "IP address of eNB to advertise for DL GTP-U Traffic")
|
||||
("enb.s1c_bind_addr", bpo::value<string>(&args->stack.s1ap.s1c_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for S1AP connection")
|
||||
("enb.s1c_bind_port", bpo::value<uint16_t>(&args->stack.s1ap.s1c_bind_port)->default_value(0), "Source port for S1AP connection (0 means any)")
|
||||
("enb.n_prb", bpo::value<uint32_t>(&args->enb.n_prb)->default_value(25), "Number of PRB")
|
||||
("enb.nof_ports", bpo::value<uint32_t>(&args->enb.nof_ports)->default_value(1), "Number of ports")
|
||||
("enb.tm", bpo::value<uint32_t>(&args->enb.transmission_mode)->default_value(1), "Transmission mode (1-8)")
|
||||
|
|
|
@ -163,7 +163,7 @@ void sf_worker::work_imp()
|
|||
}
|
||||
|
||||
if (!running) {
|
||||
phy->worker_end(this, tx_buffer, tx_time);
|
||||
phy->worker_end(this, true, tx_buffer, tx_time, false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -201,14 +201,14 @@ void sf_worker::work_imp()
|
|||
if (sf_type == SRSRAN_SF_NORM) {
|
||||
if (stack->get_dl_sched(tti_tx_dl, dl_grants) < 0) {
|
||||
Error("Getting DL scheduling from MAC");
|
||||
phy->worker_end(this, tx_buffer, tx_time);
|
||||
phy->worker_end(this, false, tx_buffer, tx_time, false);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
dl_grants[0].cfi = mbsfn_cfg.non_mbsfn_region_length;
|
||||
if (stack->get_mch_sched(tti_tx_dl, mbsfn_cfg.is_mcch, dl_grants)) {
|
||||
Error("Getting MCH packets from MAC");
|
||||
phy->worker_end(this, tx_buffer, tx_time);
|
||||
phy->worker_end(this, false, tx_buffer, tx_time, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -216,7 +216,7 @@ void sf_worker::work_imp()
|
|||
// Get UL scheduling for the TX TTI from MAC
|
||||
if (stack->get_ul_sched(tti_tx_ul, ul_grants_tx) < 0) {
|
||||
Error("Getting UL scheduling from MAC");
|
||||
phy->worker_end(this, tx_buffer, tx_time);
|
||||
phy->worker_end(this, false, tx_buffer, tx_time, false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -243,7 +243,7 @@ void sf_worker::work_imp()
|
|||
|
||||
Debug("Sending to radio");
|
||||
tx_buffer.set_nof_samples(SRSRAN_SF_LEN_PRB(phy->get_nof_prb(0)));
|
||||
phy->worker_end(this, tx_buffer, tx_time);
|
||||
phy->worker_end(this, true, tx_buffer, tx_time, false);
|
||||
|
||||
#ifdef DEBUG_WRITE_FILE
|
||||
fwrite(signal_buffer_tx, SRSRAN_SF_LEN_PRB(phy->cell.nof_prb) * sizeof(cf_t), 1, f);
|
||||
|
|
|
@ -24,20 +24,20 @@
|
|||
|
||||
namespace srsenb {
|
||||
namespace nr {
|
||||
cc_worker::cc_worker(uint32_t cc_idx_, srslog::basic_logger& log, phy_nr_state* phy_state_) :
|
||||
cc_worker::cc_worker(uint32_t cc_idx_, srslog::basic_logger& log, phy_nr_state& phy_state_) :
|
||||
cc_idx(cc_idx_), phy_state(phy_state_), logger(log)
|
||||
{
|
||||
cf_t* buffer_c[SRSRAN_MAX_PORTS] = {};
|
||||
|
||||
// Allocate buffers
|
||||
buffer_sz = SRSRAN_SF_LEN_PRB(phy_state->args.dl.nof_max_prb);
|
||||
for (uint32_t i = 0; i < phy_state_->args.dl.nof_tx_antennas; i++) {
|
||||
buffer_sz = SRSRAN_SF_LEN_PRB(phy_state.args.dl.nof_max_prb);
|
||||
for (uint32_t i = 0; i < phy_state.args.dl.nof_tx_antennas; i++) {
|
||||
tx_buffer[i] = srsran_vec_cf_malloc(buffer_sz);
|
||||
rx_buffer[i] = srsran_vec_cf_malloc(buffer_sz);
|
||||
buffer_c[i] = tx_buffer[i];
|
||||
}
|
||||
|
||||
if (srsran_enb_dl_nr_init(&enb_dl, buffer_c, &phy_state_->args.dl)) {
|
||||
if (srsran_enb_dl_nr_init(&enb_dl, buffer_c, &phy_state.args.dl)) {
|
||||
ERROR("Error initiating UE DL NR");
|
||||
return;
|
||||
}
|
||||
|
@ -69,8 +69,8 @@ bool cc_worker::set_carrier(const srsran_carrier_nr_t* carrier)
|
|||
coreset.freq_resources[0] = true; // Enable the bottom 6 PRB for PDCCH
|
||||
coreset.duration = 2;
|
||||
|
||||
srsran_dci_cfg_nr_t dci_cfg = phy_state->cfg.get_dci_cfg();
|
||||
if (srsran_enb_dl_nr_set_pdcch_config(&enb_dl, &phy_state->cfg.pdcch, &dci_cfg) < SRSRAN_SUCCESS) {
|
||||
srsran_dci_cfg_nr_t dci_cfg = phy_state.cfg.get_dci_cfg();
|
||||
if (srsran_enb_dl_nr_set_pdcch_config(&enb_dl, &phy_state.cfg.pdcch, &dci_cfg) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error setting coreset");
|
||||
return false;
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ void cc_worker::set_tti(uint32_t tti)
|
|||
|
||||
cf_t* cc_worker::get_tx_buffer(uint32_t antenna_idx)
|
||||
{
|
||||
if (antenna_idx >= phy_state->args.dl.nof_tx_antennas) {
|
||||
if (antenna_idx >= phy_state.args.dl.nof_tx_antennas) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,7 @@ cf_t* cc_worker::get_tx_buffer(uint32_t antenna_idx)
|
|||
|
||||
cf_t* cc_worker::get_rx_buffer(uint32_t antenna_idx)
|
||||
{
|
||||
if (antenna_idx >= phy_state->args.dl.nof_tx_antennas) {
|
||||
if (antenna_idx >= phy_state.args.dl.nof_tx_antennas) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,10 +23,10 @@
|
|||
|
||||
namespace srsenb {
|
||||
namespace nr {
|
||||
sf_worker::sf_worker(phy_common* phy_, phy_nr_state* phy_state_, srslog::basic_logger& logger) :
|
||||
phy(phy_), phy_state(phy_state_), logger(logger)
|
||||
sf_worker::sf_worker(srsran::phy_common_interface& common_, phy_nr_state& phy_state_, srslog::basic_logger& logger) :
|
||||
common(common_), phy_state(phy_state_), logger(logger)
|
||||
{
|
||||
for (uint32_t i = 0; i < phy_state->args.nof_carriers; i++) {
|
||||
for (uint32_t i = 0; i < phy_state.args.nof_carriers; i++) {
|
||||
cc_worker* w = new cc_worker(i, logger, phy_state);
|
||||
cc_workers.push_back(std::unique_ptr<cc_worker>(w));
|
||||
}
|
||||
|
@ -91,14 +91,14 @@ void sf_worker::work_imp()
|
|||
// Get Transmission buffers
|
||||
srsran::rf_buffer_t tx_buffer = {};
|
||||
srsran::rf_timestamp_t dummy_ts = {};
|
||||
for (uint32_t cc = 0; cc < phy->get_nof_carriers_nr(); cc++) {
|
||||
for (uint32_t ant = 0; ant < phy->get_nof_ports(0); ant++) {
|
||||
tx_buffer.set(cc, ant, phy->get_nof_ports(0), cc_workers[cc]->get_tx_buffer(ant));
|
||||
for (uint32_t cc = 0; cc < phy_state.args.nof_carriers; cc++) {
|
||||
for (uint32_t ant = 0; ant < phy_state.args.nof_ports; ant++) {
|
||||
tx_buffer.set(cc, ant, phy_state.args.nof_ports, cc_workers[cc]->get_tx_buffer(ant));
|
||||
}
|
||||
}
|
||||
|
||||
// Configure user
|
||||
phy_state->cfg.pdsch.rbg_size_cfg_1 = false;
|
||||
phy_state.cfg.pdsch.rbg_size_cfg_1 = false;
|
||||
|
||||
// Fill grant (this comes from the scheduler)
|
||||
srsran_slot_cfg_t dl_cfg = {};
|
||||
|
@ -125,7 +125,7 @@ void sf_worker::work_imp()
|
|||
w->work_dl(dl_cfg, grants);
|
||||
}
|
||||
|
||||
phy->worker_end(this, tx_buffer, dummy_ts, true);
|
||||
common.worker_end(this, true, tx_buffer, dummy_ts, true);
|
||||
}
|
||||
|
||||
} // namespace nr
|
||||
|
|
|
@ -25,8 +25,15 @@ namespace nr {
|
|||
|
||||
worker_pool::worker_pool(uint32_t max_workers) : pool(max_workers) {}
|
||||
|
||||
bool worker_pool::init(const phy_args_t& args, phy_common* common, srslog::sink& log_sink, int prio)
|
||||
bool worker_pool::init(const phy_cell_cfg_list_nr_t& cell_list,
|
||||
const phy_args_t& args,
|
||||
srsran::phy_common_interface& common,
|
||||
srslog::sink& log_sink,
|
||||
int prio)
|
||||
{
|
||||
// Save cell list
|
||||
phy_state.cell_list = cell_list;
|
||||
|
||||
// Add workers to workers pool and start threads
|
||||
srslog::basic_levels log_level = srslog::str_to_basic_level(args.log.phy_level);
|
||||
for (uint32_t i = 0; i < args.nof_phy_threads; i++) {
|
||||
|
@ -34,11 +41,11 @@ bool worker_pool::init(const phy_args_t& args, phy_common* common, srslog::sink&
|
|||
log.set_level(log_level);
|
||||
log.set_hex_dump_max_size(args.log.phy_hex_limit);
|
||||
|
||||
auto w = new sf_worker(common, &phy_state, log);
|
||||
auto w = new sf_worker(common, phy_state, log);
|
||||
pool.init_worker(i, w, prio);
|
||||
workers.push_back(std::unique_ptr<sf_worker>(w));
|
||||
|
||||
srsran_carrier_nr_t c = common->get_cell_nr(0);
|
||||
srsran_carrier_nr_t c = phy_state.cell_list[0].carrier;
|
||||
w->set_carrier_unlocked(0, &c);
|
||||
}
|
||||
|
||||
|
|
|
@ -141,7 +141,7 @@ int phy::init(const phy_args_t& args,
|
|||
lte_workers.init(args, &workers_common, log_sink, WORKERS_THREAD_PRIO);
|
||||
}
|
||||
if (not cfg.phy_cell_cfg_nr.empty()) {
|
||||
nr_workers.init(args, &workers_common, log_sink, WORKERS_THREAD_PRIO);
|
||||
nr_workers.init(cfg.phy_cell_cfg_nr, args, workers_common, log_sink, WORKERS_THREAD_PRIO);
|
||||
}
|
||||
|
||||
// For each carrier, initialise PRACH worker
|
||||
|
|
|
@ -113,7 +113,11 @@ void phy_common::set_ul_grants(uint32_t tti, const stack_interface_phy_lte::ul_s
|
|||
* Each worker uses this function to indicate that all processing is done and data is ready for transmission or
|
||||
* there is no transmission at all (tx_enable). In that case, the end of burst message will be sent to the radio
|
||||
*/
|
||||
void phy_common::worker_end(void* tx_sem_id, srsran::rf_buffer_t& buffer, srsran::rf_timestamp_t& tx_time, bool is_nr)
|
||||
void phy_common::worker_end(void* tx_sem_id,
|
||||
bool tx_enable,
|
||||
srsran::rf_buffer_t& buffer,
|
||||
srsran::rf_timestamp_t& tx_time,
|
||||
bool is_nr)
|
||||
{
|
||||
// Wait for the green light to transmit in the current TTI
|
||||
semaphore.wait(tx_sem_id);
|
||||
|
|
|
@ -237,6 +237,7 @@ int mac::ue_set_crnti(uint16_t temp_crnti, uint16_t crnti, const sched_interface
|
|||
|
||||
int mac::cell_cfg(const std::vector<sched_interface::cell_cfg_t>& cell_cfg_)
|
||||
{
|
||||
srsran::rwlock_write_guard lock(rwlock);
|
||||
cell_config = cell_cfg_;
|
||||
return scheduler.cell_cfg(cell_config);
|
||||
}
|
||||
|
@ -340,10 +341,10 @@ int mac::push_pdu(uint32_t tti_rx,
|
|||
tti_rx,
|
||||
nof_bytes,
|
||||
(int)pdu->size());
|
||||
auto process_pdu_task = [this, rnti, ul_nof_prbs](srsran::unique_byte_buffer_t& pdu) {
|
||||
auto process_pdu_task = [this, rnti, enb_cc_idx, ul_nof_prbs](srsran::unique_byte_buffer_t& pdu) {
|
||||
srsran::rwlock_read_guard lock(rwlock);
|
||||
if (check_ue_active(rnti)) {
|
||||
ue_db[rnti]->process_pdu(std::move(pdu), ul_nof_prbs);
|
||||
ue_db[rnti]->process_pdu(std::move(pdu), enb_cc_idx, ul_nof_prbs);
|
||||
} else {
|
||||
logger.debug("Discarding PDU rnti=0x%x", rnti);
|
||||
}
|
||||
|
@ -585,6 +586,8 @@ int mac::get_dl_sched(uint32_t tti_tx_dl, dl_sched_list_t& dl_sched_res_list)
|
|||
add_padding();
|
||||
}
|
||||
|
||||
srsran::rwlock_read_guard lock(rwlock);
|
||||
|
||||
for (uint32_t enb_cc_idx = 0; enb_cc_idx < cell_config.size(); enb_cc_idx++) {
|
||||
// Run scheduler with current info
|
||||
sched_interface::dl_sched_res_t sched_result = {};
|
||||
|
@ -596,9 +599,6 @@ int mac::get_dl_sched(uint32_t tti_tx_dl, dl_sched_list_t& dl_sched_res_list)
|
|||
int n = 0;
|
||||
dl_sched_t* dl_sched_res = &dl_sched_res_list[enb_cc_idx];
|
||||
|
||||
{
|
||||
srsran::rwlock_read_guard lock(rwlock);
|
||||
|
||||
// Copy data grants
|
||||
for (uint32_t i = 0; i < sched_result.data.size(); i++) {
|
||||
uint32_t tb_count = 0;
|
||||
|
@ -657,9 +657,6 @@ int mac::get_dl_sched(uint32_t tti_tx_dl, dl_sched_list_t& dl_sched_res_list)
|
|||
}
|
||||
}
|
||||
|
||||
// No more uses of shared ue_db beyond here
|
||||
}
|
||||
|
||||
// Copy RAR grants
|
||||
for (uint32_t i = 0; i < sched_result.rar.size(); i++) {
|
||||
// Copy dci info
|
||||
|
@ -737,12 +734,9 @@ int mac::get_dl_sched(uint32_t tti_tx_dl, dl_sched_list_t& dl_sched_res_list)
|
|||
}
|
||||
|
||||
// Count number of TTIs for all active users
|
||||
{
|
||||
srsran::rwlock_read_guard lock(rwlock);
|
||||
for (auto& u : ue_db) {
|
||||
u.second->metrics_cnt();
|
||||
}
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
@ -985,9 +979,12 @@ void mac::write_mcch(const srsran::sib2_mbms_t* sib2_,
|
|||
sib13 = *sib13_;
|
||||
memcpy(mcch_payload_buffer, mcch_payload, mcch_payload_length * sizeof(uint8_t));
|
||||
current_mcch_length = mcch_payload_length;
|
||||
ue_db[SRSRAN_MRNTI] = std::unique_ptr<ue>{
|
||||
new ue(SRSRAN_MRNTI, 0, &scheduler, rrc_h, rlc_h, phy_h, logger, cells.size(), softbuffer_pool.get())};
|
||||
|
||||
std::unique_ptr<ue> ptr = std::unique_ptr<ue>{
|
||||
new ue(SRSRAN_MRNTI, args.nof_prb, &scheduler, rrc_h, rlc_h, phy_h, logger, cells.size(), softbuffer_pool.get())};
|
||||
auto ret = ue_db.insert(SRSRAN_MRNTI, std::move(ptr));
|
||||
if (!ret) {
|
||||
logger.info("Failed to allocate rnti=0x%x.for eMBMS", SRSRAN_MRNTI);
|
||||
}
|
||||
rrc_h->add_user(SRSRAN_MRNTI, {});
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,12 @@ sched_time_pf::sched_time_pf(const sched_cell_params_t& cell_params_, const sche
|
|||
|
||||
void sched_time_pf::new_tti(sched_ue_list& ue_db, sf_sched* tti_sched)
|
||||
{
|
||||
while (not dl_queue.empty()) {
|
||||
dl_queue.pop();
|
||||
}
|
||||
while (not ul_queue.empty()) {
|
||||
ul_queue.pop();
|
||||
}
|
||||
current_tti_rx = tti_point{tti_sched->get_tti_rx()};
|
||||
// remove deleted users from history
|
||||
for (auto it = ue_history_db.begin(); it != ue_history_db.end();) {
|
||||
|
|
|
@ -296,7 +296,7 @@ uint32_t ue::set_ta(int ta_)
|
|||
return nof_cmd;
|
||||
}
|
||||
|
||||
void ue::process_pdu(srsran::unique_byte_buffer_t pdu, uint32_t grant_nof_prbs)
|
||||
void ue::process_pdu(srsran::unique_byte_buffer_t pdu, uint32_t ue_cc_idx, uint32_t grant_nof_prbs)
|
||||
{
|
||||
// Unpack ULSCH MAC PDU
|
||||
mac_msg_ul.init_rx(pdu->size(), true);
|
||||
|
@ -309,11 +309,11 @@ void ue::process_pdu(srsran::unique_byte_buffer_t pdu, uint32_t grant_nof_prbs)
|
|||
}
|
||||
|
||||
if (pcap != nullptr) {
|
||||
pcap->write_ul_crnti(pdu->data(), pdu->size(), rnti, true, last_tti, UL_CC_IDX);
|
||||
pcap->write_ul_crnti(pdu->data(), pdu->size(), rnti, true, last_tti, ue_cc_idx);
|
||||
}
|
||||
|
||||
if (pcap_net != nullptr) {
|
||||
pcap_net->write_ul_crnti(pdu->data(), pdu->size(), rnti, true, last_tti, UL_CC_IDX);
|
||||
pcap_net->write_ul_crnti(pdu->data(), pdu->size(), rnti, true, last_tti, ue_cc_idx);
|
||||
}
|
||||
|
||||
uint32_t lcid_most_data = 0;
|
||||
|
|
|
@ -514,7 +514,7 @@ bool ngap::connect_amf()
|
|||
logger.info("Connecting to AMF %s:%d", args.amf_addr.c_str(), int(AMF_PORT));
|
||||
|
||||
// Init SCTP socket and bind it
|
||||
if (not sctp_init_client(&amf_socket, socket_type::seqpacket, args.ngc_bind_addr.c_str())) {
|
||||
if (not sctp_init_client(&amf_socket, socket_type::seqpacket, args.ngc_bind_addr.c_str(), 0)) {
|
||||
return false;
|
||||
}
|
||||
logger.info("SCTP socket opened. fd=%d", amf_socket.fd());
|
||||
|
|
|
@ -302,7 +302,7 @@ void rrc::write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t p
|
|||
|
||||
void rrc::notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid)
|
||||
{
|
||||
logger.warning("Received integrity protection failure indication, rnti=0x%u, lcid=%u", rnti, lcid);
|
||||
logger.warning("Received integrity protection failure indication, rnti=0x%x, lcid=%u", rnti, lcid);
|
||||
s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::unspecified);
|
||||
}
|
||||
|
||||
|
|
|
@ -485,7 +485,7 @@ bool s1ap::connect_mme()
|
|||
logger.info("Connecting to MME %s:%d", args.mme_addr.c_str(), int(MME_PORT));
|
||||
|
||||
// Init SCTP socket and bind it
|
||||
if (not sctp_init_client(&mme_socket, socket_type::seqpacket, args.s1c_bind_addr.c_str())) {
|
||||
if (not sctp_init_client(&mme_socket, socket_type::seqpacket, args.s1c_bind_addr.c_str(), args.s1c_bind_port)) {
|
||||
return false;
|
||||
}
|
||||
logger.info("SCTP socket opened. fd=%d", mme_socket.fd());
|
||||
|
|
|
@ -81,23 +81,40 @@ int test_finite_target_snr()
|
|||
}
|
||||
|
||||
// TEST: PHR is negative. Checks:
|
||||
// - one TPC should be sent to decrease power. No more TPCs != 0 should be sent until the next PHR
|
||||
// - TPCs sent should be negative or zero
|
||||
// - The accumulation of TPCs should lead to next PHR being zero.
|
||||
snr_diff = -10;
|
||||
int next_phr = -2;
|
||||
tpcfsm.set_snr(target_snr + snr_diff, tpc::PUSCH_CODE);
|
||||
tpcfsm.set_snr(target_snr + snr_diff, tpc::PUCCH_CODE);
|
||||
sum_pucch = 0;
|
||||
for (uint32_t i = 0; i < 3; ++i) {
|
||||
tpcfsm.set_phr(-2, 1);
|
||||
tpcfsm.new_tti();
|
||||
TESTASSERT(decode_tpc(tpcfsm.encode_pusch_tpc()) == -1);
|
||||
TESTASSERT(decode_tpc(tpcfsm.encode_pucch_tpc()) == 3); // PUCCH doesnt get affected by neg PHR
|
||||
tpcfsm.set_phr(next_phr, 1);
|
||||
for (uint32_t j = 0; j < 100; ++j) {
|
||||
tpcfsm.new_tti();
|
||||
TESTASSERT(decode_tpc(tpcfsm.encode_pusch_tpc()) == 0);
|
||||
int tpc_pusch = decode_tpc(tpcfsm.encode_pusch_tpc());
|
||||
TESTASSERT(tpc_pusch <= 0);
|
||||
next_phr -= tpc_pusch;
|
||||
sum_pucch += decode_tpc(tpcfsm.encode_pucch_tpc());
|
||||
}
|
||||
TESTASSERT(next_phr == 0);
|
||||
}
|
||||
tpcfsm.set_phr(20, 1);
|
||||
TESTASSERT(sum_pucch == -snr_diff); // PUCCH doesnt get affected by neg PHR
|
||||
|
||||
// TEST: PHR is positive and SINR < target SINR. Checks:
|
||||
// - accumulation of TPCs should not make next PHR negative
|
||||
// - TPCs should be positive or zero
|
||||
next_phr = 5;
|
||||
snr_diff = -10;
|
||||
tpcfsm.set_phr(next_phr, 1);
|
||||
tpcfsm.set_snr(target_snr + snr_diff, tpc::PUSCH_CODE);
|
||||
for (uint32_t j = 0; j < 100; ++j) {
|
||||
tpcfsm.new_tti();
|
||||
TESTASSERT(decode_tpc(tpcfsm.encode_pusch_tpc()) == 3);
|
||||
int tpc_pusch = decode_tpc(tpcfsm.encode_pusch_tpc());
|
||||
next_phr -= tpc_pusch;
|
||||
TESTASSERT(tpc_pusch >= 0);
|
||||
}
|
||||
TESTASSERT(next_phr == 0);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
@ -173,11 +190,49 @@ int test_undefined_target_snr()
|
|||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
void test_finite_target_snr_tpc_period_above_1()
|
||||
{
|
||||
const uint32_t nof_prbs = 50;
|
||||
const int target_snr = 15;
|
||||
|
||||
tpc tpcfsm(0x46, nof_prbs, 15, 15, true, 0, 5);
|
||||
|
||||
// TEST: While UL SNR ~ target, no TPC commands are sent
|
||||
for (uint32_t i = 0; i < 100 and tpcfsm.get_ul_snr_estim(0) < 14; ++i) {
|
||||
tpcfsm.set_snr(15, 0);
|
||||
tpcfsm.set_snr(15, 1);
|
||||
tpcfsm.new_tti();
|
||||
}
|
||||
for (uint32_t i = 0; i < 100; ++i) {
|
||||
tpcfsm.new_tti();
|
||||
TESTASSERT(decode_tpc(tpcfsm.encode_pucch_tpc()) == 0);
|
||||
TESTASSERT(decode_tpc(tpcfsm.encode_pusch_tpc()) == 0);
|
||||
}
|
||||
|
||||
// TEST: current SNR above target SNR. Checks:
|
||||
// - TPC commands should be sent to decrease power
|
||||
// - The sum power of TPC commands should not exceed the difference between current and target SNRs
|
||||
int snr_diff = 10;
|
||||
tpcfsm.set_snr(target_snr + snr_diff, tpc::PUSCH_CODE);
|
||||
tpcfsm.set_snr(target_snr + snr_diff, tpc::PUCCH_CODE);
|
||||
int sum_pusch = 0, sum_pucch = 0;
|
||||
for (uint32_t i = 0; i < 100; ++i) {
|
||||
tpcfsm.new_tti();
|
||||
int tpc = decode_tpc(tpcfsm.encode_pusch_tpc());
|
||||
TESTASSERT(tpc <= 0);
|
||||
sum_pusch += tpc;
|
||||
sum_pucch += decode_tpc(tpcfsm.encode_pucch_tpc());
|
||||
TESTASSERT(sum_pucch < 0 and sum_pucch >= -snr_diff);
|
||||
}
|
||||
TESTASSERT(sum_pusch == -snr_diff);
|
||||
}
|
||||
|
||||
} // namespace srsenb
|
||||
|
||||
int main()
|
||||
{
|
||||
TESTASSERT(srsenb::test_finite_target_snr() == 0);
|
||||
TESTASSERT(srsenb::test_undefined_target_snr() == 0);
|
||||
srsenb::test_finite_target_snr_tpc_period_above_1();
|
||||
printf("Success\n");
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ struct dummy_socket_manager : public srsran::socket_manager_itf {
|
|||
return true;
|
||||
}
|
||||
|
||||
int s1u_fd;
|
||||
int s1u_fd = 0;
|
||||
recv_callback_t callback;
|
||||
};
|
||||
|
||||
|
@ -150,7 +150,7 @@ int main(int argc, char** argv)
|
|||
args.ngc_bind_addr = "127.0.0.100";
|
||||
args.tac = 7;
|
||||
args.gtp_bind_addr = "127.0.0.100";
|
||||
args.amf_addr = amf_addr_str;
|
||||
args.amf_addr = "127.0.0.1";
|
||||
args.gnb_name = "srsgnb01";
|
||||
rrc_interface_ngap_nr rrc;
|
||||
ngap_obj.init(args, &rrc);
|
||||
|
|
|
@ -774,6 +774,31 @@ bool nas::handle_detach_request(uint32_t m_tmsi,
|
|||
ecm_ctx_t* ecm_ctx = &nas_ctx->m_ecm_ctx;
|
||||
sec_ctx_t* sec_ctx = &nas_ctx->m_sec_ctx;
|
||||
|
||||
// TS 24.301, Sec 5.5.2.2.1, UE initiated detach request
|
||||
if (detach_req.detach_type.switch_off == 0) {
|
||||
// UE expects detach accept
|
||||
srsran::unique_byte_buffer_t nas_tx = srsran::make_byte_buffer();
|
||||
if (nas_tx == nullptr) {
|
||||
nas_logger.error("Couldn't allocate PDU in %s().", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
LIBLTE_MME_DETACH_ACCEPT_MSG_STRUCT detach_accept = {};
|
||||
err = liblte_mme_pack_detach_accept_msg(&detach_accept,
|
||||
LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS,
|
||||
sec_ctx->dl_nas_count,
|
||||
(LIBLTE_BYTE_MSG_STRUCT*)nas_tx.get());
|
||||
if (err != LIBLTE_SUCCESS) {
|
||||
nas_logger.error("Error packing Detach Accept\n");
|
||||
}
|
||||
|
||||
nas_logger.info("Sending detach accept.\n");
|
||||
sec_ctx->dl_nas_count++;
|
||||
s1ap->send_downlink_nas_transport(enb_ue_s1ap_id, s1ap->get_next_mme_ue_s1ap_id(), nas_tx.get(), *enb_sri);
|
||||
} else {
|
||||
nas_logger.info("UE is switched off\n");
|
||||
}
|
||||
|
||||
gtpc->send_delete_session_request(emm_ctx->imsi);
|
||||
emm_ctx->state = EMM_STATE_DEREGISTERED;
|
||||
sec_ctx->ul_nas_count++;
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace nr {
|
|||
class cc_worker
|
||||
{
|
||||
public:
|
||||
cc_worker(uint32_t cc_idx, srslog::basic_logger& log, state* phy_state_);
|
||||
cc_worker(uint32_t cc_idx, srslog::basic_logger& log, state& phy_state_);
|
||||
~cc_worker();
|
||||
|
||||
bool update_cfg();
|
||||
|
@ -58,7 +58,7 @@ private:
|
|||
std::array<cf_t*, SRSRAN_MAX_PORTS> rx_buffer = {};
|
||||
std::array<cf_t*, SRSRAN_MAX_PORTS> tx_buffer = {};
|
||||
uint32_t buffer_sz = 0;
|
||||
state* phy = nullptr;
|
||||
state& phy;
|
||||
srsran_ssb_t ssb = {};
|
||||
srsran_ue_dl_nr_t ue_dl = {};
|
||||
srsran_ue_ul_nr_t ue_ul = {};
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
#ifndef SRSUE_NR_PHCH_WORKER_H
|
||||
#define SRSUE_NR_PHCH_WORKER_H
|
||||
|
||||
#include "../phy_common.h"
|
||||
#include "cc_worker.h"
|
||||
#include "srsran/common/thread_pool.h"
|
||||
#include "srsran/interfaces/phy_common_interface.h"
|
||||
|
||||
namespace srsue {
|
||||
namespace nr {
|
||||
|
@ -40,7 +40,7 @@ namespace nr {
|
|||
class sf_worker final : public srsran::thread_pool::worker
|
||||
{
|
||||
public:
|
||||
sf_worker(phy_common* phy, state* phy_state_, srslog::basic_logger& logger);
|
||||
sf_worker(srsran::phy_common_interface& common_, state& phy_state_, srslog::basic_logger& logger);
|
||||
~sf_worker() = default;
|
||||
|
||||
bool update_cfg(uint32_t cc_idx);
|
||||
|
@ -60,8 +60,8 @@ private:
|
|||
|
||||
std::vector<std::unique_ptr<cc_worker> > cc_workers;
|
||||
|
||||
phy_common* phy = nullptr;
|
||||
state* phy_state = nullptr;
|
||||
srsran::phy_common_interface& common;
|
||||
state& phy_state;
|
||||
srslog::basic_logger& logger;
|
||||
|
||||
uint32_t tti_rx = 0;
|
||||
|
|
|
@ -42,7 +42,7 @@ public:
|
|||
sf_worker* operator[](std::size_t pos) { return workers.at(pos).get(); }
|
||||
|
||||
worker_pool(uint32_t max_workers);
|
||||
bool init(const phy_args_nr_t& args_, phy_common* common, stack_interface_phy_nr* stack_, int prio);
|
||||
bool init(const phy_args_nr_t& args_, srsran::phy_common_interface& common, stack_interface_phy_nr* stack_, int prio);
|
||||
sf_worker* wait_worker(uint32_t tti);
|
||||
void start_worker(sf_worker* w);
|
||||
void stop();
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "srsran/adt/circular_array.h"
|
||||
#include "srsran/common/gen_mch_tables.h"
|
||||
#include "srsran/common/tti_sempahore.h"
|
||||
#include "srsran/interfaces/phy_common_interface.h"
|
||||
#include "srsran/interfaces/phy_interface_types.h"
|
||||
#include "srsran/interfaces/radio_interfaces.h"
|
||||
#include "srsran/interfaces/rrc_interface_types.h"
|
||||
|
@ -55,7 +56,7 @@ public:
|
|||
};
|
||||
|
||||
/* Subclass that manages variables common to all workers */
|
||||
class phy_common
|
||||
class phy_common : public srsran::phy_common_interface
|
||||
{
|
||||
public:
|
||||
/* Common variables used by all phy workers */
|
||||
|
@ -138,7 +139,11 @@ public:
|
|||
srsran_pdsch_ack_resource_t resource);
|
||||
bool get_dl_pending_ack(srsran_ul_sf_cfg_t* sf, uint32_t cc_idx, srsran_pdsch_ack_cc_t* ack);
|
||||
|
||||
void worker_end(void* h, bool tx_enable, srsran::rf_buffer_t& buffer, srsran::rf_timestamp_t& tx_time, bool is_nr);
|
||||
void worker_end(void* h,
|
||||
bool tx_enable,
|
||||
srsran::rf_buffer_t& buffer,
|
||||
srsran::rf_timestamp_t& tx_time,
|
||||
bool is_nr) override;
|
||||
|
||||
void set_cell(const srsran_cell_t& c);
|
||||
|
||||
|
|
|
@ -191,6 +191,7 @@ void sf_worker::work_imp()
|
|||
}
|
||||
}
|
||||
}
|
||||
tx_signal_ptr.set_nof_samples(nof_samples);
|
||||
|
||||
/***** Uplink Generation + Transmission *******/
|
||||
|
||||
|
@ -225,7 +226,6 @@ void sf_worker::work_imp()
|
|||
}
|
||||
}
|
||||
}
|
||||
tx_signal_ptr.set_nof_samples(nof_samples);
|
||||
|
||||
// Set PRACH buffer signal pointer
|
||||
if (prach_ptr) {
|
||||
|
|
|
@ -26,25 +26,25 @@
|
|||
|
||||
namespace srsue {
|
||||
namespace nr {
|
||||
cc_worker::cc_worker(uint32_t cc_idx_, srslog::basic_logger& log, state* phy_state_) :
|
||||
cc_worker::cc_worker(uint32_t cc_idx_, srslog::basic_logger& log, state& phy_state_) :
|
||||
cc_idx(cc_idx_), phy(phy_state_), logger(log)
|
||||
{
|
||||
cf_t* rx_buffer_c[SRSRAN_MAX_PORTS] = {};
|
||||
|
||||
// Allocate buffers
|
||||
buffer_sz = SRSRAN_SF_LEN_PRB(phy->args.dl.nof_max_prb) * 5;
|
||||
for (uint32_t i = 0; i < phy_state_->args.dl.nof_rx_antennas; i++) {
|
||||
buffer_sz = SRSRAN_SF_LEN_PRB(phy.args.dl.nof_max_prb) * 5;
|
||||
for (uint32_t i = 0; i < phy.args.dl.nof_rx_antennas; i++) {
|
||||
rx_buffer[i] = srsran_vec_cf_malloc(buffer_sz);
|
||||
rx_buffer_c[i] = rx_buffer[i];
|
||||
tx_buffer[i] = srsran_vec_cf_malloc(buffer_sz);
|
||||
}
|
||||
|
||||
if (srsran_ue_dl_nr_init(&ue_dl, rx_buffer.data(), &phy_state_->args.dl) < SRSRAN_SUCCESS) {
|
||||
if (srsran_ue_dl_nr_init(&ue_dl, rx_buffer.data(), &phy.args.dl) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error initiating UE DL NR");
|
||||
return;
|
||||
}
|
||||
|
||||
if (srsran_ue_ul_nr_init(&ue_ul, tx_buffer[0], &phy_state_->args.ul) < SRSRAN_SUCCESS) {
|
||||
if (srsran_ue_ul_nr_init(&ue_ul, tx_buffer[0], &phy.args.ul) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error initiating UE DL NR");
|
||||
return;
|
||||
}
|
||||
|
@ -76,38 +76,38 @@ cc_worker::~cc_worker()
|
|||
|
||||
bool cc_worker::update_cfg()
|
||||
{
|
||||
if (srsran_ue_dl_nr_set_carrier(&ue_dl, &phy->cfg.carrier) < SRSRAN_SUCCESS) {
|
||||
if (srsran_ue_dl_nr_set_carrier(&ue_dl, &phy.cfg.carrier) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error setting carrier");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (srsran_ue_ul_nr_set_carrier(&ue_ul, &phy->cfg.carrier) < SRSRAN_SUCCESS) {
|
||||
if (srsran_ue_ul_nr_set_carrier(&ue_ul, &phy.cfg.carrier) < SRSRAN_SUCCESS) {
|
||||
ERROR("Error setting carrier");
|
||||
return false;
|
||||
}
|
||||
|
||||
srsran_dci_cfg_nr_t dci_cfg = phy->cfg.get_dci_cfg();
|
||||
if (srsran_ue_dl_nr_set_pdcch_config(&ue_dl, &phy->cfg.pdcch, &dci_cfg) < SRSRAN_SUCCESS) {
|
||||
srsran_dci_cfg_nr_t dci_cfg = phy.cfg.get_dci_cfg();
|
||||
if (srsran_ue_dl_nr_set_pdcch_config(&ue_dl, &phy.cfg.pdcch, &dci_cfg) < SRSRAN_SUCCESS) {
|
||||
logger.error("Error setting NR PDCCH configuration");
|
||||
return false;
|
||||
}
|
||||
|
||||
double abs_freq_point_a_freq =
|
||||
srsran::srsran_band_helper().nr_arfcn_to_freq(phy->cfg.carrier.absolute_frequency_point_a);
|
||||
double abs_freq_ssb_freq = srsran::srsran_band_helper().nr_arfcn_to_freq(phy->cfg.carrier.absolute_frequency_ssb);
|
||||
srsran::srsran_band_helper().nr_arfcn_to_freq(phy.cfg.carrier.absolute_frequency_point_a);
|
||||
double abs_freq_ssb_freq = srsran::srsran_band_helper().nr_arfcn_to_freq(phy.cfg.carrier.absolute_frequency_ssb);
|
||||
|
||||
double carrier_center_freq = abs_freq_point_a_freq + (phy->cfg.carrier.nof_prb / 2 *
|
||||
SRSRAN_SUBC_SPACING_NR(phy->cfg.carrier.scs) * SRSRAN_NRE);
|
||||
double carrier_center_freq =
|
||||
abs_freq_point_a_freq + (phy.cfg.carrier.nof_prb / 2 * SRSRAN_SUBC_SPACING_NR(phy.cfg.carrier.scs) * SRSRAN_NRE);
|
||||
uint16_t band = srsran::srsran_band_helper().get_band_from_dl_freq_Hz(carrier_center_freq);
|
||||
|
||||
srsran_ssb_cfg_t ssb_cfg = {};
|
||||
ssb_cfg.srate_hz = srsran_min_symbol_sz_rb(phy->cfg.carrier.nof_prb) * SRSRAN_SUBC_SPACING_NR(phy->cfg.carrier.scs);
|
||||
ssb_cfg.srate_hz = srsran_min_symbol_sz_rb(phy.cfg.carrier.nof_prb) * SRSRAN_SUBC_SPACING_NR(phy.cfg.carrier.scs);
|
||||
ssb_cfg.center_freq_hz = carrier_center_freq;
|
||||
ssb_cfg.ssb_freq_hz = abs_freq_ssb_freq;
|
||||
ssb_cfg.scs = phy->cfg.ssb.scs;
|
||||
ssb_cfg.pattern = srsran::srsran_band_helper().get_ssb_pattern(band, phy->cfg.ssb.scs);
|
||||
ssb_cfg.scs = phy.cfg.ssb.scs;
|
||||
ssb_cfg.pattern = srsran::srsran_band_helper().get_ssb_pattern(band, phy.cfg.ssb.scs);
|
||||
ssb_cfg.duplex_mode = srsran::srsran_band_helper().get_duplex_mode(band);
|
||||
ssb_cfg.periodicity_ms = phy->cfg.ssb.periodicity_ms;
|
||||
ssb_cfg.periodicity_ms = phy.cfg.ssb.periodicity_ms;
|
||||
|
||||
if (srsran_ssb_set_cfg(&ssb, &ssb_cfg) < SRSRAN_SUCCESS) {
|
||||
logger.error("Error setting SSB configuration");
|
||||
|
@ -128,7 +128,7 @@ void cc_worker::set_tti(uint32_t tti)
|
|||
|
||||
cf_t* cc_worker::get_rx_buffer(uint32_t antenna_idx)
|
||||
{
|
||||
if (antenna_idx >= phy->args.dl.nof_rx_antennas) {
|
||||
if (antenna_idx >= phy.args.dl.nof_rx_antennas) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -137,7 +137,7 @@ cf_t* cc_worker::get_rx_buffer(uint32_t antenna_idx)
|
|||
|
||||
cf_t* cc_worker::get_tx_buffer(uint32_t antenna_idx)
|
||||
{
|
||||
if (antenna_idx >= phy->args.dl.nof_rx_antennas) {
|
||||
if (antenna_idx >= phy.args.dl.nof_rx_antennas) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -152,7 +152,7 @@ uint32_t cc_worker::get_buffer_len()
|
|||
void cc_worker::decode_pdcch_dl()
|
||||
{
|
||||
std::array<srsran_dci_dl_nr_t, SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR> dci_rx = {};
|
||||
srsue::mac_interface_phy_nr::sched_rnti_t rnti = phy->stack->get_dl_sched_rnti_nr(dl_slot_cfg.idx);
|
||||
srsue::mac_interface_phy_nr::sched_rnti_t rnti = phy.stack->get_dl_sched_rnti_nr(dl_slot_cfg.idx);
|
||||
|
||||
// Skip search if no valid RNTI is given
|
||||
if (rnti.id == SRSRAN_INVALID_RNTI) {
|
||||
|
@ -177,7 +177,7 @@ void cc_worker::decode_pdcch_dl()
|
|||
}
|
||||
|
||||
// Enqueue UL grants
|
||||
phy->set_dl_pending_grant(dl_slot_cfg, dci_rx[i]);
|
||||
phy.set_dl_pending_grant(dl_slot_cfg, dci_rx[i]);
|
||||
}
|
||||
|
||||
if (logger.debug.enabled()) {
|
||||
|
@ -203,7 +203,7 @@ void cc_worker::decode_pdcch_dl()
|
|||
void cc_worker::decode_pdcch_ul()
|
||||
{
|
||||
std::array<srsran_dci_ul_nr_t, SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR> dci_rx = {};
|
||||
srsue::mac_interface_phy_nr::sched_rnti_t rnti = phy->stack->get_ul_sched_rnti_nr(ul_slot_cfg.idx);
|
||||
srsue::mac_interface_phy_nr::sched_rnti_t rnti = phy.stack->get_ul_sched_rnti_nr(ul_slot_cfg.idx);
|
||||
|
||||
// Skip search if no valid RNTI is given
|
||||
if (rnti.id == SRSRAN_INVALID_RNTI) {
|
||||
|
@ -228,7 +228,7 @@ void cc_worker::decode_pdcch_ul()
|
|||
}
|
||||
|
||||
// Enqueue UL grants
|
||||
phy->set_ul_pending_grant(dl_slot_cfg.idx, dci_rx[i]);
|
||||
phy.set_ul_pending_grant(dl_slot_cfg.idx, dci_rx[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,7 +238,7 @@ bool cc_worker::decode_pdsch_dl()
|
|||
uint32_t pid = 0;
|
||||
srsran_sch_cfg_nr_t pdsch_cfg = {};
|
||||
srsran_pdsch_ack_resource_nr_t ack_resource = {};
|
||||
if (not phy->get_dl_pending_grant(dl_slot_cfg.idx, pdsch_cfg, ack_resource, pid)) {
|
||||
if (not phy.get_dl_pending_grant(dl_slot_cfg.idx, pdsch_cfg, ack_resource, pid)) {
|
||||
// Early return if no grant was available
|
||||
return true;
|
||||
}
|
||||
|
@ -251,13 +251,13 @@ bool cc_worker::decode_pdsch_dl()
|
|||
mac_dl_grant.ndi = pdsch_cfg.grant.tb[0].ndi;
|
||||
mac_dl_grant.tbs = pdsch_cfg.grant.tb[0].tbs / 8;
|
||||
mac_dl_grant.tti = dl_slot_cfg.idx;
|
||||
phy->stack->new_grant_dl(0, mac_dl_grant, &dl_action);
|
||||
phy.stack->new_grant_dl(0, mac_dl_grant, &dl_action);
|
||||
|
||||
// Abort if MAC says it doesn't need the TB
|
||||
if (not dl_action.tb.enabled) {
|
||||
// Force positive ACK
|
||||
if (pdsch_cfg.grant.rnti_type == srsran_rnti_type_c) {
|
||||
phy->set_pending_ack(dl_slot_cfg.idx, ack_resource, true);
|
||||
phy.set_pending_ack(dl_slot_cfg.idx, ack_resource, true);
|
||||
}
|
||||
|
||||
logger.info("Decoding not required. Skipping PDSCH. ack_tti_tx=%d", TTI_ADD(dl_slot_cfg.idx, ack_resource.k1));
|
||||
|
@ -311,17 +311,17 @@ bool cc_worker::decode_pdsch_dl()
|
|||
|
||||
// Enqueue PDSCH ACK information only if the RNTI is type C
|
||||
if (pdsch_cfg.grant.rnti_type == srsran_rnti_type_c) {
|
||||
phy->set_pending_ack(dl_slot_cfg.idx, ack_resource, pdsch_res.tb[0].crc);
|
||||
phy.set_pending_ack(dl_slot_cfg.idx, ack_resource, pdsch_res.tb[0].crc);
|
||||
}
|
||||
|
||||
// Notify MAC about PDSCH decoding result
|
||||
mac_interface_phy_nr::tb_action_dl_result_t mac_dl_result = {};
|
||||
mac_dl_result.ack = pdsch_res.tb[0].crc;
|
||||
mac_dl_result.payload = mac_dl_result.ack ? std::move(data) : nullptr; // only pass data when successful
|
||||
phy->stack->tb_decoded(cc_idx, mac_dl_grant, std::move(mac_dl_result));
|
||||
phy.stack->tb_decoded(cc_idx, mac_dl_grant, std::move(mac_dl_result));
|
||||
|
||||
if (pdsch_cfg.grant.rnti_type == srsran_rnti_type_ra) {
|
||||
phy->rar_grant_tti = dl_slot_cfg.idx;
|
||||
phy.rar_grant_tti = dl_slot_cfg.idx;
|
||||
}
|
||||
|
||||
if (pdsch_res.tb[0].crc) {
|
||||
|
@ -330,7 +330,7 @@ bool cc_worker::decode_pdsch_dl()
|
|||
dl_m.mcs = pdsch_cfg.grant.tb[0].mcs;
|
||||
dl_m.fec_iters = pdsch_res.tb[0].avg_iter;
|
||||
dl_m.evm = pdsch_res.evm[0];
|
||||
phy->set_dl_metrics(dl_m);
|
||||
phy.set_dl_metrics(dl_m);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -343,7 +343,7 @@ bool cc_worker::measure_csi()
|
|||
srsran_csi_trs_measurements_t meas = {};
|
||||
|
||||
// Iterate all possible candidates
|
||||
const std::array<bool, SRSRAN_SSB_NOF_CANDIDATES> position_in_burst = phy->cfg.ssb.position_in_burst;
|
||||
const std::array<bool, SRSRAN_SSB_NOF_CANDIDATES> position_in_burst = phy.cfg.ssb.position_in_burst;
|
||||
for (uint32_t ssb_idx = 0; ssb_idx < SRSRAN_SSB_NOF_CANDIDATES; ssb_idx++) {
|
||||
// Skip SSB candidate if not enabled
|
||||
if (not position_in_burst[ssb_idx]) {
|
||||
|
@ -351,7 +351,7 @@ bool cc_worker::measure_csi()
|
|||
}
|
||||
|
||||
// Measure SSB candidate
|
||||
if (srsran_ssb_csi_measure(&ssb, phy->cfg.carrier.pci, ssb_idx, rx_buffer[0], &meas) < SRSRAN_SUCCESS) {
|
||||
if (srsran_ssb_csi_measure(&ssb, phy.cfg.carrier.pci, ssb_idx, rx_buffer[0], &meas) < SRSRAN_SUCCESS) {
|
||||
logger.error("Error measuring SSB");
|
||||
return false;
|
||||
}
|
||||
|
@ -369,13 +369,13 @@ bool cc_worker::measure_csi()
|
|||
ch_metrics.rsrq = 0.0f; // Not supported
|
||||
ch_metrics.rssi = 0.0f; // Not supported
|
||||
ch_metrics.sync_err =
|
||||
meas.delay_us / (float)(ue_dl.fft->fft_plan.size * SRSRAN_SUBC_SPACING_NR(phy->cfg.carrier.scs));
|
||||
phy->set_channel_metrics(ch_metrics);
|
||||
meas.delay_us / (float)(ue_dl.fft->fft_plan.size * SRSRAN_SUBC_SPACING_NR(phy.cfg.carrier.scs));
|
||||
phy.set_channel_metrics(ch_metrics);
|
||||
|
||||
// Compute synch metrics and report it to the PHY state
|
||||
sync_metrics_t sync_metrics = {};
|
||||
sync_metrics.cfo = meas.cfo_hz;
|
||||
phy->set_sync_metrics(sync_metrics);
|
||||
phy.set_sync_metrics(sync_metrics);
|
||||
|
||||
// Report SSB candidate channel measurement to the PHY state
|
||||
// ...
|
||||
|
@ -385,7 +385,7 @@ bool cc_worker::measure_csi()
|
|||
// Iterate all NZP-CSI-RS marked as TRS and perform channel measurements
|
||||
for (uint32_t resource_set_id = 0; resource_set_id < SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_SETS; resource_set_id++) {
|
||||
// Select NZP-CSI-RS set
|
||||
const srsran_csi_rs_nzp_set_t& nzp_set = phy->cfg.pdsch.nzp_csi_rs_sets[resource_set_id];
|
||||
const srsran_csi_rs_nzp_set_t& nzp_set = phy.cfg.pdsch.nzp_csi_rs_sets[resource_set_id];
|
||||
|
||||
// Skip set if not set as TRS (it will be processed later)
|
||||
if (not nzp_set.trs_info) {
|
||||
|
@ -418,13 +418,13 @@ bool cc_worker::measure_csi()
|
|||
ch_metrics.rsrq = 0.0f; // Not supported
|
||||
ch_metrics.rssi = 0.0f; // Not supported
|
||||
ch_metrics.sync_err =
|
||||
trs_measurements.delay_us / (float)(ue_dl.fft->fft_plan.size * SRSRAN_SUBC_SPACING_NR(phy->cfg.carrier.scs));
|
||||
phy->set_channel_metrics(ch_metrics);
|
||||
trs_measurements.delay_us / (float)(ue_dl.fft->fft_plan.size * SRSRAN_SUBC_SPACING_NR(phy.cfg.carrier.scs));
|
||||
phy.set_channel_metrics(ch_metrics);
|
||||
|
||||
// Compute synch metrics and report it to the PHY state
|
||||
sync_metrics_t sync_metrics = {};
|
||||
sync_metrics.cfo = trs_measurements.cfo_hz;
|
||||
phy->set_sync_metrics(sync_metrics);
|
||||
phy.set_sync_metrics(sync_metrics);
|
||||
|
||||
// Convert to CSI channel measurement and report new NZP-CSI-RS measurement to the PHY state
|
||||
srsran_csi_channel_measurements_t measurements = {};
|
||||
|
@ -434,13 +434,13 @@ bool cc_worker::measure_csi()
|
|||
measurements.wideband_snr_db = trs_measurements.snr_dB;
|
||||
measurements.nof_ports = 1; // Other values are not supported
|
||||
measurements.K_csi_rs = (uint32_t)n;
|
||||
phy->new_nzp_csi_rs_channel_measurement(measurements, resource_set_id);
|
||||
phy.new_nzp_csi_rs_channel_measurement(measurements, resource_set_id);
|
||||
}
|
||||
|
||||
// Iterate all NZP-CSI-RS not marked as TRS and perform channel measurements
|
||||
for (uint32_t resource_set_id = 0; resource_set_id < SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_SETS; resource_set_id++) {
|
||||
// Select NZP-CSI-RS set
|
||||
const srsran_csi_rs_nzp_set_t& nzp_set = phy->cfg.pdsch.nzp_csi_rs_sets[resource_set_id];
|
||||
const srsran_csi_rs_nzp_set_t& nzp_set = phy.cfg.pdsch.nzp_csi_rs_sets[resource_set_id];
|
||||
|
||||
// Skip set if set as TRS (it was processed previously)
|
||||
if (nzp_set.trs_info) {
|
||||
|
@ -467,7 +467,7 @@ bool cc_worker::measure_csi()
|
|||
measurements.wideband_snr_db);
|
||||
|
||||
// Report new measurement to the PHY state
|
||||
phy->new_nzp_csi_rs_channel_measurement(measurements, resource_set_id);
|
||||
phy.new_nzp_csi_rs_channel_measurement(measurements, resource_set_id);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -481,7 +481,7 @@ bool cc_worker::work_dl()
|
|||
}
|
||||
|
||||
// Check if it is a DL slot, if not skip
|
||||
if (!srsran_tdd_nr_is_dl(&phy->cfg.tdd, 0, dl_slot_cfg.idx)) {
|
||||
if (!srsran_tdd_nr_is_dl(&phy.cfg.tdd, 0, dl_slot_cfg.idx)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -512,7 +512,7 @@ bool cc_worker::work_dl()
|
|||
bool cc_worker::work_ul()
|
||||
{
|
||||
// Check if it is a UL slot, if not skip
|
||||
if (!srsran_tdd_nr_is_ul(&phy->cfg.tdd, 0, ul_slot_cfg.idx)) {
|
||||
if (!srsran_tdd_nr_is_ul(&phy.cfg.tdd, 0, ul_slot_cfg.idx)) {
|
||||
// No NR signal shall be transmitted
|
||||
srsran_vec_cf_zero(tx_buffer[0], ue_ul.ifft.sf_sz);
|
||||
return true;
|
||||
|
@ -523,11 +523,11 @@ bool cc_worker::work_ul()
|
|||
|
||||
// Gather PDSCH ACK information
|
||||
srsran_pdsch_ack_nr_t pdsch_ack = {};
|
||||
bool has_ul_ack = phy->get_pending_ack(ul_slot_cfg.idx, pdsch_ack);
|
||||
bool has_ul_ack = phy.get_pending_ack(ul_slot_cfg.idx, pdsch_ack);
|
||||
|
||||
// Request grant to PHY state for this transmit TTI
|
||||
srsran_sch_cfg_nr_t pusch_cfg = {};
|
||||
bool has_pusch_grant = phy->get_ul_pending_grant(ul_slot_cfg.idx, pusch_cfg, pid);
|
||||
bool has_pusch_grant = phy.get_ul_pending_grant(ul_slot_cfg.idx, pusch_cfg, pid);
|
||||
|
||||
// If PDSCH UL ACK is available, load into UCI
|
||||
if (has_ul_ack) {
|
||||
|
@ -540,7 +540,7 @@ bool cc_worker::work_ul()
|
|||
}
|
||||
}
|
||||
|
||||
if (srsran_ue_dl_nr_gen_ack(&phy->cfg.harq_ack, &pdsch_ack, &uci_data) < SRSRAN_SUCCESS) {
|
||||
if (srsran_ue_dl_nr_gen_ack(&phy.cfg.harq_ack, &pdsch_ack, &uci_data) < SRSRAN_SUCCESS) {
|
||||
ERROR("Filling UCI ACK bits");
|
||||
return false;
|
||||
}
|
||||
|
@ -548,11 +548,11 @@ bool cc_worker::work_ul()
|
|||
|
||||
// Add SR to UCI data only if there is no UL grant!
|
||||
if (!has_ul_ack) {
|
||||
phy->get_pending_sr(ul_slot_cfg.idx, uci_data);
|
||||
phy.get_pending_sr(ul_slot_cfg.idx, uci_data);
|
||||
}
|
||||
|
||||
// Add CSI reports to UCI data if available
|
||||
phy->get_periodic_csi(ul_slot_cfg.idx, uci_data);
|
||||
phy.get_periodic_csi(ul_slot_cfg.idx, uci_data);
|
||||
|
||||
if (has_pusch_grant) {
|
||||
// Notify MAC about PUSCH found grant
|
||||
|
@ -565,7 +565,7 @@ bool cc_worker::work_ul()
|
|||
mac_ul_grant.ndi = pusch_cfg.grant.tb[0].ndi;
|
||||
mac_ul_grant.rv = pusch_cfg.grant.tb[0].rv;
|
||||
mac_ul_grant.is_rar_grant = (pusch_cfg.grant.rnti_type == srsran_rnti_type_ra);
|
||||
phy->stack->new_grant_ul(0, mac_ul_grant, &ul_action);
|
||||
phy.stack->new_grant_ul(0, mac_ul_grant, &ul_action);
|
||||
|
||||
// Don't process further if MAC can't provide PDU
|
||||
if (not ul_action.tb.enabled) {
|
||||
|
@ -574,7 +574,7 @@ bool cc_worker::work_ul()
|
|||
}
|
||||
|
||||
// Set UCI configuration following procedures
|
||||
srsran_ra_ul_set_grant_uci_nr(&phy->cfg.carrier, &phy->cfg.pusch, &uci_data.cfg, &pusch_cfg);
|
||||
srsran_ra_ul_set_grant_uci_nr(&phy.cfg.carrier, &phy.cfg.pusch, &uci_data.cfg, &pusch_cfg);
|
||||
|
||||
// Assigning MAC provided values to PUSCH config structs
|
||||
pusch_cfg.grant.tb[0].softbuffer.tx = ul_action.tb.softbuffer;
|
||||
|
@ -621,18 +621,18 @@ bool cc_worker::work_ul()
|
|||
ul_metrics_t ul_m = {};
|
||||
ul_m.mcs = pusch_cfg.grant.tb[0].mcs;
|
||||
ul_m.power = srsran_convert_power_to_dB(srsran_vec_avg_power_cf(tx_buffer[0], ue_ul.ifft.sf_sz));
|
||||
phy->set_ul_metrics(ul_m);
|
||||
phy.set_ul_metrics(ul_m);
|
||||
|
||||
} else if (srsran_uci_nr_total_bits(&uci_data.cfg) > 0) {
|
||||
// Get PUCCH resource
|
||||
srsran_pucch_nr_resource_t resource = {};
|
||||
if (srsran_ra_ul_nr_pucch_resource(&phy->cfg.pucch, &uci_data.cfg, &resource) < SRSRAN_SUCCESS) {
|
||||
if (srsran_ra_ul_nr_pucch_resource(&phy.cfg.pucch, &uci_data.cfg, &resource) < SRSRAN_SUCCESS) {
|
||||
ERROR("Selecting PUCCH resource");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Encode PUCCH message
|
||||
if (srsran_ue_ul_nr_encode_pucch(&ue_ul, &ul_slot_cfg, &phy->cfg.pucch.common, &resource, &uci_data) <
|
||||
if (srsran_ue_ul_nr_encode_pucch(&ue_ul, &ul_slot_cfg, &phy.cfg.pucch.common, &resource, &uci_data) <
|
||||
SRSRAN_SUCCESS) {
|
||||
ERROR("Encoding PUCCH");
|
||||
return false;
|
||||
|
|
|
@ -36,10 +36,10 @@ static int plot_worker_id = -1;
|
|||
|
||||
namespace srsue {
|
||||
namespace nr {
|
||||
sf_worker::sf_worker(phy_common* phy_, state* phy_state_, srslog::basic_logger& log) :
|
||||
phy_state(phy_state_), phy(phy_), logger(log)
|
||||
sf_worker::sf_worker(srsran::phy_common_interface& common_, state& phy_state_, srslog::basic_logger& log) :
|
||||
phy_state(phy_state_), common(common_), logger(log)
|
||||
{
|
||||
for (uint32_t i = 0; i < phy_state->args.nof_carriers; i++) {
|
||||
for (uint32_t i = 0; i < phy_state.args.nof_carriers; i++) {
|
||||
cc_worker* w = new cc_worker(i, log, phy_state);
|
||||
cc_workers.push_back(std::unique_ptr<cc_worker>(w));
|
||||
}
|
||||
|
@ -88,8 +88,8 @@ void sf_worker::work_imp()
|
|||
}
|
||||
|
||||
// Align workers, wait for previous workers to finish DL processing before starting UL processing
|
||||
phy_state->dl_ul_semaphore.wait(this);
|
||||
phy_state->dl_ul_semaphore.release();
|
||||
phy_state.dl_ul_semaphore.wait(this);
|
||||
phy_state.dl_ul_semaphore.release();
|
||||
|
||||
// Check if PRACH is available
|
||||
if (prach_ptr != nullptr) {
|
||||
|
@ -97,14 +97,14 @@ void sf_worker::work_imp()
|
|||
tx_buffer.set(0, prach_ptr);
|
||||
|
||||
// Notify MAC about PRACH transmission
|
||||
phy_state->stack->prach_sent(TTI_TX(tti_rx),
|
||||
srsran_prach_nr_start_symbol_fr1_unpaired(phy_state->cfg.prach.config_idx),
|
||||
SRSRAN_SLOT_NR_MOD(phy_state->cfg.carrier.scs, TTI_TX(tti_rx)),
|
||||
phy_state.stack->prach_sent(TTI_TX(tti_rx),
|
||||
srsran_prach_nr_start_symbol_fr1_unpaired(phy_state.cfg.prach.config_idx),
|
||||
SRSRAN_SLOT_NR_MOD(phy_state.cfg.carrier.scs, TTI_TX(tti_rx)),
|
||||
0,
|
||||
0);
|
||||
|
||||
// Transmit NR PRACH
|
||||
phy->worker_end(this, false, tx_buffer, dummy_ts, true);
|
||||
common.worker_end(this, true, tx_buffer, dummy_ts, true);
|
||||
|
||||
// Reset PRACH pointer
|
||||
prach_ptr = nullptr;
|
||||
|
@ -123,7 +123,7 @@ void sf_worker::work_imp()
|
|||
}
|
||||
|
||||
// Always call worker_end before returning
|
||||
phy->worker_end(this, false, tx_buffer, dummy_ts, true);
|
||||
common.worker_end(this, true, tx_buffer, dummy_ts, true);
|
||||
|
||||
// Tell the plotting thread to draw the plots
|
||||
#ifdef ENABLE_GUI
|
||||
|
|
|
@ -25,7 +25,10 @@ namespace nr {
|
|||
|
||||
worker_pool::worker_pool(uint32_t max_workers) : pool(max_workers), logger(srslog::fetch_basic_logger("PHY-NR")) {}
|
||||
|
||||
bool worker_pool::init(const phy_args_nr_t& args, phy_common* common, stack_interface_phy_nr* stack_, int prio)
|
||||
bool worker_pool::init(const phy_args_nr_t& args,
|
||||
srsran::phy_common_interface& common,
|
||||
stack_interface_phy_nr* stack_,
|
||||
int prio)
|
||||
{
|
||||
phy_state.stack = stack_;
|
||||
phy_state.args = args;
|
||||
|
@ -52,7 +55,7 @@ bool worker_pool::init(const phy_args_nr_t& args, phy_common* common, stack_inte
|
|||
log.set_level(srslog::str_to_basic_level(args.log.phy_level));
|
||||
log.set_hex_dump_max_size(args.log.phy_hex_limit);
|
||||
|
||||
auto w = new sf_worker(common, &phy_state, log);
|
||||
auto w = new sf_worker(common, phy_state, log);
|
||||
pool.init_worker(i, w, prio, args.worker_cpu_mask);
|
||||
workers.push_back(std::unique_ptr<sf_worker>(w));
|
||||
}
|
||||
|
|
|
@ -624,7 +624,7 @@ void phy::set_mch_period_stop(uint32_t stop)
|
|||
|
||||
int phy::init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_)
|
||||
{
|
||||
if (!nr_workers.init(args_, &common, stack_, WORKERS_THREAD_PRIO)) {
|
||||
if (!nr_workers.init(args_, common, stack_, WORKERS_THREAD_PRIO)) {
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
|
|
|
@ -229,6 +229,7 @@ void ra_proc::initialization()
|
|||
preambleTransmissionCounter = 1;
|
||||
mux_unit->msg3_flush();
|
||||
backoff_param_ms = 0;
|
||||
transmitted_crnti = 0;
|
||||
resource_selection();
|
||||
}
|
||||
|
||||
|
@ -414,10 +415,12 @@ void ra_proc::tb_decoded_ok(const uint8_t cc_idx, const uint32_t tti)
|
|||
rar_pdu_msg.get()->get_ta_cmd(),
|
||||
rar_pdu_msg.get()->get_temp_crnti());
|
||||
|
||||
// Save Temp-CRNTI before generating the reply
|
||||
rntis->temp_rnti = rar_pdu_msg.get()->get_temp_crnti();
|
||||
|
||||
// Perform actions when preamble was selected by UE MAC
|
||||
if (preambleIndex <= 0) {
|
||||
mux_unit->msg3_prepare();
|
||||
rntis->temp_rnti = rar_pdu_msg.get()->get_temp_crnti();
|
||||
|
||||
// If this is the first successfully received RAR within this procedure, Msg3 is empty
|
||||
if (mux_unit->msg3_is_empty()) {
|
||||
|
|
|
@ -2339,6 +2339,7 @@ int mac_random_access_test()
|
|||
my_test.rar_nof_invalid_rapid = 0;
|
||||
my_test.check_ra_successful = true;
|
||||
my_test.temp_rnti++; // Temporal C-RNTI has to change to avoid duplicate
|
||||
my_test.crnti = 0;
|
||||
TESTASSERT(!run_mac_ra_test(my_test, &mac, &phy, &tti, &stack));
|
||||
stack.run_tti(tti++);
|
||||
TESTASSERT(rrc.ho_finish_successful);
|
||||
|
|
|
@ -31,3 +31,5 @@ if (ZEROMQ_FOUND AND ENABLE_ZMQ_TEST)
|
|||
endforeach (cell_n_prb)
|
||||
endforeach (num_cc)
|
||||
endif (ZEROMQ_FOUND AND ENABLE_ZMQ_TEST)
|
||||
|
||||
add_subdirectory(phy)
|
|
@ -0,0 +1,25 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB)
|
||||
add_executable(nr_phy_test nr_phy_test.cc)
|
||||
target_link_libraries(nr_phy_test
|
||||
srsue_phy_nr
|
||||
srsue_phy
|
||||
srsran_common
|
||||
srsran_phy
|
||||
srsran_radio
|
||||
srsenb_phy
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${Boost_LIBRARIES}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${Boost_LIBRARIES}
|
||||
${ATOMIC_LIBS})
|
||||
|
||||
add_nr_test(nr_phy_test nr_phy_test)
|
||||
endif ()
|
|
@ -0,0 +1,113 @@
|
|||
/**
|
||||
*
|
||||
* \section COPYRIGHT
|
||||
*
|
||||
* Copyright 2013-2021 Software Radio Systems Limited
|
||||
*
|
||||
* By using this file, you agree to the terms and conditions set
|
||||
* forth in the LICENSE file which can be found at the top level of
|
||||
* the distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "srsenb/hdr/phy/nr/worker_pool.h"
|
||||
#include "srsran/common/test_common.h"
|
||||
#include "srsue/hdr/phy/nr/worker_pool.h"
|
||||
|
||||
class phy_common : public srsran::phy_common_interface
|
||||
{
|
||||
public:
|
||||
void
|
||||
worker_end(void* h, bool tx_enable, srsran::rf_buffer_t& buffer, srsran::rf_timestamp_t& tx_time, bool is_nr) override
|
||||
{}
|
||||
};
|
||||
|
||||
class ue_dummy_stack : public srsue::stack_interface_phy_nr
|
||||
{
|
||||
public:
|
||||
void in_sync() override {}
|
||||
void out_of_sync() override {}
|
||||
void run_tti(const uint32_t tti) override {}
|
||||
int sf_indication(const uint32_t tti) override { return 0; }
|
||||
sched_rnti_t get_dl_sched_rnti_nr(const uint32_t tti) override { return sched_rnti_t(); }
|
||||
sched_rnti_t get_ul_sched_rnti_nr(const uint32_t tti) override { return sched_rnti_t(); }
|
||||
void new_grant_dl(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, tb_action_dl_t* action) override {}
|
||||
void tb_decoded(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, tb_action_dl_result_t result) override {}
|
||||
void new_grant_ul(const uint32_t cc_idx, const mac_nr_grant_ul_t& grant, tb_action_ul_t* action) override {}
|
||||
void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id) override {}
|
||||
bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx) override { return false; }
|
||||
};
|
||||
|
||||
class test_bench
|
||||
{
|
||||
private:
|
||||
srsenb::nr::worker_pool gnb_phy;
|
||||
phy_common gnb_phy_com;
|
||||
srsue::nr::worker_pool ue_phy;
|
||||
phy_common ue_phy_com;
|
||||
ue_dummy_stack ue_stack;
|
||||
bool initialised = false;
|
||||
|
||||
public:
|
||||
struct args_t {
|
||||
uint32_t nof_threads = 6;
|
||||
uint32_t nof_prb = 52;
|
||||
|
||||
bool parse(int argc, char** argv);
|
||||
};
|
||||
|
||||
test_bench(const args_t& args) : ue_phy(args.nof_threads), gnb_phy(args.nof_threads)
|
||||
{
|
||||
// Prepare cell list
|
||||
srsenb::phy_cell_cfg_list_nr_t cell_list(1);
|
||||
cell_list[0].carrier.nof_prb = args.nof_prb;
|
||||
|
||||
// Prepare gNb PHY arguments
|
||||
srsenb::phy_args_t gnb_phy_args = {};
|
||||
|
||||
// Initialise gnb
|
||||
if (not gnb_phy.init(cell_list, gnb_phy_args, gnb_phy_com, srslog::get_default_sink(), 31)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare PHY
|
||||
srsue::phy_args_nr_t ue_phy_args = {};
|
||||
|
||||
// Initialise UE PHY
|
||||
if (not ue_phy.init(ue_phy_args, ue_phy_com, &ue_stack, 31)) {
|
||||
return;
|
||||
}
|
||||
|
||||
initialised = true;
|
||||
}
|
||||
|
||||
~test_bench()
|
||||
{
|
||||
gnb_phy.stop();
|
||||
ue_phy.stop();
|
||||
}
|
||||
|
||||
bool is_initialised() const { return initialised; }
|
||||
};
|
||||
|
||||
bool test_bench::args_t::parse(int argc, char** argv)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
test_bench::args_t args = {};
|
||||
|
||||
// Parse arguments
|
||||
TESTASSERT(args.parse(argc, argv));
|
||||
|
||||
// Create test bench
|
||||
test_bench tb(args);
|
||||
|
||||
// Assert bench is initialised correctly
|
||||
TESTASSERT(tb.is_initialised());
|
||||
|
||||
// If reached here, the test is successful
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
Loading…
Reference in New Issue