diff --git a/CMakeLists.txt b/CMakeLists.txt index 5242bf265..030c92f1a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,7 +67,7 @@ option(ENABLE_SRSENB "Build srsENB application" ON) option(ENABLE_SRSEPC "Build srsEPC application" ON) option(DISABLE_SIMD "Disable SIMD instructions" OFF) option(AUTO_DETECT_ISA "Autodetect supported ISA extensions" ON) - + option(ENABLE_GUI "Enable GUI (using srsGUI)" ON) option(ENABLE_UHD "Enable UHD" ON) option(ENABLE_BLADERF "Enable BladeRF" ON) diff --git a/lib/examples/npdsch_ue.c b/lib/examples/npdsch_ue.c index 9fd23b883..a43c4e048 100644 --- a/lib/examples/npdsch_ue.c +++ b/lib/examples/npdsch_ue.c @@ -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 diff --git a/lib/examples/pssch_ue.c b/lib/examples/pssch_ue.c index cb9b28c68..2f14b909e 100644 --- a/lib/examples/pssch_ue.c +++ b/lib/examples/pssch_ue.c @@ -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 diff --git a/lib/include/srsran/adt/accumulators.h b/lib/include/srsran/adt/accumulators.h index 2748b3032..9cf9d76f3 100644 --- a/lib/include/srsran/adt/accumulators.h +++ b/lib/include/srsran/adt/accumulators.h @@ -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; diff --git a/lib/include/srsran/common/nas_pcap.h b/lib/include/srsran/common/nas_pcap.h index 1079d331e..3bec3e3ef 100644 --- a/lib/include/srsran/common/nas_pcap.h +++ b/lib/include/srsran/common/nas_pcap.h @@ -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 @@ -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); diff --git a/lib/include/srsran/common/network_utils.h b/lib/include/srsran/common/network_utils.h index eaff437cf..741bce934 100644 --- a/lib/include/srsran/common/network_utils.h +++ b/lib/include/srsran/common/network_utils.h @@ -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 diff --git a/lib/include/srsran/common/pcap.h b/lib/include/srsran/common/pcap.h index a3b337aea..9731ea5f6 100644 --- a/lib/include/srsran/common/pcap.h +++ b/lib/include/srsran/common/pcap.h @@ -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); diff --git a/lib/include/srsran/interfaces/enb_s1ap_interfaces.h b/lib/include/srsran/interfaces/enb_s1ap_interfaces.h index 1ff458b2c..952561904 100644 --- a/lib/include/srsran/interfaces/enb_s1ap_interfaces.h +++ b/lib/include/srsran/interfaces/enb_s1ap_interfaces.h @@ -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; }; diff --git a/lib/include/srsran/interfaces/gnb_ngap_interfaces.h b/lib/include/srsran/interfaces/gnb_ngap_interfaces.h index 490374ae4..3ce15bcfd 100644 --- a/lib/include/srsran/interfaces/gnb_ngap_interfaces.h +++ b/lib/include/srsran/interfaces/gnb_ngap_interfaces.h @@ -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 diff --git a/lib/include/srsran/interfaces/phy_common_interface.h b/lib/include/srsran/interfaces/phy_common_interface.h new file mode 100644 index 000000000..5dec8e06c --- /dev/null +++ b/lib/include/srsran/interfaces/phy_common_interface.h @@ -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 diff --git a/lib/include/srsran/phy/common/phy_common_nr.h b/lib/include/srsran/phy/common/phy_common_nr.h index 031cafb15..e4b93a001 100644 --- a/lib/include/srsran/phy/common/phy_common_nr.h +++ b/lib/include/srsran/phy/common/phy_common_nr.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 diff --git a/lib/include/srsran/phy/sync/ssb.h b/lib/include/srsran/phy/sync/ssb.h index 474b94a91..93a9835f7 100644 --- a/lib/include/srsran/phy/sync/ssb.h +++ b/lib/include/srsran/phy/sync/ssb.h @@ -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 diff --git a/lib/include/srsran/phy/ue/ue_sync_nr.h b/lib/include/srsran/phy/ue/ue_sync_nr.h new file mode 100644 index 000000000..da4fcb381 --- /dev/null +++ b/lib/include/srsran/phy/ue/ue_sync_nr.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 diff --git a/lib/src/common/mac_pcap.cc b/lib/src/common/mac_pcap.cc index 073b0d390..02f46a366 100644 --- a/lib/src/common/mac_pcap.cc +++ b/lib/src/common/mac_pcap.cc @@ -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 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; } diff --git a/lib/src/common/nas_pcap.cc b/lib/src/common/nas_pcap.cc index 71702458a..6c2031b86 100644 --- a/lib/src/common/nas_pcap.cc +++ b/lib/src/common/nas_pcap.cc @@ -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()); + filename = filename_; + 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) diff --git a/lib/src/common/network_utils.cc b/lib/src/common/network_utils.cc index b76beba02..81136d59e 100644 --- a/lib/src/common/network_utils.cc +++ b/lib/src/common/network_utils.cc @@ -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) diff --git a/lib/src/common/pcap.c b/lib/src/common/pcap.c index 597acfcfa..f33e73931 100644 --- a/lib/src/common/pcap.c +++ b/lib/src/common/pcap.c @@ -27,7 +27,7 @@ #include /* 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); diff --git a/lib/src/common/rlc_pcap.cc b/lib/src/common/rlc_pcap.cc index 15d94e16d..9b1241ddc 100644 --- a/lib/src/common/rlc_pcap.cc +++ b/lib/src/common/rlc_pcap.cc @@ -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_) diff --git a/lib/src/common/s1ap_pcap.cc b/lib/src/common/s1ap_pcap.cc index 833a4e244..697e5c29b 100644 --- a/lib/src/common/s1ap_pcap.cc +++ b/lib/src/common/s1ap_pcap.cc @@ -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) diff --git a/lib/src/mac/pdu_queue.cc b/lib/src/mac/pdu_queue.cc index 8cbb8cbe4..db6080a11 100644 --- a/lib/src/mac/pdu_queue.cc +++ b/lib/src/mac/pdu_queue.cc @@ -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) diff --git a/lib/src/phy/common/phy_common_nr.c b/lib/src/phy/common/phy_common_nr.c index 4e0ac59b9..7948615c9 100644 --- a/lib/src/phy/common/phy_common_nr.c +++ b/lib/src/phy/common/phy_common_nr.c @@ -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; +} diff --git a/lib/src/phy/sync/ssb.c b/lib/src/phy/sync/ssb.c index 537acc7ec..eea257f73 100644 --- a/lib/src/phy/sync/ssb.c +++ b/lib/src/phy/sync/ssb.c @@ -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); +} diff --git a/lib/src/phy/ue/test/CMakeLists.txt b/lib/src/phy/ue/test/CMakeLists.txt index ee3fb9d8b..ccd27785c 100644 --- a/lib/src/phy/ue/test/CMakeLists.txt +++ b/lib/src/phy/ue/test/CMakeLists.txt @@ -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) diff --git a/lib/src/phy/ue/test/ue_sync_nr_test.c b/lib/src/phy/ue/test/ue_sync_nr_test.c new file mode 100644 index 000000000..eff7480ce --- /dev/null +++ b/lib/src/phy/ue/test/ue_sync_nr_test.c @@ -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 +#include + +// 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; +} \ No newline at end of file diff --git a/lib/src/phy/ue/ue_sync_nr.c b/lib/src/phy/ue/ue_sync_nr.c new file mode 100644 index 000000000..97ae3df10 --- /dev/null +++ b/lib/src/phy/ue/ue_sync_nr.c @@ -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; +} diff --git a/lib/src/upper/pdcp_entity_base.cc b/lib/src/upper/pdcp_entity_base.cc index 92f420324..a346c22da 100644 --- a/lib/src/upper/pdcp_entity_base.cc +++ b/lib/src/upper/pdcp_entity_base.cc @@ -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; diff --git a/lib/src/upper/rlc_am_lte.cc b/lib/src/upper/rlc_am_lte.cc index 80883bdb0..5ba6b16de 100644 --- a/lib/src/upper/rlc_am_lte.cc +++ b/lib/src/upper/rlc_am_lte.cc @@ -1177,48 +1177,55 @@ void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t no return; } - std::unique_lock 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; - logger.debug(payload, nof_bytes, "%s Rx control PDU", RB_NAME); + { + std::lock_guard lock(mutex); - rlc_status_pdu_t status; - rlc_am_read_status_pdu(payload, nof_bytes, &status); + logger.debug(payload, nof_bytes, "%s Rx control PDU", RB_NAME); - log_rlc_am_status_pdu_to_string(logger.info, "%s Rx Status PDU: %s", &status, RB_NAME); + rlc_am_read_status_pdu(payload, nof_bytes, &status); - // make sure ACK_SN is within our Tx window - if (((MOD + status.ack_sn - vt_a) % MOD > RLC_AM_WINDOW_SIZE) || - ((MOD + vt_s - status.ack_sn) % MOD > RLC_AM_WINDOW_SIZE)) { - logger.warning("%s Received invalid status PDU (ack_sn=%d, vt_a=%d, vt_s=%d). Dropping PDU.", - RB_NAME, - status.ack_sn, - vt_a, - vt_s); - return; - } + log_rlc_am_status_pdu_to_string(logger.info, "%s Rx Status PDU: %s", &status, RB_NAME); - // Sec 5.2.2.2, stop poll reTx timer if status PDU comprises a positive _or_ negative acknowledgement - // for the RLC data PDU with sequence number poll_sn - if (poll_retx_timer.is_valid() && (TX_MOD_BASE(poll_sn) < TX_MOD_BASE(status.ack_sn))) { - logger.debug("%s Stopping pollRetx timer", RB_NAME); - poll_retx_timer.stop(); - } + // make sure ACK_SN is within our Tx window + if (((MOD + status.ack_sn - vt_a) % MOD > RLC_AM_WINDOW_SIZE) || + ((MOD + vt_s - status.ack_sn) % MOD > RLC_AM_WINDOW_SIZE)) { + logger.warning("%s Received invalid status PDU (ack_sn=%d, vt_a=%d, vt_s=%d). Dropping PDU.", + RB_NAME, + status.ack_sn, + vt_a, + vt_s); + return; + } - // flush retx queue to avoid unordered SNs, we expect the Rx to request lost PDUs again - if (status.N_nack > 0) { - retx_queue.clear(); - } + // Sec 5.2.2.2, stop poll reTx timer if status PDU comprises a positive _or_ negative acknowledgement + // for the RLC data PDU with sequence number poll_sn + if (poll_retx_timer.is_valid() && (TX_MOD_BASE(poll_sn) < TX_MOD_BASE(status.ack_sn))) { + logger.debug("%s Stopping pollRetx timer", RB_NAME); + poll_retx_timer.stop(); + } - // Handle ACKs and NACKs - bool update_vt_a = true; - uint32_t i = vt_a; + // flush retx queue to avoid unordered SNs, we expect the Rx to request lost PDUs again + if (status.N_nack > 0) { + retx_queue.clear(); + } + + 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 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 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 - 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(); + { + // Make sure vt_a points to valid SN + std::lock_guard 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); diff --git a/lib/src/upper/rlc_um_lte.cc b/lib/src/upper/rlc_um_lte.cc index a067c410a..eb3acd37d 100644 --- a/lib/src/upper/rlc_um_lte.cc +++ b/lib/src/upper/rlc_um_lte.cc @@ -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) { diff --git a/lib/test/common/network_utils_test.cc b/lib/test/common/network_utils_test.cc index 03afc55fc..a4ae87f7c 100644 --- a/lib/test/common/network_utils_test.cc +++ b/lib/test/common/network_utils_test.cc @@ -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)); diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example index 36b3ee83e..4eacc23e1 100644 --- a/srsenb/enb.conf.example +++ b/srsenb/enb.conf.example @@ -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 diff --git a/srsenb/hdr/phy/nr/cc_worker.h b/srsenb/hdr/phy/nr/cc_worker.h index 835db67a6..28c2df7a8 100644 --- a/srsenb/hdr/phy/nr/cc_worker.h +++ b/srsenb/hdr/phy/nr/cc_worker.h @@ -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 tx_buffer = {}; std::array 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; }; diff --git a/srsenb/hdr/phy/nr/sf_worker.h b/srsenb/hdr/phy/nr/sf_worker.h index bcd838134..59d581395 100644 --- a/srsenb/hdr/phy/nr/sf_worker.h +++ b/srsenb/hdr/phy/nr/sf_worker.h @@ -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,9 +58,9 @@ private: std::vector > cc_workers; - phy_common* phy = nullptr; - phy_nr_state* phy_state = nullptr; - srslog::basic_logger& logger; + srsran::phy_common_interface& common; + phy_nr_state& phy_state; + srslog::basic_logger& logger; // Temporal attributes srsran_softbuffer_tx_t softbuffer_tx = {}; @@ -70,4 +70,4 @@ private: } // namespace nr } // namespace srsenb -#endif // SRSUE_NR_PHCH_WORKER_H +#endif // SRSENB_NR_PHCH_WORKER_H diff --git a/srsenb/hdr/phy/nr/worker_pool.h b/srsenb/hdr/phy/nr/worker_pool.h index 4d26bfdba..680a553b3 100644 --- a/srsenb/hdr/phy/nr/worker_pool.h +++ b/srsenb/hdr/phy/nr/worker_pool.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 diff --git a/srsenb/hdr/phy/phy_common.h b/srsenb/hdr/phy/phy_common.h index 8b0ae1d04..feebbdbb0 100644 --- a/srsenb/hdr/phy/phy_common.h +++ b/srsenb/hdr/phy/phy_common.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) { diff --git a/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h b/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h index cd691dcda..1ad983f3a 100644 --- a/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h +++ b/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h @@ -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); } - 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); + + // 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); } - // target SINR is finite and there is power headroom - float diff = target_snr_dB - ch_snr.last_snr_sample; - 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(ch_snr.pending_delta) > last_phr) { - // cap PUSCH TPC when PHR is low - ch_snr.pending_delta = last_phr > 1 ? 1 : 0; + 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); } - 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; + snr = ch_snr.tpc_snr_avg.value(); } + + float diff = target_snr_dB - snr; + diff -= ch_snr.win_tpc_values.value() + ch_snr.acc_tpc_values; + 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::null_value(); // SNR average estimation with irregular sample spacing @@ -243,10 +245,12 @@ private: srsran::exp_average_irreg_sampling snr_avg; int last_snr_sample; // Accumulation of past TPC commands - srsran::sliding_sum win_tpc_values; - int8_t pending_delta = 0; - int acc_tpc_values = 0; - uint32_t last_tpc_tti_count = 0; + srsran::sliding_sum win_tpc_values; + 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 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), diff --git a/srsenb/hdr/stack/mac/ue.h b/srsenb/hdr/stack/mac/ue.h index 963d67f97..95b89ecd3 100644 --- a/srsenb/hdr/stack/mac/ue.h +++ b/srsenb/hdr/stack/mac/ue.h @@ -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 diff --git a/srsenb/hdr/stack/ngap/ngap.h b/srsenb/hdr/stack/ngap/ngap.h index 811c8bce9..428a14cbb 100644 --- a/srsenb/hdr/stack/ngap/ngap.h +++ b/srsenb/hdr/stack/ngap/ngap.h @@ -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; diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 54139b936..dad7fbd82 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -83,6 +83,7 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("enb.gtp_bind_addr", bpo::value(&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(&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(&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(&args->stack.s1ap.s1c_bind_port)->default_value(0), "Source port for S1AP connection (0 means any)") ("enb.n_prb", bpo::value(&args->enb.n_prb)->default_value(25), "Number of PRB") ("enb.nof_ports", bpo::value(&args->enb.nof_ports)->default_value(1), "Number of ports") ("enb.tm", bpo::value(&args->enb.transmission_mode)->default_value(1), "Transmission mode (1-8)") diff --git a/srsenb/src/phy/lte/sf_worker.cc b/srsenb/src/phy/lte/sf_worker.cc index baf99ae83..39a7fe828 100644 --- a/srsenb/src/phy/lte/sf_worker.cc +++ b/srsenb/src/phy/lte/sf_worker.cc @@ -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); diff --git a/srsenb/src/phy/nr/cc_worker.cc b/srsenb/src/phy/nr/cc_worker.cc index b9b4e49fe..f50badb34 100644 --- a/srsenb/src/phy/nr/cc_worker.cc +++ b/srsenb/src/phy/nr/cc_worker.cc @@ -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; } diff --git a/srsenb/src/phy/nr/sf_worker.cc b/srsenb/src/phy/nr/sf_worker.cc index 4352868fb..adf781da5 100644 --- a/srsenb/src/phy/nr/sf_worker.cc +++ b/srsenb/src/phy/nr/sf_worker.cc @@ -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(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 diff --git a/srsenb/src/phy/nr/worker_pool.cc b/srsenb/src/phy/nr/worker_pool.cc index befe9801f..4d46e9cf0 100644 --- a/srsenb/src/phy/nr/worker_pool.cc +++ b/srsenb/src/phy/nr/worker_pool.cc @@ -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(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); } diff --git a/srsenb/src/phy/phy.cc b/srsenb/src/phy/phy.cc index 779d45c0a..8d07bbc8f 100644 --- a/srsenb/src/phy/phy.cc +++ b/srsenb/src/phy/phy.cc @@ -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 diff --git a/srsenb/src/phy/phy_common.cc b/srsenb/src/phy/phy_common.cc index 4f6c471ea..c0e511b25 100644 --- a/srsenb/src/phy/phy_common.cc +++ b/srsenb/src/phy/phy_common.cc @@ -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); diff --git a/srsenb/src/stack/mac/mac.cc b/srsenb/src/stack/mac/mac.cc index aace8d77d..77badc48f 100644 --- a/srsenb/src/stack/mac/mac.cc +++ b/srsenb/src/stack/mac/mac.cc @@ -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& 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,68 +599,62 @@ 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; - // Copy data grants - for (uint32_t i = 0; i < sched_result.data.size(); i++) { - uint32_t tb_count = 0; + // Get UE + uint16_t rnti = sched_result.data[i].dci.rnti; - // Get UE - uint16_t rnti = sched_result.data[i].dci.rnti; + if (ue_db.contains(rnti)) { + // Copy dci info + dl_sched_res->pdsch[n].dci = sched_result.data[i].dci; - if (ue_db.contains(rnti)) { - // Copy dci info - dl_sched_res->pdsch[n].dci = sched_result.data[i].dci; + for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) { + dl_sched_res->pdsch[n].softbuffer_tx[tb] = + ue_db[rnti]->get_tx_softbuffer(enb_cc_idx, sched_result.data[i].dci.pid, tb); - for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) { - dl_sched_res->pdsch[n].softbuffer_tx[tb] = - ue_db[rnti]->get_tx_softbuffer(enb_cc_idx, sched_result.data[i].dci.pid, tb); + // If the Rx soft-buffer is not given, abort transmission + if (dl_sched_res->pdsch[n].softbuffer_tx[tb] == nullptr) { + continue; + } - // If the Rx soft-buffer is not given, abort transmission - if (dl_sched_res->pdsch[n].softbuffer_tx[tb] == nullptr) { - continue; + if (sched_result.data[i].nof_pdu_elems[tb] > 0) { + /* Get PDU if it's a new transmission */ + dl_sched_res->pdsch[n].data[tb] = ue_db[rnti]->generate_pdu(enb_cc_idx, + sched_result.data[i].dci.pid, + tb, + sched_result.data[i].pdu[tb], + sched_result.data[i].nof_pdu_elems[tb], + sched_result.data[i].tbs[tb]); + + if (!dl_sched_res->pdsch[n].data[tb]) { + logger.error("Error! PDU was not generated (rnti=0x%04x, tb=%d)", rnti, tb); } - if (sched_result.data[i].nof_pdu_elems[tb] > 0) { - /* Get PDU if it's a new transmission */ - dl_sched_res->pdsch[n].data[tb] = ue_db[rnti]->generate_pdu(enb_cc_idx, - sched_result.data[i].dci.pid, - tb, - sched_result.data[i].pdu[tb], - sched_result.data[i].nof_pdu_elems[tb], - sched_result.data[i].tbs[tb]); - - if (!dl_sched_res->pdsch[n].data[tb]) { - logger.error("Error! PDU was not generated (rnti=0x%04x, tb=%d)", rnti, tb); - } - - if (pcap) { - pcap->write_dl_crnti( - dl_sched_res->pdsch[n].data[tb], sched_result.data[i].tbs[tb], rnti, true, tti_tx_dl, enb_cc_idx); - } - if (pcap_net) { - pcap_net->write_dl_crnti( - dl_sched_res->pdsch[n].data[tb], sched_result.data[i].tbs[tb], rnti, true, tti_tx_dl, enb_cc_idx); - } - } else { - /* TB not enabled OR no data to send: set pointers to NULL */ - dl_sched_res->pdsch[n].data[tb] = nullptr; + if (pcap) { + pcap->write_dl_crnti( + dl_sched_res->pdsch[n].data[tb], sched_result.data[i].tbs[tb], rnti, true, tti_tx_dl, enb_cc_idx); } - - tb_count++; + if (pcap_net) { + pcap_net->write_dl_crnti( + dl_sched_res->pdsch[n].data[tb], sched_result.data[i].tbs[tb], rnti, true, tti_tx_dl, enb_cc_idx); + } + } else { + /* TB not enabled OR no data to send: set pointers to NULL */ + dl_sched_res->pdsch[n].data[tb] = nullptr; } - // Count transmission if at least one TB has succesfully added - if (tb_count > 0) { - n++; - } - } else { - logger.warning("Invalid DL scheduling result. User 0x%x does not exist", rnti); + tb_count++; } - } - // No more uses of shared ue_db beyond here + // Count transmission if at least one TB has succesfully added + if (tb_count > 0) { + n++; + } + } else { + logger.warning("Invalid DL scheduling result. User 0x%x does not exist", rnti); + } } // Copy RAR grants @@ -737,11 +734,8 @@ 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(); - } + for (auto& u : ue_db) { + u.second->metrics_cnt(); } return SRSRAN_SUCCESS; @@ -830,9 +824,9 @@ int mac::get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res int requested_bytes = (mcs_data.tbs / 8 > (int)mch.mtch_sched[mtch_index].lcid_buffer_size) ? (mch.mtch_sched[mtch_index].lcid_buffer_size) : ((mcs_data.tbs / 8) - 2); - int bytes_received = ue_db[SRSRAN_MRNTI]->read_pdu(current_lcid, mtch_payload_buffer, requested_bytes); - mch.pdu[0].lcid = current_lcid; - mch.pdu[0].nbytes = bytes_received; + int bytes_received = ue_db[SRSRAN_MRNTI]->read_pdu(current_lcid, mtch_payload_buffer, requested_bytes); + mch.pdu[0].lcid = current_lcid; + mch.pdu[0].nbytes = bytes_received; mch.mtch_sched[0].mtch_payload = mtch_payload_buffer; dl_sched_res->pdsch[0].dci.rnti = SRSRAN_MRNTI; if (bytes_received) { @@ -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{ - new ue(SRSRAN_MRNTI, 0, &scheduler, rrc_h, rlc_h, phy_h, logger, cells.size(), softbuffer_pool.get())}; - + std::unique_ptr ptr = std::unique_ptr{ + 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, {}); } diff --git a/srsenb/src/stack/mac/schedulers/sched_time_pf.cc b/srsenb/src/stack/mac/schedulers/sched_time_pf.cc index 3387d66be..62e144c74 100644 --- a/srsenb/src/stack/mac/schedulers/sched_time_pf.cc +++ b/srsenb/src/stack/mac/schedulers/sched_time_pf.cc @@ -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();) { diff --git a/srsenb/src/stack/mac/ue.cc b/srsenb/src/stack/mac/ue.cc index ea9548591..b05004315 100644 --- a/srsenb/src/stack/mac/ue.cc +++ b/srsenb/src/stack/mac/ue.cc @@ -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; diff --git a/srsenb/src/stack/ngap/ngap.cc b/srsenb/src/stack/ngap/ngap.cc index 1ac1ab475..6ed213af7 100644 --- a/srsenb/src/stack/ngap/ngap.cc +++ b/srsenb/src/stack/ngap/ngap.cc @@ -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()); diff --git a/srsenb/src/stack/rrc/rrc.cc b/srsenb/src/stack/rrc/rrc.cc index 99a686d0d..bec38cd59 100644 --- a/srsenb/src/stack/rrc/rrc.cc +++ b/srsenb/src/stack/rrc/rrc.cc @@ -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); } diff --git a/srsenb/src/stack/s1ap/s1ap.cc b/srsenb/src/stack/s1ap/s1ap.cc index a8aec4fa1..abff8a24d 100644 --- a/srsenb/src/stack/s1ap/s1ap.cc +++ b/srsenb/src/stack/s1ap/s1ap.cc @@ -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()); diff --git a/srsenb/test/mac/sched_tpc_test.cc b/srsenb/test/mac/sched_tpc_test.cc index 6c9221928..5569c4a10 100644 --- a/srsenb/test/mac/sched_tpc_test.cc +++ b/srsenb/test/mac/sched_tpc_test.cc @@ -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 - snr_diff = -10; + // - 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); + } + 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(); + int tpc_pusch = decode_tpc(tpcfsm.encode_pusch_tpc()); + next_phr -= tpc_pusch; + TESTASSERT(tpc_pusch >= 0); } - tpcfsm.set_phr(20, 1); - tpcfsm.new_tti(); - TESTASSERT(decode_tpc(tpcfsm.encode_pusch_tpc()) == 3); + 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"); } diff --git a/srsenb/test/ngap/ngap_test.cc b/srsenb/test/ngap/ngap_test.cc index 4f7699966..f2fcb33f9 100644 --- a/srsenb/test/ngap/ngap_test.cc +++ b/srsenb/test/ngap/ngap_test.cc @@ -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); diff --git a/srsepc/src/mme/nas.cc b/srsepc/src/mme/nas.cc index 1633a2164..bc582be5d 100644 --- a/srsepc/src/mme/nas.cc +++ b/srsepc/src/mme/nas.cc @@ -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++; diff --git a/srsue/hdr/phy/nr/cc_worker.h b/srsue/hdr/phy/nr/cc_worker.h index 20940ca87..5082c0bac 100644 --- a/srsue/hdr/phy/nr/cc_worker.h +++ b/srsue/hdr/phy/nr/cc_worker.h @@ -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,10 +58,10 @@ private: std::array rx_buffer = {}; std::array tx_buffer = {}; uint32_t buffer_sz = 0; - state* phy = nullptr; - srsran_ssb_t ssb = {}; - srsran_ue_dl_nr_t ue_dl = {}; - srsran_ue_ul_nr_t ue_ul = {}; + state& phy; + srsran_ssb_t ssb = {}; + srsran_ue_dl_nr_t ue_dl = {}; + srsran_ue_ul_nr_t ue_ul = {}; srslog::basic_logger& logger; // Methods for DCI blind search diff --git a/srsue/hdr/phy/nr/sf_worker.h b/srsue/hdr/phy/nr/sf_worker.h index b4c8534b0..71e0aebeb 100644 --- a/srsue/hdr/phy/nr/sf_worker.h +++ b/srsue/hdr/phy/nr/sf_worker.h @@ -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,9 +60,9 @@ private: std::vector > cc_workers; - phy_common* phy = nullptr; - state* phy_state = nullptr; - srslog::basic_logger& logger; + srsran::phy_common_interface& common; + state& phy_state; + srslog::basic_logger& logger; uint32_t tti_rx = 0; cf_t* prach_ptr = nullptr; diff --git a/srsue/hdr/phy/nr/worker_pool.h b/srsue/hdr/phy/nr/worker_pool.h index c4daf6f8c..384f95b61 100644 --- a/srsue/hdr/phy/nr/worker_pool.h +++ b/srsue/hdr/phy/nr/worker_pool.h @@ -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(); diff --git a/srsue/hdr/phy/phy_common.h b/srsue/hdr/phy/phy_common.h index bbd62795f..264e767a6 100644 --- a/srsue/hdr/phy/phy_common.h +++ b/srsue/hdr/phy/phy_common.h @@ -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); diff --git a/srsue/src/phy/lte/sf_worker.cc b/srsue/src/phy/lte/sf_worker.cc index 7ae722ea9..c6ad785ac 100644 --- a/srsue/src/phy/lte/sf_worker.cc +++ b/srsue/src/phy/lte/sf_worker.cc @@ -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) { diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index b21d71f18..818169d86 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -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); - uint16_t band = srsran::srsran_band_helper().get_band_from_dl_freq_Hz(carrier_center_freq); + 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 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 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 position_in_burst = phy->cfg.ssb.position_in_burst; + const std::array 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; diff --git a/srsue/src/phy/nr/sf_worker.cc b/srsue/src/phy/nr/sf_worker.cc index 7ff3d7c66..478245d54 100644 --- a/srsue/src/phy/nr/sf_worker.cc +++ b/srsue/src/phy/nr/sf_worker.cc @@ -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(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)), - 0, - 0); + 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 diff --git a/srsue/src/phy/nr/worker_pool.cc b/srsue/src/phy/nr/worker_pool.cc index fef7ce239..23d245e03 100644 --- a/srsue/src/phy/nr/worker_pool.cc +++ b/srsue/src/phy/nr/worker_pool.cc @@ -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(w)); } diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc index b1b6c7992..98c5d18f4 100644 --- a/srsue/src/phy/phy.cc +++ b/srsue/src/phy/phy.cc @@ -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; } diff --git a/srsue/src/stack/mac/proc_ra.cc b/srsue/src/stack/mac/proc_ra.cc index 44c6524a9..6357bdce7 100644 --- a/srsue/src/stack/mac/proc_ra.cc +++ b/srsue/src/stack/mac/proc_ra.cc @@ -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()) { diff --git a/srsue/src/stack/mac/test/mac_test.cc b/srsue/src/stack/mac/test/mac_test.cc index 52e00e0aa..4fe055fd0 100644 --- a/srsue/src/stack/mac/test/mac_test.cc +++ b/srsue/src/stack/mac/test/mac_test.cc @@ -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); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 776fc00e0..7ee3b05b0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/test/phy/CMakeLists.txt b/test/phy/CMakeLists.txt new file mode 100644 index 000000000..fd5f8e3f5 --- /dev/null +++ b/test/phy/CMakeLists.txt @@ -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 () \ No newline at end of file diff --git a/test/phy/nr_phy_test.cc b/test/phy/nr_phy_test.cc new file mode 100644 index 000000000..7d4f7b825 --- /dev/null +++ b/test/phy/nr_phy_test.cc @@ -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; +} \ No newline at end of file