diff --git a/CHANGELOG b/CHANGELOG index 74a35a449..202e5d490 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,12 @@ Change Log for Releases ======================= +## 22.04.1 + * Various bug fixes in RLC AM and PDCP for NR + * Fix crash when UE attempted to reestablish in SA + * Remove fixed coreset0 index for SSB + * Add support for SIB5 and SIB6 transmission in LTE + ## 22.04 * Added baseline 5G-SA support to srsUE and srsENB * Added dynamic loading of RF libraries diff --git a/cmake/modules/SRSRANVersion.cmake b/cmake/modules/SRSRANVersion.cmake index 618d15e4c..7a259ecbb 100644 --- a/cmake/modules/SRSRANVersion.cmake +++ b/cmake/modules/SRSRANVersion.cmake @@ -20,6 +20,6 @@ SET(SRSRAN_VERSION_MAJOR 22) SET(SRSRAN_VERSION_MINOR 04) -SET(SRSRAN_VERSION_PATCH 0) +SET(SRSRAN_VERSION_PATCH 1) SET(SRSRAN_VERSION_STRING "${SRSRAN_VERSION_MAJOR}.${SRSRAN_VERSION_MINOR}.${SRSRAN_VERSION_PATCH}") SET(SRSRAN_SOVERSION 0) diff --git a/lib/include/srsran/asn1/liblte_mme.h b/lib/include/srsran/asn1/liblte_mme.h index 33d1d6115..93d29f348 100644 --- a/lib/include/srsran/asn1/liblte_mme.h +++ b/lib/include/srsran/asn1/liblte_mme.h @@ -2430,6 +2430,7 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_security_protected_nas_msg(LIBLTE_BYTE_MSG_STR #define LIBLTE_MME_EPS_NETWORK_FEATURE_SUPPORT_IEI 0x64 #define LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_IEI 0xF #define LIBLTE_MME_T3412_EXTENDED_VALUE_IEI 0x5E +#define LIBLTE_MME_ADDITIONAL_INFORMATION_IEI 0x65 // Enums // Structs typedef struct { diff --git a/lib/src/asn1/liblte_mme.cc b/lib/src/asn1/liblte_mme.cc index e3d1cff84..cfde8de37 100644 --- a/lib/src/asn1/liblte_mme.cc +++ b/lib/src/asn1/liblte_mme.cc @@ -7600,7 +7600,11 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_downlink_generic_nas_transport_msg( liblte_mme_pack_generic_message_container_ie(&dl_generic_nas_transport->generic_msg_cont, &msg_ptr); // Additional Information - liblte_mme_pack_additional_information_ie(&dl_generic_nas_transport->add_info, &msg_ptr); + if (dl_generic_nas_transport->add_info_present) { + *msg_ptr = LIBLTE_MME_ADDITIONAL_INFORMATION_IEI; + msg_ptr++; + liblte_mme_pack_additional_information_ie(&dl_generic_nas_transport->add_info, &msg_ptr); + } // Fill in the number of bytes used msg->N_bytes = msg_ptr - msg->msg; @@ -7637,8 +7641,13 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_downlink_generic_nas_transport_msg( liblte_mme_unpack_generic_message_container_ie(&msg_ptr, &dl_generic_nas_transport->generic_msg_cont); // Additional Information - liblte_mme_unpack_additional_information_ie(&msg_ptr, &dl_generic_nas_transport->add_info); - + if (LIBLTE_MME_ADDITIONAL_INFORMATION_IEI == *msg_ptr) { + msg_ptr++; + liblte_mme_unpack_additional_information_ie(&msg_ptr, &dl_generic_nas_transport->add_info); + dl_generic_nas_transport->add_info_present = true; + } else { + dl_generic_nas_transport->add_info_present = false; + } err = LIBLTE_SUCCESS; } @@ -9053,7 +9062,7 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_deactivate_eps_bearer_context_request_msg( *msg_ptr = count & 0xFF; msg_ptr++; } - + // Protocol Discriminator and EPS Bearer ID *msg_ptr = (deact_eps_bearer_context_req->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); msg_ptr++; diff --git a/lib/test/asn1/srsran_asn1_nas_test.cc b/lib/test/asn1/srsran_asn1_nas_test.cc index ebfcb4398..871975260 100644 --- a/lib/test/asn1/srsran_asn1_nas_test.cc +++ b/lib/test/asn1/srsran_asn1_nas_test.cc @@ -20,19 +20,12 @@ */ #include "srsran/asn1/liblte_mme.h" +#include "srsran/common/test_common.h" #include "srsran/srslog/srslog.h" #include #include #include -#define TESTASSERT(cond) \ - { \ - if (!(cond)) { \ - std::cout << "[" << __FUNCTION__ << "][Line " << __LINE__ << "]: FAIL at " << (#cond) << std::endl; \ - return -1; \ - } \ - } - int nas_dedicated_eps_bearer_context_setup_request_test() { auto& nas_logger = srslog::fetch_basic_logger("NAS", false); @@ -112,7 +105,95 @@ int nas_dedicated_eps_bearer_context_setup_request_test() srslog::flush(); printf("Test NAS Activate Dedicated EPS Bearer Context Request successfull\n"); - return 0; + return SRSRAN_SUCCESS; +} + +int downlink_generic_nas_transport_unpacking_test() +{ + uint8_t nas_message[] = { + 0x27, 0xae, 0x80, 0xc8, 0xf9, 0x06, 0x07, 0x68, 0x01, 0x00, 0x06, 0xf0, 0x00, 0x00, 0x00, 0x08, 0x70}; + srsran::unique_byte_buffer_t buf; + LIBLTE_MME_DOWNLINK_GENERIC_NAS_TRANSPORT_MSG_STRUCT dl_generic_nas_transport; + LIBLTE_ERROR_ENUM err; + + copy_msg_to_buffer(buf, nas_message); + err = liblte_mme_unpack_downlink_generic_nas_transport_msg((LIBLTE_BYTE_MSG_STRUCT*)buf.get(), + &dl_generic_nas_transport); + TESTASSERT(err == LIBLTE_SUCCESS); + TESTASSERT(dl_generic_nas_transport.generic_msg_cont_type == 1); + TESTASSERT(dl_generic_nas_transport.generic_msg_cont.N_bytes == 6); + TESTASSERT(dl_generic_nas_transport.add_info_present == false); + + return SRSRAN_SUCCESS; +} + +int downlink_generic_nas_transport_packing_test() +{ + uint8_t nas_message[] = { + 0x27, 0x00, 0x00, 0x00, 0x00, 0xff, 0x07, 0x68, 0x01, 0x00, 0x06, 0xf0, 0x00, 0x00, 0x00, 0x08, 0x70}; + uint8_t generic_msg_cont[] = {0xf0, 0x00, 0x00, 0x00, 0x08, 0x70}; + LIBLTE_BYTE_MSG_STRUCT buf; + LIBLTE_MME_DOWNLINK_GENERIC_NAS_TRANSPORT_MSG_STRUCT dl_generic_nas_transport; + LIBLTE_ERROR_ENUM err; + + dl_generic_nas_transport.generic_msg_cont_type = 1; + dl_generic_nas_transport.generic_msg_cont.N_bytes = sizeof(generic_msg_cont); + memcpy(dl_generic_nas_transport.generic_msg_cont.msg, generic_msg_cont, sizeof(generic_msg_cont)); + dl_generic_nas_transport.add_info_present = false; + + err = liblte_mme_pack_downlink_generic_nas_transport_msg( + &dl_generic_nas_transport, LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED, 0xffffffff, &buf); + + TESTASSERT(err == LIBLTE_SUCCESS); + TESTASSERT(buf.N_bytes == sizeof(nas_message)); + TESTASSERT(memcmp(buf.msg, nas_message, buf.N_bytes) == 0); + return SRSRAN_SUCCESS; +} + +int downlink_generic_nas_transport_with_add_info_unpacking_test() +{ + uint8_t nas_message[] = {0x27, 0xae, 0x80, 0xc8, 0xf9, 0x06, 0x07, 0x68, 0x01, 0x00, 0x06, + 0xf0, 0x00, 0x00, 0x00, 0x08, 0x70, 0x65, 0x02, 0x11, 0x11}; + srsran::unique_byte_buffer_t buf; + LIBLTE_MME_DOWNLINK_GENERIC_NAS_TRANSPORT_MSG_STRUCT dl_generic_nas_transport; + LIBLTE_ERROR_ENUM err; + + copy_msg_to_buffer(buf, nas_message); + err = liblte_mme_unpack_downlink_generic_nas_transport_msg((LIBLTE_BYTE_MSG_STRUCT*)buf.get(), + &dl_generic_nas_transport); + TESTASSERT(err == LIBLTE_SUCCESS); + TESTASSERT(dl_generic_nas_transport.generic_msg_cont_type == 1); + TESTASSERT(dl_generic_nas_transport.generic_msg_cont.N_bytes == 6); + TESTASSERT(dl_generic_nas_transport.add_info_present == true); + TESTASSERT(dl_generic_nas_transport.add_info.N_octets == 2); + + return SRSRAN_SUCCESS; +} + +int downlink_generic_nas_transport_with_add_info_packing_test() +{ + uint8_t nas_message[] = {0x27, 0x00, 0x00, 0x00, 0x00, 0xff, 0x07, 0x68, 0x01, 0x00, 0x06, + 0xf0, 0x00, 0x00, 0x00, 0x08, 0x70, 0x65, 0x02, 0x11, 0x11}; + uint8_t generic_msg_cont[] = {0xf0, 0x00, 0x00, 0x00, 0x08, 0x70}; + uint8_t add_info[] = {0x11, 0x11}; + LIBLTE_BYTE_MSG_STRUCT buf; + LIBLTE_MME_DOWNLINK_GENERIC_NAS_TRANSPORT_MSG_STRUCT dl_generic_nas_transport; + LIBLTE_ERROR_ENUM err; + + dl_generic_nas_transport.generic_msg_cont_type = 1; + dl_generic_nas_transport.generic_msg_cont.N_bytes = sizeof(generic_msg_cont); + memcpy(dl_generic_nas_transport.generic_msg_cont.msg, generic_msg_cont, sizeof(generic_msg_cont)); + dl_generic_nas_transport.add_info_present = true; + dl_generic_nas_transport.add_info.N_octets = sizeof(add_info); + memcpy(dl_generic_nas_transport.add_info.info, add_info, sizeof(add_info)); + + err = liblte_mme_pack_downlink_generic_nas_transport_msg( + &dl_generic_nas_transport, LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED, 0xffffffff, &buf); + + TESTASSERT(err == LIBLTE_SUCCESS); + TESTASSERT(buf.N_bytes == sizeof(nas_message)); + TESTASSERT(memcmp(buf.msg, nas_message, buf.N_bytes) == 0); + return SRSRAN_SUCCESS; } int main(int argc, char** argv) @@ -121,9 +202,13 @@ int main(int argc, char** argv) asn1_logger.set_level(srslog::basic_levels::debug); asn1_logger.set_hex_dump_max_size(-1); - srslog::init(); + srsran::test_init(argc, argv); - int result = nas_dedicated_eps_bearer_context_setup_request_test(); + TESTASSERT(nas_dedicated_eps_bearer_context_setup_request_test() == SRSRAN_SUCCESS); + TESTASSERT(downlink_generic_nas_transport_unpacking_test() == SRSRAN_SUCCESS); + TESTASSERT(downlink_generic_nas_transport_packing_test() == SRSRAN_SUCCESS); + TESTASSERT(downlink_generic_nas_transport_with_add_info_unpacking_test() == SRSRAN_SUCCESS); + TESTASSERT(downlink_generic_nas_transport_with_add_info_packing_test() == SRSRAN_SUCCESS); - return result; + return SRSRAN_SUCCESS; } diff --git a/srsenb/hdr/phy/phy_common.h b/srsenb/hdr/phy/phy_common.h index 174179f3b..64f8dfdb5 100644 --- a/srsenb/hdr/phy/phy_common.h +++ b/srsenb/hdr/phy/phy_common.h @@ -151,6 +151,21 @@ public: return ret; } + double get_ssb_freq_hz(uint32_t cc_idx) + { + double ret = 0.0; + + if (cc_idx < cell_list_lte.size()) { + ret = cell_list_lte[cc_idx].dl_freq_hz; + } + + cc_idx -= cell_list_lte.size(); + if (cc_idx < cell_list_nr.size()) { + ret = cell_list_nr[cc_idx].carrier.ssb_center_freq_hz; + } + + return ret; + } uint32_t get_rf_port(uint32_t cc_idx) { uint32_t ret = 0; diff --git a/srsenb/hdr/stack/rrc/rrc_paging.h b/srsenb/hdr/stack/rrc/rrc_paging.h index 5bef508d4..8a5c71f1e 100644 --- a/srsenb/hdr/stack/rrc/rrc_paging.h +++ b/srsenb/hdr/stack/rrc/rrc_paging.h @@ -41,7 +41,7 @@ public: T(default_paging_cycle_), Nb(static_cast((float)T * nb_)), N(std::min(T, Nb)), - Ns(std::max(1U, Nb)), + Ns(std::max(1U, static_cast(nb_))), logger(srslog::fetch_basic_logger("RRC")) { for (subframe_info& sf_obj : sf_pending_pcch) { diff --git a/srsenb/sib.conf.example b/srsenb/sib.conf.example index 69bc9c077..c38cdbeb2 100644 --- a/srsenb/sib.conf.example +++ b/srsenb/sib.conf.example @@ -137,6 +137,71 @@ sib3 = } }; +##################################################################### +# sib5 configuration options (See TS 36.331) +# Contains information relevant for inter-frequency cell re-selection. +# Must be added to sib1::sched_info::si_mapping_info array parameter to be transmitted +# +# inter_freq_carrier_freq_list: A list of neighbouring inter-frequencies. +# dl_carrier_freq: The EARFCN for the EUTRA carrier frequency. +# q_rx_lev_min: Minimum received RSRP level in the E-UTRA cell, ([field_val] * 2) = [level in dBm]. +# p_max: Optional maximum allowed transmission power for the neighbouring E-UTRA cells on this carrier frequency. +# t_resel_eutra: Cell reselection timer (seconds). +# t_resel_eutra_sf: Optional speed dependent ScalingFactor for t_resel_eutra. +# sf_medium: Scaling factor if the UE is in Medium Mobility state, one of "0.25", "0.5", "0.75" or "1.0". +# sf_high: Scaling factor if the UE is in High Mobility state, one of "0.25", "0.5", "0.75" or "1.0". +# thresh_x_high: Srclev threshold (dB) to select to a higher-priority RAT/Frequency. +# thresh_x_low: Srclev threshold (dB) to select to a lower-priority RAT/Frequency. +# allowed_meas_bw: Maximum allowed measurement bandwidth on a carrier frequency . +# presence_ant_port_1: whether all the neighbouring cells use Antenna Port 1. +# cell_resel_prio: Optional absolute priority of the carrier frequency group. +# neigh_cell_cfg: Information related to MBSFN and TDD UL/DL configuration of neighbour cells. +# q_offset_freq: Frequency specific offset for equal priority E-UTRAN frequencies. +# inter_freq_neigh_cell_list: A List of inter-frequency neighbouring cells with specific cell re-selection parameters. +# phys_cell_id: Physical layer identity of the cell. +# q_offset_cell: Cell spcific offset. +# inter_freq_black_cell_list: A List of blacklisted inter-frequency neighbouring cells. +# start: The lowest physical cell identity in the range. +# range: The number of physical cell identities in the range. +# +##################################################################### +sib5 = +{ + inter_freq_carrier_freq_list = + ( + { + dl_carrier_freq = 1450; + q_rx_lev_min = -70; + t_resel_eutra = 2; + t_resel_eutra_sf = { + sf_medium = "0.25"; + sf_high = "1.0"; + }; + thresh_x_high = 3; + thresh_x_low = 2; + allowed_meas_bw = 75; + presence_ant_port_1 = True; + cell_resel_prio = 4; + neigh_cell_cfg = 2; + q_offset_freq = -6; + inter_freq_neigh_cell_list = + ( + { + phys_cell_id = 500; + q_offset_cell = 2; + } + ); + inter_freq_black_cell_list = + ( + { + start = 123; + range = 4; + } + ); + } + ); +}; + ##################################################################### # sib6 configuration options (See TS 36.331) # Contains UTRA neighbor information for inter-rat handover. diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index c9b642192..df348c2ce 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -151,6 +151,156 @@ int field_intra_black_cell_list::parse(libconfig::Setting& root) return 0; } +int field_inter_freq_carrier_freq_list::parse(libconfig::Setting& root) +{ + data->inter_freq_carrier_freq_list.resize((uint32_t)root.getLength()); + for (uint32_t i = 0; i < data->inter_freq_carrier_freq_list.size() && i < ASN1_RRC_MAX_FREQ; i++) { + unsigned int dl_carrier_freq = 0; + if (!root[i].lookupValue("dl_carrier_freq", dl_carrier_freq)) { + ERROR("Missing field `dl_carrier_freq` in inter_freq_carrier_freq_list=%d", i); + return SRSRAN_ERROR; + } + data->inter_freq_carrier_freq_list[i].dl_carrier_freq = dl_carrier_freq; + + int q_rx_lev_min = 0; + if (!root[i].lookupValue("q_rx_lev_min", q_rx_lev_min)) { + ERROR("Missing field `q_rx_lev_min` in inter_freq_carrier_freq_list=%d", i); + return SRSRAN_ERROR; + } + data->inter_freq_carrier_freq_list[i].q_rx_lev_min = q_rx_lev_min; + + int p_max = 0; + if (root[i].lookupValue("p_max", p_max)) { + data->inter_freq_carrier_freq_list[i].p_max_present = true; + data->inter_freq_carrier_freq_list[i].p_max = p_max; + } + + unsigned int t_resel_eutra = 0; + if (!root[i].lookupValue("t_resel_eutra", t_resel_eutra)) { + ERROR("Missing field `t_resel_eutra` in inter_freq_carrier_freq_list=%d", i); + return SRSRAN_ERROR; + } + data->inter_freq_carrier_freq_list[i].t_resel_eutra = t_resel_eutra; + + if (root[i].exists("t_resel_eutra_sf")) { + data->inter_freq_carrier_freq_list[i].t_resel_eutra_sf_present = true; + + field_asn1_enum_number_str sf_medium( + "sf_medium", &data->inter_freq_carrier_freq_list[i].t_resel_eutra_sf.sf_medium); + if (sf_medium.parse(root[i]["t_resel_eutra_sf"])) { + ERROR("Error parsing `sf_medium` in inter_freq_carrier_freq_list=%d t_resel_eutra_sf", i); + return SRSRAN_ERROR; + } + + field_asn1_enum_number_str sf_high( + "sf_high", &data->inter_freq_carrier_freq_list[i].t_resel_eutra_sf.sf_high); + if (sf_high.parse(root[i]["t_resel_eutra_sf"])) { + ERROR("Error parsing `sf_high` in inter_freq_carrier_freq_list=%d t_resel_eutra_sf", i); + return SRSRAN_ERROR; + } + } + + unsigned int thresh_x_high = 0; + if (!root[i].lookupValue("thresh_x_high", thresh_x_high)) { + ERROR("Missing field `thresh_x_high` in inter_freq_carrier_freq_list=%d", i); + return SRSRAN_ERROR; + } + data->inter_freq_carrier_freq_list[i].thresh_x_high = thresh_x_high; + + unsigned int thresh_x_low = 0; + if (!root[i].lookupValue("thresh_x_low", thresh_x_low)) { + ERROR("Missing field `thresh_x_low` in inter_freq_carrier_freq_list=%d", i); + return SRSRAN_ERROR; + } + data->inter_freq_carrier_freq_list[i].thresh_x_low = thresh_x_low; + + field_asn1_enum_number allowed_meas_bw( + "allowed_meas_bw", &data->inter_freq_carrier_freq_list[i].allowed_meas_bw); + if (allowed_meas_bw.parse(root[i])) { + ERROR("Error parsing `allowed_meas_bw` in inter_freq_carrier_freq_list=%d", i); + return SRSRAN_ERROR; + } + + bool presence_ant_port1 = 0; + if (!root[i].lookupValue("presence_ant_port_1", presence_ant_port1)) { + ERROR("Missing field `presence_ant_port_1` in inter_freq_carrier_freq_list=%d", i); + return SRSRAN_ERROR; + } + data->inter_freq_carrier_freq_list[i].presence_ant_port1 = presence_ant_port1; + + unsigned int cell_resel_prio = 0; + if (root[i].lookupValue("cell_resel_prio", cell_resel_prio)) { + data->inter_freq_carrier_freq_list[i].cell_resel_prio_present = true; + data->inter_freq_carrier_freq_list[i].cell_resel_prio = cell_resel_prio; + } + + field_asn1_enum_number q_offset_freq( + "q_offset_freq", &data->inter_freq_carrier_freq_list[i].q_offset_freq); + if (!q_offset_freq.parse(root[i])) { + data->inter_freq_carrier_freq_list[i].q_offset_freq_present = true; + } + + field_asn1_bitstring_number, uint8_t> neigh_cell_cfg( + "neigh_cell_cfg", &data->inter_freq_carrier_freq_list[i].neigh_cell_cfg); + if (neigh_cell_cfg.parse(root[i])) { + ERROR("Error parsing `neigh_cell_cfg` in inter_freq_carrier_freq_list=%d", i); + return SRSRAN_ERROR; + } + + if (root[i].exists("inter_freq_neigh_cell_list")) { + auto inter_neigh_cell_list_parser = new field_inter_freq_neigh_cell_list(&data->inter_freq_carrier_freq_list[i]); + HANDLEPARSERCODE(inter_neigh_cell_list_parser->parse(root[i]["inter_freq_neigh_cell_list"])); + } + + if (root[i].exists("inter_freq_black_cell_list")) { + auto inter_black_cell_list_parser = new field_inter_freq_black_cell_list(&data->inter_freq_carrier_freq_list[i]); + HANDLEPARSERCODE(inter_black_cell_list_parser->parse(root[i]["inter_freq_black_cell_list"])); + } + } + return 0; +} + +int field_inter_freq_neigh_cell_list::parse(libconfig::Setting& root) +{ + data->inter_freq_neigh_cell_list.resize((uint32_t)root.getLength()); + data->inter_freq_neigh_cell_list_present = data->inter_freq_neigh_cell_list.size() > 0; + for (uint32_t i = 0; i < data->inter_freq_neigh_cell_list.size() && i < ASN1_RRC_MAX_CELL_BLACK; i++) { + if (not parse_enum_by_number(data->inter_freq_neigh_cell_list[i].q_offset_cell, "q_offset_cell", root[i])) { + ERROR("Missing field q_offset_cell in neigh_cell=%d\n", i); + return SRSRAN_ERROR; + } + + unsigned int phys_cell_id = 0; + if (!root[i].lookupValue("phys_cell_id", phys_cell_id)) { + ERROR("Missing field phys_cell_id in neigh_cell=%d\n", i); + return SRSRAN_ERROR; + } + data->inter_freq_neigh_cell_list[i].pci = (uint16)phys_cell_id; + } + return 0; +} + +int field_inter_freq_black_cell_list::parse(libconfig::Setting& root) +{ + data->inter_freq_black_cell_list.resize((uint32_t)root.getLength()); + data->inter_freq_black_cell_list_present = data->inter_freq_black_cell_list.size() > 0; + for (uint32_t i = 0; i < data->inter_freq_black_cell_list.size() && i < ASN1_RRC_MAX_CELL_BLACK; i++) { + if (not parse_enum_by_number(data->inter_freq_black_cell_list[i].range, "range", root[i])) { + ERROR("Missing field range in black_cell=%d\n", i); + return SRSRAN_ERROR; + } + data->inter_freq_black_cell_list[i].range_present = true; + + unsigned int start = 0; + if (!root[i].lookupValue("start", start)) { + ERROR("Missing field start in black_cell=%d\n", i); + return SRSRAN_ERROR; + } + data->inter_freq_black_cell_list[i].start = (uint16)start; + } + return 0; +} + int field_carrier_freq_list_utra_fdd::parse(libconfig::Setting& root) { data->carrier_freq_list_utra_fdd.resize((uint32_t)root.getLength()); @@ -1378,6 +1528,7 @@ static int parse_nr_cell_list(all_args_t* args, rrc_nr_cfg_t* rrc_cfg_nr, rrc_cf parse_opt_field(cell_cfg.phy_cell.rf_port, cellroot, "rf_port"); HANDLEPARSERCODE(parse_required_field(cell_cfg.phy_cell.carrier.pci, cellroot, "pci")); HANDLEPARSERCODE(parse_required_field(cell_cfg.phy_cell.cell_id, cellroot, "cell_id")); + HANDLEPARSERCODE(parse_opt_field(cell_cfg.coreset0_idx, cellroot, "coreset0_idx")); HANDLEPARSERCODE(parse_required_field(cell_cfg.prach_root_seq_idx, cellroot, "root_seq_idx")); HANDLEPARSERCODE(parse_required_field(cell_cfg.tac, cellroot, "tac")); @@ -1999,54 +2150,11 @@ int set_derived_args_nr(all_args_t* args_, rrc_nr_cfg_t* rrc_nr_cfg_, phy_cfg_t* ERROR("Only 10 MHz bandwidth supported."); return SRSRAN_ERROR; } - if (rrc_nr_cfg_->is_standalone) { - if (is_valid_arfcn(cfg.band, cfg.dl_arfcn) == false) { - ERROR("DL-ARFCN %d in band n%d not supported with coreset0 config.", cfg.dl_arfcn, cfg.band); - ERROR("Valid ARFCNs for band n%d are: %s", cfg.band, valid_arfcns_to_string(cfg.band).c_str()); - return SRSRAN_ERROR; - } - if (cfg.duplex_mode == SRSRAN_DUPLEX_MODE_TDD) { - ERROR("Only FDD duplex supported in SA mode."); - return SRSRAN_ERROR; - } - } } return SRSRAN_SUCCESS; } -// List of selected ARFCNs in band n3, n7 and n20 that match the coreset0 config -using arfcn_list_t = std::list; -std::map valid_arfcn = {{3, {363500, 368500, 369500, 374500, 375000}}, - {7, {525000, 526200, 531000}}, - {20, {159000, 160200}}}; - -std::string valid_arfcns_to_string(uint32_t band) -{ - std::string band_string; - if (valid_arfcn.find(band) != valid_arfcn.end()) { - for (const auto& arfcn : valid_arfcn.at(band)) { - band_string += std::to_string(arfcn); - band_string += ", "; - } - } - return band_string; -} - -bool is_valid_arfcn(uint32_t band, uint32_t dl_arfcn) -{ - if (valid_arfcn.find(band) == valid_arfcn.end()) { - return false; - } - const auto& arfcn_list = valid_arfcn.at(band); - for (const auto& arfcn : arfcn_list) { - if (arfcn == dl_arfcn) { - return true; - } - } - return false; -} - } // namespace enb_conf_sections namespace sib_sections { @@ -2345,6 +2453,20 @@ int parse_sib4(std::string filename, sib_type4_s* data) return parser::parse_section(std::move(filename), &sib4); } +int parse_sib5(std::string filename, sib_type5_s* data) +{ + parser::section sib5("sib5"); + + // interFreqCarrierFreqList + parser::section inter_freq_carrier_freq_list("inter_freq_carrier_freq_list"); + sib5.add_subsection(&inter_freq_carrier_freq_list); + bool dummy_bool = false; + inter_freq_carrier_freq_list.set_optional(&dummy_bool); + inter_freq_carrier_freq_list.add_field(new field_inter_freq_carrier_freq_list(data)); + + return parser::parse_section(std::move(filename), &sib5); +} + int parse_sib6(std::string filename, sib_type6_s* data) { parser::section sib6("sib6"); @@ -2458,6 +2580,7 @@ int parse_sibs(all_args_t* args_, rrc_cfg_t* rrc_cfg_, srsenb::phy_cfg_t* phy_co sib_type2_s* sib2 = &rrc_cfg_->sibs[1].set_sib2(); sib_type3_s* sib3 = &rrc_cfg_->sibs[2].set_sib3(); sib_type4_s* sib4 = &rrc_cfg_->sibs[3].set_sib4(); + sib_type5_s* sib5 = &rrc_cfg_->sibs[4].set_sib5(); sib_type6_s* sib6 = &rrc_cfg_->sibs[5].set_sib6(); sib_type7_s* sib7 = &rrc_cfg_->sibs[6].set_sib7(); sib_type9_s* sib9 = &rrc_cfg_->sibs[8].set_sib9(); @@ -2528,6 +2651,13 @@ int parse_sibs(all_args_t* args_, rrc_cfg_t* rrc_cfg_, srsenb::phy_cfg_t* phy_co } } + // Generate SIB5 if defined in mapping info + if (sib_is_present(sib1->sched_info_list, sib_type_e::sib_type5)) { + if (sib_sections::parse_sib5(args_->enb_files.sib_config, sib5) != SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + } + // Generate SIB6 if defined in mapping info if (sib_is_present(sib1->sched_info_list, sib_type_e::sib_type6)) { if (sib_sections::parse_sib6(args_->enb_files.sib_config, sib6) != SRSRAN_SUCCESS) { diff --git a/srsenb/src/enb_cfg_parser.h b/srsenb/src/enb_cfg_parser.h index 0e5d1eefd..f07753095 100644 --- a/srsenb/src/enb_cfg_parser.h +++ b/srsenb/src/enb_cfg_parser.h @@ -63,6 +63,7 @@ int parse_sib1(std::string filename, asn1::rrc::sib_type1_s* data); int parse_sib2(std::string filename, asn1::rrc::sib_type2_s* data); int parse_sib3(std::string filename, asn1::rrc::sib_type3_s* data); int parse_sib4(std::string filename, asn1::rrc::sib_type4_s* data); +int parse_sib5(std::string filename, asn1::rrc::sib_type5_s* data); int parse_sib6(std::string filename, asn1::rrc::sib_type6_s* data); int parse_sib7(std::string filename, asn1::rrc::sib_type7_s* data); int parse_sib9(std::string filename, asn1::rrc::sib_type9_s* data); @@ -150,6 +151,39 @@ private: asn1::rrc::sib_type4_s* data; }; +class field_inter_freq_carrier_freq_list final : public parser::field_itf +{ +public: + explicit field_inter_freq_carrier_freq_list(asn1::rrc::sib_type5_s* data_) { data = data_; } + int parse(Setting& root) override; + const char* get_name() override { return "inter_freq_carrier_freq_list"; } + +private: + asn1::rrc::sib_type5_s* data; +}; + +class field_inter_freq_neigh_cell_list final : public parser::field_itf +{ +public: + explicit field_inter_freq_neigh_cell_list(asn1::rrc::inter_freq_carrier_freq_info_s* data_) { data = data_; } + int parse(Setting& root) override; + const char* get_name() override { return "inter_freq_neigh_cell_list"; } + +private: + asn1::rrc::inter_freq_carrier_freq_info_s* data; +}; + +class field_inter_freq_black_cell_list final : public parser::field_itf +{ +public: + explicit field_inter_freq_black_cell_list(asn1::rrc::inter_freq_carrier_freq_info_s* data_) { data = data_; } + int parse(Setting& root) override; + const char* get_name() override { return "inter_freq_black_cell_list"; } + +private: + asn1::rrc::inter_freq_carrier_freq_info_s* data; +}; + class field_carrier_freq_list_utra_fdd final : public parser::field_itf { public: diff --git a/srsenb/src/phy/txrx.cc b/srsenb/src/phy/txrx.cc index 9b82db00d..c2cfd8fb8 100644 --- a/srsenb/src/phy/txrx.cc +++ b/srsenb/src/phy/txrx.cc @@ -21,11 +21,11 @@ #include +#include "srsenb/hdr/phy/txrx.h" +#include "srsran/common/band_helper.h" #include "srsran/common/threads.h" #include "srsran/srsran.h" -#include "srsenb/hdr/phy/txrx.h" - #define Error(fmt, ...) \ if (SRSRAN_DEBUG_ENABLED) \ logger.error(fmt, ##__VA_ARGS__) @@ -94,6 +94,8 @@ void txrx::run_thread() float samp_rate = srsran_sampling_freq_hz(worker_com->get_nof_prb(0)); + srsran::srsran_band_helper band_helper; + // Configure radio radio_h->set_rx_srate(samp_rate); radio_h->set_tx_srate(samp_rate); @@ -103,11 +105,23 @@ void txrx::run_thread() double tx_freq_hz = worker_com->get_dl_freq_hz(cc_idx); double rx_freq_hz = worker_com->get_ul_freq_hz(cc_idx); uint32_t rf_port = worker_com->get_rf_port(cc_idx); - srsran::console("Setting frequency: DL=%.1f Mhz, UL=%.1f MHz for cc_idx=%d nof_prb=%d\n", - tx_freq_hz / 1e6f, - rx_freq_hz / 1e6f, - cc_idx, - worker_com->get_nof_prb(cc_idx)); + if (cc_idx < worker_com->get_nof_carriers_lte()) { + srsran::console("Setting frequency: DL=%.1f Mhz, UL=%.1f MHz for cc_idx=%d nof_prb=%d\n", + tx_freq_hz / 1e6f, + rx_freq_hz / 1e6f, + cc_idx, + worker_com->get_nof_prb(cc_idx)); + } else { + srsran::console( + "Setting frequency: DL=%.1f Mhz, DL_SSB=%.2f Mhz (SSB-ARFCN=%d), UL=%.1f MHz for cc_idx=%d nof_prb=%d\n", + tx_freq_hz / 1e6f, + worker_com->get_ssb_freq_hz(cc_idx) / 1e6f, + band_helper.freq_to_nr_arfcn(worker_com->get_ssb_freq_hz(cc_idx)), + rx_freq_hz / 1e6f, + cc_idx, + worker_com->get_nof_prb(cc_idx)); + } + radio_h->set_tx_freq(rf_port, tx_freq_hz); radio_h->set_rx_freq(rf_port, rx_freq_hz); } diff --git a/srsenb/test/rrc/CMakeLists.txt b/srsenb/test/rrc/CMakeLists.txt index d931a4fed..9a387d199 100644 --- a/srsenb/test/rrc/CMakeLists.txt +++ b/srsenb/test/rrc/CMakeLists.txt @@ -30,6 +30,10 @@ target_link_libraries(erab_setup_test test_helpers ${LIBCONFIGPP_LIBRARIES} ${AT add_executable(rrc_mobility_test rrc_mobility_test.cc) target_link_libraries(rrc_mobility_test srsran_asn1 test_helpers ${ATOMIC_LIBS}) +add_executable(rrc_paging_test rrc_paging_test.cc) +target_link_libraries(rrc_paging_test srsran_asn1 test_helpers) + add_test(rrc_mobility_test rrc_mobility_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) add_test(erab_setup_test erab_setup_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) add_test(rrc_meascfg_test rrc_meascfg_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) +add_test(rrc_paging_test rrc_paging_test) diff --git a/srsenb/test/rrc/rrc_paging_test.cc b/srsenb/test/rrc/rrc_paging_test.cc new file mode 100644 index 000000000..c1d55bbfa --- /dev/null +++ b/srsenb/test/rrc/rrc_paging_test.cc @@ -0,0 +1,48 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsenb/hdr/stack/rrc/rrc_paging.h" +#include "srsran/common/test_common.h" + +using namespace srsenb; + +void test_paging() +{ + unsigned paging_cycle = 32; + float nb = 1; + paging_manager pcch_manager{paging_cycle, nb}; + + unsigned ue_id = 4780; + unsigned mmec = 10; + uint8_t m_tmsi[] = {0x64, 0x04, 0x00, 0x02}; + + pcch_manager.add_tmsi_paging(ue_id, mmec, m_tmsi); + + // \remark: See TS 36.304, section 7.1. + unsigned N = std::min(paging_cycle, (unsigned)std::round(nb * paging_cycle)); + unsigned Ns = std::max(1, (int)nb); + unsigned i_s = (ue_id / N) % Ns; + TESTASSERT_EQ(0, i_s); + tti_point t{0}; + for (unsigned count = 0; count < 1024 * 10; ++count, ++t) { + if (pcch_manager.pending_pcch_bytes(t) > 0) { + fmt::print("[{}]\n", t); + TESTASSERT_EQ((paging_cycle / N) * (ue_id % N), (t.sfn() % paging_cycle)); + TESTASSERT_EQ(9, t.sf_idx()); // PO when i_s == 0. + } + } +} + +int main() +{ + test_paging(); +} \ No newline at end of file diff --git a/srsgnb/hdr/stack/mac/sched_nr_grant_allocator.h b/srsgnb/hdr/stack/mac/sched_nr_grant_allocator.h index 17179dfd5..d97522860 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_grant_allocator.h +++ b/srsgnb/hdr/stack/mac/sched_nr_grant_allocator.h @@ -66,6 +66,8 @@ struct bwp_slot_grid { explicit bwp_slot_grid(const bwp_params_t& bwp_params, uint32_t slot_idx_); void reset(); + void reserve_pdsch(const prb_grant& grant) { pdschs.reserve_prbs(grant); } + bool is_dl() const { return cfg->slots[slot_idx].is_dl; } bool is_ul() const { return cfg->slots[slot_idx].is_ul; } }; diff --git a/srsgnb/hdr/stack/mac/sched_nr_interface.h b/srsgnb/hdr/stack/mac/sched_nr_interface.h index 0c637e6c3..932213c37 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_interface.h +++ b/srsgnb/hdr/stack/mac/sched_nr_interface.h @@ -89,6 +89,7 @@ struct sched_nr_cell_cfg_t { uint32_t nof_layers; uint32_t pci; + uint32_t ssb_offset; uint32_t dl_cell_nof_prb; uint32_t ul_cell_nof_prb; asn1::rrc_nr::dl_cfg_common_sib_s dl_cfg_common; diff --git a/srsgnb/hdr/stack/mac/sched_nr_sch.h b/srsgnb/hdr/stack/mac/sched_nr_sch.h index 07fe9ba5b..4a518d220 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_sch.h +++ b/srsgnb/hdr/stack/mac/sched_nr_sch.h @@ -55,6 +55,9 @@ public: return dl_prbs.prbs(); } + /// Marks a range of PRBS as occupied, preventing further allocations + void reserve_prbs(const prb_grant& grant) { dl_prbs |= grant; } + /// Verifies if the input arguments are valid for an SI allocation and grant doesnt collide with other grants alloc_result is_si_grant_valid(uint32_t ss_id, const prb_grant& grant) const; diff --git a/srsgnb/hdr/stack/rrc/rrc_nr_config.h b/srsgnb/hdr/stack/rrc/rrc_nr_config.h index 37a796564..30b808561 100644 --- a/srsgnb/hdr/stack/rrc/rrc_nr_config.h +++ b/srsgnb/hdr/stack/rrc/rrc_nr_config.h @@ -45,6 +45,7 @@ struct rrc_cell_cfg_nr_t { srsran_duplex_mode_t duplex_mode; double ssb_freq_hz; uint32_t ssb_absolute_freq_point; // derived from DL ARFCN (SSB arfcn) + uint32_t ssb_offset; srsran_subcarrier_spacing_t ssb_scs; srsran_ssb_pattern_t ssb_pattern; asn1::rrc_nr::pdcch_cfg_common_s pdcch_cfg_common; diff --git a/srsgnb/src/stack/mac/sched_nr_interface_utils.cc b/srsgnb/src/stack/mac/sched_nr_interface_utils.cc index 08ae2b905..d6530eaf1 100644 --- a/srsgnb/src/stack/mac/sched_nr_interface_utils.cc +++ b/srsgnb/src/stack/mac/sched_nr_interface_utils.cc @@ -36,7 +36,7 @@ void make_mib_cfg(const sched_nr_cell_cfg_t& cfg, srsran_mib_nr_t* mib) { *mib = {}; mib->scs_common = (srsran_subcarrier_spacing_t)cfg.dl_cfg_common.init_dl_bwp.generic_params.subcarrier_spacing.value; - mib->ssb_offset = 6; // TODO + mib->ssb_offset = cfg.ssb_offset; mib->dmrs_typeA_pos = (srsran_dmrs_sch_typeA_pos_t)cfg.dmrs_type_a_position.value; mib->coreset0_idx = cfg.pdcch_cfg_sib1.ctrl_res_set_zero; mib->ss0_idx = cfg.pdcch_cfg_sib1.search_space_zero; diff --git a/srsgnb/src/stack/mac/sched_nr_signalling.cc b/srsgnb/src/stack/mac/sched_nr_signalling.cc index 864d69c6b..9beb4f509 100644 --- a/srsgnb/src/stack/mac/sched_nr_signalling.cc +++ b/srsgnb/src/stack/mac/sched_nr_signalling.cc @@ -105,6 +105,17 @@ void sched_dl_signalling(bwp_slot_allocator& bwp_alloc) // Schedule SSB sched_ssb_basic(sl_pdcch, bwp_params.cell_cfg.ssb.periodicity_ms, bwp_params.cell_cfg.mib, sl_grid.dl.phy.ssb); + // Mark SSB region as occupied + if (!sl_grid.dl.phy.ssb.empty()) { + float ssb_offset_hz = + bwp_params.cell_cfg.carrier.ssb_center_freq_hz - bwp_params.cell_cfg.carrier.dl_center_frequency_hz; + int ssb_offset_rb = ceil(ssb_offset_hz / (15000.0f * 12)); + int ssb_start_rb = bwp_params.cell_cfg.carrier.nof_prb / 2 + ssb_offset_rb - 10; + uint32_t ssb_len_rb = 20; + assert(ssb_start_rb >= 0 && ssb_start_rb + ssb_len_rb < bwp_params.cell_cfg.carrier.nof_prb); + sl_grid.reserve_pdsch(prb_grant({(uint32_t)ssb_start_rb, ssb_start_rb + ssb_len_rb})); + } + // Schedule NZP-CSI-RS sched_nzp_csi_rs(bwp_params.cfg.pdsch.nzp_csi_rs_sets, cfg, sl_grid.dl.phy.nzp_csi_rs); } @@ -159,12 +170,10 @@ void si_sched::run_slot(bwp_slot_allocator& bwp_alloc) } } else if (si.win_start + si.win_len_slots >= sl_pdcch) { // If end of SI message window - if (si.n == 0) { - logger.error("SCHED: Could not allocate SIB1, len=%d. Cause: %s", si.len_bytes, to_string(si.result)); - } else { - logger.warning( - "SCHED: Could not allocate SI message idx=%d, len=%d. Cause: %s", si.n, si.len_bytes, to_string(si.result)); - } + srsran_always_assert( + si.n == 0, "SCHED: Could not allocate SIB1, len=%d. Cause: %s", si.len_bytes, to_string(si.result)); + logger.warning( + "SCHED: Could not allocate SI message idx=%d, len=%d. Cause: %s", si.n, si.len_bytes, to_string(si.result)); si.win_start.clear(); } } diff --git a/srsgnb/src/stack/mac/sched_nr_worker.cc b/srsgnb/src/stack/mac/sched_nr_worker.cc index 6bab05b8d..7e3890ab5 100644 --- a/srsgnb/src/stack/mac/sched_nr_worker.cc +++ b/srsgnb/src/stack/mac/sched_nr_worker.cc @@ -83,9 +83,15 @@ dl_sched_res_t* cc_worker::run_slot(slot_point tx_sl, ue_map_t& ue_db) // Log UEs state for slot log_sched_slot_ues(logger, tx_sl, cfg.cc, slot_ues); + const uint32_t ss_id = 0; + slot_point sl_pdcch = bwp_alloc.get_pdcch_tti(); + + prb_bitmap prbs_before = bwp_alloc.res_grid()[sl_pdcch].pdschs.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); // Allocate cell DL signalling sched_dl_signalling(bwp_alloc); + prb_bitmap prbs_after = bwp_alloc.res_grid()[sl_pdcch].pdschs.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + // Allocate pending SIBs bwps[0].si.run_slot(bwp_alloc); diff --git a/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h b/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h index b7567e06e..6d1559c9c 100644 --- a/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h +++ b/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h @@ -80,7 +80,7 @@ inline sched_nr_cell_cfg_t get_default_cell_cfg(const srsran::phy_cfg_nr_t& phy_ // TODO: phy_cfg.ssb_positions_in_burst.group_presence_present cell_cfg.dmrs_type_a_position.value = asn1::rrc_nr::mib_s::dmrs_type_a_position_opts::pos2; cell_cfg.ssb_scs.value = (asn1::rrc_nr::subcarrier_spacing_opts::options)phy_cfg.ssb.scs; - cell_cfg.pdcch_cfg_sib1.ctrl_res_set_zero = 6; + cell_cfg.pdcch_cfg_sib1.ctrl_res_set_zero = 0; cell_cfg.pdcch_cfg_sib1.search_space_zero = 0; cell_cfg.bwps.resize(1); diff --git a/srsgnb/src/stack/rrc/cell_asn1_config.cc b/srsgnb/src/stack/rrc/cell_asn1_config.cc index 9f6230ebf..d4fd165eb 100644 --- a/srsgnb/src/stack/rrc/cell_asn1_config.cc +++ b/srsgnb/src/stack/rrc/cell_asn1_config.cc @@ -1098,7 +1098,7 @@ int fill_mib_from_enb_cfg(const rrc_cell_cfg_nr_t& cell_cfg, asn1::rrc_nr::mib_s default: srsran_terminate("Invalid carrier SCS=%d Hz", SRSRAN_SUBC_SPACING_NR(cell_cfg.phy_cell.carrier.scs)); } - mib.ssb_subcarrier_offset = 6; // TODO: currently hard-coded + mib.ssb_subcarrier_offset = cell_cfg.ssb_offset; mib.dmrs_type_a_position.value = mib_s::dmrs_type_a_position_opts::pos2; mib.pdcch_cfg_sib1.search_space_zero = 0; mib.pdcch_cfg_sib1.ctrl_res_set_zero = cell_cfg.coreset0_idx; diff --git a/srsgnb/src/stack/rrc/rrc_nr.cc b/srsgnb/src/stack/rrc/rrc_nr.cc index 58efb5e4d..827347e30 100644 --- a/srsgnb/src/stack/rrc/rrc_nr.cc +++ b/srsgnb/src/stack/rrc/rrc_nr.cc @@ -347,6 +347,7 @@ void rrc_nr::config_mac() cell.ssb_positions_in_burst = du_cfg->cell(cc).serv_cell_cfg_common().ssb_positions_in_burst; cell.ssb_periodicity_ms = du_cfg->cell(cc).serv_cell_cfg_common().ssb_periodicity_serving_cell.to_number(); cell.ssb_scs.value = (subcarrier_spacing_e::options)cfg.cell_list[0].phy_cell.carrier.scs; + cell.ssb_offset = du_cfg->cell(cc).mib.ssb_subcarrier_offset; if (not cfg.is_standalone) { const serving_cell_cfg_common_s& serv_cell = cell_ctxt->master_cell_group->sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common; diff --git a/srsgnb/src/stack/rrc/rrc_nr_config_utils.cc b/srsgnb/src/stack/rrc/rrc_nr_config_utils.cc index 861a80adb..29536cff2 100644 --- a/srsgnb/src/stack/rrc/rrc_nr_config_utils.cc +++ b/srsgnb/src/stack/rrc/rrc_nr_config_utils.cc @@ -124,7 +124,7 @@ void generate_default_nr_phy_cell(phy_cell_cfg_nr_t& phy_cell) void generate_default_nr_cell(rrc_cell_cfg_nr_t& cell) { cell = {}; - cell.coreset0_idx = 6; + cell.coreset0_idx = 7; cell.ssb_absolute_freq_point = 0; // auto derived cell.num_ra_preambles = 8; generate_default_nr_phy_cell(cell.phy_cell); @@ -196,7 +196,41 @@ int derive_ssb_params(bool is_sa, band); // Convert to frequency for PHY - cell.ssb_freq_hz = band_helper.nr_arfcn_to_freq(ssb_abs_freq_point); + cell.ssb_absolute_freq_point = ssb_abs_freq_point; + cell.ssb_freq_hz = band_helper.nr_arfcn_to_freq(ssb_abs_freq_point); + + double pointA_abs_freq_Hz = dl_freq_hz - nof_prb * SRSRAN_NRE * SRSRAN_SUBC_SPACING_NR(pdcch_scs) / 2; + uint32_t ssb_pointA_freq_offset_Hz = + (cell.ssb_freq_hz > pointA_abs_freq_Hz) ? (uint32_t)(cell.ssb_freq_hz - pointA_abs_freq_Hz) : 0; + + cell.ssb_offset = (uint32_t)(ssb_pointA_freq_offset_Hz / SRSRAN_SUBC_SPACING_NR(pdcch_scs)) % SRSRAN_NRE; + + // Validate Coreset0 has space + srsran_coreset_t coreset0 = {}; + ERROR_IF_NOT( + srsran_coreset_zero( + cell.phy_cell.cell_id, ssb_pointA_freq_offset_Hz, cell.ssb_scs, pdcch_scs, coreset0_idx, &coreset0) == 0, + "Deriving parameters for coreset0: index=%d, ssb_pointA_offset=%d kHz\n", + coreset0_idx, + ssb_pointA_freq_offset_Hz / 1000); + + ERROR_IF_NOT(srsran_coreset_start_rb(&coreset0) + srsran_coreset_get_bw(&coreset0) <= cell.phy_cell.carrier.nof_prb, + "Coreset0 index=%d is not compatible with DL ARFCN %d in band %d\n", + coreset0_idx, + cell.dl_arfcn, + cell.band); + + // Validate Coreset0 has less than 3 symbols + ERROR_IF_NOT(coreset0.duration < 3, + "Coreset0 index=%d is not supported due to overlap with SSB. Select a coreset0 index from 38.213 Table " + "13-1 such that N_symb_coreset < 3\n", + coreset0_idx); + + // Validate Coreset0 has more than 24 RB + ERROR_IF_NOT(srsran_coreset_get_bw(&coreset0) > 24, + "Coreset0 configuration index=%d has only %d RB. A coreset0 index >= 6 is required such as N_rb >= 48\n", + srsran_coreset_get_bw(&coreset0), + coreset0_idx); return SRSRAN_SUCCESS; } @@ -255,15 +289,16 @@ int set_derived_nr_cell_params(bool is_sa, rrc_cell_cfg_nr_t& cell) derive_phy_cell_freq_params(cell.dl_arfcn, cell.ul_arfcn, cell.phy_cell); // Derive SSB params - derive_ssb_params(is_sa, - cell.dl_arfcn, - cell.band, - cell.phy_cell.carrier.scs, - cell.coreset0_idx, - cell.phy_cell.carrier.nof_prb, - cell); + ERROR_IF_NOT(derive_ssb_params(is_sa, + cell.dl_arfcn, + cell.band, + cell.phy_cell.carrier.scs, + cell.coreset0_idx, + cell.phy_cell.carrier.nof_prb, + cell) == 0, + "Deriving SSB parameters\n"); + cell.phy_cell.carrier.ssb_center_freq_hz = cell.ssb_freq_hz; - cell.ssb_absolute_freq_point = band_helper.freq_to_nr_arfcn(cell.ssb_freq_hz); // Derive remaining config params if (not is_sa) { diff --git a/srsgnb/src/stack/rrc/test/rrc_nr_test.cc b/srsgnb/src/stack/rrc/test/rrc_nr_test.cc index 5b2868e68..da102146b 100644 --- a/srsgnb/src/stack/rrc/test/rrc_nr_test.cc +++ b/srsgnb/src/stack/rrc/test/rrc_nr_test.cc @@ -120,6 +120,7 @@ int test_rrc_setup() generate_default_nr_cell(rrc_cfg_nr.cell_list[0]); rrc_cfg_nr.cell_list[0].phy_cell.carrier.pci = 500; rrc_cfg_nr.cell_list[0].dl_arfcn = 634240; + rrc_cfg_nr.cell_list[0].coreset0_idx = 3; rrc_cfg_nr.cell_list[0].band = 78; rrc_cfg_nr.cell_list[0].phy_cell.carrier.nof_prb = 52; rrc_cfg_nr.is_standalone = false; diff --git a/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc b/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc index 36858ecb4..6922994e7 100644 --- a/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc +++ b/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc @@ -440,11 +440,6 @@ rrc_nr::cell_selection_proc::handle_cell_search_result(const rrc_interface_phy_n Error("Unsupported SCS %s", srsran_subcarrier_spacing_to_str(mib.scs_common)); return proc_outcome_t::error; } - // TODO: calculate SSB offset - if (mib.ssb_offset != 6) { - Error("Unsupported SSB offset %d", mib.ssb_offset); - return proc_outcome_t::error; - } // Logs the PCI, cell measurements and decoded MIB Info("Cell search found ARFCN=%d PCI=%d %s %s", diff --git a/srsue/ue.conf.example b/srsue/ue.conf.example index 00ea59d52..5bae8ce76 100644 --- a/srsue/ue.conf.example +++ b/srsue/ue.conf.example @@ -175,9 +175,9 @@ imei = 353490069873319 # pass: Password for CHAP authentication # force_imsi_attach: Whether to always perform an IMSI attach # eia: List of integrity algorithms included in UE capabilities -# Supported: 1 - Snow3G, 2 - AES +# Supported: 1 - Snow3G, 2 - AES, 3 - ZUC # eea: List of ciphering algorithms included in UE capabilities -# Supported: 0 - NULL, 1 - Snow3G, 2 - AES +# Supported: 0 - NULL, 1 - Snow3G, 2 - AES, 3 - ZUC ##################################################################### [nas] #apn = internetinternet @@ -185,8 +185,8 @@ imei = 353490069873319 #user = srsuser #pass = srspass #force_imsi_attach = false -#eia = 1,2 -#eea = 0,1,2 +#eia = 1,2,3 +#eea = 0,1,2,3 ##################################################################### # GW configuration