Merge remote-tracking branch 'origin/nr_ue_multiple_rf' into integration_2026_w05 (!3593)

NR UE: add support for multiple RF frontends

This MR adds basic support for multiple RF-frontends at the UE.
There are multiple reasons why we might want this:
1. There might be different antennas needed for different bands (e.g. NTN or
   TN antennas). These antennas might be connected to different RF-frontends.
   In case of Handover, the UE needs to dynamically switch between these
   RF-frontends.
2. Each rf-simulator connection carries baseband I/Q samples. To be able to
   perform a handover between two cells at different frequencies, there must
   be two rf-simulator connections, as the baseband channels should not
   interfere and thus cannot simply be added.
3. When we want to support multiple UE simulation within one instance of
   nr-uesoftmodem, and allow multiple cells and probably even handover between
   these cells, we need either:
   1. dedicated RF-frontends for each simulated UE, or
   2. dedicated RF-frontends for each connected cell and dynamically assign
      simulated UEs accordingly.

This MR implements what is needed for 1., 2. and 3.1.
This commit is contained in:
Jaroslava Fiedlerova
2026-01-28 20:19:48 +01:00
153 changed files with 1993 additions and 1082 deletions
+2 -2
View File
@@ -187,9 +187,9 @@ e2_agent = {
```bash
cd <path-to>/build
# for gNB-mono
sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --rfsim --uicc0.imsi 001010000000001 --rfsimulator.serveraddr 127.0.0.1
sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --rfsim --uicc0.imsi 001010000000001 --rfsimulator.[0].serveraddr 127.0.0.1
# for CU/DU and CU-CP/CU-UP/DU split
sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3450720000 --rfsim --uicc0.imsi 001010000000001 --rfsimulator.serveraddr 127.0.0.1
sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3450720000 --rfsim --uicc0.imsi 001010000000001 --rfsimulator.[0].serveraddr 127.0.0.1
```
Note: [multi-UE rfsim deployment tutorial](../../doc/NR_SA_Tutorial_OAI_multi_UE.md?ref_type=heads#run-multiple-ues-in-rfsimulator)
+1 -1
View File
@@ -202,7 +202,7 @@ typedef enum {
{CONFIG_STRING_RU_END_OF_BURST_DELAY, NULL, 0, .uptr=NULL, .defuintval=400, TYPE_UINT, 0}, \
{CONFIG_STRING_RU_OTA_SYNC_ENABLE, NULL, 0, .strptr=NULL, .defstrval="no", TYPE_STRING, 0}, \
{CONFIG_STRING_RU_BF_WEIGHTS_LIST, NULL, 0, .iptr=NULL, .defintarrayval=DEFBFW, TYPE_INTARRAY, 0}, \
{CONFIG_STRING_RU_IF_FREQUENCY, NULL, 0, .u64ptr=NULL, .defuintval=0, TYPE_UINT64, 0}, \
{CONFIG_STRING_RU_IF_FREQUENCY, NULL, 0, .u64ptr=NULL, .defint64val=0, TYPE_UINT64, 0}, \
{CONFIG_STRING_RU_IF_FREQ_OFFSET, NULL, 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \
{CONFIG_STRING_RU_DO_PRECODING, NULL, 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \
{CONFIG_STRING_RU_SF_AHEAD, HLP_RU_SF_AHEAD, 0, .iptr=NULL, .defintval=4, TYPE_INT, 0}, \
+30 -16
View File
@@ -137,6 +137,7 @@ static void config_common_ue_sa(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommo
AssertFatal(frequencyInfoDL->frequencyBandList.list.array[0]->freqBandIndicatorNR, "Field mandatory present for DL in SIB1\n");
mac->nr_band = *frequencyInfoDL->frequencyBandList.list.array[0]->freqBandIndicatorNR;
AssertFatal(mac->numerology == frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, "DL and SSB subcarrierSpacing must be the same!\n");
int bw_index = get_supported_band_index(frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing,
mac->frequency_range,
frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth);
@@ -149,7 +150,7 @@ static void config_common_ue_sa(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommo
// Initial cell selection: derive from command-line parameter
uint64_t dl_bw_khz = (12 * frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth) *
(15 << frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing);
cfg->carrier_config.dl_frequency = (downlink_frequency[cc_idP][0]/1000) - (dl_bw_khz>>1);
cfg->carrier_config.dl_frequency = mac->dl_frequency / 1000 - (dl_bw_khz >> 1);
LOG_I(NR_MAC,
"[UE %d] Initial cell selection: dl_frequency=%u kHz (from command-line, band=%d, scs=%ld)\n",
mac->ue_id,
@@ -172,25 +173,35 @@ static void config_common_ue_sa(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommo
NR_FrequencyInfoUL_SIB_t *frequencyInfoUL = &scc->uplinkConfigCommon->frequencyInfoUL;
mac->p_Max = frequencyInfoUL->p_Max ? *frequencyInfoUL->p_Max : INT_MIN;
AssertFatal(mac->numerology == frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, "UL and SSB subcarrierSpacing must be the same!\n");
bw_index = get_supported_band_index(frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing,
mac->frequency_range,
frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth);
cfg->carrier_config.uplink_bandwidth = get_supported_bw_mhz(mac->frequency_range, bw_index);
/** Only set UL frequency if not already initialized (e.g., from handover reconfigurationWithSync)
* MAC maintains its own frequency state, don't overwrite it with command-line parameter which
* is related to the initial cell selection. */
* MAC maintains its own frequency state, don't overwrite it with command-line parameter which
* is related to the initial cell selection. */
if (cfg->carrier_config.uplink_frequency == 0) {
// Initial cell selection: derive from DL frequency
LOG_I(NR_MAC,
"Initial cell selection: uplink_frequency=%u kHz (from dl_frequency=%u kHz, uplink_frequency_offset=%u kHz)\n",
cfg->carrier_config.uplink_frequency,
cfg->carrier_config.dl_frequency,
uplink_frequency_offset[cc_idP][0]);
if (frequencyInfoUL->absoluteFrequencyPointA == NULL)
// Initial cell selection: derive from DL frequency or frequencyInfoUL->absoluteFrequencyPointA
if (frequencyInfoUL->absoluteFrequencyPointA == NULL) {
cfg->carrier_config.uplink_frequency = cfg->carrier_config.dl_frequency;
else
cfg->carrier_config.uplink_frequency = cfg->carrier_config.dl_frequency + (uplink_frequency_offset[cc_idP][0] / 1000);
LOG_I(NR_MAC,
"Initial cell selection: uplink_frequency=%u kHz (from dl_frequency=%u kHz)\n",
cfg->carrier_config.uplink_frequency,
cfg->carrier_config.dl_frequency);
} else {
cfg->carrier_config.uplink_frequency = from_nrarfcn(*frequencyInfoUL->frequencyBandList->list.array[0]->freqBandIndicatorNR,
frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing,
*frequencyInfoUL->absoluteFrequencyPointA)
/ 1000; // freq in kHz
LOG_I(NR_MAC,
"Initial cell selection: uplink_frequency=%u kHz (from absoluteFrequencyPointA=%ld, band=%ld, scs=%ld)\n",
cfg->carrier_config.uplink_frequency,
*frequencyInfoUL->absoluteFrequencyPointA,
*frequencyInfoUL->frequencyBandList->list.array[0]->freqBandIndicatorNR,
frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing);
}
}
for (int i = 0; i < 5; i++) {
@@ -204,7 +215,7 @@ static void config_common_ue_sa(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommo
}
}
frame_type_t frame_type = get_frame_type(mac->nr_band, get_softmodem_params()->numerology);
frame_type_t frame_type = get_frame_type(mac->nr_band, mac->numerology);
// cell config
cfg->cell_config.phy_cell_id = mac->physCellId;
cfg->cell_config.frame_duplex_type = frame_type;
@@ -212,7 +223,7 @@ static void config_common_ue_sa(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommo
// SSB config
cfg->ssb_config.ss_pbch_power = scc->ss_PBCH_BlockPower;
cfg->ssb_config.scs_common = get_softmodem_params()->numerology;
cfg->ssb_config.scs_common = mac->numerology;
// SSB Table config
cfg->ssb_table.ssb_offset_point_a = frequencyInfoDL->offsetToPointA;
@@ -231,7 +242,7 @@ static void config_common_ue_sa(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommo
}
int period_idx = mac->tdd_UL_DL_ConfigurationCommon ? get_tdd_period_idx(mac->tdd_UL_DL_ConfigurationCommon) : 0;
config_frame_structure(get_softmodem_params()->numerology,
config_frame_structure(mac->numerology,
mac->tdd_UL_DL_ConfigurationCommon,
period_idx,
frame_type,
@@ -443,14 +454,16 @@ static void config_common_ue(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommon_t
// carrier config
LOG_D(MAC, "[UE %d] Entering UE Config Common\n", mac->ue_id);
mac->numerology = *scc->ssbSubcarrierSpacing;
AssertFatal(scc->downlinkConfigCommon, "Not expecting downlinkConfigCommon to be NULL here\n");
NR_FrequencyInfoDL_t *frequencyInfoDL = scc->downlinkConfigCommon->frequencyInfoDL;
if (frequencyInfoDL) { // NeedM for inter-freq handover
mac->nr_band = *frequencyInfoDL->frequencyBandList.list.array[0];
frame_type = get_frame_type(mac->nr_band, get_softmodem_params()->numerology);
frame_type = get_frame_type(mac->nr_band, mac->numerology);
mac->frequency_range = get_freq_range_from_band(mac->nr_band);
AssertFatal(mac->numerology == frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, "DL and SSB subcarrierSpacing must be the same!\n");
int bw_index = get_supported_band_index(frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing,
mac->frequency_range,
frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth);
@@ -483,6 +496,7 @@ static void config_common_ue(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommon_t
NR_FrequencyInfoUL_t *frequencyInfoUL = scc->uplinkConfigCommon->frequencyInfoUL;
mac->p_Max = frequencyInfoUL->p_Max ? *frequencyInfoUL->p_Max : INT_MIN;
AssertFatal(mac->numerology == frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing, "UL and SSB subcarrierSpacing must be the same!\n");
int bw_index = get_supported_band_index(frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing,
mac->frequency_range,
frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth);
+2
View File
@@ -606,6 +606,8 @@ typedef struct NR_UE_MAC_INST_s {
uint16_t nr_band;
uint8_t ssb_subcarrier_offset;
int ssb_start_subcarrier;
uint64_t dl_frequency;
int numerology;
NR_SSB_meas_t ssb_measurements[MAX_NB_SSB];
NR_CSIRS_meas_t csirs_measurements;
+1 -1
View File
@@ -106,7 +106,7 @@ typedef struct nr_neighbor_cell_info {
void nr_rrc_mac_config_req_meas(module_id_t module_id, const nr_neighbor_cell_info_t *neighbor_cells, int num_neighbors);
/**\brief initialization NR UE MAC instance(s)*/
NR_UE_MAC_INST_t * nr_l2_init_ue(int nb_inst);
NR_UE_MAC_INST_t * nr_l2_init_ue(int nb_inst, int numerology);
/**\brief fetch MAC instance by module_id
\param module_id index of MAC instance(s)*/
+4 -3
View File
@@ -97,7 +97,7 @@ void nr_ue_mac_default_configs(NR_UE_MAC_INST_t *mac)
// default values as defined in 38.331 sec 9.2.2
// sf80 default for retxBSR_Timer sf10 for periodicBSR_Timer
int mu = mac->current_UL_BWP ? mac->current_UL_BWP->scs : get_softmodem_params()->numerology;
int mu = mac->current_UL_BWP ? mac->current_UL_BWP->scs : mac->numerology;
int subframes_per_slot = get_slots_per_frame_from_scs(mu) / 10;
nr_timer_setup(&mac->scheduling_info.retxBSR_Timer, 80 * subframes_per_slot, 1); // 1 slot update rate
nr_timer_setup(&mac->scheduling_info.periodicBSR_Timer, 10 * subframes_per_slot, 1); // 1 slot update rate
@@ -130,7 +130,7 @@ NR_UE_L2_STATE_t nr_ue_get_sync_state(module_id_t mod_id)
return mac->state;
}
NR_UE_MAC_INST_t *nr_l2_init_ue(int instance_id)
NR_UE_MAC_INST_t *nr_l2_init_ue(int instance_id, int numerology)
{
AssertFatal(instance_id < MAX_NUM_NR_UE_INST, "instance_id %d is out of range\n", instance_id);
AssertFatal(nr_ue_mac_inst[instance_id] == NULL, "MAC instance %d already initialized\n", instance_id);
@@ -138,12 +138,13 @@ NR_UE_MAC_INST_t *nr_l2_init_ue(int instance_id)
NR_UE_MAC_INST_t *mac = nr_ue_mac_inst[instance_id];
mac->ue_id = instance_id;
mac->numerology = numerology;
nr_ue_init_mac(mac);
int ret = pthread_mutex_init(&mac->if_mutex, NULL);
AssertFatal(ret == 0, "Mutex init failed\n");
nr_ue_mac_default_configs(mac);
if (IS_SA_MODE(get_softmodem_params()))
ue_init_config_request(mac, get_slots_per_frame_from_scs(get_softmodem_params()->numerology));
ue_init_config_request(mac, get_slots_per_frame_from_scs(numerology));
static bool initialized = false;
if (!initialized) {
@@ -427,7 +427,7 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl
const NR_UE_DL_BWP_t *current_DL_BWP = mac->current_DL_BWP;
NR_BWP_Id_t dl_bwp_id = current_DL_BWP ? current_DL_BWP->bwp_id : 0;
NR_BWP_PDCCH_t *pdcch_config = &mac->config_BWP_PDCCH[dl_bwp_id];
int scs = current_DL_BWP ? current_DL_BWP->scs : get_softmodem_params()->numerology;
int scs = current_DL_BWP ? current_DL_BWP->scs : mac->numerology;
const int slots_per_frame = get_slots_per_frame_from_scs(scs);
if (mac->get_sib1) {
int ssb_sc_offset_norm;