Compare commits

...

10 Commits

Author SHA1 Message Date
Rakesh Mundlamuri
1d8932cbdf HACK in the scheduler to test end-to-end MU-MIMO 2026-05-18 20:00:45 +05:30
Rakesh Mundlamuri
e7fea4aaec max_nb_pusch is no longer 1 for MU-MIMO in the physimulators
Signed-off-by: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org>
2026-05-18 20:00:45 +05:30
Rakesh Mundlamuri
506fd4e928 Use SIMD for Interference addition
Signed-off-by: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org>
2026-05-18 18:29:43 +05:30
Rakesh Mundlamuri
157ea51bf9 Introduce uplink MU-MIMO feature in nr_ulsim
This commit introduces the uplink MU-MIMO joint processing framework.
It models a scenario where a single gNB serves multiple spatially
multiplexed UEs sharing the exact same time and frequency resources.

Note that we dont handle SRS and PTRS for multiple UEs here.
Also, the cuda is restricted to single UE scenario.

Signed-off-by: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org>
2026-05-18 18:29:43 +05:30
Rakesh Mundlamuri
5d3b8f36ef Use malloc_or_fail() and calloc_or_fail in nr_ulsim
Signed-off-by: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org>
2026-05-18 18:29:43 +05:30
Rakesh Mundlamuri
c0f741a36c Introduce uplink multi-user MIMO (MU-MIMO) simulator
This commit introduces a dedicated simulation environment to validate
the uplink MU-MIMO joint processing framework. It models a scenario
where a single gNB serves multiple spatially multiplexed UEs sharing
the exact same time and frequency resources.

The simulator executes the complete physical layer baseband pipeline:
- PUSCH data generation and LDPC encoding per UE
- OFDM modulation (TX)
- Signal propagation through a multi-path channel model
- OFDM demodulation (RX) at the gNB
- Joint PUSCH decoding, equalization, and spatial demultiplexing

Performance metrics such as scrambling errors and BLER per UE is
evaluated at the end of the simulation.

Note: The current simulation evaluates performance based on a single
HARQ round (retransmissions are not supported).

Signed-off-by: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org>
2026-05-18 18:29:43 +05:30
Rakesh Mundlamuri
05cc5be04f Introduce MU-MIMO joint processing for UE group sizes greater than 1
- The nr_rx_pusch_tp() function, which handles single UE PUSCH RX
  processing, is extended to nr_rx_pusch_group_tp() for group based
  processing, where all UEs belonging to the same group are processed
  together.

- A joint PDU is created that accumulates all the layers of the UEs in
  a group to process them jointly. This creates a virtual single UE
  MIMO configuration that can be processed using existing OAI functions.
  The resulting output consists of the LLRs from all the UEs. The LLRs
  are further separated per UE, and unscrambling is performed
  individually.

- Note that the unscrambling step is removed from
  nr_pusch_symbol_processing(). Because the resulting output from joint
  processing consists of LLRs from multiple UEs, these LLRs need to be
  separated before they can be unscrambled individually.

Signed-off-by: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org>
2026-05-18 18:29:40 +05:30
Rakesh Mundlamuri
a8971f94b8 Create a helper function for dumping PUSCH data
Signed-off-by: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org>
2026-05-15 00:29:08 +05:30
Rakesh Mundlamuri
2a7b90e810 Introduce group-based processing using FAPI groups
Refactor ULSCH_id based processing of the PUSCH PDUs to group based
processing using FAPI groups. This is required to process multiple
PUSCH PDUs jointly for MU-MIMO. This is an initial commit that
supports group size = 1 and will be extended to > 1 in the following
commits.

The flow of group information from MAC to PHY using FAPI is as follows:

- In MAC, nr_schedule_pusch_fapi_groups() is introduced to group UEs
  that have PUSCH PDUs with the same frequency, time, and QAM order
  along with orthogonal DMRS ports into a single group. The FAPI
  groups consist of UE lists filled with a PDU index. Based on the
  parameter UL_tti_req->n_group, the number of unique groups is
  identified. In each group, the parameter group->n_ue defines the
  number of UEs present in the group along with their PDU indices.

- After receiving the FAPI message in the PHY from MAC, in
  nr_save_ul_tti_req(), a group number and group size for each
  PUSCH PDU is allocated in nr_fill_ulsch(), and a PUSCH job is created.

- Further, after a job is created, in phy_procedures_gNB_uespec_RX(),
  the PUSCH PDUs of the ULSCH_ids for each group are segregated based
  on the group index. They are processed jointly using
  handle_pusch_rx_group_trigger(), and the PUSCH DTX per UE in the
  group is calculated using handle_pusch_DTX().

Signed-off-by: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org>
2026-05-15 00:29:08 +05:30
Rakesh Mundlamuri
fcca760982 Refactor handle_pusch_decode_trigger separating pusch_rx and pusch_DTX
The function handle_pusch_decode_trigger() consists of PUSCH RX processing and PUSCH DTX detection
per UE. This is now split into two functions: handle_pusch_rx_trigger() and handle_pusch_DTX(). This
is needed for group based processing where each group consists of multiple UEs and
handle_pusch_rx_trigger() processes PUSCH RX for multiple UEs jointly while handle_pusch_DTX() need to
detect PUSCH DTX per UE.

Signed-off-by: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org>
2026-05-15 00:29:08 +05:30
17 changed files with 2553 additions and 828 deletions

View File

@@ -2099,6 +2099,19 @@ target_link_libraries(nr_srssim PRIVATE
m pthread ITTI dl nr_ue_phy_meas physim_common softmodem_common
)
add_executable(nr_ulsim_mu_mimo
${OPENAIR1_DIR}/SIMULATION/NR_PHY/ulsim_mu_mimo.c
${NFAPI_USER_DIR}/nfapi.c
${NFAPI_USER_DIR}/gnb_ind_vars.c
${PHY_INTERFACE_DIR}/queue_t.c
)
target_link_libraries(nr_ulsim_mu_mimo PRIVATE
-Wl,--start-group UTIL SIMU PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON CONFIG_LIB L2_NR -Wl,--end-group
m pthread ${T_LIB} ITTI dl nr_ue_phy_meas physim_common softmodem_common NR_L2_UE
)
target_link_libraries(nr_ulsim_mu_mimo PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs)
if(CUDA_ENABLE)
if (TARGET oai_cuda_lib)
target_link_libraries(nr_dlsim PRIVATE oai_cuda_lib)
@@ -2156,7 +2169,7 @@ if (${T_TRACER})
nr-uesoftmodem dlsim dlsim_tm4 dlsim_tm7
ulsim pbchsim scansim mbmssim pdcchsim pucchsim prachsim
syncsim nr_ulsim nr_dlsim nr_dlschsim nr_pbchsim nr_pucchsim
nr_ulschsim ldpctest polartest smallblocktest nr_srssim
nr_ulschsim ldpctest polartest smallblocktest nr_srssim nr_ulsim_mu_mimo
#all "add_library" definitions
ITTI lte_rrc nr_rrc s1ap x2ap m2ap m3ap f1ap
params_libconfig

View File

@@ -277,7 +277,7 @@ function main() {
SIMUS_PHY=1
CMAKE_CMD="$CMAKE_CMD -DENABLE_PHYSIM_TESTS=ON"
# TODO: fix: dlsim_tm4 pucchsim prachsim pdcchsim pbchsim mbmssim
TARGET_LIST="$TARGET_LIST dlsim ulsim ldpctest polartest smallblocktest nr_pbchsim nr_dlschsim nr_ulschsim nr_dlsim nr_ulsim nr_pucchsim nr_prachsim nr_psbchsim nr_srssim"
TARGET_LIST="$TARGET_LIST dlsim ulsim ldpctest polartest smallblocktest nr_pbchsim nr_dlschsim nr_ulschsim nr_dlsim nr_ulsim nr_pucchsim nr_prachsim nr_psbchsim nr_srssim nr_ulsim_mu_mimo"
echo_info "Will compile dlsim, ulsim, ..."
shift;;
-V | --vcd)

View File

@@ -92,13 +92,14 @@ void free_gNB_dlsch(NR_gNB_DLSCH_t *dlsch, uint16_t N_RB, const NR_DL_FRAME_PARM
@param frame Frame number
@param slot Slot number
*/
int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
NR_gNB_PUSCH *pusch_vars,
const nfapi_nr_pusch_pdu_t *rel15_ul,
uint32_t *ret_unav_res,
uint32_t frame,
uint8_t slot,
int beam_nb);
int nr_rx_pusch_group_tp(PHY_VARS_gNB *gNB,
NR_gNB_PUSCH **pusch_vars,
const nfapi_nr_pusch_pdu_t **rel15_ul,
uint32_t **ret_unav_res,
uint8_t group_size,
uint32_t frame,
uint8_t slot,
int beam_nb);
/*!
\brief This function implements the idft transform precoding in PUSCH
@@ -166,7 +167,9 @@ void nr_ulsch_shift_llr(int16_t **llr_layers, uint32_t nb_re, uint32_t rxdataF_e
void nr_fill_ulsch(PHY_VARS_gNB *gNB,
int frame,
int slot,
nfapi_nr_pusch_pdu_t *ulsch_pdu);
nfapi_nr_pusch_pdu_t *ulsch_pdu,
int16_t mu_group_idx,
uint8_t mu_group_size);
void nr_schedule_rx_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu);

View File

@@ -89,8 +89,12 @@ static void dump_pusch_pdu(int instance, int frame, int slot, nfapi_nr_pusch_pdu
pusch_pdu->pusch_data.num_cb);
}
void nr_fill_ulsch(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_pusch_pdu_t *ulsch_pdu)
void nr_fill_ulsch(PHY_VARS_gNB *gNB,
int frame,
int slot,
nfapi_nr_pusch_pdu_t *ulsch_pdu,
int16_t mu_group_idx,
uint8_t mu_group_size)
{
dump_pusch_pdu(gNB->Mod_id, frame, slot, ulsch_pdu);
LOG_D(NR_PHY,
@@ -101,7 +105,11 @@ void nr_fill_ulsch(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_pusch_pdu_t
ulsch_pdu->pusch_data.harq_process_id,
ulsch_pdu->pusch_data.new_data_indicator);
NR_gNB_PUSCH_job_t pusch = {.frame = frame, .slot = slot, .pusch_pdu = *ulsch_pdu};
NR_gNB_PUSCH_job_t pusch = {.frame = frame,
.slot = slot,
.pusch_pdu = *ulsch_pdu,
.mu_group_idx = mu_group_idx,
.mu_group_size = mu_group_size};
if (gNB->common_vars.beam_id) {
int fapi_beam_idx = ulsch_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx;
int bitmap = SL_to_bitmap(ulsch_pdu->start_symbol_index, ulsch_pdu->nr_of_symbols);

View File

@@ -1011,12 +1011,16 @@ typedef struct puschSymbolProc_s {
int startSymbol;
int numSymbols;
int16_t *llr;
int16_t *scramblingSequence;
uint32_t nvar;
int beam_nb;
task_ans_t *ans;
c16_t *pusch_ch_est_dmrs_interpl_slot_mem;
c16_t *rxFext_slot_mem;
uint8_t group_size;
const nfapi_nr_pusch_pdu_t **rel15_ul_group;
NR_gNB_PUSCH **pusch_vars_group;
int16_t **scrambling_sequences;
int *layer_offsets;
} puschSymbolProc_t;
static void nr_pusch_symbol_processing(void *arg)
@@ -1053,22 +1057,34 @@ static void nr_pusch_symbol_processing(void *arg)
rdata->pusch_ch_est_dmrs_interpl_slot_mem);
int nb_re_pusch = pusch_vars->ul_valid_re_per_slot[symbol];
// layer de-mapping
int16_t *llr_ptr = llrs[0];
if (rel15_ul->nrOfLayers != 1) {
llr_ptr = &rdata->llr[pusch_vars->llr_offset[symbol] * rel15_ul->nrOfLayers];
for (int i = 0; i < (nb_re_pusch); i++)
for (int l = 0; l < rel15_ul->nrOfLayers; l++)
for (int m = 0; m < rel15_ul->qam_mod_order; m++)
llr_ptr[i * rel15_ul->nrOfLayers * rel15_ul->qam_mod_order + l * rel15_ul->qam_mod_order + m] =
llrss[l][i * rel15_ul->qam_mod_order + m];
for (int u = 0; u < rdata->group_size; u++) {
NR_gNB_PUSCH *ue_pusch_vars = rdata->pusch_vars_group[u];
const nfapi_nr_pusch_pdu_t *ue_pdu = rdata->rel15_ul_group[u];
int16_t *ue_scrambling_seq = rdata->scrambling_sequences[u];
int ue_layers = ue_pdu->nrOfLayers;
int qam = ue_pdu->qam_mod_order;
ue_pusch_vars->llr_offset[symbol] = pusch_vars->llr_offset[symbol];
ue_pusch_vars->ul_valid_re_per_slot[symbol] = nb_re_pusch;
int sym_bit_offset = ue_pusch_vars->llr_offset[symbol] * ue_layers;
int16_t *llr_dest = &ue_pusch_vars->llr[sym_bit_offset];
int16_t *s_seq = &ue_scrambling_seq[sym_bit_offset];
for (int i = 0; i < nb_re_pusch; i++) {
for (int l = 0; l < ue_layers; l++) {
int joint_l = rdata->layer_offsets[u] + l;
for (int m = 0; m < qam; m++) {
int16_t soft_bit = llrss[joint_l][i * qam + m];
int bit_idx = i * ue_layers * qam + l * qam + m;
llr_dest[bit_idx] = soft_bit * s_seq[bit_idx];
}
}
}
}
// unscrambling
int16_t *llr16 = (int16_t*)&rdata->llr[pusch_vars->llr_offset[symbol] * rel15_ul->nrOfLayers];
int16_t *s = rdata->scramblingSequence + pusch_vars->llr_offset[symbol] * rel15_ul->nrOfLayers;
const int end = nb_re_pusch * rel15_ul->qam_mod_order * rel15_ul->nrOfLayers;
for (int i = 0; i < end; i++)
llr16[i] = llr_ptr[i] * s[i];
}
// Task running in // completed
@@ -1098,22 +1114,33 @@ static uint32_t average_u32(const uint32_t *x, uint16_t size)
return (uint32_t)(sum_x / size);
}
int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
NR_gNB_PUSCH *pusch_vars,
const nfapi_nr_pusch_pdu_t *rel15_ul,
uint32_t *ret_unav_res,
uint32_t frame,
uint8_t slot,
int beam_nb)
int nr_rx_pusch_group_tp(PHY_VARS_gNB *gNB,
NR_gNB_PUSCH **pusch_vars_group,
const nfapi_nr_pusch_pdu_t **rel15_ul_group,
uint32_t **ret_unav_res_group,
uint8_t group_size,
uint32_t frame,
uint8_t slot,
int beam_nb)
{
// This is a reference pdu since all the UEs in the group have same resource related parameters.
const nfapi_nr_pusch_pdu_t *rel15_ul_ref = rel15_ul_group[0];
NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms;
uint32_t bwp_start_subcarrier = ((rel15_ul->rb_start + rel15_ul->bwp_start) * NR_NB_SC_PER_RB + frame_parms->first_carrier_offset) % frame_parms->ofdm_symbol_size;
LOG_D(PHY,"pusch %d.%d : bwp_start_subcarrier %d, rb_start %d, first_carrier_offset %d\n", frame,slot,bwp_start_subcarrier, rel15_ul->rb_start, frame_parms->first_carrier_offset);
LOG_D(PHY,"pusch %d.%d : ul_dmrs_symb_pos %x\n",frame,slot,rel15_ul->ul_dmrs_symb_pos);
uint32_t bwp_start_subcarrier =
((rel15_ul_ref->rb_start + rel15_ul_ref->bwp_start) * NR_NB_SC_PER_RB + frame_parms->first_carrier_offset)
% frame_parms->ofdm_symbol_size;
LOG_D(PHY,
"pusch %d.%d : bwp_start_subcarrier %d, rb_start %d, first_carrier_offset %d\n",
frame,
slot,
bwp_start_subcarrier,
rel15_ul_ref->rb_start,
frame_parms->first_carrier_offset);
LOG_D(PHY, "pusch %d.%d : ul_dmrs_symb_pos %x\n", frame, slot, rel15_ul_ref->ul_dmrs_symb_pos);
// Memories to store data for data recording
int buffer_length_slot = rel15_ul->rb_size * NR_NB_SC_PER_RB * 14; // 14 OFDM Symbols per slot
int buffer_length_slot = rel15_ul_ref->rb_size * NR_NB_SC_PER_RB * NR_SYMBOLS_PER_SLOT;
// data recording application supports only a single layer.
// nb_rx_ant (= frame_parms->nb_antennas_rx) is limited to 1 for data recording application.
// int nb_layer (= rel15_ul->nrOfLayers) is limited to 1 for data recording application.
@@ -1145,40 +1172,85 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
memset(rxFext_slot_mem, 0, sizeof(c16_t) * buffer_length_slot * 1 * 1);
#endif
// Create a virtual multi layer pdu by accumulating the layers over UEs in the group and storing dmrs ports for joint processing
uint32_t combined_dmrs_ports = 0;
int total_layers = 0;
int layer_offset[group_size];
for (int u = 0; u < group_size; u++) {
const nfapi_nr_pusch_pdu_t *p = rel15_ul_group[u];
combined_dmrs_ports |= p->dmrs_ports;
layer_offset[u] = total_layers;
total_layers += rel15_ul_group[u]->nrOfLayers;
}
AssertFatal(total_layers <= NR_MAX_NB_LAYERS,
"MU-MIMO group total_layers=%d > NR_MAX_NB_LAYERS=%d\n",
total_layers,
NR_MAX_NB_LAYERS);
nfapi_nr_pusch_pdu_t joint_pdu = *rel15_ul_ref;
joint_pdu.nrOfLayers = total_layers;
joint_pdu.dmrs_ports = combined_dmrs_ports;
NR_gNB_PUSCH *joint_pv = pusch_vars_group[0];
LOG_D(PHY,
"%4u.%u MU-MIMO joint RX: %d UEs, %d total layers, rb_start=%u rb_size=%u qam=%u\n",
frame,
slot,
group_size,
total_layers,
rel15_ul_ref->rb_start,
rel15_ul_ref->rb_size,
rel15_ul_ref->qam_mod_order);
//----------------------------------------------------------
//------------------- Channel estimation -------------------
//----------------------------------------------------------
start_meas(&gNB->ulsch_channel_estimation_stats);
int max_ch = 0;
uint32_t nvar = 0;
int end_symbol = rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols;
for (uint8_t symbol = rel15_ul->start_symbol_index; symbol < end_symbol; symbol++) {
uint8_t dmrs_symbol_flag = (rel15_ul->ul_dmrs_symb_pos >> symbol) & 0x01;
int end_symbol = rel15_ul_ref->start_symbol_index + rel15_ul_ref->nr_of_symbols;
for (uint8_t symbol = rel15_ul_ref->start_symbol_index; symbol < end_symbol; symbol++) {
uint8_t dmrs_symbol_flag = (rel15_ul_ref->ul_dmrs_symb_pos >> symbol) & 0x01;
LOG_D(PHY, "symbol %d, dmrs_symbol_flag :%d\n", symbol, dmrs_symbol_flag);
if (dmrs_symbol_flag == 1) {
for (int nl = 0; nl < rel15_ul->nrOfLayers; nl++) {
uint32_t nvar_tmp = 0;
nr_pusch_channel_estimation(gNB,
slot,
nl,
get_dmrs_port(nl, rel15_ul->dmrs_ports),
symbol,
pusch_vars,
beam_nb,
bwp_start_subcarrier,
rel15_ul,
&max_ch,
&nvar_tmp,
pusch_dmrs_slot_mem,
pusch_ch_est_dmrs_pos_slot_mem);
nvar += nvar_tmp;
for (int u = 0; u < group_size; u++) {
const nfapi_nr_pusch_pdu_t *p = rel15_ul_group[u];
for (int nl = 0; nl < p->nrOfLayers; nl++) {
int global_layer = layer_offset[u] + nl;
uint32_t nvar_tmp = 0;
nr_pusch_channel_estimation(gNB,
slot,
global_layer,
get_dmrs_port(nl, p->dmrs_ports),
symbol,
joint_pv,
beam_nb,
bwp_start_subcarrier,
&joint_pdu,
&max_ch,
&nvar_tmp,
pusch_dmrs_slot_mem,
pusch_ch_est_dmrs_pos_slot_mem);
nvar += nvar_tmp;
}
}
}
}
nvar /= (rel15_ul->nr_of_symbols * rel15_ul->nrOfLayers * frame_parms->nb_antennas_rx);
nvar /= (rel15_ul_ref->nr_of_symbols * total_layers * frame_parms->nb_antennas_rx);
// averaging time domain channel estimates
// Change to joint processing
if (gNB->chest_time == 1)
nr_chest_time_domain_avg(frame_parms,
joint_pv->ul_ch_estimates,
rel15_ul_ref->nr_of_symbols,
rel15_ul_ref->start_symbol_index,
rel15_ul_ref->ul_dmrs_symb_pos, // change needed ?
rel15_ul_ref->rb_size);
// ULSCH signal and noise power measurements
// This is same for all the UEs in the group
allocCast2D(n0_subband_power,
unsigned int,
gNB->measurements.n0_subband_power,
@@ -1186,15 +1258,15 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
frame_parms->N_RB_UL,
false);
int start_sc = (rel15_ul->bwp_start + rel15_ul->rb_start) * NR_NB_SC_PER_RB;
int start_sc = (rel15_ul_ref->bwp_start + rel15_ul_ref->rb_start) * NR_NB_SC_PER_RB;
int middle_sc = frame_parms->ofdm_symbol_size - frame_parms->first_carrier_offset;
int end_sc = (start_sc + rel15_ul->rb_size * NR_NB_SC_PER_RB - 1) % frame_parms->ofdm_symbol_size;
int end_sc = (start_sc + rel15_ul_ref->rb_size * NR_NB_SC_PER_RB - 1) % frame_parms->ofdm_symbol_size;
for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) {
pusch_vars->ulsch_power[aarx] = 0;
pusch_vars->ulsch_noise_power[aarx] = 0;
joint_pv->ulsch_power[aarx] = 0;
joint_pv->ulsch_noise_power[aarx] = 0;
int64_t symb_energy = 0;
for (uint8_t symbol = rel15_ul->start_symbol_index; symbol < end_symbol; symbol++) {
for (uint8_t symbol = rel15_ul_ref->start_symbol_index; symbol < end_symbol; symbol++) {
int offset0 = ((slot % RU_RX_SLOT_DEPTH) * frame_parms->symbols_per_slot + symbol) * frame_parms->ofdm_symbol_size;
int offset = offset0 + (frame_parms->first_carrier_offset + start_sc) % frame_parms->ofdm_symbol_size;
c16_t *ul_ch = &gNB->common_vars.rxdataF[beam_nb][aarx][offset];
@@ -1202,82 +1274,78 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
int64_t symb_energy_aux = signal_energy_nodc(ul_ch, middle_sc - start_sc) * (middle_sc - start_sc);
ul_ch = &gNB->common_vars.rxdataF[beam_nb][aarx][offset0];
symb_energy_aux += (signal_energy_nodc(ul_ch, end_sc + 1) * (end_sc + 1));
symb_energy += symb_energy_aux / (rel15_ul->rb_size * NR_NB_SC_PER_RB);
symb_energy += symb_energy_aux / (rel15_ul_ref->rb_size * NR_NB_SC_PER_RB);
} else {
symb_energy += signal_energy_nodc(ul_ch, rel15_ul->rb_size * NR_NB_SC_PER_RB);
symb_energy += signal_energy_nodc(ul_ch, rel15_ul_ref->rb_size * NR_NB_SC_PER_RB);
}
}
pusch_vars->ulsch_power[aarx] += (symb_energy / rel15_ul->nr_of_symbols);
joint_pv->ulsch_power[aarx] += (symb_energy / rel15_ul_ref->nr_of_symbols);
pusch_vars->ulsch_noise_power[aarx] +=
average_u32(&n0_subband_power[aarx][rel15_ul->bwp_start + rel15_ul->rb_start], rel15_ul->rb_size);
joint_pv->ulsch_noise_power[aarx] +=
average_u32(&n0_subband_power[aarx][rel15_ul_ref->bwp_start + rel15_ul_ref->rb_start], rel15_ul_ref->rb_size);
LOG_D(PHY,
"aa %d, bwp_start%d, rb_start %d, rb_size %d: ulsch_power %d, ulsch_noise_power %d\n",
aarx,
rel15_ul->bwp_start,
rel15_ul->rb_start,
rel15_ul->rb_size,
pusch_vars->ulsch_power[aarx],
pusch_vars->ulsch_noise_power[aarx]);
rel15_ul_ref->bwp_start,
rel15_ul_ref->rb_start,
rel15_ul_ref->rb_size,
joint_pv->ulsch_power[aarx],
joint_pv->ulsch_noise_power[aarx]);
}
// averaging time domain channel estimates
if (gNB->chest_time == 1)
nr_chest_time_domain_avg(frame_parms,
pusch_vars->ul_ch_estimates,
rel15_ul->nr_of_symbols,
rel15_ul->start_symbol_index,
rel15_ul->ul_dmrs_symb_pos,
rel15_ul->rb_size);
stop_meas(&gNB->ulsch_channel_estimation_stats);
start_meas(&gNB->rx_pusch_init_stats);
// Scrambling initialization
int number_dmrs_symbols = 0;
for (int l = rel15_ul->start_symbol_index; l < end_symbol; l++)
number_dmrs_symbols += ((rel15_ul->ul_dmrs_symb_pos)>>l) & 0x01;
int nb_re_dmrs;
if (rel15_ul->dmrs_config_type == pusch_dmrs_type1)
nb_re_dmrs = 6*rel15_ul->num_dmrs_cdm_grps_no_data;
else
nb_re_dmrs = 4*rel15_ul->num_dmrs_cdm_grps_no_data;
// Calculate number of unavailable resources due to PTRS
// This is assumed to be same for all the UEs (same PTRS configuration for all UEs)
uint32_t unav_res = 0;
if (rel15_ul->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) {
if (rel15_ul_ref->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) {
uint16_t ptrsSymbPos = 0;
set_ptrs_symb_idx(&ptrsSymbPos,
rel15_ul->nr_of_symbols,
rel15_ul->start_symbol_index,
1 << rel15_ul->pusch_ptrs.ptrs_time_density,
rel15_ul->ul_dmrs_symb_pos);
int ptrsSymbPerSlot = get_ptrs_symbols_in_slot(ptrsSymbPos, rel15_ul->start_symbol_index, rel15_ul->nr_of_symbols);
int n_ptrs = (rel15_ul->rb_size + rel15_ul->pusch_ptrs.ptrs_freq_density - 1) / rel15_ul->pusch_ptrs.ptrs_freq_density;
rel15_ul_ref->nr_of_symbols,
rel15_ul_ref->start_symbol_index,
1 << rel15_ul_ref->pusch_ptrs.ptrs_time_density,
rel15_ul_ref->ul_dmrs_symb_pos);
int ptrsSymbPerSlot = get_ptrs_symbols_in_slot(ptrsSymbPos, rel15_ul_ref->start_symbol_index, rel15_ul_ref->nr_of_symbols);
int n_ptrs =
(rel15_ul_ref->rb_size + rel15_ul_ref->pusch_ptrs.ptrs_freq_density - 1) / rel15_ul_ref->pusch_ptrs.ptrs_freq_density;
unav_res = n_ptrs * ptrsSymbPerSlot;
}
// get how many bit in a slot //
int G = nr_get_G(rel15_ul->rb_size,
rel15_ul->nr_of_symbols,
nb_re_dmrs,
number_dmrs_symbols, // number of dmrs symbols irrespective of single or double symbol dmrs
unav_res,
rel15_ul->qam_mod_order,
rel15_ul->nrOfLayers);
*ret_unav_res = unav_res;
// Scrambling initialization
int number_dmrs_symbols = 0;
for (int l = rel15_ul_ref->start_symbol_index; l < end_symbol; l++)
number_dmrs_symbols += ((rel15_ul_ref->ul_dmrs_symb_pos) >> l) & 0x01;
int nb_re_dmrs;
if (rel15_ul_ref->dmrs_config_type == pusch_dmrs_type1)
nb_re_dmrs = 6 * rel15_ul_ref->num_dmrs_cdm_grps_no_data;
else
nb_re_dmrs = 4 * rel15_ul_ref->num_dmrs_cdm_grps_no_data;
// initialize scrambling sequence //
int16_t scramblingSequence[G + 96] __attribute__((aligned(32)));
int max_G = 0;
for (int u = 0; u < group_size; u++) {
const nfapi_nr_pusch_pdu_t *p = rel15_ul_group[u];
int G_u = nr_get_G(p->rb_size, p->nr_of_symbols, nb_re_dmrs, number_dmrs_symbols, unav_res, p->qam_mod_order, p->nrOfLayers);
if (G_u > max_G)
max_G = G_u;
}
nr_codeword_unscrambling_init(scramblingSequence, G, 0, rel15_ul->data_scrambling_id, rel15_ul->rnti);
int16_t scrambling_sequences[group_size][max_G + 96] __attribute__((aligned(32)));
int16_t *scrambling_sequences_arr[group_size];
// first the computation of channel levels
for (int u = 0; u < group_size; u++) {
scrambling_sequences_arr[u] = scrambling_sequences[u];
const nfapi_nr_pusch_pdu_t *p = rel15_ul_group[u];
int G_u = nr_get_G(p->rb_size, p->nr_of_symbols, nb_re_dmrs, number_dmrs_symbols, unav_res, p->qam_mod_order, p->nrOfLayers);
nr_codeword_unscrambling_init(scrambling_sequences_arr[u], G_u, 0, p->data_scrambling_id, p->rnti);
}
// Computation of channel levels
int nb_re_pusch = 0, meas_symbol = -1;
for(meas_symbol = rel15_ul->start_symbol_index; meas_symbol < end_symbol; meas_symbol++)
if ((nb_re_pusch = get_nb_re_pusch(frame_parms, rel15_ul, meas_symbol)) > 0)
for (meas_symbol = rel15_ul_ref->start_symbol_index; meas_symbol < end_symbol; meas_symbol++)
if ((nb_re_pusch = get_nb_re_pusch(frame_parms, &joint_pdu, meas_symbol)) > 0)
break;
AssertFatal(nb_re_pusch > 0 && meas_symbol >= 0,
@@ -1292,27 +1360,27 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
nb_re_pusch = ceil_mod(nb_re_pusch, 16);
int dmrs_symbol;
if (gNB->chest_time == 0)
dmrs_symbol = get_valid_dmrs_idx_for_channel_est(rel15_ul->ul_dmrs_symb_pos, meas_symbol);
dmrs_symbol = get_valid_dmrs_idx_for_channel_est(rel15_ul_ref->ul_dmrs_symb_pos, meas_symbol);
else // average of channel estimates stored in first symbol
dmrs_symbol = get_next_dmrs_symbol_in_slot(rel15_ul->ul_dmrs_symb_pos, rel15_ul->start_symbol_index, end_symbol);
dmrs_symbol = get_next_dmrs_symbol_in_slot(rel15_ul_ref->ul_dmrs_symb_pos, rel15_ul_ref->start_symbol_index, end_symbol);
int size_est = nb_re_pusch * frame_parms->symbols_per_slot;
__attribute__((aligned(32))) int ul_ch_estimates_ext[rel15_ul->nrOfLayers * frame_parms->nb_antennas_rx][size_est];
__attribute__((aligned(32))) int ul_ch_estimates_ext[total_layers * frame_parms->nb_antennas_rx][size_est];
memset(ul_ch_estimates_ext, 0, sizeof(ul_ch_estimates_ext));
int buffer_length = rel15_ul->rb_size * NR_NB_SC_PER_RB;
int buffer_length = rel15_ul_ref->rb_size * NR_NB_SC_PER_RB;
c16_t temp_rxFext[frame_parms->nb_antennas_rx][buffer_length] __attribute__((aligned(32)));
for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++)
for (int nl = 0; nl < rel15_ul->nrOfLayers; nl++)
for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++)
for (int nl = 0; nl < total_layers; nl++)
nr_ulsch_extract_rbs(gNB->common_vars.rxdataF[beam_nb][aarx],
(c16_t *)pusch_vars->ul_ch_estimates[nl * frame_parms->nb_antennas_rx + aarx],
(c16_t *)joint_pv->ul_ch_estimates[nl * frame_parms->nb_antennas_rx + aarx],
temp_rxFext[aarx],
(c16_t*)&ul_ch_estimates_ext[nl * frame_parms->nb_antennas_rx + aarx][meas_symbol * nb_re_pusch],
(c16_t *)&ul_ch_estimates_ext[nl * frame_parms->nb_antennas_rx + aarx][meas_symbol * nb_re_pusch],
soffset + meas_symbol * frame_parms->ofdm_symbol_size,
dmrs_symbol * frame_parms->ofdm_symbol_size,
(rel15_ul->ul_dmrs_symb_pos >> meas_symbol) & 0x01,
rel15_ul,
(rel15_ul_ref->ul_dmrs_symb_pos >> meas_symbol) & 0x01,
&joint_pdu,
frame_parms);
uint8_t shift_ch_ext = rel15_ul->nrOfLayers > 1 ? log2_approx(max_ch >> 11) : 0;
uint8_t shift_ch_ext = total_layers > 1 ? log2_approx(max_ch >> 11) : 0;
//----------------------------------------------------------
//--------------------- Channel Scaling --------------------
@@ -1321,54 +1389,55 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
ul_ch_estimates_ext,
meas_symbol,
nb_re_pusch,
rel15_ul->nrOfLayers,
total_layers,
frame_parms->nb_antennas_rx,
shift_ch_ext);
int avg[frame_parms->nb_antennas_rx*rel15_ul->nrOfLayers];
int avg[frame_parms->nb_antennas_rx * total_layers];
nr_channel_level(meas_symbol,
size_est,
(c16_t (*)[size_est])ul_ch_estimates_ext,
(c16_t(*)[size_est])ul_ch_estimates_ext,
frame_parms->nb_antennas_rx,
rel15_ul->nrOfLayers,
total_layers,
avg,
nb_re_pusch);
int avgs = 0;
for (int nl = 0; nl < rel15_ul->nrOfLayers; nl++)
for (int nl = 0; nl < total_layers; nl++)
for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++)
avgs = cmax(avgs, avg[nl * frame_parms->nb_antennas_rx + aarx]);
if (rel15_ul->nrOfLayers == 2 && rel15_ul->qam_mod_order > 6)
pusch_vars->log2_maxh = (log2_approx(avgs) >> 1) - 3; // for MMSE
else if (rel15_ul->nrOfLayers == 2)
pusch_vars->log2_maxh = (log2_approx(avgs) >> 1) - 2 + log2_approx(frame_parms->nb_antennas_rx >> 1);
else
pusch_vars->log2_maxh = (log2_approx(avgs) >> 1) + 1 + log2_approx(frame_parms->nb_antennas_rx >> 1);
if (total_layers == 2 && rel15_ul_ref->qam_mod_order > 6)
joint_pv->log2_maxh = (log2_approx(avgs) >> 1) - 3; // for MMSE
else if (total_layers == 2)
joint_pv->log2_maxh = (log2_approx(avgs) >> 1) - 2 + log2_approx(frame_parms->nb_antennas_rx >> 1);
else
joint_pv->log2_maxh = (log2_approx(avgs) >> 1) + 1 + log2_approx(frame_parms->nb_antennas_rx >> 1);
if (pusch_vars->log2_maxh < 0)
pusch_vars->log2_maxh = 0;
if (joint_pv->log2_maxh < 0)
joint_pv->log2_maxh = 0;
stop_meas(&gNB->rx_pusch_init_stats);
start_meas(&gNB->rx_pusch_symbol_processing_stats);
int numSymbols = gNB->num_pusch_symbols_per_thread;
int total_res = 0;
int const loop_iter = CEILIDIV(rel15_ul->nr_of_symbols, numSymbols);
int const loop_iter = CEILIDIV(rel15_ul_ref->nr_of_symbols, numSymbols);
puschSymbolProc_t arr[loop_iter];
task_ans_t ans;
init_task_ans(&ans, loop_iter);
int sz_arr = 0;
for(uint8_t task_index = 0; task_index < loop_iter; task_index++) {
int symbol = task_index * numSymbols + rel15_ul->start_symbol_index;
int symbol = task_index * numSymbols + rel15_ul_ref->start_symbol_index;
int res_per_task = 0;
for (int s = 0; s < numSymbols && s + symbol < end_symbol; s++) {
pusch_vars->ul_valid_re_per_slot[symbol+s] = get_nb_re_pusch(frame_parms,rel15_ul,symbol+s);
pusch_vars->llr_offset[symbol+s] = ((symbol+s) == rel15_ul->start_symbol_index) ?
0 :
pusch_vars->llr_offset[symbol+s-1] + pusch_vars->ul_valid_re_per_slot[symbol+s-1] * rel15_ul->qam_mod_order;
res_per_task += pusch_vars->ul_valid_re_per_slot[symbol + s];
joint_pv->ul_valid_re_per_slot[symbol + s] = get_nb_re_pusch(frame_parms, &joint_pdu, symbol + s);
joint_pv->llr_offset[symbol + s] =
((symbol + s) == rel15_ul_ref->start_symbol_index)
? 0
: joint_pv->llr_offset[symbol + s - 1] + joint_pv->ul_valid_re_per_slot[symbol + s - 1] * rel15_ul_ref->qam_mod_order;
res_per_task += joint_pv->ul_valid_re_per_slot[symbol + s];
}
total_res += res_per_task;
if (res_per_task > 0) {
@@ -1378,20 +1447,24 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
rdata->gNB = gNB;
rdata->frame_parms = frame_parms;
rdata->rel15_ul = rel15_ul;
rdata->rel15_ul = &joint_pdu;
rdata->slot = slot;
rdata->startSymbol = symbol;
// Last task processes remainder symbols
rdata->numSymbols = task_index == loop_iter - 1 ? rel15_ul->nr_of_symbols - (loop_iter - 1) * numSymbols : numSymbols;
rdata->pusch_vars = pusch_vars;
rdata->llr = pusch_vars->llr;
rdata->scramblingSequence = scramblingSequence;
rdata->numSymbols = task_index == loop_iter - 1 ? rel15_ul_ref->nr_of_symbols - (loop_iter - 1) * numSymbols : numSymbols;
rdata->pusch_vars = joint_pv;
rdata->llr = joint_pv->llr;
rdata->nvar = nvar;
rdata->beam_nb = beam_nb;
rdata->rxFext_slot_mem = rxFext_slot_mem;
rdata->pusch_ch_est_dmrs_interpl_slot_mem = pusch_ch_est_dmrs_interpl_slot_mem;
rdata->group_size = group_size;
rdata->rel15_ul_group = rel15_ul_group;
rdata->pusch_vars_group = pusch_vars_group;
rdata->scrambling_sequences = scrambling_sequences_arr;
rdata->layer_offsets = layer_offset;
if (rel15_ul->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) {
if (rel15_ul_ref->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) {
nr_pusch_symbol_processing(rdata);
} else {
task_t t = {.func = &nr_pusch_symbol_processing, .args = rdata};
@@ -1405,52 +1478,85 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
} // symbol loop
#if T_TRACER
int dmrs_port = get_dmrs_port(0, rel15_ul->dmrs_ports);
int dmrs_port = get_dmrs_port(0, rel15_ul_ref->dmrs_ports);
log_ul_fd_dmrs(frame, slot, frame_parms, rel15_ul,
number_dmrs_symbols, dmrs_port,
log_ul_fd_dmrs(frame,
slot,
frame_parms,
rel15_ul_ref,
number_dmrs_symbols,
dmrs_port,
(const c16_t *)(&(pusch_dmrs_slot_mem[0])),
rel15_ul->rb_size * NR_NB_SC_PER_RB * rel15_ul->nr_of_symbols * 4);
rel15_ul_ref->rb_size * NR_NB_SC_PER_RB * rel15_ul_ref->nr_of_symbols * 4);
log_ul_fd_chan_est_dmrs_pos(frame, slot, frame_parms, rel15_ul,
number_dmrs_symbols, dmrs_port,
log_ul_fd_chan_est_dmrs_pos(frame,
slot,
frame_parms,
rel15_ul_ref,
number_dmrs_symbols,
dmrs_port,
(const c16_t *)(&(pusch_ch_est_dmrs_pos_slot_mem[0])),
rel15_ul->rb_size * NR_NB_SC_PER_RB * rel15_ul->nr_of_symbols * 4);
rel15_ul_ref->rb_size * NR_NB_SC_PER_RB * rel15_ul_ref->nr_of_symbols * 4);
log_ul_fd_pusch_iq(frame, slot, frame_parms, rel15_ul,
number_dmrs_symbols, dmrs_port,
log_ul_fd_pusch_iq(frame,
slot,
frame_parms,
rel15_ul_ref,
number_dmrs_symbols,
dmrs_port,
(const c16_t *)(&(rxFext_slot_mem[0])),
rel15_ul->rb_size * NR_NB_SC_PER_RB * rel15_ul->nr_of_symbols * frame_parms->nb_antennas_rx * 4);
rel15_ul_ref->rb_size * NR_NB_SC_PER_RB * rel15_ul_ref->nr_of_symbols * frame_parms->nb_antennas_rx * 4);
log_ul_fd_chan_est_dmrs_interpl(frame, slot, frame_parms, rel15_ul,
number_dmrs_symbols, dmrs_port,
(const c16_t *)pusch_ch_est_dmrs_interpl_slot_mem,
rel15_ul->rb_size * NR_NB_SC_PER_RB * rel15_ul->nr_of_symbols
* frame_parms->nb_antennas_rx * rel15_ul->nrOfLayers * 4);
log_ul_fd_chan_est_dmrs_interpl(
frame,
slot,
frame_parms,
rel15_ul_ref,
number_dmrs_symbols,
dmrs_port,
(const c16_t *)pusch_ch_est_dmrs_interpl_slot_mem,
rel15_ul_ref->rb_size * NR_NB_SC_PER_RB * rel15_ul_ref->nr_of_symbols * frame_parms->nb_antennas_rx * total_layers * 4);
#endif
join_task_ans(&ans);
for (int u = 0; u < group_size; u++) {
NR_gNB_PUSCH *pv = pusch_vars_group[u];
// Copy unavailable resources per UE
*ret_unav_res_group[u] = unav_res;
// Copy power measurements per UE
pv->ulsch_power_tot = 0;
pv->ulsch_noise_power_tot = 0;
for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) {
pv->ulsch_power[aarx] = joint_pv->ulsch_power[aarx];
pv->ulsch_noise_power[aarx] = joint_pv->ulsch_noise_power[aarx];
pv->ulsch_power_tot += pv->ulsch_power[aarx];
pv->ulsch_noise_power_tot += pv->ulsch_noise_power[aarx];
}
}
stop_meas(&gNB->rx_pusch_symbol_processing_stats);
// Copy the data to the scope. This cannot be performed in one call to gNBscopeCopy because the data is not contiguous in the
// buffer due to reference symbol extraction and padding. The gNBscopeCopy call is broken up into steps: trylock, copy, unlock.
metadata mt = {.slot = slot, .frame = frame};
if (gNBTryLockScopeData(gNB, gNBPuschRxIq, sizeof(c16_t), 1, total_res, &mt)) {
int buffer_length = ceil_mod(rel15_ul->rb_size * NR_NB_SC_PER_RB, 16);
int buffer_length = ceil_mod(rel15_ul_ref->rb_size * NR_NB_SC_PER_RB, 16);
size_t offset = 0;
for (uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols);
for (uint8_t symbol = rel15_ul_ref->start_symbol_index;
symbol < (rel15_ul_ref->start_symbol_index + rel15_ul_ref->nr_of_symbols);
symbol++) {
gNBscopeCopyUnsafe(gNB,
gNBPuschRxIq,
&pusch_vars->rxdataF_comp[0][symbol * buffer_length],
sizeof(c16_t) * pusch_vars->ul_valid_re_per_slot[symbol],
&pusch_vars_group[0]->rxdataF_comp[0][symbol * buffer_length],
sizeof(c16_t) * pusch_vars_group[0]->ul_valid_re_per_slot[symbol],
offset,
symbol - rel15_ul->start_symbol_index);
offset += sizeof(c16_t) * pusch_vars->ul_valid_re_per_slot[symbol];
symbol - rel15_ul_ref->start_symbol_index);
offset += sizeof(c16_t) * pusch_vars_group[0]->ul_valid_re_per_slot[symbol];
}
gNBunlockScopeData(gNB, gNBPuschRxIq)
}
uint32_t total_llrs = total_res * rel15_ul->qam_mod_order * rel15_ul->nrOfLayers;
gNBscopeCopyWithMetadata(gNB, gNBPuschLlr, pusch_vars->llr, sizeof(c16_t), 1, total_llrs, 0, &mt);
uint32_t total_llrs = total_res * rel15_ul_ref->qam_mod_order * rel15_ul_ref->nrOfLayers;
gNBscopeCopyWithMetadata(gNB, gNBPuschLlr, pusch_vars_group[0]->llr, sizeof(c16_t), 1, total_llrs, 0, &mt);
return 0;
}

View File

@@ -189,6 +189,11 @@ typedef struct {
uint32_t slot;
/// ULSCH PDU
nfapi_nr_pusch_pdu_t pusch_pdu;
// Multi-User (MU) group index
// -1 : unallocated
int16_t mu_group_idx;
// 1 for Single-User (SU) and > 1 is MU
uint8_t mu_group_size;
} NR_gNB_PUSCH_job_t;
typedef struct {

View File

@@ -939,18 +939,15 @@ static void handle_pucch(PHY_VARS_gNB *gNB, c16_t **rxdataF, const NR_gNB_PUCCH_
}
}
static bool handle_pusch_decode_trigger(PHY_VARS_gNB *gNB, NR_gNB_PUSCH *pusch_vars, NR_gNB_ULSCH_t *ulsch, NR_UL_IND_t *UL_INFO, int *pusch_DTX)
{
NR_UL_gNB_HARQ_t *ulsch_harq = ulsch->harq_process;
AssertFatal(ulsch_harq != NULL, "harq_pid %d is not allocated\n", ulsch->harq_pid);
const nfapi_nr_pusch_pdu_t *pdu = &ulsch_harq->ulsch_pdu;
#ifdef DEBUG_RXDATA
static void dump_pusch_rx_data(PHY_VARS_gNB *gNB, NR_gNB_ULSCH_t *ulsch, const nfapi_nr_pusch_pdu_t *pdu)
{
NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms;
RU_t *ru = gNB->RU_list[0];
int slot_offset = frame_parms->get_samples_slot_timestamp(ulsch->slot, frame_parms, 0);
int slot_offset = get_samples_slot_timestamp(frame_parms, ulsch->slot);
slot_offset -= ru->N_TA_offset;
int32_t sample_offset = gNB->common_vars.debugBuff_sample_offset;
int16_t buf = (int16_t *)&gNB->common_vars.debugBuff[offset];
int16_t *buf = (int16_t *)&gNB->common_vars.debugBuff[sample_offset];
buf[0] = (int16_t)ulsch->rnti;
buf[1] = (int16_t)pdu->rb_size;
buf[2] = (int16_t)pdu->rb_start;
@@ -961,30 +958,49 @@ static bool handle_pusch_decode_trigger(PHY_VARS_gNB *gNB, NR_gNB_PUSCH *pusch_v
buf[7] = (int16_t)ulsch->harq_pid;
memcpy(&gNB->common_vars.debugBuff[gNB->common_vars.debugBuff_sample_offset + 4],
&ru->common.rxdata[0][slot_offset],
frame_parms->get_samples_per_slot(ulsch->slot, frame_parms) * sizeof(int32_t));
gNB->common_vars.debugBuff_sample_offset += (frame_parms->get_samples_per_slot(ulsch->slot, frame_parms) + 1000 + 4);
if (gNB->common_vars.debugBuff_sample_offset > ((frame_parms->get_samples_per_slot(ulsch->slot, frame_parms) + 1000 + 2) * 20)) {
FILE *f;
f = fopen("rxdata_buff.raw", "w");
get_samples_per_slot(ulsch->slot, frame_parms) * sizeof(int32_t));
gNB->common_vars.debugBuff_sample_offset += (get_samples_per_slot(ulsch->slot, frame_parms) + 1000 + 4);
if (gNB->common_vars.debugBuff_sample_offset > ((get_samples_per_slot(ulsch->slot, frame_parms) + 1000 + 2) * 20)) {
FILE *f = fopen("rxdata_buff.raw", "w");
if (f == NULL)
exit(1);
fwrite((int16_t *)gNB->common_vars.debugBuff,
2,
(frame_parms->get_samples_per_slot(ulsch->slot, frame_parms) + 1000 + 4) * 20 * 2,
f);
fwrite((int16_t *)gNB->common_vars.debugBuff, 2, (get_samples_per_slot(ulsch->slot, frame_parms) + 1000 + 4) * 20 * 2, f);
fclose(f);
exit(-1);
}
}
#endif
static void handle_pusch_rx_group_trigger(PHY_VARS_gNB *gNB,
NR_gNB_PUSCH **pusch_vars_group,
const nfapi_nr_pusch_pdu_t **ulsch_pdu_group,
uint32_t **ret_unav_res_group,
int group_size,
uint32_t frame,
uint8_t slot,
int beam_nb)
{
#ifdef DEBUG_RXDATA
NR_gNB_ULSCH_t *ulsch = ulsch_group[0]; // fix this
const nfapi_nr_pusch_pdu_t *pdu = ulsch_pdu_group[0];
dump_pusch_rx_data(gNB, ulsch, pdu);
#endif
start_meas(&gNB->rx_pusch_stats);
nr_rx_pusch_tp(gNB, pusch_vars, pdu, &ulsch->unav_res, ulsch->frame, ulsch->slot, ulsch->beam_nb);
pusch_vars->ulsch_power_tot = 0;
pusch_vars->ulsch_noise_power_tot = 0;
for (int aarx = 0; aarx < gNB->frame_parms.nb_antennas_rx; aarx++) {
pusch_vars->ulsch_power_tot += pusch_vars->ulsch_power[aarx];
pusch_vars->ulsch_noise_power_tot += pusch_vars->ulsch_noise_power[aarx];
}
nr_rx_pusch_group_tp(gNB, pusch_vars_group, ulsch_pdu_group, ret_unav_res_group, group_size, frame, slot, beam_nb);
stop_meas(&gNB->rx_pusch_stats);
}
static bool handle_pusch_DTX(PHY_VARS_gNB *gNB,
NR_gNB_PUSCH *pusch_vars,
NR_gNB_ULSCH_t *ulsch,
NR_UL_IND_t *UL_INFO,
int *pusch_DTX)
{
NR_UL_gNB_HARQ_t *ulsch_harq = ulsch->harq_process;
AssertFatal(ulsch_harq != NULL, "harq_pid %d is not allocated\n", ulsch->harq_pid);
const nfapi_nr_pusch_pdu_t *pdu = &ulsch_harq->ulsch_pdu;
if (dB_fixed_x10(pusch_vars->ulsch_power_tot) < dB_fixed_x10(pusch_vars->ulsch_noise_power_tot) + gNB->pusch_thres) {
NR_gNB_PHY_STATS_t *stats = get_phy_stats(gNB, ulsch->rnti);
@@ -1020,7 +1036,6 @@ static bool handle_pusch_decode_trigger(PHY_VARS_gNB *gNB, NR_gNB_PUSCH *pusch_v
pusch_vars->DTX = 0;
}
stop_meas(&gNB->rx_pusch_stats);
return true;
}
@@ -1191,35 +1206,62 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N
UL_INFO->rx_ind.pdu_list = UL_INFO->rx_pdu_list;
int ulsch_idx_to_decode[MAX_UL_PDUS_PER_SLOT];
int num_pusch = 0;
// Group the jobs using group index
int group_jobs[MAX_UL_PDUS_PER_SLOT][MAX_UL_PDUS_PER_SLOT];
uint8_t group_size[MAX_UL_PDUS_PER_SLOT] = {0};
int16_t active_groups[MAX_UL_PDUS_PER_SLOT];
int n_active_groups = 0;
for (int i = 0; i < n_pusch_jobs; ++i) {
int ULSCH_id = handle_pusch_job_trigger(gNB, &pusch[i]);
if (ULSCH_id < 0)
continue;
NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ULSCH_id];
NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id];
if (handle_pusch_decode_trigger(gNB, pusch_vars, ulsch, UL_INFO, &pusch_DTX))
ulsch_idx_to_decode[num_pusch++] = ULSCH_id;
int g = pusch[i].mu_group_idx;
AssertFatal(g >= 0, "Ungrouped ULSCH_id %d\n", ULSCH_id);
if (group_size[g] == 0)
active_groups[n_active_groups++] = g;
group_jobs[g][group_size[g]++] = ULSCH_id;
}
/* Do ULSCH decoding time measurement only when number of PUSCH is limited to 1
* (valid for unitary physical simulators). ULSCH processing lopp is then executed
* only once, which ensures exactly one start and stop of the ULSCH decoding time
* measurement per processed TB.*/
if (gNB->max_nb_pusch == 1)
start_meas(&gNB->ulsch_decoding_stats);
for (int i = 0; i < n_active_groups; i++) {
int g = active_groups[i];
int gsz = group_size[g];
NR_gNB_PUSCH *pusch_vars_group[gsz];
const nfapi_nr_pusch_pdu_t *ulsch_pdu_group[gsz];
uint32_t *unav_res_group[gsz];
int beam_nb = -1;
for (int u = 0; u < gsz; u++) {
int ulsch_id = group_jobs[g][u];
LOG_I(NR_PHY, "Group %d, Group size %d, ulsch_id %d\n", g, gsz, ulsch_id);
pusch_vars_group[u] = &gNB->pusch_vars[ulsch_id];
ulsch_pdu_group[u] = &gNB->ulsch[ulsch_id].harq_process->ulsch_pdu;
unav_res_group[u] = &gNB->ulsch[ulsch_id].unav_res;
int this_beam = gNB->ulsch[ulsch_id].beam_nb;
if (beam_nb < 0) {
beam_nb = this_beam;
} else {
AssertFatal(beam_nb == this_beam, "MU-MIMO group %d has inconsistent beam_nb : %d and %d\n", g, beam_nb, this_beam);
}
}
handle_pusch_rx_group_trigger(gNB, pusch_vars_group, ulsch_pdu_group, unav_res_group, gsz, frame_rx, slot_rx, beam_nb);
for (int u = 0; u < gsz; u++) {
int ULSCH_id = group_jobs[g][u];
NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ULSCH_id];
NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id];
if (handle_pusch_DTX(gNB, pusch_vars, ulsch, UL_INFO, &pusch_DTX))
ulsch_idx_to_decode[num_pusch++] = ULSCH_id;
}
}
start_meas(&gNB->ulsch_decoding_stats);
if (num_pusch > 0) {
int ret_nr_ulsch_procedures = nr_ulsch_procedures(gNB, frame_rx, slot_rx, ulsch_idx_to_decode, num_pusch, UL_INFO);
if (ret_nr_ulsch_procedures != 0)
LOG_E(PHY,"Error in nr_ulsch_procedures, returned %d\n",ret_nr_ulsch_procedures);
}
/* Do ULSCH decoding time measurement only when number of PUSCH is limited to 1
* (valid for unitary physical simulators). ULSCH processing loop is then executed
* only once, which ensures exactly one start and stop of the ULSCH decoding time
* measurement per processed TB.*/
if (gNB->max_nb_pusch == 1)
stop_meas(&gNB->ulsch_decoding_stats);
stop_meas(&gNB->ulsch_decoding_stats);
UL_INFO->srs_ind.sfn = frame_rx;
UL_INFO->srs_ind.slot = slot_rx;
@@ -1252,6 +1294,19 @@ void nr_save_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti_req
int frame = UL_tti_req->SFN;
int slot = UL_tti_req->Slot;
int16_t pdu_group_idx[MAX_UL_PDUS_PER_SLOT] = {[0 ... MAX_UL_PDUS_PER_SLOT - 1] = -1};
uint8_t pdu_group_size[MAX_UL_PDUS_PER_SLOT] = {0};
for (int g = 0; g < UL_tti_req->n_group; g++) {
const nfapi_nr_ul_tti_request_number_of_groups_t *grp = &UL_tti_req->groups_list[g];
for (int u = 0; u < grp->n_ue; u++) {
int pdu_idx = grp->ue_list[u].pdu_idx;
if (pdu_idx >= 0 && pdu_idx < UL_tti_req->n_pdus) {
pdu_group_idx[pdu_idx] = g;
pdu_group_size[pdu_idx] = grp->n_ue;
}
}
}
for (int i = 0; i < UL_tti_req->n_pdus; i++) {
int type = UL_tti_req->pdus_list[i].pdu_type;
LOG_D(NR_PHY,
@@ -1263,7 +1318,12 @@ void nr_save_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti_req
UL_tti_req->Slot);
switch (type) {
case NFAPI_NR_UL_CONFIG_PUSCH_PDU_TYPE:
nr_fill_ulsch(gNB, UL_tti_req->SFN, UL_tti_req->Slot, &UL_tti_req->pdus_list[i].pusch_pdu);
nr_fill_ulsch(gNB,
UL_tti_req->SFN,
UL_tti_req->Slot,
&UL_tti_req->pdus_list[i].pusch_pdu,
pdu_group_idx[i],
pdu_group_size[i]);
break;
case NFAPI_NR_UL_CONFIG_PUCCH_PDU_TYPE:
nr_fill_pucch(gNB, UL_tti_req->SFN, UL_tti_req->Slot, &UL_tti_req->pdus_list[i].pucch_pdu);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -373,3 +373,45 @@ void multipath_channel_float(channel_desc_t *desc,
}
#endif
// \f$\mathbf{y} = y + \mathbf{x}\f$
#ifdef CHANNEL_SSE
void __attribute__((no_sanitize_address)) add_rx_signals_simde(float **y_re,
float **y_im,
float **x_re,
float **x_im,
int n_rx,
int length)
{
for (int aarx = 0; aarx < n_rx; aarx++) {
int i = 0;
for (; i <= length - 4; i += 4) {
// Add real values
simde__m128 vec_re_x = simde_mm_loadu_ps(&x_re[aarx][i]);
simde__m128 vec_re_y = simde_mm_loadu_ps(&y_re[aarx][i]);
simde_mm_storeu_ps(&y_re[aarx][i], simde_mm_add_ps(vec_re_y, vec_re_x));
// Add imaginary values
simde__m128 vec_im_x = simde_mm_loadu_ps(&x_im[aarx][i]);
simde__m128 vec_im_y = simde_mm_loadu_ps(&y_im[aarx][i]);
simde_mm_storeu_ps(&y_im[aarx][i], simde_mm_add_ps(vec_im_y, vec_im_x));
}
// Add remaining samples
for (; i < length; i++) {
y_re[aarx][i] += x_re[aarx][i];
y_im[aarx][i] += x_im[aarx][i];
}
}
}
#else
void add_rx_signals_simde(float **y_re, float **y_im, float **x_re, float **x_im, int n_rx, int length)
{
for (int aarx = 0; aarx < n_rx; aarx++) {
for (int i = 0; i < length; i++) {
y_re[aarx][i] += x_re[aarx][i];
y_im[aarx][i] += x_im[aarx][i];
}
}
}
#endif

View File

@@ -428,6 +428,8 @@ void add_noise_float(c16_t **rxdata,
bool apply_phase_noise,
const uint8_t nb_antennas_rx);
void add_rx_signals_simde(float **y_re, float **y_im, float **x_re, float **x_im, int n_rx, int length);
/*
\fn double compute_pbch_sinr(channel_desc_t *desc,
channel_desc_t *desc_i1,

View File

@@ -115,6 +115,48 @@ static void copy_ul_tti_req(nfapi_nr_ul_tti_request_t *to, nfapi_nr_ul_tti_reque
to->groups_list[i] = from->groups_list[i];
}
static void nr_schedule_pusch_fapi_groups(nfapi_nr_ul_tti_request_t *UL_tti_req)
{
UL_tti_req->n_group = 0;
bool pdu_grouped[MAX_UL_PDUS_PER_SLOT] = {false};
for (int i = 0; i < UL_tti_req->n_pdus; i++) {
// We only group PUSCH PDUs
if (UL_tti_req->pdus_list[i].pdu_type != NFAPI_NR_UL_CONFIG_PUSCH_PDU_TYPE)
continue;
if (pdu_grouped[i])
continue;
nfapi_nr_ul_tti_request_number_of_groups_t *group = &UL_tti_req->groups_list[UL_tti_req->n_group];
group->n_ue = 0;
group->ue_list[group->n_ue++].pdu_idx = i;
pdu_grouped[i] = true;
nfapi_nr_pusch_pdu_t *pdu_i = &UL_tti_req->pdus_list[i].pusch_pdu;
// Group the UEs sharing the exact same resource blocks
for (int j = i + 1; j < UL_tti_req->n_pdus; j++) {
if (UL_tti_req->pdus_list[j].pdu_type != NFAPI_NR_UL_CONFIG_PUSCH_PDU_TYPE)
continue;
if (pdu_grouped[j])
continue;
nfapi_nr_pusch_pdu_t *pdu_j = &UL_tti_req->pdus_list[j].pusch_pdu;
// Check for resource overlap
bool same_alloc = (pdu_i->rb_start == pdu_j->rb_start) && (pdu_i->rb_size == pdu_j->rb_size)
&& (pdu_i->start_symbol_index == pdu_j->start_symbol_index)
&& (pdu_i->nr_of_symbols == pdu_j->nr_of_symbols) && (pdu_i->qam_mod_order == pdu_j->qam_mod_order);
// Check orthogonal DMRS ports
bool orthogonal_dmrs = ((pdu_i->dmrs_ports & pdu_j->dmrs_ports) == 0);
if (same_alloc && orthogonal_dmrs) {
group->ue_list[group->n_ue++].pdu_idx = j;
pdu_grouped[j] = true;
}
}
UL_tti_req->n_group++;
}
}
void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, slot_t slot, NR_Sched_Rsp_t *sched_info)
{
protocol_ctxt_t ctxt = {0};
@@ -232,6 +274,8 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, slot_t slo
const int current_index = ul_buffer_index(frame, slot, slots_frame, gNB->UL_tti_req_ahead_size);
copy_ul_tti_req(&sched_info->UL_tti_req, &gNB->UL_tti_req_ahead[0][current_index]);
nr_schedule_pusch_fapi_groups(&sched_info->UL_tti_req);
stop_meas(&gNB->gNB_scheduler);
NR_SCHED_UNLOCK(&gNB->sched_lock);
}

View File

@@ -1263,7 +1263,17 @@ void config_uldci(const NR_UE_ServingCell_Info_t *sc_info,
// antenna_ports.val = 0 for transform precoder is disabled, dmrs-Type=1, maxLength=1, Rank=1/2/3/4
// Antenna Ports
dci_pdu_rel15->antenna_ports.val = 0;
// dci_pdu_rel15->antenna_ports.val = 0;
// MU-MIMO HACK
if (pusch_pdu->rb_size == 90) {
if (pusch_pdu->dmrs_ports == 1) {
dci_pdu_rel15->antenna_ports.val = 0;
} else {
dci_pdu_rel15->antenna_ports.val = 1;
}
} else {
dci_pdu_rel15->antenna_ports.val = 0;
}
// DMRS sequence initialization
dci_pdu_rel15->dmrs_sequence_initialization.val = pusch_pdu->scid;

View File

@@ -1885,6 +1885,8 @@ static int pf_ul(gNB_MAC_INST *nrmac,
const int CC_id = 0;
int frame = pp_pusch->frame;
int slot = pp_pusch->slot;
bool used_pusch_dmrs_ports[4] = {0};
int mu_mimo_users_scheduled = 0;
NR_ServingCellConfigCommon_t *scc = nrmac->common_channels[CC_id].ServingCellConfigCommon;
int slots_per_frame = nrmac->frame_structure.numb_slots_frame;
DevAssert(tda_info->valid_tda);
@@ -2182,6 +2184,58 @@ static int pf_ul(gNB_MAC_INST *nrmac,
>> 3;
}
// MU-MIMO HACK
// ------------
bool is_user_data = false;
for (int lcid = 4; lcid <= 32; lcid++) {
if (iterator->UE->mac_stats.ul.lc_bytes[lcid] > 0) {
is_user_data = true;
break;
}
}
if (is_user_data) {
for (int rb = 16; rb < 16 + 90; rb++) {
if (nrmac->ulprbbl[rb] != 0) {
LOG_D(NR_MAC, "MU-MIMO region blocked by PUCCH/SRS at PRB %d. Falling back to SU-MIMO.\n", rb);
is_user_data = false; // Cancel MU-MIMO for this UE, use default OAI scheduling
break;
}
}
}
if (is_user_data) {
sched.rbStart = 16;
sched.rbSize = 90;
int assigned_port = -1;
for (int p = 0; p < 2; p++) {
if (used_pusch_dmrs_ports[p] == 0) {
assigned_port = p;
used_pusch_dmrs_ports[p] = 1;
break;
}
}
if (assigned_port == -1) {
LOG_W(NR_MAC, "MU-MIMO limit reached in this slot. Deferring UE %04x to next slot.\n", iterator->UE->rnti);
continue; // Skip scheduling this UE until the next slot
}
sched.dmrs_info.dmrs_ports = (assigned_port == 0) ? 1 : 2;
sched.tb_size = nr_compute_tbs(sched.Qm,
sched.R,
sched.rbSize,
sched.tda_info.nrOfSymbols,
sched.dmrs_info.N_PRB_DMRS * sched.dmrs_info.num_dmrs_symb,
0, // nb_rb_oh
0,
sched.nrOfLayers)
>> 3;
}
// --------------
LOG_D(NR_MAC,
"rbSize %d (available_rb %d), TBS %d, est buf %d, sched_ul %d, B %d, CCE %d, num_dmrs_symb %d, N_PRB_DMRS %d\n",
sched.rbSize,
@@ -2202,9 +2256,25 @@ static int pf_ul(gNB_MAC_INST *nrmac,
/* save allocation to FAPI structures */
post_process_ulsch(nrmac, pp_pusch, iterator->UE, &sched);
n_rb_sched[beam.idx] -= sched.rbSize;
for (int rb = bi.bwpStart + sched.rbStart; rb < bi.bwpStart + sched.rbStart + sched.rbSize; rb++)
rballoc_mask[rb] |= slbitmap;
if (is_user_data) {
mu_mimo_users_scheduled++;
// If we have successfully scheduled 2 UEs, the MU-MIMO group is full.
// We MUST lock the PRB mask now so a 3rd UE doesn't overwrite them.
if (mu_mimo_users_scheduled == 2) {
n_rb_sched[beam.idx] -= sched.rbSize;
for (int rb = bi.bwpStart + sched.rbStart; rb < bi.bwpStart + sched.rbStart + sched.rbSize; rb++) {
rballoc_mask[rb] |= slbitmap;
}
}
} else {
// Standard SU-MIMO / Control Traffic: Lock the PRB mask immediately
n_rb_sched[beam.idx] -= sched.rbSize;
for (int rb = bi.bwpStart + sched.rbStart; rb < bi.bwpStart + sched.rbStart + sched.rbSize; rb++) {
rballoc_mask[rb] |= slbitmap;
}
}
// ==========================================================
/* reduce max_num_ue once we are sure UE can be allocated, i.e., has CCE */
remainUEs[beam.idx]--;
@@ -2252,7 +2322,16 @@ nfapi_nr_pusch_pdu_t *prepare_pusch_pdu(nfapi_nr_ul_tti_request_t *future_ul_tti
pusch_pdu->nrOfLayers = sched_pusch->nrOfLayers;
// DMRS
pusch_pdu->num_dmrs_cdm_grps_no_data = sched_pusch->dmrs_info.num_dmrs_cdm_grps_no_data;
pusch_pdu->dmrs_ports = ((1 << sched_pusch->nrOfLayers) - 1);
// MU-MIMO HACK
if (sched_pusch->rbSize == 90) {
pusch_pdu->dmrs_ports = sched_pusch->dmrs_info.dmrs_ports;
} else {
// Normal SU-MIMO behavior for RRC Signaling
pusch_pdu->dmrs_ports = ((1 << sched_pusch->nrOfLayers) - 1);
}
// pusch_pdu->dmrs_ports = ((1 << sched_pusch->nrOfLayers) - 1);
pusch_pdu->ul_dmrs_symb_pos = sched_pusch->dmrs_info.ul_dmrs_symb_pos;
pusch_pdu->dmrs_config_type = sched_pusch->dmrs_info.dmrs_config_type;
pusch_pdu->scid = sched_pusch->dmrs_info.scid; // DMRS sequence initialization [TS38.211, sec 6.4.1.1.1]

View File

@@ -417,6 +417,7 @@ typedef struct NR_pusch_dmrs {
uint8_t num_dmrs_symb;
uint16_t ul_dmrs_symb_pos;
uint8_t num_dmrs_cdm_grps_no_data;
uint16_t dmrs_ports;
nfapi_nr_dmrs_type_e dmrs_config_type;
int dmrs_scrambling_id;
int pusch_identity;

View File

@@ -487,6 +487,7 @@ void reset_sched_response(NR_Sched_Rsp_t *sched_response, int frame, int slot, i
UL_tti_req->SFN = frame;
UL_tti_req->Slot = slot;
UL_tti_req->n_pdus = 0;
UL_tti_req->n_group = 0;
nfapi_nr_tx_data_request_t *TX_req = &sched_response->TX_req;
TX_req->SFN = frame;

View File

@@ -222,7 +222,7 @@ if(PACKAGING_PHYSIM)
--------------------------------------------------------
Coding focused: polartest, smallblocktest, ldpctest
Downlink PHY: nr_dlschsim, nr_psbchsim, nr_dlsim
Uplink PHY: nr_pucchsim nr_prachsim nr_ulschsim nr_ulsim
Uplink PHY: nr_pucchsim nr_prachsim nr_ulschsim nr_ulsim nr_ulsim_mu_mimo
--------------------------------------------------------
polartest: test polar codes which are used in 5G control channels, validates encoding, decoding and error correction performance*
smallblocktest: generic test for small channel coding blocks, validates encoding and decoding for small transport blocks
@@ -233,7 +233,8 @@ if(PACKAGING_PHYSIM)
nr_dlsim: simulates NR downlink, including multiple downlink channels and measurements of BLER/throughput
nr_prachsim: simulates the Physical Random Access Channel, including UE initial access, preamble detection and timing alignment
nr_ulschsim: simulates the Uplink Shared Channel, including LDPC encoding, modulation, transmission and decoding
nr_ulsim: simulates the NR uplink simulation, including PUCCH, PUSCH and PRACH")
nr_ulsim: simulates the NR uplink simulation, including PUCCH, PUSCH and PRACH
nr_ulsim_mu_mimo: simulates the NR uplink simulation with PUSCH multi user MIMO")
if(PACKAGING_RPM)
set(CPACK_RPM_PACKAGE_OAI-PHYSIM_SUMMARY "OpenAirInterface PhySim package")
@@ -254,7 +255,7 @@ if(PACKAGING_PHYSIM)
install(FILES "${CMAKE_SOURCE_DIR}/doc/physical-simulators.md" DESTINATION share/doc/ COMPONENT oai-physim)
#Add the targets to the package
install(TARGETS polartest smallblocktest ldpctest nr_dlschsim nr_psbchsim nr_pucchsim nr_dlsim nr_prachsim nr_ulschsim nr_ulsim
install(TARGETS polartest smallblocktest ldpctest nr_dlschsim nr_psbchsim nr_pucchsim nr_dlsim nr_prachsim nr_ulschsim nr_ulsim nr_ulsim_mu_mimo
COMPONENT oai-physim
RUNTIME DESTINATION /usr/bin
LIBRARY DESTINATION /usr/lib