/* * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The OpenAirInterface Software Alliance licenses this file to You under * the OAI Public License, Version 1.1 (the "License"); you may not use this file * except in compliance with the License. * You may obtain a copy of the License at * * http://www.openairinterface.org/?page_id=698 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *------------------------------------------------------------------------------- * For more information about the OpenAirInterface (OAI) Software Alliance: * contact@openairinterface.org */ /*! \file rfsim.c * \brief function for simulated RF device * \author R. Knopp * \date 2018 * \version 1.0 * \company Eurecom * \email: openair_tech@eurecom.fr * \note * \warning */ #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <signal.h> #include <execinfo.h> #include <time.h> #include <mcheck.h> #include <sys/timerfd.h> #include "assertions.h" #include "rfsim.h" #include "openair1/SIMULATION/TOOLS/sim.h" #include "enb_config.h" #include "enb_paramdef.h" #include "platform_constants.h" #include "common/config/config_paramdesc.h" #include "common/config/config_userapi.h" #include "common/ran_context.h" #include "PHY/defs_UE.h" #include "PHY/defs_eNB.h" #include "common/utils/LOG/vcd_signal_dumper.h" RAN_CONTEXT_t RC; extern PHY_VARS_UE ***PHY_vars_UE_g; // put all of these in a common structure after sim_t sim; void init_ru_devices(void); void *rfsim_top(void *n_frames); void wait_RUs(void) { int i; // wait for all RUs to be configured over fronthaul pthread_mutex_lock(&RC.ru_mutex); while (RC.ru_mask>0) { pthread_cond_wait(&RC.ru_cond,&RC.ru_mutex); } // copy frame parameters from RU to UEs for (i=0;i<NB_UE_INST;i++) { sim.current_UE_rx_timestamp[i][0] = RC.ru[0]->frame_parms.samples_per_tti + RC.ru[0]->frame_parms.ofdm_symbol_size + RC.ru[0]->frame_parms.nb_prefix_samples0; } for (int ru_id=0;ru_id<RC.nb_RU;ru_id++) sim.current_ru_rx_timestamp[ru_id][0] = RC.ru[ru_id]->frame_parms.samples_per_tti; printf("RUs are ready, let's go\n"); } void wait_eNBs(void) { return; } void RCConfig_sim(void) { paramlist_def_t RUParamList = {CONFIG_STRING_RU_LIST,NULL,0}; // AssertFatal(RC.config_file_name!=NULL,"configuration file is undefined\n"); // Get num RU instances config_getlist( &RUParamList,NULL,0, NULL); RC.nb_RU = RUParamList.numelt; printf("returned with %d rus\n",RC.nb_RU); init_RU(NULL); printf("Waiting for RUs to get set up\n"); wait_RUs(); init_ru_devices(); static int nframes = 100000; AssertFatal(0 == pthread_create(&sim.rfsim_thread, NULL, rfsim_top, (void*)&nframes), ""); } int ru_trx_start(openair0_device *device) { return(0); } void ru_trx_end(openair0_device *device) { return; } int ru_trx_stop(openair0_device *device) { return(0); } int UE_trx_start(openair0_device *device) { return(0); } void UE_trx_end(openair0_device *device) { return; } int UE_trx_stop(openair0_device *device) { return(0); } int ru_trx_set_freq(openair0_device *device, openair0_config_t *openair0_cfg, int dummy) { return(0); } int ru_trx_set_gains(openair0_device *device, openair0_config_t *openair0_cfg) { return(0); } int UE_trx_set_freq(openair0_device *device, openair0_config_t *openair0_cfg, int dummy) { return(0); } int UE_trx_set_gains(openair0_device *device, openair0_config_t *openair0_cfg) { return(0); } extern pthread_mutex_t subframe_mutex; extern int subframe_ru_mask,subframe_UE_mask; int ru_trx_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc) { int ru_id = device->Mod_id; int CC_id = device->CC_id; int subframe; int sample_count=0; *ptimestamp = sim.last_ru_rx_timestamp[ru_id][CC_id]; LOG_D(SIM,"RU_trx_read nsamps %d TS(%llu,%llu) => subframe %d\n",nsamps, (unsigned long long)sim.current_ru_rx_timestamp[ru_id][CC_id], (unsigned long long)sim.last_ru_rx_timestamp[ru_id][CC_id], (int)((*ptimestamp/RC.ru[ru_id]->frame_parms.samples_per_tti)%10)); // if we're at a subframe boundary generate UL signals for this ru while (sample_count<nsamps) { while (sim.current_ru_rx_timestamp[ru_id][CC_id]< (nsamps+sim.last_ru_rx_timestamp[ru_id][CC_id])) { LOG_D(SIM,"RU: current TS %"PRIi64", last TS %"PRIi64", sleeping\n",sim.current_ru_rx_timestamp[ru_id][CC_id],sim.last_ru_rx_timestamp[ru_id][CC_id]); usleep(500); } subframe = (sim.last_ru_rx_timestamp[ru_id][CC_id]/RC.ru[ru_id]->frame_parms.samples_per_tti)%10; if (subframe_select(&RC.ru[ru_id]->frame_parms,subframe) != SF_DL || RC.ru[ru_id]->frame_parms.frame_type == FDD) { LOG_D(SIM,"RU_trx_read generating UL subframe %d (Ts %llu, current TS %llu)\n", subframe,(unsigned long long)*ptimestamp, (unsigned long long)sim.current_ru_rx_timestamp[ru_id][CC_id]); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_SIM_DO_UL_SIGNAL,1); do_UL_sig(&sim, subframe, 0, // abstraction_flag &RC.ru[ru_id]->frame_parms, 0, // frame is only used for abstraction ru_id, CC_id); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_SIM_DO_UL_SIGNAL,0); } sim.last_ru_rx_timestamp[ru_id][CC_id] += RC.ru[ru_id]->frame_parms.samples_per_tti; sample_count += RC.ru[ru_id]->frame_parms.samples_per_tti; } return(nsamps); } int UE_trx_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc) { VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_SIM_UE_TRX_READ,1); int UE_id = device->Mod_id; int CC_id = device->CC_id; int subframe; int sample_count=0; int read_size; int sptti = PHY_vars_UE_g[UE_id][CC_id]->frame_parms.samples_per_tti; *ptimestamp = sim.last_UE_rx_timestamp[UE_id][CC_id]; LOG_D(PHY,"UE %d DL simulation 0: UE_trx_read nsamps %d TS %llu (%llu, offset %d) antenna %d\n", UE_id, nsamps, (unsigned long long)sim.current_UE_rx_timestamp[UE_id][CC_id], (unsigned long long)sim.last_UE_rx_timestamp[UE_id][CC_id], (int)(sim.last_UE_rx_timestamp[UE_id][CC_id]%sptti), cc); if (nsamps < sptti) read_size = nsamps; else read_size = sptti; while (sample_count<nsamps) { LOG_D(SIM,"UE %d: DL simulation 1: UE_trx_read : current TS now %"PRIi64", last TS %"PRIi64"\n",UE_id,sim.current_UE_rx_timestamp[UE_id][CC_id],sim.last_UE_rx_timestamp[UE_id][CC_id]); while (sim.current_UE_rx_timestamp[UE_id][CC_id] < (sim.last_UE_rx_timestamp[UE_id][CC_id]+read_size)) { LOG_D(SIM,"UE %d: DL simulation 2: UE_trx_read : current TS %"PRIi64", last TS %"PRIi64", sleeping\n",UE_id,sim.current_UE_rx_timestamp[UE_id][CC_id],sim.last_UE_rx_timestamp[UE_id][CC_id]); usleep(500); } LOG_D(SIM,"UE %d: DL simulation 3: UE_trx_read : current TS now %"PRIi64", last TS %"PRIi64"\n",UE_id,sim.current_UE_rx_timestamp[UE_id][CC_id],sim.last_UE_rx_timestamp[UE_id][CC_id]); // if we cross a subframe-boundary subframe = (sim.last_UE_rx_timestamp[UE_id][CC_id]/sptti)%10; // tell top-level we are busy pthread_mutex_lock(&sim.subframe_mutex); sim.subframe_UE_mask|=(1<<UE_id); LOG_D(SIM,"Setting UE_id %d mask to busy (%d)\n",UE_id,sim.subframe_UE_mask); pthread_mutex_unlock(&sim.subframe_mutex); LOG_D(PHY,"UE %d: DL simulation 4: UE_trx_read generating DL subframe %d (Ts %llu, current TS %llu,nsamps %d)\n", UE_id,subframe,(unsigned long long)*ptimestamp, (unsigned long long)sim.current_UE_rx_timestamp[UE_id][CC_id], nsamps); LOG_D(SIM,"UE %d: DL simulation 5: Doing DL simulation for %d samples starting in subframe %d at offset %d\n", UE_id,nsamps,subframe, (int)(sim.last_UE_rx_timestamp[UE_id][CC_id]%sptti)); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_SIM_DO_DL_SIGNAL,1); do_DL_sig(&sim, subframe, sim.last_UE_rx_timestamp[UE_id][CC_id]%sptti, sptti, 0, //abstraction_flag, &PHY_vars_UE_g[UE_id][CC_id]->frame_parms, UE_id, CC_id); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_SIM_DO_DL_SIGNAL,0); LOG_D(PHY,"UE %d: DL simulation 6: UE_trx_read @ TS %"PRIi64" (%"PRIi64")=> frame %d, subframe %d\n", UE_id, sim.current_UE_rx_timestamp[UE_id][CC_id], sim.last_UE_rx_timestamp[UE_id][CC_id], (int)((sim.last_UE_rx_timestamp[UE_id][CC_id]/(sptti*10))&1023), subframe); sim.last_UE_rx_timestamp[UE_id][CC_id] += read_size; sample_count += read_size; } VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_SIM_UE_TRX_READ,0); return(nsamps); } int ru_trx_write(openair0_device *device,openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags) { int ru_id = device->Mod_id; LTE_DL_FRAME_PARMS *frame_parms = &RC.ru[ru_id]->frame_parms; pthread_mutex_lock(&sim.subframe_mutex); LOG_D(SIM,"[TXPATH] ru_trx_write: RU %d mask %d\n",ru_id,sim.subframe_ru_mask); pthread_mutex_unlock(&sim.subframe_mutex); // compute amplitude of TX signal from first symbol in subframe // note: assumes that the packet is an entire subframe sim.ru_amp[ru_id] = 0; for (int aa=0; aa<RC.ru[ru_id]->nb_tx; aa++) { sim.ru_amp[ru_id] += (double)signal_energy((int32_t*)buff[aa],frame_parms->ofdm_symbol_size)/(12*frame_parms->N_RB_DL); } sim.ru_amp[ru_id] = sqrt(sim.ru_amp[ru_id]); LOG_D(PHY,"Setting amp for RU %d to %f (%d)\n",ru_id,sim.ru_amp[ru_id], dB_fixed((double)signal_energy((int32_t*)buff[0],frame_parms->ofdm_symbol_size))); // tell top-level we are done pthread_mutex_lock(&sim.subframe_mutex); sim.subframe_ru_mask|=(1<<ru_id); LOG_D(SIM,"Setting RU %d to busy\n",ru_id); pthread_mutex_unlock(&sim.subframe_mutex); return(nsamps); } int UE_trx_write(openair0_device *device,openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags) { return(nsamps); } void init_ru_devices(){ module_id_t ru_id; RU_t *ru; // allocate memory for RU if not already done if (RC.ru==NULL) RC.ru = (RU_t**)malloc(RC.nb_RU*sizeof(RU_t*)); for (ru_id=0;ru_id<RC.nb_RU;ru_id++) { LOG_D(SIM,"Initiaizing rfdevice for RU %d\n",ru_id); if (RC.ru[ru_id]==NULL) RC.ru[ru_id] = (RU_t*)malloc(sizeof(RU_t)); ru = RC.ru[ru_id]; ru->rfdevice.Mod_id = ru_id; ru->rfdevice.CC_id = 0; ru->rfdevice.trx_start_func = ru_trx_start; ru->rfdevice.trx_read_func = ru_trx_read; ru->rfdevice.trx_write_func = ru_trx_write; ru->rfdevice.trx_end_func = ru_trx_end; ru->rfdevice.trx_stop_func = ru_trx_stop; ru->rfdevice.trx_set_freq_func = ru_trx_set_freq; ru->rfdevice.trx_set_gains_func = ru_trx_set_gains; sim.last_ru_rx_timestamp[ru_id][0] = 0; } } void init_ue_devices(PHY_VARS_UE *UE) { AssertFatal(UE!=NULL,"UE context is not allocated\n"); printf("Initializing UE %d.%d\n",UE->Mod_id,UE->CC_id); UE->rfdevice.Mod_id = UE->Mod_id; UE->rfdevice.CC_id = UE->CC_id; UE->rfdevice.trx_start_func = UE_trx_start; UE->rfdevice.trx_read_func = UE_trx_read; UE->rfdevice.trx_write_func = UE_trx_write; UE->rfdevice.trx_end_func = UE_trx_end; UE->rfdevice.trx_stop_func = UE_trx_stop; UE->rfdevice.trx_set_freq_func = UE_trx_set_freq; UE->rfdevice.trx_set_gains_func = UE_trx_set_gains; sim.last_UE_rx_timestamp[UE->Mod_id][UE->CC_id] = 0; } void init_ocm(double snr_dB,double sinr_dB) { module_id_t UE_id, ru_id; int CC_id; randominit(0); set_taus_seed(0); init_channel_vars ();//fp, &s_re, &s_im, &r_re, &r_im, &r_re0, &r_im0); // initialize channel descriptors LOG_I(PHY,"Initializing channel descriptors (nb_RU %d, nb_UE %d)\n",RC.nb_RU,NB_UE_INST); for (ru_id = 0; ru_id < RC.nb_RU; ru_id++) { for (UE_id = 0; UE_id < NB_UE_INST; UE_id++) { for (CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) { LOG_I(PHY,"Initializing channel descriptors (RU %d, UE %d) for N_RB_DL %d\n",ru_id,UE_id, RC.ru[ru_id]->frame_parms.N_RB_DL); sim.RU2UE[ru_id][UE_id][CC_id] = new_channel_desc_scm(RC.ru[ru_id]->nb_tx, PHY_vars_UE_g[UE_id][CC_id]->frame_parms.nb_antennas_rx, AWGN, N_RB2sampling_rate(RC.ru[ru_id]->frame_parms.N_RB_DL), N_RB2channel_bandwidth(RC.ru[ru_id]->frame_parms.N_RB_DL), 0.0, 0, 0); random_channel(sim.RU2UE[ru_id][UE_id][CC_id],0); LOG_D(OCM,"[SIM] Initializing channel (%s) from UE %d to ru %d\n", "AWGN",UE_id, ru_id); sim.UE2RU[UE_id][ru_id][CC_id] = new_channel_desc_scm(PHY_vars_UE_g[UE_id][CC_id]->frame_parms.nb_antennas_tx, RC.ru[ru_id]->nb_rx, AWGN, N_RB2sampling_rate(RC.ru[ru_id]->frame_parms.N_RB_UL), N_RB2channel_bandwidth(RC.ru[ru_id]->frame_parms.N_RB_UL), 0.0, 0, 0); random_channel(sim.UE2RU[UE_id][ru_id][CC_id],0); // to make channel reciprocal uncomment following line instead of previous. However this only works for SISO at the moment. For MIMO the channel would need to be transposed. //UE2RU[UE_id][ru_id] = RU2UE[ru_id][UE_id]; AssertFatal(sim.RU2UE[ru_id][UE_id][CC_id]!=NULL,"RU2UE[%d][%d][%d] is null\n",ru_id,UE_id,CC_id); AssertFatal(sim.UE2RU[UE_id][ru_id][CC_id]!=NULL,"UE2RU[%d][%d][%d] is null\n",UE_id,ru_id,CC_id); //pathloss: -132.24 dBm/15kHz RE + target SNR - eNB TX power per RE if (ru_id == (UE_id % RC.nb_RU)) { sim.RU2UE[ru_id][UE_id][CC_id]->path_loss_dB = -132.24 + snr_dB - RC.ru[ru_id]->frame_parms.pdsch_config_common.referenceSignalPower; sim.UE2RU[UE_id][ru_id][CC_id]->path_loss_dB = -132.24 + snr_dB - RC.ru[ru_id]->frame_parms.pdsch_config_common.referenceSignalPower; } else { sim.RU2UE[ru_id][UE_id][CC_id]->path_loss_dB = -132.24 + sinr_dB - RC.ru[ru_id]->frame_parms.pdsch_config_common.referenceSignalPower; sim.UE2RU[UE_id][ru_id][CC_id]->path_loss_dB = -132.24 + sinr_dB - RC.ru[ru_id]->frame_parms.pdsch_config_common.referenceSignalPower; } LOG_I(OCM,"Path loss from eNB %d to UE %d (CCid %d)=> %f dB (eNB TX %d, SNR %f)\n",ru_id,UE_id,CC_id, sim.RU2UE[ru_id][UE_id][CC_id]->path_loss_dB, RC.ru[ru_id]->frame_parms.pdsch_config_common.referenceSignalPower,snr_dB); } } } } void update_ocm(double snr_dB,double sinr_dB) { module_id_t UE_id, ru_id; int CC_id; for (ru_id = 0; ru_id < RC.nb_RU; ru_id++) { for (UE_id = 0; UE_id < NB_UE_INST; UE_id++) { for (CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) { AssertFatal(sim.RU2UE[ru_id][UE_id][CC_id]!=NULL,"RU2UE[%d][%d][%d] is null\n",ru_id,UE_id,CC_id); AssertFatal(sim.UE2RU[UE_id][ru_id][CC_id]!=NULL,"UE2RU[%d][%d][%d] is null\n",UE_id,ru_id,CC_id); //pathloss: -132.24 dBm/15kHz RE + target SNR - eNB TX power per RE if (ru_id == (UE_id % RC.nb_RU)) { sim.RU2UE[ru_id][UE_id][CC_id]->path_loss_dB = -132.24 + snr_dB - RC.ru[ru_id]->frame_parms.pdsch_config_common.referenceSignalPower; sim.UE2RU[UE_id][ru_id][CC_id]->path_loss_dB = -132.24 + snr_dB - RC.ru[ru_id]->frame_parms.pdsch_config_common.referenceSignalPower; } else { sim.RU2UE[ru_id][UE_id][CC_id]->path_loss_dB = -132.24 + sinr_dB - RC.ru[ru_id]->frame_parms.pdsch_config_common.referenceSignalPower; sim.UE2RU[UE_id][ru_id][CC_id]->path_loss_dB = -132.24 + sinr_dB - RC.ru[ru_id]->frame_parms.pdsch_config_common.referenceSignalPower; } LOG_D(OCM,"Path loss from eNB %d to UE %d (CCid %d)=> %f dB (eNB TX %d, SNR %f)\n",ru_id,UE_id,CC_id, sim.RU2UE[ru_id][UE_id][CC_id]->path_loss_dB, RC.ru[ru_id]->frame_parms.pdsch_config_common.referenceSignalPower,snr_dB); } } } } void init_channel_vars(void) { int i; memset(sim.RU_output_mask,0,sizeof(int)*NUMBER_OF_UE_MAX); for (i=0;i<NB_UE_INST;i++) pthread_mutex_init(&sim.RU_output_mutex[i],NULL); memset(sim.UE_output_mask,0,sizeof(int)*NUMBER_OF_RU_MAX); for (i=0;i<RC.nb_RU;i++) pthread_mutex_init(&sim.UE_output_mutex[i],NULL); } void *rfsim_top(void *n_frames) { wait_sync("rfsim_top"); printf("Running rfsim with %d frames\n",*(int*)n_frames); for (int frame = 0; frame < *(int*)n_frames; frame++) { for (int sf = 0; sf < 10; sf++) { int CC_id=0; int all_done=0; while (all_done==0) { pthread_mutex_lock(&sim.subframe_mutex); int subframe_ru_mask_local = (subframe_select(&RC.ru[0]->frame_parms,(sf+4)%10)!=SF_UL) ? sim.subframe_ru_mask : ((1<<RC.nb_RU)-1); int subframe_UE_mask_local = (RC.ru[0]->frame_parms.frame_type == FDD || subframe_select(&RC.ru[0]->frame_parms,(sf+4)%10)!=SF_DL) ? sim.subframe_UE_mask : ((1<<NB_UE_INST)-1); pthread_mutex_unlock(&sim.subframe_mutex); LOG_D(SIM,"Frame %d, Subframe %d, NB_RU %d, NB_UE %d: Checking masks %x,%x\n",frame,sf,RC.nb_RU,NB_UE_INST,subframe_ru_mask_local,subframe_UE_mask_local); if ((subframe_ru_mask_local == ((1<<RC.nb_RU)-1)) && (subframe_UE_mask_local == ((1<<NB_UE_INST)-1))) all_done=1; else usleep(1500); } //clear subframe masks for next round pthread_mutex_lock(&sim.subframe_mutex); sim.subframe_ru_mask=0; sim.subframe_UE_mask=0; pthread_mutex_unlock(&sim.subframe_mutex); // increment timestamps for (int ru_id=0;ru_id<RC.nb_RU;ru_id++) { sim.current_ru_rx_timestamp[ru_id][CC_id] += RC.ru[ru_id]->frame_parms.samples_per_tti; LOG_D(SIM,"RU %d/%d: TS %"PRIi64"\n",ru_id,CC_id,sim.current_ru_rx_timestamp[ru_id][CC_id]); } for (int UE_inst = 0; UE_inst<NB_UE_INST;UE_inst++) { sim.current_UE_rx_timestamp[UE_inst][CC_id] += PHY_vars_UE_g[UE_inst][CC_id]->frame_parms.samples_per_tti; LOG_D(SIM,"UE %d/%d: TS %"PRIi64"\n",UE_inst,CC_id,sim.current_UE_rx_timestamp[UE_inst][CC_id]); } if (oai_exit == 1) return((void*)NULL); } } return((void*)NULL); }