diff --git a/ci-scripts/conf_files/gnb.sa.band261.u3.32prb.rfsim.conf b/ci-scripts/conf_files/gnb.sa.band261.u3.32prb.rfsim.conf index 7cc7a9e3f9..d5b1695d5c 100644 --- a/ci-scripts/conf_files/gnb.sa.band261.u3.32prb.rfsim.conf +++ b/ci-scripts/conf_files/gnb.sa.band261.u3.32prb.rfsim.conf @@ -21,7 +21,6 @@ gNBs = sib1_tda = 15; min_rxtxtime = 6; - beam_weights = [0]; // single SSB -> one analog beam servingCellConfigCommon = ( { @@ -179,6 +178,7 @@ MACRLCs = ({ set_analog_beamforming = 1; beam_duration = 1; beams_per_period = 1; + beam_weights = [0]; // single SSB -> one analog beam }); L1s = ( diff --git a/doc/analog_beamforming.md b/doc/analog_beamforming.md index 6d9e5bf71c..4ff208343e 100644 --- a/doc/analog_beamforming.md +++ b/doc/analog_beamforming.md @@ -10,6 +10,8 @@ There are three main beamforming techinques: analog, digital and hybrid. The nam The presence of a limited number of predefined beams at RU poses constraints to the scheduler at gNB. As a matter of fact, the scheduler can serve only a limited number of beams, depending on the RU characteristics (possibly only 1), in a given time scale, that also depends on the RU characteristics (e.g. 1 slot or 1 symbol). This limitation doesn't exist for digital beamforming. +Analog beamforming implementation also allows to enable distributed antenna systems (DAS), where each beam corrisponds to one antenna (or a set of antennas) of the system. In this scenario, the scheduler constaint is alleviated because normally the number of concurrent beams allowed equals the total number of beams. + # Configuration file fields for analog beamforming A set of parameters in configuration files controls the implementation of analog beamforming and instructs the scheduler on how to behave in such scenarios. Since most notably this technique in 5G is employed in FR2, the configuration file example currently available is a RFsim one for band 261. [Config file example](../ci-scripts/conf_files/gnb.sa.band261.u3.32prb.rfsim.conf) @@ -18,8 +20,9 @@ In the `MACRLC` section of configuration files, there are three new parameters: - `set_analog_beamforming` can be set to 1 or 0 to activate or desactivate analog beamforming (default value is 0) - `beam_duration` is the number of slots (currently minimum duration of a beam) the scheduler is tied to a beam (default value is 1) - `beams_per_period` is the number of concurrent beams the RU can handle in the beam duration (default value is 1) +- `beam_weights` is a vector field containing the set of beam indices to be provided by the OAI L1 to the RU is also required. In current implementation, the number of beam indices should be equal to the number of SSBs transmitted -In the `gNBs` section of the configuration file, under the phyisical parameters, a vector field containing the set of beam indices to be provided by the OAI L1 to the RU is also required. This field is called `beam_weights`. In current implementation, the number of beam indices should be equal to the number of SSBs transmitted. +DAS is enabled by setting to 1 the parameter `enable_das` in the L1 section of the configuration file. In case of DAS enabled, the field `beam_weights` in `MACRLC` section can be omitted. # Implementation in OAI scheduler @@ -28,3 +31,28 @@ This matrix contains the beams already allocated in a given slot, to flag the sc To this goal, we extended the virtual resource block (VRB) map by one dimension to also contain information per allocated beam. As said, the scheduler can independently schedule users in a number of beams up to `beams_per_period` concurrently. It is important to note that in current implementation, there are several periodical channels, e.g. PRACH or PUCCH for CSI et cetera, that have the precendence in being assigned a beam, that is because the scheduling is automatic, set in RRC configuration, and not up to the scheduler. For these instances, we assume the beam is available (if not there are assertions to stop the process). For data channels, the currently implemented PF scheduler is used. The only modification is that a UE can be served only if there is a free beam available or the one of the beams already in use correspond to that UE beam. + +# FAPI implementation + +To be noted that in our implementation analog beamforming is only supported in non-split/monolithic mode because we don't support yet SCF P19 interface that would be needed to manage these procedure in a split scenario with SCF FAPI. + +In `config_request` structure, a vendor extension (`nfapi_nr_analog_beamforming_ve_t`) configures the lower layers at initialization with the following information: +- `analog_bf_vendor_ext` which can assume values 1 or 0 for enabling or disabling analog beamforming +- `num_beams_period_vendor_ext` which corresponds to the configuration parameter `beams_per_period` +- `total_num_beams_vendor_ext` which corresponds to the number of beams configured in `beam_weights` +- `analog_beam_list` which contains the RU beamforming indices configured in `beam_weights` + +Additionally, L2 provides in each channel FAPI message information about the beam index. Small Cell Forum (SCF) FAPI provides in its PHY API specifications for the channels only a field for digital beamforming as part of the `precoding_and_beamforming` stucture. Therefore without a better option, we are currently using that one to store the internal analog beamforming index. This is the index used internally by the code to progressively identify the beam with a value from 0 to `total_num_beams_vendor_ext`. + +# L1 implementation + +To handle multiple concurrent beams, the buffers containing Tx and Rx data in frequency domain (`txdataF` and `rxdataF`) have been extended by one dimension to contain multiple concurrent beams to be transmitted/received. +The function `beam_index_allocation`, called by every L1 channel, is responsible to match the FAPI analog beam index to the RU beam index and to store the latter `beam_id` structure, which allocates the beams per symbol, despite L2 only supporting beam change at slot level. At the same time, the function returns the concurrent beam index, to be used to store data in frequency domain buffers. While doing so, the function also checks if there is room for current beam in the list of concurrent beams, which should always be the case, if L2 properly allocated the channels. + +In case of DAS, since each beam corresponds to a specific antenna port, the `beam_index_allocation` function is simplified in the sense that the beam index corresponds to the antenna port index of the frequency domain buffers. + +# RU implementation + +The implementation is still work in progress. + +The first dimension of the Tx and Rx buffers that used to contain the number of Tx/Rx antennas, it is now extended to contain the number of parallel streams which is the number of antennas multiplied by the number of concurrent beams. diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index 9f20e6f409..acab30751b 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -360,32 +360,29 @@ void term_gNB_Tpool(int inst) { } /// eNB kept in function name for nffapi calls, TO FIX -void init_eNB_afterRU(void) { - int inst,ru_id,i,aa; - PHY_VARS_gNB *gNB; - - for (inst=0; instnum_RU>0,"Number of RU attached to gNB %d is zero\n",gNB->Mod_id); LOG_D(NR_PHY, "Mapping RX ports from %d RUs to gNB %d\n", gNB->num_RU, gNB->Mod_id); - - for (ru_id=0,aa=0; ru_idnum_RU; ru_id++) { + int aa = 0; + for (int ru_id = 0; ru_id < gNB->num_RU; ru_id++) { AssertFatal(gNB->RU_list[ru_id]->common.rxdataF != NULL, "RU %d : common.rxdataF is NULL\n", gNB->RU_list[ru_id]->idx); AssertFatal(gNB->RU_list[ru_id]->prach_rxsigF != NULL, "RU %d : prach_rxsigF is NULL\n", gNB->RU_list[ru_id]->idx); - - for (i=0; iRU_list[ru_id]->nb_rx; aa++,i++) { - LOG_I(PHY,"Attaching RU %d antenna %d to gNB antenna %d\n",gNB->RU_list[ru_id]->idx,i,aa); + for (int i = 0; i < gNB->RU_list[ru_id]->nb_rx; aa++, i++) { + LOG_I(PHY,"Attaching RU %d antenna %d to gNB antenna %d\n", gNB->RU_list[ru_id]->idx, i, aa); gNB->prach_vars.rxsigF[aa] = gNB->RU_list[ru_id]->prach_rxsigF[0][i]; - // TODO hardcoded beam to 0, still need to understand how to handle this properly - gNB->common_vars.rxdataF[0][aa] = (c16_t *)gNB->RU_list[ru_id]->common.rxdataF[i]; + for (int b = 0; b < gNB->RU_list[ru_id]->num_beams_period; b++) { + int idx = i + b * gNB->RU_list[ru_id]->nb_rx; + gNB->common_vars.rxdataF[b][aa] = (c16_t *)gNB->RU_list[ru_id]->common.rxdataF[idx]; + } } } - /* TODO: review this code, there is something wrong. * In monolithic mode, we come here with nb_antennas_rx == 0 * (not tested in other modes). @@ -393,7 +390,6 @@ void init_eNB_afterRU(void) { //init_precoding_weights(RC.gNB[inst]); init_gNB_Tpool(inst); } - } /** diff --git a/executables/nr-ru.c b/executables/nr-ru.c index 6187c9bc2e..411caf9a63 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -240,20 +240,20 @@ int connect_rau(RU_t *ru) { void fh_if5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp) { if (ru == RC.ru[0]) VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, ru->proc.timestamp_tx&0xffffffff ); int offset = ru->nr_frame_parms->get_samples_slot_timestamp(slot,ru->nr_frame_parms,0); - void *buffs[ru->nb_tx]; - for (int aid=0;aidnb_tx;aid++) buffs[aid] = (void*)&ru->common.txdata[aid][offset]; + void *buffs[ru->nb_tx]; + for (int aid = 0; aid < ru->nb_tx; aid++) + buffs[aid] = (void*)&ru->common.txdata[aid][offset]; struct timespec txmeas; clock_gettime(CLOCK_MONOTONIC, &txmeas); LOG_D(NR_PHY,"IF5 TX %d.%d, TS %llu, buffs[0] %p, buffs[1] %p ener0 %f dB, tx start %d\n",frame,slot,(unsigned long long)timestamp,buffs[0],buffs[1], 10*log10((double)signal_energy(buffs[0],ru->nr_frame_parms->get_samples_per_slot(slot,ru->nr_frame_parms))),(int)txmeas.tv_nsec); ru->ifdevice.trx_write_func2(&ru->ifdevice, - timestamp, + timestamp, buffs, - 0, - ru->nr_frame_parms->get_samples_per_slot(slot,ru->nr_frame_parms), - 0, - ru->nb_tx); - + 0, + ru->nr_frame_parms->get_samples_per_slot(slot,ru->nr_frame_parms), + 0, + ru->nb_tx); } // southbound IF4p5 fronthaul @@ -591,33 +591,28 @@ static void rx_rf(RU_t *ru, int *frame, int *slot) RU_proc_t *proc = &ru->proc; NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; openair0_config_t *cfg = &ru->openair0_cfg; - void *rxp[ru->nb_rx]; - unsigned int rxs; - int i; - uint32_t samples_per_slot = fp->get_samples_per_slot(*slot,fp); - uint32_t samples_per_slot_prev ; - openair0_timestamp ts,old_ts; - AssertFatal(*slotslots_per_frame && *slot>=0, "slot %d is illegal (%d)\n",*slot,fp->slots_per_frame); + uint32_t samples_per_slot = fp->get_samples_per_slot(*slot, fp); + AssertFatal(*slot < fp->slots_per_frame && *slot >= 0, "slot %d is illegal (%d)\n", *slot, fp->slots_per_frame); start_meas(&ru->rx_fhaul); - for (i=0; inb_rx; i++) - rxp[i] = (void *)&ru->common.rxdata[i][fp->get_samples_slot_timestamp(*slot,fp,0)]; + int nb = ru->nb_rx * ru->num_beams_period; + void *rxp[nb]; + for (int i = 0; i < nb; i++) + rxp[i] = (void *)&ru->common.rxdata[i][fp->get_samples_slot_timestamp(*slot, fp, 0)]; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 1 ); - old_ts = proc->timestamp_rx; - LOG_D(PHY,"Reading %d samples for slot %d (%p)\n",samples_per_slot,*slot,rxp[0]); + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 1); + openair0_timestamp old_ts = proc->timestamp_rx; + LOG_D(PHY,"Reading %d samples for slot %d (%p)\n", samples_per_slot, *slot, rxp[0]); + openair0_timestamp ts; + unsigned int rxs; if(emulate_rf) { wait_on_condition(&proc->mutex_emulateRF,&proc->cond_emulateRF,&proc->instance_cnt_emulateRF,"emulatedRF_thread"); release_thread(&proc->mutex_emulateRF,&proc->instance_cnt_emulateRF,"emulatedRF_thread"); rxs = samples_per_slot; ts = old_ts + rxs; } else { - rxs = ru->rfdevice.trx_read_func(&ru->rfdevice, - &ts, - rxp, - samples_per_slot, - ru->nb_rx); + rxs = ru->rfdevice.trx_read_func(&ru->rfdevice, &ts, rxp, samples_per_slot, nb); } VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 0 ); @@ -629,7 +624,7 @@ static void rx_rf(RU_t *ru, int *frame, int *slot) LOG_E(PHY, "rx_rf: Asked for %d samples, got %d from USRP\n", samples_per_slot, rxs); if (proc->first_rx != 1) { - samples_per_slot_prev = fp->get_samples_per_slot((*slot-1)%fp->slots_per_frame,fp); + uint32_t samples_per_slot_prev = fp->get_samples_per_slot((*slot - 1) % fp->slots_per_frame, fp); if (proc->timestamp_rx - old_ts != samples_per_slot_prev) { LOG_D(PHY, @@ -752,8 +747,6 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp) RU_proc_t *proc = &ru->proc; NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; nfapi_nr_config_request_scf_t *cfg = &ru->config; - void *txp[ru->nb_tx]; - int i; T(T_ENB_PHY_OUTPUT_SIGNAL, T_INT(0), T_INT(0), @@ -820,7 +813,9 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp) VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_RU, frame); VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_TX0_RU, slot); - for (i = 0; i < ru->nb_tx; i++) + int nt = ru->nb_tx * ru->num_beams_period; + void *txp[nt]; + for (int i = 0; i < nt; i++) txp[i] = (void *)&ru->common.txdata[i][fp->get_samples_slot_timestamp(slot, fp, 0)] - sf_extension * sizeof(int32_t); VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, (timestamp + ru->ts_offset) & 0xffffffff); @@ -830,12 +825,12 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp) timestamp + ru->ts_offset - sf_extension, txp, siglen + sf_extension, - ru->nb_tx, flags); + nt, + flags); LOG_D(PHY, - "[TXPATH] RU %d aa %d tx_rf, writing to TS %llu, %d.%d, unwrapped_frame %d, slot %d, flags %d, siglen+sf_extension %d, " + "[TXPATH] RU %d tx_rf, writing to TS %llu, %d.%d, unwrapped_frame %d, slot %d, flags %d, siglen+sf_extension %d, " "returned %d, E %f\n", ru->idx, - i, (long long unsigned int)(timestamp + ru->ts_offset - sf_extension), frame, slot, @@ -851,7 +846,6 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp) static void fill_rf_config(RU_t *ru, char *rf_config_file) { - int i; NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; nfapi_nr_config_request_scf_t *config = &ru->config; //tmp index openair0_config_t *cfg = &ru->openair0_cfg; @@ -877,13 +871,14 @@ static void fill_rf_config(RU_t *ru, char *rf_config_file) AssertFatal(ru->nb_rx > 0 && ru->nb_rx <= 8, "openair0 does not support more than 8 antennas\n"); cfg->Mod_id = 0; - cfg->num_rb_dl=N_RB; - cfg->tx_num_channels=ru->nb_tx; - cfg->rx_num_channels=ru->nb_rx; + cfg->num_rb_dl = N_RB; + cfg->tx_num_channels = ru->nb_tx * ru->num_beams_period; + cfg->rx_num_channels = ru->nb_rx * ru->num_beams_period; + cfg->num_distributed_ru = ru->num_beams_period; LOG_I(PHY,"Setting RF config for N_RB %d, NB_RX %d, NB_TX %d\n",cfg->num_rb_dl,cfg->rx_num_channels,cfg->tx_num_channels); LOG_I(PHY,"tune_offset %.0f Hz, sample_rate %.0f Hz\n",cfg->tune_offset,cfg->sample_rate); - for (i=0; inb_tx; i++) { + for (int i = 0; i < ru->nb_tx * ru->num_beams_period; i++) { if (ru->if_frequency == 0) { cfg->tx_freq[i] = (double)fp->dl_CarrierFreq; } else if (ru->if_freq_offset) { @@ -898,7 +893,7 @@ static void fill_rf_config(RU_t *ru, char *rf_config_file) i, cfg->tx_gain[i],cfg->tx_freq[i]); } - for (i=0; inb_rx; i++) { + for (int i = 0; i < ru->nb_rx * ru->num_beams_period; i++) { if (ru->if_frequency == 0) { cfg->rx_freq[i] = (double)fp->ul_CarrierFreq; } else if (ru->if_freq_offset) { @@ -944,8 +939,8 @@ static void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_ Each rf chain is is addressed by the card number and the chain on the card. The rf_map specifies for each antenna port, on which rf chain the mapping should start. Multiple antennas are mapped to successive RF chains on the same card. */ -int setup_RU_buffers(RU_t *ru) { - int i,j; +int setup_RU_buffers(RU_t *ru) +{ int card,ant; //uint16_t N_TA_offset = 0; NR_DL_FRAME_PARMS *fp; @@ -967,27 +962,27 @@ int setup_RU_buffers(RU_t *ru) { if (ru->openair0_cfg.mmapped_dma == 1) { // replace RX signal buffers with mmaped HW versions - for (i=0; inb_rx; i++) { - card = i/4; - ant = i%4; + for (int i = 0; i < ru->nb_rx * ru->num_beams_period; i++) { + card = i / 4; + ant = i % 4; LOG_D(PHY, "Mapping RU id %u, rx_ant %d, on card %d, chain %d\n", ru->idx, i, ru->rf_map.card + card, ru->rf_map.chain + ant); free(ru->common.rxdata[i]); ru->common.rxdata[i] = ru->openair0_cfg.rxbase[ru->rf_map.chain + ant]; - for (j = 0; j < 16; j++) { - ru->common.rxdata[i][j] = 16-j; + for (int j = 0; j < 16; j++) { + ru->common.rxdata[i][j] = 16 - j; } } - for (i=0; inb_tx; i++) { - card = i/4; - ant = i%4; + for (int i = 0; i < ru->nb_tx * ru->num_beams_period; i++) { + card = i / 4; + ant = i % 4; LOG_D(PHY, "Mapping RU id %u, tx_ant %d, on card %d, chain %d\n", ru->idx, i, ru->rf_map.card + card, ru->rf_map.chain + ant); free(ru->common.txdata[i]); ru->common.txdata[i] = ru->openair0_cfg.txbase[ru->rf_map.chain + ant]; - for (j = 0; j < 16; j++) { - ru->common.txdata[i][j] = 16-j; + for (int j = 0; j < 16; j++) { + ru->common.txdata[i][j] = 16 - j; } } } else { // not memory-mapped DMA @@ -1054,7 +1049,7 @@ void ru_tx_func(void *param) ru->fh_north_out(ru); } else { if(frame_tx == print_frame) { - for (int i=0; inb_tx; i++) { + for (int i = 0; i < ru->nb_tx * ru->num_beams_period; i++) { if(slot_tx == 0) { sprintf(filename,"gNBdataF_frame%d_sl%d.m", print_frame, slot_tx); LOG_M(filename,"txdataF_frame",&ru->gNB_list[0]->common_vars.txdataF[i][0],fp->samples_per_frame_wCP, 1, 1); @@ -1324,10 +1319,12 @@ void *ru_thread(void *param) for (int prach_oc = 0; prach_oc < p->num_prach_ocas; prach_oc++) { int prachStartSymbol = p->prachStartSymbol + prach_oc * N_dur; + int beam_id = ru->prach_list[prach_id].beam ? ru->prach_list[prach_id].beam[prach_oc] : 0; //comment FK: the standard 38.211 section 5.3.2 has one extra term +14*N_RA_slot. This is because there prachStartSymbol is given wrt to start of the 15kHz slot or 60kHz slot. Here we work slot based, so this function is anyway only called in slots where there is PRACH. Its up to the MAC to schedule another PRACH PDU in the case there are there N_RA_slot \in {0,1}. rx_nr_prach_ru(ru, p->fmt, // could also use format p->numRA, + beam_id, prachStartSymbol, p->slot, prach_oc, diff --git a/nfapi/open-nFAPI/fapi/src/nr_fapi_p5.c b/nfapi/open-nFAPI/fapi/src/nr_fapi_p5.c index 796f424abe..281043b6d3 100644 --- a/nfapi/open-nFAPI/fapi/src/nr_fapi_p5.c +++ b/nfapi/open-nFAPI/fapi/src/nr_fapi_p5.c @@ -1124,6 +1124,33 @@ uint8_t pack_nr_config_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *e // only increase if it was set numTLVs += pNfapiMsg->analog_beamforming_ve.analog_bf_vendor_ext.tl.tag == NFAPI_NR_FAPI_ANALOG_BF_VENDOR_EXTENSION_TAG; + AssertFatal(pNfapiMsg->analog_beamforming_ve.total_num_beams_vendor_ext.tl.tag == 0, + "Total num beams Vendor extension shouldn't be set!"); + // The call to pack the TLV would be the same as any other TLV, it is only packed if the tag is set, + // so, it's safe to add the call to pack_nr_tlv even if it is not always set + retval &= pack_nr_tlv(NFAPI_NR_FAPI_TOTAL_NUM_BEAMS_VENDOR_EXTENSION_TAG, + &(pNfapiMsg->analog_beamforming_ve.total_num_beams_vendor_ext), + ppWritePackedMsg, + end, + &pack_uint8_tlv_value); + // only increase if it was set + numTLVs += + pNfapiMsg->analog_beamforming_ve.total_num_beams_vendor_ext.tl.tag == NFAPI_NR_FAPI_TOTAL_NUM_BEAMS_VENDOR_EXTENSION_TAG; + + for (int beam = 0; beam < pNfapiMsg->analog_beamforming_ve.total_num_beams_vendor_ext.value; beam++) { + AssertFatal(pNfapiMsg->analog_beamforming_ve.analog_beam_list[beam].tl.tag == 0, + "Analog beams list Vendor extension shouldn't be set!"); + // The call to pack the TLV would be the same as any other TLV, it is only packed if the tag is set, + // so, it's safe to add the call to pack_nr_tlv even if it is not always set + retval &= pack_nr_tlv(NFAPI_NR_FAPI_ANALOG_BEAM_VENDOR_EXTENSION_TAG, + &(pNfapiMsg->analog_beamforming_ve.analog_beam_list[beam]), + ppWritePackedMsg, + end, + &pack_uint8_tlv_value); + // only increase if it was set + numTLVs += pNfapiMsg->analog_beamforming_ve.analog_bf_vendor_ext.tl.tag == NFAPI_NR_FAPI_TOTAL_NUM_BEAMS_VENDOR_EXTENSION_TAG; + } + pNfapiMsg->num_tlv = numTLVs; retval &= push8(pNfapiMsg->num_tlv, &pNumTLVFields, end); return retval; @@ -1138,6 +1165,7 @@ uint8_t unpack_nr_config_request(uint8_t **ppReadPackedMsg, uint8_t *end, void * int config_beam_idx = 0; int tdd_periodicity_idx = 0; int symbol_per_slot_idx = 0; + int beam_ve_idx = 0; nfapi_nr_config_request_scf_t *pNfapiMsg = (nfapi_nr_config_request_scf_t *)msg; // unpack TLVs @@ -1188,6 +1216,10 @@ uint8_t unpack_nr_config_request(uint8_t **ppReadPackedMsg, uint8_t *end, void * &unpack_uint8_tlv_value}, {NFAPI_NR_CONFIG_TDD_PERIOD_TAG, &(pNfapiMsg->tdd_table.tdd_period), &unpack_uint8_tlv_value}, {NFAPI_NR_CONFIG_SLOT_CONFIG_TAG, NULL, &unpack_uint8_tlv_value}, + {NFAPI_NR_FAPI_NUM_BEAMS_PERIOD_VENDOR_EXTENSION_TAG, &(pNfapiMsg->analog_beamforming_ve.num_beams_period_vendor_ext), &unpack_uint8_tlv_value}, + {NFAPI_NR_FAPI_ANALOG_BF_VENDOR_EXTENSION_TAG, &(pNfapiMsg->analog_beamforming_ve.analog_bf_vendor_ext), &unpack_uint8_tlv_value}, + {NFAPI_NR_FAPI_TOTAL_NUM_BEAMS_VENDOR_EXTENSION_TAG, &(pNfapiMsg->analog_beamforming_ve.total_num_beams_vendor_ext), &unpack_uint8_tlv_value}, + {NFAPI_NR_FAPI_ANALOG_BEAM_VENDOR_EXTENSION_TAG, NULL, &unpack_uint8_tlv_value}, {NFAPI_NR_CONFIG_RSSI_MEASUREMENT_TAG, &(pNfapiMsg->measurement_config.rssi_measurement), &unpack_uint8_tlv_value}, {NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV4_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv4), &unpack_ipv4_address_value}, {NFAPI_NR_NFAPI_P7_VNF_ADDRESS_IPV6_TAG, &(pNfapiMsg->nfapi_config.p7_vnf_address_ipv6), &unpack_ipv6_address_value}, @@ -1221,6 +1253,21 @@ uint8_t unpack_nr_config_request(uint8_t **ppReadPackedMsg, uint8_t *end, void * } int result = 0; switch (generic_tl.tag) { + case NFAPI_NR_FAPI_TOTAL_NUM_BEAMS_VENDOR_EXTENSION_TAG: + pNfapiMsg->analog_beamforming_ve.total_num_beams_vendor_ext.tl.tag = generic_tl.tag; + pNfapiMsg->analog_beamforming_ve.total_num_beams_vendor_ext.tl.length = generic_tl.length; + result = (*unpack_fns[idx].unpack_func)(&pNfapiMsg->analog_beamforming_ve.total_num_beams_vendor_ext, ppReadPackedMsg, end); + pNfapiMsg->analog_beamforming_ve.analog_beam_list = (nfapi_uint8_tlv_t *)malloc( + pNfapiMsg->analog_beamforming_ve.total_num_beams_vendor_ext.value * sizeof(nfapi_uint8_tlv_t)); + beam_ve_idx = 0; + break; + case NFAPI_NR_FAPI_ANALOG_BEAM_VENDOR_EXTENSION_TAG: + unpack_fns[idx].tlv = &pNfapiMsg->analog_beamforming_ve.analog_beam_list[beam_ve_idx]; + pNfapiMsg->analog_beamforming_ve.analog_beam_list[beam_ve_idx].tl.tag = generic_tl.tag; + pNfapiMsg->analog_beamforming_ve.analog_beam_list[beam_ve_idx].tl.length = generic_tl.length; + result = (*unpack_fns[idx].unpack_func)(&pNfapiMsg->analog_beamforming_ve.analog_beam_list[beam_ve_idx], ppReadPackedMsg, end); + beam_ve_idx ++; + break; case NFAPI_NR_CONFIG_NUM_PRACH_FD_OCCASIONS_TAG: pNfapiMsg->prach_config.num_prach_fd_occasions.tl.tag = generic_tl.tag; pNfapiMsg->prach_config.num_prach_fd_occasions.tl.length = generic_tl.length; diff --git a/nfapi/open-nFAPI/fapi/src/nr_fapi_p5_utils.c b/nfapi/open-nFAPI/fapi/src/nr_fapi_p5_utils.c index 4267992d07..1b5e3f0ad8 100644 --- a/nfapi/open-nFAPI/fapi/src/nr_fapi_p5_utils.c +++ b/nfapi/open-nFAPI/fapi/src/nr_fapi_p5_utils.c @@ -276,6 +276,17 @@ bool eq_config_request(const nfapi_nr_config_request_scf_t *unpacked_req, const EQ_TLV(unpacked_req->nfapi_config.timing_info_mode, req->nfapi_config.timing_info_mode); EQ_TLV(unpacked_req->nfapi_config.timing_info_period, req->nfapi_config.timing_info_period); + + EQ_TLV(unpacked_req->analog_beamforming_ve.num_beams_period_vendor_ext, req->analog_beamforming_ve.num_beams_period_vendor_ext); + + EQ_TLV(unpacked_req->analog_beamforming_ve.analog_bf_vendor_ext, req->analog_beamforming_ve.analog_bf_vendor_ext); + + EQ_TLV(unpacked_req->analog_beamforming_ve.total_num_beams_vendor_ext, req->analog_beamforming_ve.total_num_beams_vendor_ext); + + for (int i = 0; i < unpacked_req->analog_beamforming_ve.total_num_beams_vendor_ext.value; ++i) { + EQ_TLV(unpacked_req->analog_beamforming_ve.analog_beam_list[i], req->analog_beamforming_ve.analog_beam_list[i]); + } + return true; } @@ -422,6 +433,10 @@ void free_config_request(nfapi_nr_config_request_scf_t *msg) if (msg->pmi_list.pmi_pdu) { free(msg->pmi_list.pmi_pdu); } + + if (msg->analog_beamforming_ve.analog_beam_list) { + free(msg->analog_beamforming_ve.analog_beam_list); + } } void free_config_response(nfapi_nr_config_response_scf_t *msg) @@ -832,6 +847,21 @@ void copy_config_request(const nfapi_nr_config_request_scf_t *src, nfapi_nr_conf COPY_TLV(dst->nfapi_config.ul_dci_timing_offset, src->nfapi_config.ul_dci_timing_offset); COPY_TLV(dst->nfapi_config.tx_data_timing_offset, src->nfapi_config.tx_data_timing_offset); + + COPY_TLV(dst->analog_beamforming_ve.num_beams_period_vendor_ext, src->analog_beamforming_ve.num_beams_period_vendor_ext); + + COPY_TLV(dst->analog_beamforming_ve.analog_bf_vendor_ext, src->analog_beamforming_ve.analog_bf_vendor_ext); + + COPY_TLV(dst->analog_beamforming_ve.total_num_beams_vendor_ext, src->analog_beamforming_ve.total_num_beams_vendor_ext); + + if (dst->analog_beamforming_ve.total_num_beams_vendor_ext.value > 0) { + dst->analog_beamforming_ve.analog_beam_list = (nfapi_uint8_tlv_t *)malloc( + dst->analog_beamforming_ve.total_num_beams_vendor_ext.value * sizeof(nfapi_uint8_tlv_t)); + for (int i = 0; i < src->analog_beamforming_ve.total_num_beams_vendor_ext.value; ++i) { + COPY_TLV(dst->analog_beamforming_ve.analog_beam_list[i], src->analog_beamforming_ve.analog_beam_list[i]); + } + } + } void copy_config_response(const nfapi_nr_config_response_scf_t *src, nfapi_nr_config_response_scf_t *dst) diff --git a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface.h b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface.h index 39e9eca3e0..b7e6577fa2 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface.h @@ -31,6 +31,8 @@ #define NFAPI_NR_NFAPI_TIMING_INFO_PERIOD_TAG 0x0120 #define NFAPI_NR_FAPI_NUM_BEAMS_PERIOD_VENDOR_EXTENSION_TAG 0xA000 #define NFAPI_NR_FAPI_ANALOG_BF_VENDOR_EXTENSION_TAG 0xA001 +#define NFAPI_NR_FAPI_TOTAL_NUM_BEAMS_VENDOR_EXTENSION_TAG 0xA002 +#define NFAPI_NR_FAPI_ANALOG_BEAM_VENDOR_EXTENSION_TAG 0xA003 typedef struct { uint16_t phy_id; diff --git a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h index 2c7960d045..0533a76c6d 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h @@ -24,7 +24,7 @@ #define NFAPI_MAX_NUM_GROUPS 8 #define NFAPI_MAX_NUM_CB 8 #define NFAPI_MAX_NUM_PRGS 1 -#define NFAPI_MAX_NUM_BG_IF 1 +#define NFAPI_MAX_NUM_BG_IF 6 #define NFAPI_MAX_NUM_PERIODS 8 // Extension to the generic structures for single tlv values @@ -496,6 +496,8 @@ typedef struct { typedef struct { nfapi_uint8_tlv_t num_beams_period_vendor_ext; nfapi_uint8_tlv_t analog_bf_vendor_ext; + nfapi_uint8_tlv_t total_num_beams_vendor_ext; + nfapi_uint8_tlv_t *analog_beam_list; } nfapi_nr_analog_beamforming_ve_t; // ERROR enums diff --git a/nfapi/tests/p5/nr_fapi_config_request_test.c b/nfapi/tests/p5/nr_fapi_config_request_test.c index 3b6ae99e94..93ff5b5f3a 100644 --- a/nfapi/tests/p5/nr_fapi_config_request_test.c +++ b/nfapi/tests/p5/nr_fapi_config_request_test.c @@ -229,6 +229,33 @@ static void fill_config_request_tlv_tdd_rand(nfapi_nr_config_request_scf_t *nfap FILL_TLV(nfapi_resp->nfapi_config.timing_info_period, NFAPI_NR_NFAPI_TIMING_INFO_PERIOD_TAG, rand8()); nfapi_resp->num_tlv++; +/* + // TODO: Uncomment this block when ready to enable the pack of the following VE TLVs in nr_fapi_p5.c + // NFAPI_NR_FAPI_NUM_BEAMS_PERIOD_VENDOR_EXTENSION_TAG + // NFAPI_NR_FAPI_ANALOG_BF_VENDOR_EXTENSION_TAG + // NFAPI_NR_FAPI_TOTAL_NUM_BEAMS_VENDOR_EXTENSION_TAG + // NFAPI_NR_FAPI_ANALOG_BEAM_VENDOR_EXTENSION_TAG + FILL_TLV(nfapi_resp->analog_beamforming_ve.num_beams_period_vendor_ext, + NFAPI_NR_FAPI_NUM_BEAMS_PERIOD_VENDOR_EXTENSION_TAG, + rand8()); + nfapi_resp->num_tlv++; + + FILL_TLV(nfapi_resp->analog_beamforming_ve.analog_bf_vendor_ext, NFAPI_NR_FAPI_ANALOG_BF_VENDOR_EXTENSION_TAG, rand8()); + nfapi_resp->num_tlv++; + + FILL_TLV(nfapi_resp->analog_beamforming_ve.total_num_beams_vendor_ext, + NFAPI_NR_FAPI_TOTAL_NUM_BEAMS_VENDOR_EXTENSION_TAG, + rand8()); + nfapi_resp->num_tlv++; + + if (nfapi_resp->analog_beamforming_ve.total_num_beams_vendor_ext.value > 0) { + nfapi_resp->analog_beamforming_ve.analog_beam_list = + (nfapi_uint8_tlv_t *)malloc(nfapi_resp->analog_beamforming_ve.total_num_beams_vendor_ext.value * sizeof(nfapi_uint8_tlv_t)); + for (int i = 0; i < nfapi_resp->analog_beamforming_ve.total_num_beams_vendor_ext.value; ++i) { + FILL_TLV(nfapi_resp->analog_beamforming_ve.analog_beam_list[i], NFAPI_NR_FAPI_ANALOG_BEAM_VENDOR_EXTENSION_TAG, rand8()); + nfapi_resp->num_tlv++; + } + }*/ } static void test_pack_unpack(nfapi_nr_config_request_scf_t *req) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index d94d2ba493..ca453b6e65 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -172,8 +172,7 @@ void phy_init_nr_gNB(PHY_VARS_gNB *gNB) for (int i = 0; i < common_vars->num_beams_period; i++) common_vars->rxdataF[i] = (c16_t **)malloc16(Prx * sizeof(c16_t*)); - common_vars->num_beams = cfg->dbt_config.num_dig_beams; - if (common_vars->num_beams > 0) { + if (cfg->analog_beamforming_ve.analog_bf_vendor_ext.value) { common_vars->beam_id = (int **)malloc16(common_vars->num_beams_period * sizeof(int*)); for (int i = 0; i < common_vars->num_beams_period; i++) common_vars->beam_id[i] = (int*)malloc16_clear(fp->symbols_per_slot * fp->slots_per_frame * sizeof(int)); diff --git a/openair1/PHY/INIT/nr_init_ru.c b/openair1/PHY/INIT/nr_init_ru.c index 674c70cdc3..a0766c348f 100644 --- a/openair1/PHY/INIT/nr_init_ru.c +++ b/openair1/PHY/INIT/nr_init_ru.c @@ -29,7 +29,7 @@ void init_prach_ru_list(RU_t *ru); -int nr_phy_init_RU(RU_t *ru) +void nr_phy_init_RU(RU_t *ru) { NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; @@ -47,36 +47,40 @@ int nr_phy_init_RU(RU_t *ru) AssertFatal(ru->nb_log_antennas > 0 && ru->nb_log_antennas < 13, "ru->nb_log_antennas %d ! \n",ru->nb_log_antennas); - ru->common.beam_id = malloc16_clear(MAX_NUM_BEAM_PERIODS * sizeof(int*)); - for(int i = 0; i < MAX_NUM_BEAM_PERIODS; i++) + nfapi_nr_analog_beamforming_ve_t *analog_config = &cfg->analog_beamforming_ve; + ru->num_beams_period = analog_config->analog_bf_vendor_ext.value ? analog_config->num_beams_period_vendor_ext.value : 1; + int nb_tx_streams = ru->nb_tx * ru->num_beams_period; + int nb_rx_streams = ru->nb_rx * ru->num_beams_period; + LOG_I(NR_PHY, "nb_tx_streams %d, nb_rx_streams %d, num_Beams_period %d\n", nb_tx_streams, nb_rx_streams, ru->num_beams_period); + ru->common.beam_id = malloc16_clear(ru->num_beams_period * sizeof(int*)); + for(int i = 0; i < ru->num_beams_period; i++) ru->common.beam_id[i] = malloc16_clear(fp->symbols_per_slot * fp->slots_per_frame * sizeof(int)); if (ru->if_south <= REMOTE_IF5) { // this means REMOTE_IF5 or LOCAL_RF, so allocate memory for time-domain signals // Time-domain signals - ru->common.txdata = (int32_t**)malloc16(ru->nb_tx*sizeof(int32_t*)); - ru->common.rxdata = (int32_t**)malloc16(ru->nb_rx*sizeof(int32_t*) ); + ru->common.txdata = (int32_t**)malloc16(nb_tx_streams * sizeof(int32_t*)); + ru->common.rxdata = (int32_t**)malloc16(nb_rx_streams * sizeof(int32_t*)); - - for (int i = 0; i < ru->nb_tx; i++) { + for (int i = 0; i < nb_tx_streams; i++) { // Allocate 10 subframes of I/Q TX signal data (time) if not - ru->common.txdata[i] = (int32_t*)malloc16_clear((ru->sf_extension + fp->samples_per_frame)*sizeof(int32_t)); + ru->common.txdata[i] = (int32_t*)malloc16_clear((ru->sf_extension + fp->samples_per_frame) * sizeof(int32_t)); LOG_D(PHY, "[INIT] common.txdata[%d] = %p (%lu bytes,sf_extension %d)\n", i, ru->common.txdata[i], (ru->sf_extension + fp->samples_per_frame) * sizeof(int32_t), ru->sf_extension); - ru->common.txdata[i] = &ru->common.txdata[i][ru->sf_extension]; + ru->common.txdata[i] = &ru->common.txdata[i][ru->sf_extension]; LOG_D(PHY, "[INIT] common.txdata[%d] = %p \n", i, ru->common.txdata[i]); } - for (int i = 0; i < ru->nb_rx; i++) { - ru->common.rxdata[i] = (int32_t*)malloc16_clear( fp->samples_per_frame*sizeof(int32_t) ); + for (int i = 0; i < nb_rx_streams; i++) { + ru->common.rxdata[i] = (int32_t*)malloc16_clear(fp->samples_per_frame * sizeof(int32_t)); } } // IF5 or local RF else { - ru->common.txdata = (int32_t**)NULL; - ru->common.rxdata = (int32_t**)NULL; + ru->common.txdata = (int32_t**)NULL; + ru->common.rxdata = (int32_t**)NULL; } if (ru->function != NGFI_RRU_IF5) { // we need to do RX/TX RU processing LOG_D(PHY, "nb_tx %d\n", ru->nb_tx); @@ -85,7 +89,6 @@ int nr_phy_init_RU(RU_t *ru) ru->common.rxdata_7_5kHz[i] = (int32_t*)malloc16_clear( 2*fp->samples_per_subframe*2*sizeof(int32_t) ); LOG_D(PHY, "rxdata_7_5kHz[%d] %p for RU %d\n", i, ru->common.rxdata_7_5kHz[i], ru->idx); } - // allocate precoding input buffers (TX) ru->common.txdataF = (int32_t **)malloc16(ru->nb_tx*sizeof(int32_t*)); @@ -94,15 +97,15 @@ int nr_phy_init_RU(RU_t *ru) ru->common.txdataF[i] = (int32_t*)malloc16_clear(fp->samples_per_frame_wCP * sizeof(int32_t)); // allocate IFFT input buffers (TX) - ru->common.txdataF_BF = (int32_t **)malloc16(ru->nb_tx*sizeof(int32_t*)); - LOG_D(PHY, "[INIT] common.txdata_BF= %p (%lu bytes)\n", ru->common.txdataF_BF, ru->nb_tx * sizeof(int32_t *)); - for (int i = 0; i < ru->nb_tx; i++) { - ru->common.txdataF_BF[i] = (int32_t*)malloc16_clear(fp->samples_per_subframe_wCP*sizeof(int32_t) ); + ru->common.txdataF_BF = (int32_t **)malloc16(nb_tx_streams * sizeof(int32_t*)); + LOG_D(PHY, "[INIT] common.txdata_BF= %p (%lu bytes)\n", ru->common.txdataF_BF, nb_tx_streams * sizeof(int32_t *)); + for (int i = 0; i < nb_tx_streams; i++) { + ru->common.txdataF_BF[i] = (int32_t*)malloc16_clear(fp->samples_per_subframe_wCP * sizeof(int32_t)); LOG_D(PHY, "txdataF_BF[%d] %p for RU %d\n", i, ru->common.txdataF_BF[i], ru->idx); } // allocate FFT output buffers (RX) - ru->common.rxdataF = (int32_t**)malloc16(ru->nb_rx*sizeof(int32_t*) ); - for (int i = 0; i < ru->nb_rx; i++) { + ru->common.rxdataF = (int32_t**)malloc16(nb_rx_streams * sizeof(int32_t*)); + for (int i = 0; i < nb_rx_streams; i++) { // allocate 4 slots of I/Q signal data (frequency) int size = RU_RX_SLOT_DEPTH * fp->symbols_per_slot * fp->ofdm_symbol_size; ru->common.rxdataF[i] = (int32_t*)malloc16_clear(sizeof(**ru->common.rxdataF) * size); @@ -113,40 +116,38 @@ int nr_phy_init_RU(RU_t *ru) // AssertFatal(ru->nb_rx <= sizeof(ru->prach_rxsigF) / sizeof(ru->prach_rxsigF[0]), // "nb_antennas_rx too large"); for (int j = 0; j < NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX; j++) { - ru->prach_rxsigF[j] = (int16_t**)malloc(ru->nb_rx * sizeof(int16_t*)); + ru->prach_rxsigF[j] = (int16_t**)malloc(nb_rx_streams * sizeof(int16_t*)); - for (int i = 0; i < ru->nb_rx; i++) { + for (int i = 0; i < nb_rx_streams; i++) { // largest size for PRACH FFT is 4x98304 (16*24576) - ru->prach_rxsigF[j][i] = (int16_t*)malloc16_clear( 4*98304*2*sizeof(int16_t) ); - LOG_D(PHY,"[INIT] prach_vars->rxsigF[%d] = %p\n",i,ru->prach_rxsigF[j][i]); + ru->prach_rxsigF[j][i] = (int16_t*)malloc16_clear(4 * 98304 * 2 * sizeof(int16_t)); + LOG_D(PHY,"[INIT] prach_vars->rxsigF[%d] = %p\n", i, ru->prach_rxsigF[j][i]); } } - - AssertFatal(ru->num_gNB <= NUMBER_OF_gNB_MAX,"gNB instances %d > %d\n", - ru->num_gNB,NUMBER_OF_gNB_MAX); + + AssertFatal(ru->num_gNB <= NUMBER_OF_gNB_MAX, "gNB instances %d > %d\n", ru->num_gNB,NUMBER_OF_gNB_MAX); LOG_D(PHY, "[INIT] %s() ru->num_gNB:%d \n", __FUNCTION__, ru->num_gNB); - } // !=IF5 init_prach_ru_list(ru); - - return(0); } void nr_phy_free_RU(RU_t *ru) { LOG_D(PHY, "Freeing RU signal buffers (if_south %s) nb_tx %d\n", ru_if_types[ru->if_south], ru->nb_tx); + int nb_tx_streams = ru->nb_tx * ru->num_beams_period; + int nb_rx_streams = ru->nb_rx * ru->num_beams_period; if (ru->if_south <= REMOTE_IF5) { // this means REMOTE_IF5 or LOCAL_RF, so free memory for time-domain signals // Hack: undo what is done at allocation - for (int i = 0; i < ru->nb_tx; i++) { + for (int i = 0; i < nb_tx_streams; i++) { int32_t *p = &ru->common.txdata[i][-ru->sf_extension]; free_and_zero(p); } free_and_zero(ru->common.txdata); - for (int i = 0; i < ru->nb_rx; i++) + for (int i = 0; i < nb_rx_streams; i++) free_and_zero(ru->common.rxdata[i]); free_and_zero(ru->common.rxdata); } // else: IF5 or local RF -> nothing to free() @@ -162,21 +163,21 @@ void nr_phy_free_RU(RU_t *ru) free_and_zero(ru->common.txdataF); // free IFFT input buffers (TX) - for (int i = 0; i < ru->nb_tx; i++) + for (int i = 0; i < nb_tx_streams; i++) free_and_zero(ru->common.txdataF_BF[i]); free_and_zero(ru->common.txdataF_BF); // free FFT output buffers (RX) - for (int i = 0; i < ru->nb_rx; i++) + for (int i = 0; i < nb_rx_streams; i++) free_and_zero(ru->common.rxdataF[i]); free_and_zero(ru->common.rxdataF); for (int j = 0; j < NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX; j++) { - for (int i = 0; i < ru->nb_rx; i++) + for (int i = 0; i < nb_rx_streams; i++) free_and_zero(ru->prach_rxsigF[j][i]); free_and_zero(ru->prach_rxsigF[j]); } - for(int i = 0; i < MAX_NUM_BEAM_PERIODS; ++i) + for(int i = 0; i < ru->num_beams_period; ++i) free_and_zero(ru->common.beam_id[i]); free_and_zero(ru->common.beam_id); } diff --git a/openair1/PHY/NR_TRANSPORT/nr_dci.c b/openair1/PHY/NR_TRANSPORT/nr_dci.c index 6cca7fcfcc..bea9cb63c5 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dci.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dci.c @@ -81,7 +81,9 @@ static void nr_generate_dci(PHY_VARS_gNB *gNB, int dci_idx = 0; // multi-beam number (for concurrent beams) int bitmap = SL_to_bitmap(cset_start_symb, pdcch_pdu_rel15->DurationSymbols); - int beam_nb = beam_index_allocation(dci_pdu->precodingAndBeamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx, + int beam_nb = beam_index_allocation(gNB->enable_analog_das, + dci_pdu->precodingAndBeamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx, + &gNB->gNB_config.analog_beamforming_ve, &gNB->common_vars, slot, frame_parms->symbols_per_slot, diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c index 6e68a459c9..58d11413f4 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c @@ -680,7 +680,9 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC nfapi_nr_tx_precoding_and_beamforming_t *pb = &rel15->precodingAndBeamforming; // beam number in multi-beam scenario (concurrent beams) int bitmap = SL_to_bitmap(rel15->StartSymbolIndex, rel15->NrOfSymbols); - int beam_nb = beam_index_allocation(pb->prgs_list[0].dig_bf_interface_list[0].beam_idx, + int beam_nb = beam_index_allocation(gNB->enable_analog_das, + pb->prgs_list[0].dig_bf_interface_list[0].beam_idx, + &gNB->gNB_config.analog_beamforming_ve, &gNB->common_vars, slot, frame_parms->symbols_per_slot, diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index 858257893f..76d9b1185f 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -42,7 +42,6 @@ void init_prach_list(PHY_VARS_gNB *gNB) for (int i = 0; i < NUMBER_OF_NR_PRACH_MAX; i++){ gNB->prach_vars.list[i].frame = -1; gNB->prach_vars.list[i].slot = -1; - gNB->prach_vars.list[i].beam_nb = -1; gNB->prach_vars.list[i].num_slots = -1; } } @@ -51,8 +50,8 @@ void free_nr_prach_entry(PHY_VARS_gNB *gNB, int prach_id) { gNB->prach_vars.list[prach_id].frame = -1; gNB->prach_vars.list[prach_id].slot = -1; - gNB->prach_vars.list[prach_id].beam_nb = -1; gNB->prach_vars.list[prach_id].num_slots = -1; + free_and_zero(gNB->prach_vars.list[prach_id].beam_nb); } int16_t find_nr_prach(PHY_VARS_gNB *gNB,int frame, int slot, find_type_t type) { @@ -70,7 +69,7 @@ int16_t find_nr_prach(PHY_VARS_gNB *gNB,int frame, int slot, find_type_t type) { return -1; } -void nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) +int nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) { int prach_id = find_nr_prach(gNB, SFN, Slot, SEARCH_EXIST_OR_FREE); AssertFatal(((prach_id >= 0) && (prach_id < NUMBER_OF_NR_PRACH_MAX)), "illegal or no prach_id found!!! prach_id %d\n", prach_id); @@ -79,16 +78,25 @@ void nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *p prach->slot = Slot; const int format = prach_pdu->prach_format; prach->num_slots = (format < 4) ? get_long_prach_dur(format, gNB->frame_parms.numerology_index) : 1; - prach->beam_nb = 0; if (gNB->common_vars.beam_id) { - int fapi_beam_idx = prach_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx; - // TODO no idea how to compute final prach symbol here so for now we go til the end of the slot - int temp_nb_symbols = NR_NUMBER_OF_SYMBOLS_PER_SLOT - prach_pdu->prach_start_symbol; - int bitmap = SL_to_bitmap(prach_pdu->prach_start_symbol, temp_nb_symbols); - prach->beam_nb = beam_index_allocation(fapi_beam_idx, &gNB->common_vars, Slot, NR_NUMBER_OF_SYMBOLS_PER_SLOT, bitmap); + int n_symb = get_nr_prach_duration(prach_pdu->prach_format); + prach->beam_nb = calloc(prach_pdu->beamforming.dig_bf_interface, sizeof(*prach->beam_nb)); + for (int i = 0; i < prach_pdu->beamforming.dig_bf_interface; i++) { + int fapi_beam_idx = prach_pdu->beamforming.prgs_list[0].dig_bf_interface_list[i].beam_idx; + int start_symb = prach_pdu->prach_start_symbol + i * n_symb; + int bitmap = SL_to_bitmap(start_symb, n_symb); + prach->beam_nb[i] = beam_index_allocation(gNB->enable_analog_das, + fapi_beam_idx, + &gNB->gNB_config.analog_beamforming_ve, + &gNB->common_vars, + Slot, + NR_NUMBER_OF_SYMBOLS_PER_SLOT, + bitmap); + } } LOG_D(NR_PHY,"Copying prach pdu %d bytes to index %d\n", (int)sizeof(*prach_pdu), prach_id); memcpy(&prach->pdu, prach_pdu, sizeof(*prach_pdu)); + return prach_id; } void init_prach_ru_list(RU_t *ru) @@ -121,10 +129,10 @@ int16_t find_nr_prach_ru(RU_t *ru,int frame,int slot, find_type_t type) return -1; } -void nr_fill_prach_ru(RU_t *ru, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) +void nr_fill_prach_ru(RU_t *ru, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu, int *beam_id) { int prach_id = find_nr_prach_ru(ru, SFN, Slot, SEARCH_EXIST_OR_FREE); - AssertFatal(((prach_id >= 0) && (prach_id < NUMBER_OF_NR_PRACH_MAX)) || (prach_id < 0), + AssertFatal((prach_id >= 0) && (prach_id < NUMBER_OF_NR_PRACH_MAX), "illegal or no prach_id found!!! prach_id %d\n", prach_id); @@ -135,6 +143,7 @@ void nr_fill_prach_ru(RU_t *ru, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_p ru->prach_list[prach_id].num_slots = (fmt < 4) ? get_long_prach_dur(fmt, ru->nr_frame_parms->numerology_index) : 1; ru->prach_list[prach_id].fmt = fmt; ru->prach_list[prach_id].numRA = prach_pdu->num_ra; + ru->prach_list[prach_id].beam = beam_id; ru->prach_list[prach_id].prachStartSymbol = prach_pdu->prach_start_symbol; ru->prach_list[prach_id].num_prach_ocas = prach_pdu->num_prach_ocas; pthread_mutex_unlock(&ru->prach_list_mutex); @@ -151,6 +160,7 @@ void free_nr_ru_prach_entry(RU_t *ru, int prach_id) void rx_nr_prach_ru(RU_t *ru, int prachFormat, int numRA, + int beam, int prachStartSymbol, int prachStartSlot, int prachOccasion, @@ -159,7 +169,6 @@ void rx_nr_prach_ru(RU_t *ru, { AssertFatal(ru != NULL,"ru is null\n"); - int16_t **rxsigF = NULL; NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; int slot2 = slot; int16_t *prach[ru->nb_rx]; @@ -191,15 +200,16 @@ void rx_nr_prach_ru(RU_t *ru, prachStartSymbol, prachOccasion); - rxsigF = ru->prach_rxsigF[prachOccasion]; + int16_t **rxsigF = ru->prach_rxsigF[prachOccasion]; AssertFatal(ru->if_south == LOCAL_RF || ru->if_south == REMOTE_IF5, "we shouldn't call this if if_south != LOCAL_RF or REMOTE_IF5\n"); - for (int aa=0; aanb_rx; aa++){ + for (int aa = 0; aa < ru->nb_rx; aa++) { if (prach_sequence_length == 0) slot2 = prachStartSlot; - prach[aa] = (int16_t*)&ru->common.rxdata[aa][fp->get_samples_slot_timestamp(slot2, fp, 0) + sample_offset_slot - ru->N_TA_offset]; + int idx = aa + beam * ru->nb_rx; + prach[aa] = (int16_t*)&ru->common.rxdata[idx][fp->get_samples_slot_timestamp(slot2, fp, 0) + sample_offset_slot - ru->N_TA_offset]; } int reps; diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index 437a3c20db..db49237a9e 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -257,14 +257,11 @@ void nr_fill_ulsch(PHY_VARS_gNB *gNB, int slot, nfapi_nr_pusch_pdu_t *ulsch_pdu); -void nr_fill_prach(PHY_VARS_gNB *gNB, - int SFN, - int Slot, - nfapi_nr_prach_pdu_t *prach_pdu); +int nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu); void rx_nr_prach(PHY_VARS_gNB *gNB, nfapi_nr_prach_pdu_t *prach_pdu, - int prachOccasion, + int prachOccasion, int frame, int subframe, uint16_t *max_preamble, @@ -274,16 +271,14 @@ void rx_nr_prach(PHY_VARS_gNB *gNB, void rx_nr_prach_ru(RU_t *ru, int prach_fmt, int numRA, + int beam, int prachStartSymbol, int prachStartSlot, int prachOccasion, int frame, int subframe); -void nr_fill_prach_ru(RU_t *ru, - int SFN, - int Slot, - nfapi_nr_prach_pdu_t *prach_pdu); +void nr_fill_prach_ru(RU_t *ru,int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu, int *beam_nb); int16_t find_nr_prach(PHY_VARS_gNB *gNB,int frame,int slot, find_type_t type); int16_t find_nr_prach_ru(RU_t *ru,int frame,int slot, find_type_t type); @@ -309,7 +304,7 @@ int nr_get_srs_signal(PHY_VARS_gNB *gNB, void init_prach_list(PHY_VARS_gNB *gNB); void init_prach_ru_list(RU_t *ru); void free_nr_ru_prach_entry(RU_t *ru, int prach_id); -uint8_t get_nr_prach_duration(uint8_t prach_format); +int get_nr_prach_duration(uint8_t prach_format); void free_nr_prach_entry(PHY_VARS_gNB *gNB, int prach_id); diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c index 982b236b70..4621e184a6 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c @@ -76,7 +76,13 @@ void nr_fill_ulsch(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_pusch_pdu_t 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); - ulsch->beam_nb = beam_index_allocation(fapi_beam_idx, &gNB->common_vars, slot, NR_NUMBER_OF_SYMBOLS_PER_SLOT, bitmap); + ulsch->beam_nb = beam_index_allocation(gNB->enable_analog_das, + fapi_beam_idx, + &gNB->gNB_config.analog_beamforming_ve, + &gNB->common_vars, + slot, + NR_NUMBER_OF_SYMBOLS_PER_SLOT, + bitmap); } ulsch->frame = frame; ulsch->slot = slot; diff --git a/openair1/PHY/NR_TRANSPORT/pucch_rx.c b/openair1/PHY/NR_TRANSPORT/pucch_rx.c index a66812b39e..a9639ce0d7 100644 --- a/openair1/PHY/NR_TRANSPORT/pucch_rx.c +++ b/openair1/PHY/NR_TRANSPORT/pucch_rx.c @@ -57,10 +57,7 @@ //#define DEBUG_NR_PUCCH_RX 1 -void nr_fill_pucch(PHY_VARS_gNB *gNB, - int frame, - int slot, - nfapi_nr_pucch_pdu_t *pucch_pdu) +void nr_fill_pucch(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_pucch_pdu_t *pucch_pdu) { bool found = false; for (int i = 0; i < gNB->max_nb_pucch; i++) { @@ -73,7 +70,13 @@ void nr_fill_pucch(PHY_VARS_gNB *gNB, if (gNB->common_vars.beam_id) { int fapi_beam_idx = pucch_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx; int bitmap = SL_to_bitmap(pucch_pdu->start_symbol_index, pucch_pdu->nr_of_symbols); - pucch->beam_nb = beam_index_allocation(fapi_beam_idx, &gNB->common_vars, slot, NR_NUMBER_OF_SYMBOLS_PER_SLOT, bitmap); + pucch->beam_nb = beam_index_allocation(gNB->enable_analog_das, + fapi_beam_idx, + &gNB->gNB_config.analog_beamforming_ve, + &gNB->common_vars, + slot, + NR_NUMBER_OF_SYMBOLS_PER_SLOT, + bitmap); } memcpy((void *)&pucch->pucch_pdu, (void *)pucch_pdu, sizeof(nfapi_nr_pucch_pdu_t)); LOG_D(PHY, diff --git a/openair1/PHY/NR_TRANSPORT/srs_rx.c b/openair1/PHY/NR_TRANSPORT/srs_rx.c index 8ece090cc5..c8b6d99c30 100644 --- a/openair1/PHY/NR_TRANSPORT/srs_rx.c +++ b/openair1/PHY/NR_TRANSPORT/srs_rx.c @@ -59,7 +59,13 @@ void nr_fill_srs(PHY_VARS_gNB *gNB, frame_t frame, slot_t slot, nfapi_nr_srs_pdu if (gNB->common_vars.beam_id) { int bitmap = SL_to_bitmap(srs_pdu->time_start_position, 1 << srs_pdu->num_symbols); int fapi_beam_idx = srs_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx; - srs->beam_nb = beam_index_allocation(fapi_beam_idx, &gNB->common_vars, slot, NR_NUMBER_OF_SYMBOLS_PER_SLOT, bitmap); + srs->beam_nb = beam_index_allocation(gNB->enable_analog_das, + fapi_beam_idx, + &gNB->gNB_config.analog_beamforming_ve, + &gNB->common_vars, + slot, + NR_NUMBER_OF_SYMBOLS_PER_SLOT, + bitmap); } memcpy((void *)&srs->srs_pdu, (void *)srs_pdu, sizeof(nfapi_nr_srs_pdu_t)); break; diff --git a/openair1/PHY/defs_RU.h b/openair1/PHY/defs_RU.h index 1417180cb3..a2c39852f5 100644 --- a/openair1/PHY/defs_RU.h +++ b/openair1/PHY/defs_RU.h @@ -172,6 +172,7 @@ typedef struct RU_prec_t_s{ typedef struct { int aid; + int beam; struct RU_t_s *ru; int startSymbol; int endSymbol; @@ -181,6 +182,7 @@ typedef struct { typedef struct { int aid; + int beam; struct RU_t_s *ru; int slot; int startSymbol; @@ -196,6 +198,7 @@ typedef struct { int prachStartSymbol; int num_prach_ocas; int num_slots; + int *beam; } RU_PRACH_list_t; #define NUMBER_OF_NR_RU_PRACH_MAX 8 @@ -488,6 +491,8 @@ typedef struct RU_t_s { int nb_rx; /// number of TX paths on device int nb_tx; + /// number of concurrent analog beams in period + int num_beams_period; /// number of logical antennas at TX beamformer input int nb_log_antennas; /// maximum PDSCH RS EPRE diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 59d92444d5..ad17c243ae 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -138,9 +138,9 @@ typedef struct { int frame; int slot; // identifier for concurrent beams - int beam_nb; // prach duration in slots int num_slots; + int *beam_nb; nfapi_nr_prach_pdu_t pdu; } gNB_PRACH_list_t; @@ -287,7 +287,6 @@ typedef struct { /// - first index: beam index (for concurrent beams) /// - second index: beam_id [0.. symbols_per_frame[ int **beam_id; - int num_beams; int num_beams_period; bool analog_bf; int32_t *debugBuff; @@ -493,6 +492,8 @@ typedef struct PHY_VARS_gNB_s { int num_ulprbbl; uint16_t ulprbbl [MAX_BWP_SIZE]; + bool enable_analog_das; + time_stats_t phy_proc_tx; time_stats_t phy_proc_rx; time_stats_t rx_prach; diff --git a/openair1/SCHED_NR/fapi_nr_l1.c b/openair1/SCHED_NR/fapi_nr_l1.c index 5570b329fd..6b671abcfe 100644 --- a/openair1/SCHED_NR/fapi_nr_l1.c +++ b/openair1/SCHED_NR/fapi_nr_l1.c @@ -157,9 +157,9 @@ void nr_schedule_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti UL_tti_req->SFN, UL_tti_req->Slot); nfapi_nr_prach_pdu_t *prach_pdu = &UL_tti_req->pdus_list[i].prach_pdu; - nr_fill_prach(gNB, UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu); + int id = nr_fill_prach(gNB, UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu); if (gNB->RU_list[0]->if_south == LOCAL_RF || gNB->RU_list[0]->if_south == REMOTE_IF5) - nr_fill_prach_ru(gNB->RU_list[0], UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu); + nr_fill_prach_ru(gNB->RU_list[0], UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu, gNB->prach_vars.list[id].beam_nb); break; case NFAPI_NR_UL_CONFIG_SRS_PDU_TYPE: LOG_D(NR_PHY, diff --git a/openair1/SCHED_NR/nr_prach_procedures.c b/openair1/SCHED_NR/nr_prach_procedures.c index c9f6a9c8cc..c56a37ca1d 100644 --- a/openair1/SCHED_NR/nr_prach_procedures.c +++ b/openair1/SCHED_NR/nr_prach_procedures.c @@ -37,62 +37,14 @@ #include "nfapi_pnf.h" #include "common/utils/LOG/log.h" #include "common/utils/LOG/vcd_signal_dumper.h" - - #include "assertions.h" - #include -uint8_t get_nr_prach_duration(uint8_t prach_format){ - switch(prach_format){ - - case 0: // format 0 - return 0; - - case 1: // format 1 - return 0; - - case 2: // format 2 - return 0; - - case 3: // format 3 - return 0; - - case 4: // format A1 - return 2; - - case 5: // format A2 - return 4; - - case 6: // format A3 - return 6; - - case 7: // format B1 - return 2; - - case 8: // format B4 - return 12; - - case 9: // format C0 - return 2; - - case 10: // format C2 - return 6; - - case 11: // format A1/B1 - return 2; - - case 12: // format A2/B2 - return 4; - - case 13: // format A3/B3 - return 6; - - default : - AssertFatal(1==0,"Invalid Prach format\n"); - break; - - } +int get_nr_prach_duration(uint8_t prach_format) +{ + const int val[14] = {0, 0, 0, 0, 2, 4, 6, 2, 12, 2, 6, 2, 4, 6}; + AssertFatal(prach_format < sizeofArray(val), "Invalid Prach format %d\n", prach_format); + return val[prach_format]; } void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rach_indication_t *rach_ind) @@ -117,14 +69,14 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac nfapi_nr_prach_pdu_t *prach_pdu = &gNB->prach_vars.list[prach_id].pdu; const int prach_start_slot = gNB->prach_vars.list[prach_id].slot; uint8_t prachStartSymbol; - uint8_t N_dur = get_nr_prach_duration(prach_pdu->prach_format); + int N_dur = get_nr_prach_duration(prach_pdu->prach_format); for(int prach_oc = 0; prach_oc < prach_pdu->num_prach_ocas; prach_oc++) { for (ru_aa=0,aa=0;ru_aanb_rx;ru_aa++,aa++) { gNB->prach_vars.rxsigF[aa] = ru->prach_rxsigF[prach_oc][ru_aa]; } - prachStartSymbol = prach_pdu->prach_start_symbol+prach_oc*N_dur; + prachStartSymbol = prach_pdu->prach_start_symbol + prach_oc * N_dur; //comment FK: the standard 38.211 section 5.3.2 has one extra term +14*N_RA_slot. This is because there prachStartSymbol is given wrt to start of the 15kHz slot or 60kHz slot. Here we work slot based, so this function is anyway only called in slots where there is PRACH. Its up to the MAC to schedule another PRACH PDU in the case there are there N_RA_slot \in {0,1}. rx_nr_prach(gNB, diff --git a/openair1/SCHED_NR/nr_ru_procedures.c b/openair1/SCHED_NR/nr_ru_procedures.c index a0b3a69ba8..0546455e36 100644 --- a/openair1/SCHED_NR/nr_ru_procedures.c +++ b/openair1/SCHED_NR/nr_ru_procedures.c @@ -47,7 +47,7 @@ // RU OFDM Modulator gNodeB // OFDM modulation core routine, generates a first_symbol to first_symbol+num_symbols on a particular slot and TX antenna port -void nr_feptx0(RU_t *ru,int tti_tx,int first_symbol, int num_symbols, int aa) +void nr_feptx0(RU_t *ru, int tti_tx, int first_symbol, int num_symbols, int aa) { NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; @@ -175,57 +175,39 @@ void nr_feptx_ofdm(RU_t *ru,int frame_tx,int tti_tx) { } -void nr_feptx_prec(RU_t *ru, int frame_tx, int tti_tx) +void nr_feptx_prec(RU_t *ru, int frame_tx, int slot_tx) { - PHY_VARS_gNB **gNB_list = ru->gNB_list,*gNB; - NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; + PHY_VARS_gNB **gNB_list = ru->gNB_list; + AssertFatal(ru->num_gNB == 1, "Cannot handle more than 1 gNB\n"); + PHY_VARS_gNB *gNB = gNB_list[0]; nfapi_nr_config_request_scf_t *cfg = &ru->gNB_list[0]->gNB_config; - int slot_tx = tti_tx; - int txdataF_offset = (tti_tx*fp->samples_per_slot_wCP); - + NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; + int txdataF_offset = slot_tx * fp->samples_per_slot_wCP; start_meas(&ru->precoding_stats); - AssertFatal(ru->nb_log_antennas > 0, "ru->nb_log_antennas is 0!\n"); if (nr_slot_select(cfg,frame_tx,slot_tx) == NR_UPLINK_SLOT) return; - if (ru->num_gNB == 1) { - gNB = gNB_list[0]; + if (gNB->common_vars.analog_bf) { + for (int i = 0; i < ru->num_beams_period; i++) { + memcpy((void*) &ru->common.beam_id[i][slot_tx * fp->symbols_per_slot], + (void*) &gNB->common_vars.beam_id[i][slot_tx * fp->symbols_per_slot], + (fp->symbols_per_slot) * sizeof(int)); + } + } - if (ru->config.dbt_config.num_dig_beams != 0) { - for(int i = 0; i < gNB->common_vars.num_beams_period; ++i) { - memcpy((void*) &ru->common.beam_id[i][slot_tx * fp->symbols_per_slot], - (void*) &gNB->common_vars.beam_id[i][slot_tx * fp->symbols_per_slot], - (fp->symbols_per_slot) * sizeof(int)); - } - } - - if (ru->config.dbt_config.num_dig_beams == 0 || gNB->common_vars.analog_bf) { - for (int i = 0; i < ru->nb_log_antennas; ++i) { - // TODO hardcoded beam to 0, still need to understand how to handle this properly - memcpy(ru->common.txdataF_BF[i], &gNB->common_vars.txdataF[0][i][txdataF_offset], fp->samples_per_slot_wCP * sizeof(int32_t)); - } - } - else { - for(int i = 0; i < ru->nb_log_antennas; ++i) { - // TODO hardcoded beam to 0, still need to understand how to handle this properly - memcpy(ru->common.txdataF[i], &gNB->common_vars.txdataF[0][i][txdataF_offset], fp->samples_per_slot_wCP * sizeof(int32_t)); - } - for (int l = 0; l < fp->symbols_per_slot; l++) { - for (int aa = 0; aa < ru->nb_tx; aa++) { - AssertFatal(false, "This needs to be fixed by using appropriate beams from config\n"); - nr_beam_precoding((c16_t **)ru->common.txdataF, - (c16_t **)ru->common.txdataF_BF, - fp, - ru->beam_weights[0], - tti_tx, - l, - aa, - ru->nb_log_antennas, - 0); - } + // If there is no digital beamforming we just need to copy the data to RU + if (ru->config.dbt_config.num_dig_beams == 0 || ru->gNB_list[0]->common_vars.analog_bf) { + for (int b = 0; b < ru->num_beams_period; b++) { + for (int i = 0; i < ru->nb_tx; ++i) { + int tx_idx = i + b * ru->nb_tx; + memcpy((void*)ru->common.txdataF_BF[tx_idx], + (void*)&gNB->common_vars.txdataF[b][i][txdataF_offset], + fp->samples_per_slot_wCP * sizeof(int32_t)); } } + } else { + AssertFatal(false, "This needs to be fixed by using appropriate beams from config\n"); } stop_meas(&ru->precoding_stats); } @@ -238,6 +220,7 @@ void nr_feptx(void *arg) RU_t *ru = feptx->ru; int slot = feptx->slot; int aa = feptx->aid; + int bb = feptx->beam; int startSymbol = feptx->startSymbol; NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; int numSymbols = feptx->numSymbols; @@ -246,44 +229,32 @@ void nr_feptx(void *arg) int txdataF_BF_offset = startSymbol * fp->ofdm_symbol_size; VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_RU_FEPTX_PREC+feptx->aid , 1); + int tx_idx = aa + bb * ru->nb_tx; - if (aa == 0) + if (tx_idx == 0) start_meas(&ru->precoding_stats); - if (ru->config.dbt_config.num_dig_beams != 0) { - for(int i = 0; i < ru->gNB_list[0]->common_vars.num_beams_period; i++) { - memcpy(&ru->common.beam_id[i][slot * fp->symbols_per_slot], - &ru->gNB_list[0]->common_vars.beam_id[i][slot * fp->symbols_per_slot], - (fp->symbols_per_slot) * sizeof(int)); - } + if (ru->gNB_list[0]->common_vars.analog_bf) { + memcpy(&ru->common.beam_id[bb][slot * fp->symbols_per_slot], + &ru->gNB_list[0]->common_vars.beam_id[bb][slot * fp->symbols_per_slot], + (fp->symbols_per_slot) * sizeof(int)); } // If there is no digital beamforming we just need to copy the data to RU if (ru->config.dbt_config.num_dig_beams == 0 || ru->gNB_list[0]->common_vars.analog_bf) - memcpy((void*)&ru->common.txdataF_BF[aa][txdataF_BF_offset], - (void*)&ru->gNB_list[0]->common_vars.txdataF[0][aa][txdataF_offset], // TODO hardcoded beam to 0, still need to understand how to handle this properly + memcpy((void*)&ru->common.txdataF_BF[tx_idx][txdataF_BF_offset], + (void*)&ru->gNB_list[0]->common_vars.txdataF[bb][aa][txdataF_offset], numSamples * sizeof(int32_t)); else { - AssertFatal(false, "This needs to be fixed by using appropriate beams from config\n"); - for(int i = 0; i < fp->symbols_per_slot; ++i) { - nr_beam_precoding((c16_t **)ru->gNB_list[0]->common_vars.txdataF, - (c16_t **)ru->common.txdataF_BF, - fp, - ru->beam_weights[0], - slot, - i, - aa, - ru->nb_log_antennas, - txdataF_offset); - } + AssertFatal(false, "This needs to be fixed by using appropriate beams from config\n"); } - if (aa==0) + if (tx_idx == 0) stop_meas(&ru->precoding_stats); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_RU_FEPTX_PREC+feptx->aid , 0); ////////////FEPTX//////////// - nr_feptx0(ru, slot, startSymbol, numSymbols, aa); + nr_feptx0(ru, slot, startSymbol, numSymbols, tx_idx); // Task completed in // completed_task_ans(feptx->ans); @@ -299,39 +270,42 @@ void nr_feptx_tp(RU_t *ru, int frame_tx, int slot) VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_RU_FEPTX_OFDM, 1); start_meas(&ru->ofdm_total_stats); - size_t const sz = ru->nb_tx + (ru->half_slot_parallelization > 0) * ru->nb_tx; + int nt = ru->nb_tx * ru->num_beams_period; + size_t const sz = nt + (ru->half_slot_parallelization > 0) * nt; feptx_cmd_t arr[sz]; task_ans_t ans; init_task_ans(&ans, sz); int nbfeptx = 0; - for (int aid = 0; aid < ru->nb_tx; aid++) { - feptx_cmd_t *feptx_cmd = &arr[nbfeptx]; - feptx_cmd->ans = &ans; - - feptx_cmd->aid = aid; - feptx_cmd->ru = ru; - feptx_cmd->slot = slot; - feptx_cmd->startSymbol = 0; - feptx_cmd->numSymbols = - (ru->half_slot_parallelization > 0) ? ru->nr_frame_parms->symbols_per_slot >> 1 : ru->nr_frame_parms->symbols_per_slot; - - task_t t = {.func = nr_feptx, .args = feptx_cmd}; - pushTpool(ru->threadPool, t); - nbfeptx++; - if (ru->half_slot_parallelization > 0) { + for (int beam = 0; beam < ru->num_beams_period; beam++) { + for (int aid = 0; aid < ru->nb_tx; aid++) { feptx_cmd_t *feptx_cmd = &arr[nbfeptx]; feptx_cmd->ans = &ans; - + feptx_cmd->beam = beam; feptx_cmd->aid = aid; feptx_cmd->ru = ru; feptx_cmd->slot = slot; - feptx_cmd->startSymbol = ru->nr_frame_parms->symbols_per_slot >> 1; - feptx_cmd->numSymbols = ru->nr_frame_parms->symbols_per_slot >> 1; + feptx_cmd->startSymbol = 0; + feptx_cmd->numSymbols = + (ru->half_slot_parallelization > 0) ? ru->nr_frame_parms->symbols_per_slot >> 1 : ru->nr_frame_parms->symbols_per_slot; task_t t = {.func = nr_feptx, .args = feptx_cmd}; pushTpool(ru->threadPool, t); nbfeptx++; + if (ru->half_slot_parallelization > 0) { + feptx_cmd_t *feptx_cmd = &arr[nbfeptx]; + feptx_cmd->ans = &ans; + feptx_cmd->beam = beam; + feptx_cmd->aid = aid; + feptx_cmd->ru = ru; + feptx_cmd->slot = slot; + feptx_cmd->startSymbol = ru->nr_frame_parms->symbols_per_slot >> 1; + feptx_cmd->numSymbols = ru->nr_frame_parms->symbols_per_slot >> 1; + + task_t t = {.func = nr_feptx, .args = feptx_cmd}; + pushTpool(ru->threadPool, t); + nbfeptx++; + } } } join_task_ans(&ans); @@ -345,24 +319,26 @@ void nr_feptx_tp(RU_t *ru, int frame_tx, int slot) void nr_fep(void* arg) { feprx_cmd_t *feprx_cmd = (feprx_cmd_t *)arg; - RU_t *ru = feprx_cmd->ru; - int aid = feprx_cmd->aid; - int tti_rx = feprx_cmd->slot; - int startSymbol = feprx_cmd->startSymbol; - int endSymbol = feprx_cmd->endSymbol; + RU_t *ru = feprx_cmd->ru; + int aid = feprx_cmd->aid; + int beam = feprx_cmd->beam; + int slot = feprx_cmd->slot; + int startSymbol = feprx_cmd->startSymbol; + int endSymbol = feprx_cmd->endSymbol; NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; - LOG_D(PHY,"aid %d, frame %d slot %d, startSymbol %d, endSymbol %d\n", aid, ru->proc.frame_rx, tti_rx, startSymbol, endSymbol); + LOG_D(PHY,"aid %d, frame %d slot %d, startSymbol %d, endSymbol %d\n", aid, ru->proc.frame_rx, slot, startSymbol, endSymbol); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_RU_FEPRX+aid, 1); - int offset = (tti_rx % RU_RX_SLOT_DEPTH) * fp->symbols_per_slot * fp->ofdm_symbol_size; + int idx = aid + beam * ru->nb_rx; + int offset = (slot % RU_RX_SLOT_DEPTH) * fp->symbols_per_slot * fp->ofdm_symbol_size; for (int l = startSymbol; l <= endSymbol; l++) nr_slot_fep_ul(fp, - ru->common.rxdata[aid], - &ru->common.rxdataF[aid][offset], + ru->common.rxdata[idx], + &ru->common.rxdataF[idx][offset], l, - tti_rx, + slot, ru->N_TA_offset); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_RU_FEPRX+aid, 0); @@ -377,39 +353,42 @@ void nr_fep_tp(RU_t *ru, int slot) { if (ru->idx == 0) VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_RU_FEPRX, 1 ); start_meas(&ru->ofdm_demod_stats); - size_t const sz = ru->nb_rx + (ru->half_slot_parallelization > 0) * ru->nb_rx; + int nt = ru->nb_rx * ru->num_beams_period; + size_t const sz = nt + (ru->half_slot_parallelization > 0) * nt; feprx_cmd_t arr[sz]; task_ans_t ans; init_task_ans(&ans, sz); - for (int aid=0;aidnb_rx;aid++) { - feprx_cmd_t *feprx_cmd = &arr[nbfeprx]; - feprx_cmd->ans = &ans; - - feprx_cmd->aid = aid; - feprx_cmd->ru = ru; - feprx_cmd->slot = ru->proc.tti_rx; - feprx_cmd->startSymbol = 0; - feprx_cmd->endSymbol = (ru->half_slot_parallelization > 0) ? (ru->nr_frame_parms->symbols_per_slot >> 1) - 1 - : (ru->nr_frame_parms->symbols_per_slot - 1); - - task_t t = {.func = nr_fep, .args = feprx_cmd}; - pushTpool(ru->threadPool, t); - nbfeprx++; - if (ru->half_slot_parallelization > 0) { + for (int beam = 0; beam < ru->num_beams_period; beam++) { + for (int aid = 0; aid < ru->nb_rx; aid++) { feprx_cmd_t *feprx_cmd = &arr[nbfeprx]; feprx_cmd->ans = &ans; - + feprx_cmd->beam = beam; feprx_cmd->aid = aid; feprx_cmd->ru = ru; feprx_cmd->slot = ru->proc.tti_rx; - feprx_cmd->startSymbol = ru->nr_frame_parms->symbols_per_slot >> 1; - feprx_cmd->endSymbol = ru->nr_frame_parms->symbols_per_slot - 1; + feprx_cmd->startSymbol = 0; + feprx_cmd->endSymbol = (ru->half_slot_parallelization > 0) ? (ru->nr_frame_parms->symbols_per_slot >> 1) - 1 + : (ru->nr_frame_parms->symbols_per_slot - 1); task_t t = {.func = nr_fep, .args = feprx_cmd}; pushTpool(ru->threadPool, t); - nbfeprx++; + if (ru->half_slot_parallelization > 0) { + feprx_cmd_t *feprx_cmd = &arr[nbfeprx]; + feprx_cmd->ans = &ans; + feprx_cmd->beam = beam; + feprx_cmd->aid = aid; + feprx_cmd->ru = ru; + feprx_cmd->slot = ru->proc.tti_rx; + feprx_cmd->startSymbol = ru->nr_frame_parms->symbols_per_slot >> 1; + feprx_cmd->endSymbol = ru->nr_frame_parms->symbols_per_slot - 1; + + task_t t = {.func = nr_fep, .args = feprx_cmd}; + pushTpool(ru->threadPool, t); + + nbfeprx++; + } } } join_task_ans(&ans); diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 3e24d0a4eb..8da471f4c1 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -45,18 +45,26 @@ //#define DEBUG_RXDATA //#define SRS_IND_DEBUG -int beam_index_allocation(int fapi_beam_index, NR_gNB_COMMON *common_vars, int slot, int symbols_per_slot, int bitmap_symbols) +int beam_index_allocation(bool das, + int fapi_beam_index, + nfapi_nr_analog_beamforming_ve_t *analog_bf, + NR_gNB_COMMON *common_vars, + int slot, + int symbols_per_slot, + int bitmap_symbols) { if (!common_vars->beam_id) return 0; + if (das) + return fapi_beam_index; + int ru_beam_idx = analog_bf->analog_beam_list[fapi_beam_index].value; int idx = -1; for (int j = 0; j < common_vars->num_beams_period; j++) { + // L2 analog beam implementation is slot based, so we need to verify occupancy for the whole slot for (int i = 0; i < symbols_per_slot; i++) { - if (((bitmap_symbols >> i) & 0x01) == 0) - continue; int current_beam = common_vars->beam_id[j][slot * symbols_per_slot + i]; - if (current_beam == -1 || current_beam == fapi_beam_index) + if (current_beam == -1 || current_beam == ru_beam_idx) idx = j; else { idx = -1; @@ -66,12 +74,12 @@ int beam_index_allocation(int fapi_beam_index, NR_gNB_COMMON *common_vars, int s if (idx != -1) break; } - AssertFatal(idx >= 0, "Couldn't allocate beam ID %d\n", fapi_beam_index); + AssertFatal(idx >= 0, "Couldn't allocate beam ID %d\n", ru_beam_idx); for (int j = 0; j < symbols_per_slot; j++) { if (((bitmap_symbols >> j) & 0x01)) - common_vars->beam_id[idx][slot * symbols_per_slot + j] = fapi_beam_index; + common_vars->beam_id[idx][slot * symbols_per_slot + j] = ru_beam_idx; } - LOG_D(PHY, "Allocating beam %d in slot %d\n", idx, slot); + LOG_D(PHY, "Allocating beam_id[%d] %d in slot %d\n", idx, ru_beam_idx, slot); return idx; } @@ -131,7 +139,9 @@ void nr_common_signal_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_n int txdataF_offset = slot * fp->samples_per_slot_wCP; // beam number in a scenario with multiple concurrent beams int bitmap = SL_to_bitmap(ssb_start_symbol, 4); // 4 ssb symbols - int beam_nb = beam_index_allocation(pb->prgs_list[0].dig_bf_interface_list[0].beam_idx, + int beam_nb = beam_index_allocation(gNB->enable_analog_das, + pb->prgs_list[0].dig_bf_interface_list[0].beam_idx, + &cfg->analog_beamforming_ve, &gNB->common_vars, slot, fp->symbols_per_slot, @@ -272,7 +282,9 @@ void phy_procedures_gNB_TX(processingData_L1tx_t *msgTx, int lprime_num = mapping_parms.lprime + 1; for (int j = 0; j < mapping_parms.size; j++) csi_bitmap |= ((1 << lprime_num) - 1) << mapping_parms.loverline[j]; - int beam_nb = beam_index_allocation(pb->prgs_list[0].dig_bf_interface_list[0].beam_idx, + int beam_nb = beam_index_allocation(gNB->enable_analog_das, + pb->prgs_list[0].dig_bf_interface_list[0].beam_idx, + &cfg->analog_beamforming_ve, &gNB->common_vars, slot, fp->symbols_per_slot, diff --git a/openair1/SCHED_NR/sched_nr.h b/openair1/SCHED_NR/sched_nr.h index bf5a1eda3f..40c32c2199 100644 --- a/openair1/SCHED_NR/sched_nr.h +++ b/openair1/SCHED_NR/sched_nr.h @@ -44,8 +44,14 @@ void nr_feptx_prec_control(RU_t *ru,int frame,int tti_tx); void nr_fep_tp(RU_t *ru, int slot); void nr_feptx_tp(RU_t *ru, int frame_tx, int slot); void feptx_prec(RU_t *ru,int frame_tx,int tti_tx); -int nr_phy_init_RU(RU_t *ru); +void nr_phy_init_RU(RU_t *ru); void nr_phy_free_RU(RU_t *ru); void clear_slot_beamid(PHY_VARS_gNB *gNB, int slot); -int beam_index_allocation(int fapi_beam_index, NR_gNB_COMMON *common_vars, int slot, int symbols_per_slot, int bitmap_symbols); +int beam_index_allocation(bool das, + int fapi_beam_index, + nfapi_nr_analog_beamforming_ve_t *analog_bf, + NR_gNB_COMMON *common_vars, + int slot, + int symbols_per_slot, + int bitmap_symbols); #endif diff --git a/openair1/SIMULATION/NR_PHY/prachsim.c b/openair1/SIMULATION/NR_PHY/prachsim.c index aebfe4997f..a13918a606 100644 --- a/openair1/SIMULATION/NR_PHY/prachsim.c +++ b/openair1/SIMULATION/NR_PHY/prachsim.c @@ -773,7 +773,7 @@ int main(int argc, char **argv){ } } - rx_nr_prach_ru(ru, prach_format, numRA, prachStartSymbol, slot, prachOccasion, frame, slot); + rx_nr_prach_ru(ru, prach_format, numRA, 0, prachStartSymbol, slot, prachOccasion, frame, slot); for (int i = 0; i < ru->nb_rx; ++i) gNB->prach_vars.rxsigF[i] = ru->prach_rxsigF[prachOccasion][i]; diff --git a/openair2/GNB_APP/L1_nr_paramdef.h b/openair2/GNB_APP/L1_nr_paramdef.h index e313818ad2..b2b4ab93c1 100644 --- a/openair2/GNB_APP/L1_nr_paramdef.h +++ b/openair2/GNB_APP/L1_nr_paramdef.h @@ -61,6 +61,8 @@ #define HLP_L1_PHASE_COMP "Apply NR symbolwise phase rotation" #define CONFIG_STRING_NUM_ANTENNAS_PER_THREAD "dmrs_num_antennas_per_thread" #define HLP_NUM_ARX "Number of antennas per thread for PUSCH channel estimation" +#define CONFIG_STRING_ANALOG_DAS "enable_das" + /*----------------------------------------------------------------------------------------------------------------------------------------------------*/ /* L1 configuration parameters */ /* optname helpstr paramflags XXXptr defXXXval type numelt */ @@ -81,12 +83,13 @@ {CONFIG_STRING_L1_PRACH_DTX_THRESHOLD, NULL, 0, .uptr=NULL, .defintval=150, TYPE_UINT, 0}, \ {CONFIG_STRING_L1_PUSCH_DTX_THRESHOLD, NULL, 0, .uptr=NULL, .defintval=50, TYPE_UINT, 0}, \ {CONFIG_STRING_L1_SRS_DTX_THRESHOLD, NULL, 0, .uptr=NULL, .defintval=30, TYPE_UINT, 0}, \ - {CONFIG_STRING_L1_MAX_LDPC_ITERATIONS, NULL, 0, .uptr=NULL, .defintval=8, TYPE_UINT, 0}, \ - {CONFIG_STRING_L1_RX_THREAD_CORE, NULL, 0, .uptr=NULL, .defintval=-1, TYPE_UINT, 0}, \ - {CONFIG_STRING_L1_TX_THREAD_CORE, NULL, 0, .uptr=NULL, .defintval=-1, TYPE_UINT, 0}, \ + {CONFIG_STRING_L1_MAX_LDPC_ITERATIONS, NULL, 0, .uptr=NULL, .defintval=8, TYPE_UINT, 0}, \ + {CONFIG_STRING_L1_RX_THREAD_CORE, NULL, 0, .uptr=NULL, .defintval=-1, TYPE_UINT, 0}, \ + {CONFIG_STRING_L1_TX_THREAD_CORE, NULL, 0, .uptr=NULL, .defintval=-1, TYPE_UINT, 0}, \ {CONFIG_STRING_L1_TX_AMP_BACKOFF_dB, HLP_L1TX_BO,0, .uptr=NULL, .defintval=36, TYPE_UINT, 0}, \ {CONFIG_STRING_L1_PHASE_COMP, HLP_L1_PHASE_COMP,PARAMFLAG_BOOL, .uptr=NULL,.defintval=1, TYPE_UINT, 0}, \ {CONFIG_STRING_NUM_ANTENNAS_PER_THREAD, HLP_NUM_ARX,0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \ + {CONFIG_STRING_ANALOG_DAS, NULL, 0, .uptr=NULL, .defintval=0, TYPE_UINT, 0}, \ } // clang-format on #define L1_CC_IDX 0 @@ -109,6 +112,7 @@ #define L1_TX_AMP_BACKOFF_dB 17 #define L1_PHASE_COMP 18 #define NUM_ANTENNAS_PER_THREAD 19 +#define L1_ANALOG_DAS 20 /*----------------------------------------------------------------------------------------------------------------------------------------------------*/ #endif diff --git a/openair2/GNB_APP/MACRLC_nr_paramdef.h b/openair2/GNB_APP/MACRLC_nr_paramdef.h index e682c559fa..c1382d7b70 100644 --- a/openair2/GNB_APP/MACRLC_nr_paramdef.h +++ b/openair2/GNB_APP/MACRLC_nr_paramdef.h @@ -76,6 +76,7 @@ #define CONFIG_STRING_MACRLC_ANALOG_BEAMFORMING "set_analog_beamforming" #define CONFIG_STRING_MACRLC_BEAM_DURATION "beam_duration" #define CONFIG_STRING_MACRLC_BEAMS_PERIOD "beams_per_period" +#define CONFIG_STRING_MACRLC_BEAM_WEIGHTS_LIST "beam_weights" #define CONFIG_STRING_MACRLC_PUSCH_RSSI_THRESHOLD "pusch_RSSI_Threshold" #define CONFIG_STRING_MACRLC_PUCCH_RSSI_THRESHOLD "pucch_RSSI_Threshold" @@ -143,6 +144,7 @@ {CONFIG_STRING_MACRLC_ANALOG_BEAMFORMING, HLP_MACRLC_AB, PARAMFLAG_BOOL, .u8ptr=NULL, .defintval=0, TYPE_UINT8, 0}, \ {CONFIG_STRING_MACRLC_BEAM_DURATION, HLP_MACRLC_BEAM_DURATION, 0, .u8ptr=NULL, .defintval=1, TYPE_UINT8, 0}, \ {CONFIG_STRING_MACRLC_BEAMS_PERIOD, HLP_MACRLC_BEAMS_PERIOD, 0, .u8ptr=NULL, .defintval=1, TYPE_UINT8, 0}, \ + {CONFIG_STRING_MACRLC_BEAM_WEIGHTS_LIST, NULL, 0, .iptr=NULL, .defintarrayval=0, TYPE_INTARRAY,0}, \ {CONFIG_STRING_MACRLC_PUSCH_RSSI_THRESHOLD, HLP_MACRLC_PUSCH_RSSI_THRESHOLD, \ 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \ {CONFIG_STRING_MACRLC_PUCCH_RSSI_THRESHOLD, HLP_MACRLC_PUCCH_RSSI_THRESHOLD, \ @@ -189,8 +191,9 @@ #define MACRLC_ANALOG_BEAMFORMING_IDX 36 #define MACRLC_ANALOG_BEAM_DURATION_IDX 37 #define MACRLC_ANALOG_BEAMS_PERIOD_IDX 38 -#define MACRLC_PUSCH_RSSI_THRES_IDX 39 -#define MACRLC_PUCCH_RSSI_THRES_IDX 40 +#define MACRLC_BEAMWEIGHTS_IDX 39 +#define MACRLC_PUSCH_RSSI_THRES_IDX 40 +#define MACRLC_PUCCH_RSSI_THRES_IDX 41 #define MACRLCPARAMS_CHECK { \ { .s5 = { NULL } }, \ @@ -232,6 +235,7 @@ { .s5 = { NULL } }, \ { .s5 = { NULL } }, \ { .s5 = { NULL } }, \ + { .s5 = { NULL } }, \ { .s2 = { config_check_intrange, {-1280, 0}} }, /* PUSCH RSSI threshold range */ \ { .s2 = { config_check_intrange, {-1280, 0}} }, /* PUCCH RSSI threshold range */ \ } diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index ec732a4f91..0ddd097ca6 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -1067,6 +1067,7 @@ void RCconfig_NR_L1(void) gNB->TX_AMP = min(32767.0 / pow(10.0, .05 * (double)(*L1_ParamList.paramarray[j][L1_TX_AMP_BACKOFF_dB].uptr)), INT16_MAX); gNB->phase_comp = *L1_ParamList.paramarray[j][L1_PHASE_COMP].uptr; gNB->dmrs_num_antennas_per_thread = *(L1_ParamList.paramarray[j][NUM_ANTENNAS_PER_THREAD].uptr); + gNB->enable_analog_das = *(L1_ParamList.paramarray[j][L1_ANALOG_DAS].uptr); LOG_I(NR_PHY, "TX_AMP = %d (-%d dBFS)\n", gNB->TX_AMP, *L1_ParamList.paramarray[j][L1_TX_AMP_BACKOFF_dB].uptr); AssertFatal(gNB->TX_AMP > 300, "TX_AMP is too small, must be larger than 300 (is %d)\n", gNB->TX_AMP); // Midhaul configuration @@ -1621,19 +1622,6 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg) config.timer_config.n311, config.timer_config.t319); - if (config_isparamset(GNBParamList.paramarray[0], GNB_BEAMWEIGHTS_IDX)) { - int n = GNBParamList.paramarray[0][GNB_BEAMWEIGHTS_IDX].numelt; - AssertFatal(n % num_tx == 0, "Error! Number of beam input needs to be multiple of TX antennas\n"); - // each beam is described by a set of weights (one for each antenna) - // on the other hand in case of analog beamforming an index to the RU beam identifier is provided - config.nb_bfw[0] = num_tx; // number of tx antennas - config.nb_bfw[1] = n / num_tx; // number of beams - config.bw_list = malloc16_clear(n * sizeof(*config.bw_list)); - for (int b = 0; b < n; b++) { - config.bw_list[b] = GNBParamList.paramarray[0][GNB_BEAMWEIGHTS_IDX].iptr[b]; - } - } - // Construct default aggragation level list or read from config int uess_num_agg_level_candidates[NUM_PDCCH_AGG_LEVELS]; uess_num_agg_level_candidates[PDCCH_AGG_LEVEL1] = NR_SearchSpace__nrofCandidates__aggregationLevel1_n0; @@ -1763,6 +1751,7 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg) } bool ab = *MacRLC_ParamList.paramarray[j][MACRLC_ANALOG_BEAMFORMING_IDX].u8ptr; if (ab) { + AssertFatal(NFAPI_MODE == NFAPI_MONOLITHIC, "Analog beamforming only supported for monolithic scenario\n"); NR_beam_info_t *beam_info = &RC.nrmac[j]->beam_info; int beams_per_period = *MacRLC_ParamList.paramarray[j][MACRLC_ANALOG_BEAMS_PERIOD_IDX].u8ptr; beam_info->beam_allocation = malloc16(beams_per_period * sizeof(int *)); @@ -1770,6 +1759,26 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg) beam_info->beams_per_period = beams_per_period; beam_info->beam_allocation_size = -1; // to be initialized once we have information on frame configuration } + if (config_isparamset(MacRLC_ParamList.paramarray[j], MACRLC_BEAMWEIGHTS_IDX)) { + if (NFAPI_MODE == NFAPI_MONOLITHIC) { + GET_PARAMS_LIST(L1_ParamList, L1_Params, L1PARAMS_DESC, CONFIG_STRING_L1_LIST, NULL); + AssertFatal(*(L1_ParamList.paramarray[j][L1_ANALOG_DAS].uptr) == 0, "No need to set beam weights in case of DAS\n"); + } + int n = MacRLC_ParamList.paramarray[j][MACRLC_BEAMWEIGHTS_IDX].numelt; + int num_beam = n; + if (!ab) { + AssertFatal(n % num_tx == 0, "Error! Number of beam input needs to be multiple of TX antennas\n"); + num_beam = n / num_tx; + } + // each beam is described by a set of weights (one for each antenna) + // in case of analog beamforming an index to the RU beam identifier is provided + // (one for each beam regardless of the number of antennas per beam) + config.nb_bfw[0] = num_tx; // number of tx antennas + config.nb_bfw[1] = num_beam; // number of beams weights/indices + config.bw_list = calloc_or_fail(n, sizeof(*config.bw_list)); + for (int b = 0; b < n; b++) + config.bw_list[b] = MacRLC_ParamList.paramarray[j][MACRLC_BEAMWEIGHTS_IDX].iptr[b]; + } // triggers also PHY initialization in case we have L1 via FAPI nr_mac_config_scc(RC.nrmac[j], scc, &config); } // for (j=0;jcell_config.frame_duplex_type.value == TDD) set_tdd_config_nr(cfg, fs); - int nb_tx = config->nb_bfw[0]; // number of tx antennas - int nb_beams = config->nb_bfw[1]; // number of beams // precoding matrix configuration (to be improved) cfg->pmi_list = init_DL_MIMO_codebook(nrmac, pdsch_AntennaPorts); - // beamforming matrix configuration - cfg->dbt_config.num_dig_beams = nb_beams; - if (nb_beams > 0) { - cfg->dbt_config.num_txrus = nb_tx; - cfg->dbt_config.dig_beam_list = malloc16(nb_beams * sizeof(*cfg->dbt_config.dig_beam_list)); - AssertFatal(cfg->dbt_config.dig_beam_list, "out of memory\n"); - for (int i = 0; i < nb_beams; i++) { - nfapi_nr_dig_beam_t *beam = &cfg->dbt_config.dig_beam_list[i]; - beam->beam_idx = i; - beam->txru_list = malloc16(nb_tx * sizeof(*beam->txru_list)); - for (int j = 0; j < nb_tx; j++) { - nfapi_nr_txru_t *txru = &beam->txru_list[j]; - txru->dig_beam_weight_Re = config->bw_list[j + i * nb_tx] & 0xffff; - txru->dig_beam_weight_Im = (config->bw_list[j + i * nb_tx] >> 16) & 0xffff; - LOG_D(NR_MAC, "Beam %d Tx %d Weight (%d, %d)\n", i, j, txru->dig_beam_weight_Re, txru->dig_beam_weight_Im); - } - } - } + int nb_beams = config->nb_bfw[1]; // number of beams if (nrmac->beam_info.beam_allocation) { + LOG_I(NR_MAC, "Configuring analog beamforming in config_request message\n"); cfg->analog_beamforming_ve.num_beams_period_vendor_ext.tl.tag = NFAPI_NR_FAPI_NUM_BEAMS_PERIOD_VENDOR_EXTENSION_TAG; cfg->analog_beamforming_ve.num_beams_period_vendor_ext.value = nrmac->beam_info.beams_per_period; cfg->num_tlv++; cfg->analog_beamforming_ve.analog_bf_vendor_ext.tl.tag = NFAPI_NR_FAPI_ANALOG_BF_VENDOR_EXTENSION_TAG; cfg->analog_beamforming_ve.analog_bf_vendor_ext.value = 1; // analog BF enabled cfg->num_tlv++; + cfg->analog_beamforming_ve.total_num_beams_vendor_ext.tl.tag = NFAPI_NR_FAPI_TOTAL_NUM_BEAMS_VENDOR_EXTENSION_TAG; + cfg->analog_beamforming_ve.total_num_beams_vendor_ext.value = nb_beams; + cfg->num_tlv++; + cfg->analog_beamforming_ve.analog_beam_list = malloc16(nb_beams * sizeof(*cfg->analog_beamforming_ve.analog_beam_list)); + for (int i = 0; i < nb_beams; i++) { + cfg->analog_beamforming_ve.analog_beam_list[i].tl.tag = NFAPI_NR_FAPI_ANALOG_BEAM_VENDOR_EXTENSION_TAG; + cfg->analog_beamforming_ve.analog_beam_list[i].value = config->bw_list[i]; + } + } else { + cfg->analog_beamforming_ve.analog_bf_vendor_ext.value = 0; // analog BF disabled + if (NFAPI_MODE == NFAPI_MONOLITHIC) { + cfg->analog_beamforming_ve.analog_bf_vendor_ext.tl.tag = NFAPI_NR_FAPI_ANALOG_BF_VENDOR_EXTENSION_TAG; + cfg->num_tlv++; + } } } diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index e67a30e67a..e398da1eaf 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -356,10 +356,11 @@ void schedule_nr_prach(module_id_t module_idP, frame_t frameP, slot_t slotP) NR_COMMON_channels_t *cc = gNB->common_channels; NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon; - NR_RACH_ConfigCommon_t *rach_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup; + NR_BWP_UplinkCommon_t *initialUplinkBWP = scc->uplinkConfigCommon->initialUplinkBWP; + NR_RACH_ConfigCommon_t *rach_ConfigCommon = initialUplinkBWP->rach_ConfigCommon->choice.setup; NR_MsgA_ConfigCommon_r16_t *msgacc = NULL; - if (scc->uplinkConfigCommon->initialUplinkBWP->ext1 && scc->uplinkConfigCommon->initialUplinkBWP->ext1->msgA_ConfigCommon_r16) - msgacc = scc->uplinkConfigCommon->initialUplinkBWP->ext1->msgA_ConfigCommon_r16->choice.setup; + if (initialUplinkBWP->ext1 && initialUplinkBWP->ext1->msgA_ConfigCommon_r16) + msgacc = initialUplinkBWP->ext1->msgA_ConfigCommon_r16->choice.setup; int slots_frame = gNB->frame_structure.numb_slots_frame; int index = ul_buffer_index(frameP, slotP, slots_frame, gNB->UL_tti_req_ahead_size); nfapi_nr_ul_tti_request_t *UL_tti_req = &RC.nrmac[module_idP]->UL_tti_req_ahead[0][index]; @@ -371,7 +372,7 @@ void schedule_nr_prach(module_id_t module_idP, frame_t frameP, slot_t slotP) int slot_index = 0; uint16_t prach_occasion_id = -1; - int bwp_start = NRRIV2PRBOFFSET(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); + int bwp_start = NRRIV2PRBOFFSET(initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); uint8_t fdm = cfg->prach_config.num_prach_fd_occasions.value; const int ul_mu = scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; @@ -399,6 +400,16 @@ void schedule_nr_prach(module_id_t module_idP, frame_t frameP, slot_t slotP) uint32_t N_t_slot = cc->prach_info.N_t_slot; uint32_t start_symb = cc->prach_info.start_symbol; for (int fdm_index = 0; fdm_index < fdm; fdm_index++) { // one structure per frequency domain occasion + AssertFatal(UL_tti_req->n_pdus < sizeof(UL_tti_req->pdus_list) / sizeof(UL_tti_req->pdus_list[0]), + "Invalid UL_tti_req->n_pdus %d\n", + UL_tti_req->n_pdus); + + UL_tti_req->pdus_list[UL_tti_req->n_pdus].pdu_type = NFAPI_NR_UL_CONFIG_PRACH_PDU_TYPE; + UL_tti_req->pdus_list[UL_tti_req->n_pdus].pdu_size = sizeof(nfapi_nr_prach_pdu_t); + nfapi_nr_prach_pdu_t *prach_pdu = &UL_tti_req->pdus_list[UL_tti_req->n_pdus].prach_pdu; + memset(prach_pdu, 0, sizeof(nfapi_nr_prach_pdu_t)); + UL_tti_req->n_pdus += 1; + int num_td_occ = 0; for (int td_index = 0; td_index < N_t_slot; td_index++) { uint32_t config_period = cc->prach_info.x; prach_occasion_id = (((frameP % (cc->max_association_period * config_period))/config_period) * cc->total_prach_occasions_per_config_period) + @@ -407,6 +418,7 @@ void schedule_nr_prach(module_id_t module_idP, frame_t frameP, slot_t slotP) if (prach_occasion_id >= cc->total_prach_occasions) // to be confirmed: unused occasion? continue; + num_td_occ++; float num_ssb_per_RO = ssb_per_rach_occasion[cfg->prach_config.ssb_per_rach.value]; int beam_index = 0; if(num_ssb_per_RO <= 1) { @@ -417,8 +429,7 @@ void schedule_nr_prach(module_id_t module_idP, frame_t frameP, slot_t slotP) // multi-beam allocation structure beam = beam_allocation_procedure(&gNB->beam_info, frameP, slotP, beam_index, slots_frame); AssertFatal(beam.idx >= 0, "Cannot allocate PRACH corresponding to %d SSB transmitted in any available beam\n", n_ssb + 1); - } - else { + } else { int first_ssb_index = (prach_occasion_id * (int)num_ssb_per_RO) % cc->num_active_ssb; for(int j = first_ssb_index; j < first_ssb_index + num_ssb_per_RO; j++) { // fapi beam index @@ -428,40 +439,15 @@ void schedule_nr_prach(module_id_t module_idP, frame_t frameP, slot_t slotP) AssertFatal(beam.idx >= 0, "Cannot allocate PRACH corresponding to SSB %d in any available beam\n", j); } } - if(td_index == 0) { - AssertFatal(UL_tti_req->n_pdus < sizeof(UL_tti_req->pdus_list) / sizeof(UL_tti_req->pdus_list[0]), - "Invalid UL_tti_req->n_pdus %d\n", UL_tti_req->n_pdus); - - UL_tti_req->pdus_list[UL_tti_req->n_pdus].pdu_type = NFAPI_NR_UL_CONFIG_PRACH_PDU_TYPE; - UL_tti_req->pdus_list[UL_tti_req->n_pdus].pdu_size = sizeof(nfapi_nr_prach_pdu_t); - nfapi_nr_prach_pdu_t *prach_pdu = &UL_tti_req->pdus_list[UL_tti_req->n_pdus].prach_pdu; - memset(prach_pdu,0,sizeof(nfapi_nr_prach_pdu_t)); - UL_tti_req->n_pdus+=1; - + if(num_td_occ == 1) { // filling the prach fapi structure prach_pdu->phys_cell_id = *scc->physCellId; - prach_pdu->num_prach_ocas = N_t_slot; - gNB->num_scheduled_prach_rx += N_t_slot; prach_pdu->prach_start_symbol = start_symb; prach_pdu->num_ra = fdm_index; prach_pdu->num_cs = get_NCS(rach_ConfigGeneric->zeroCorrelationZoneConfig, format0, rach_ConfigCommon->restrictedSetConfig); - prach_pdu->beamforming.num_prgs = 0; - prach_pdu->beamforming.prg_size = 0; - prach_pdu->beamforming.dig_bf_interface = 1; - prach_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx = beam_index; - - LOG_D(NR_MAC, - "Frame %d, Slot %d: Prach Occasion id = %u fdm index = %u start symbol = %u slot index = %u subframe index = %u \n", - frameP, - slotP, - prach_occasion_id, - prach_pdu->num_ra, - start_symb, - slot_index, - RA_sfn_index); // SCF PRACH PDU format field does not consider A1/B1 etc. possibilities // We added 9 = A1/B1 10 = A2/B2 11 A3/B3 if (format1!=0xff) { @@ -518,8 +504,7 @@ void schedule_nr_prach(module_id_t module_idP, frame_t frameP, slot_t slotP) AssertFatal(1==0,"Invalid PRACH format"); } } - if (scc->uplinkConfigCommon->initialUplinkBWP->ext1 - && scc->uplinkConfigCommon->initialUplinkBWP->ext1->msgA_ConfigCommon_r16) { + if (initialUplinkBWP->ext1 && initialUplinkBWP->ext1->msgA_ConfigCommon_r16) { if (gNB->UE_info.connected_ue_list[0] == NULL) schedule_nr_MsgA_pusch(scc->uplinkConfigCommon, gNB, @@ -531,7 +516,26 @@ void schedule_nr_prach(module_id_t module_idP, frame_t frameP, slot_t slotP) *scc->physCellId); } } + prach_pdu->num_prach_ocas = num_td_occ; + prach_pdu->beamforming.num_prgs = 0; + prach_pdu->beamforming.prg_size = 0; + prach_pdu->beamforming.dig_bf_interface = num_td_occ; + prach_pdu->beamforming.prgs_list[0].dig_bf_interface_list[num_td_occ - 1].beam_idx = beam_index; + + LOG_D(NR_MAC, + "Frame %d, Slot %d: Prach Occasion id = %u fdm index = %u start symbol = %u slot index = %u subframe index = %u \n", + frameP, + slotP, + prach_occasion_id, + prach_pdu->num_ra, + prach_pdu->prach_start_symbol, + slot_index, + RA_sfn_index); } + if (num_td_occ == 0) // no TD PRACH occasion -> no PRACH PDU + UL_tti_req->n_pdus -= 1; + else + gNB->num_scheduled_prach_rx += num_td_occ; } // block resources in vrb_map_UL diff --git a/radio/COMMON/common_lib.h b/radio/COMMON/common_lib.h index a33e65c150..86d09b00fb 100644 --- a/radio/COMMON/common_lib.h +++ b/radio/COMMON/common_lib.h @@ -225,6 +225,8 @@ typedef struct openair0_config { int rx_num_channels; //! number of TX channels (=TX antennas) int tx_num_channels; + //! number of distributed radio-units + int num_distributed_ru; //! rx daughter card char* rx_subdev; //! tx daughter card diff --git a/radio/fhi_72/oran-config.c b/radio/fhi_72/oran-config.c index 71804c5298..891634dbfc 100644 --- a/radio/fhi_72/oran-config.c +++ b/radio/fhi_72/oran-config.c @@ -834,7 +834,7 @@ static bool set_fh_config(void *mplane_api, int ru_idx, int num_rus, enum xran_c { AssertFatal(num_rus == 1 || num_rus == 2, "only support 1 or 2 RUs as of now\n"); AssertFatal(ru_idx < num_rus, "illegal ru_idx %d: must be < %d\n", ru_idx, num_rus); - DevAssert(oai0->tx_num_channels > 0 && oai0->rx_num_channels > 0); + DevAssert(oai0->tx_num_channels > 0 && oai0->rx_num_channels > 0 && oai0->num_distributed_ru > 0); DevAssert(oai0->tx_bw > 0 && oai0->rx_bw > 0); DevAssert(oai0->tx_freq[0] > 0); for (int i = 1; i < oai0->tx_num_channels; ++i) @@ -879,7 +879,7 @@ static bool set_fh_config(void *mplane_api, int ru_idx, int num_rus, enum xran_c fh_config->dpdk_port = ru_idx; // DPDK port number used for FH fh_config->sector_id = 0; // Band sector ID for FH; not used in xran fh_config->nCC = 1; // number of Component carriers supported on FH; M-plane info - fh_config->neAxc = RTE_MAX(oai0->tx_num_channels / num_rus, oai0->rx_num_channels / num_rus); // number of eAxc supported on one CC = max(PDSCH, PUSCH) + fh_config->neAxc = RTE_MAX(oai0->num_distributed_ru * oai0->tx_num_channels / num_rus, oai0->num_distributed_ru * oai0->rx_num_channels / num_rus); // number of eAxc supported on one CC = max(PDSCH, PUSCH) fh_config->neAxcUl = 0; // number of eAxc supported on one CC for UL direction = PUSCH; used only if XRAN_CATEGORY_B fh_config->nAntElmTRx = 0; // number of antenna elements for TX and RX = SRS; used only if XRAN_CATEGORY_B fh_config->nDLFftSize = 0; // DL FFT size; not used in xran diff --git a/radio/fhi_72/oran_isolate.c b/radio/fhi_72/oran_isolate.c index 0a200a39a0..7ceec513c6 100644 --- a/radio/fhi_72/oran_isolate.c +++ b/radio/fhi_72/oran_isolate.c @@ -208,7 +208,7 @@ int trx_oran_ctlrecv(openair0_device *device, void *msg, ssize_t msg_len) void oran_fh_if4p5_south_in(RU_t *ru, int *frame, int *slot) { ru_info_t ru_info; - ru_info.nb_rx = ru->nb_rx; + ru_info.nb_rx = ru->nb_rx * ru->num_beams_period; ru_info.rxdataF = ru->common.rxdataF; ru_info.prach_buf = ru->prach_rxsigF[0]; // index: [prach_oca][ant_id] @@ -262,7 +262,7 @@ void oran_fh_if4p5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp) { start_meas(&ru->tx_fhaul); ru_info_t ru_info; - ru_info.nb_tx = ru->nb_tx; + ru_info.nb_tx = ru->nb_tx * ru->num_beams_period; ru_info.txdataF_BF = ru->common.txdataF_BF; // printf("south_out:\tframe=%d\tslot=%d\ttimestamp=%ld\n",frame,slot,timestamp);