diff --git a/openair1/SIMULATION/TOOLS/DOC/channel_simulation.md b/openair1/SIMULATION/TOOLS/DOC/channel_simulation.md index 070ee61ca0..30cc68e662 100644 --- a/openair1/SIMULATION/TOOLS/DOC/channel_simulation.md +++ b/openair1/SIMULATION/TOOLS/DOC/channel_simulation.md @@ -142,6 +142,7 @@ Channel simulation parameters can also be specified on the command line by using |:--- |:---- |:---- |:----| |`modellist` |char string |`DefaultChannelList`|select and load the `modellist` from the config file.| |`max_chan` |integer |10 |set the maximum number of channel models that can be defined in the system. Must be greater than the number of model definitions in the model list loaded at init time.| +|`noise_power_dBFS` |integer |0 |Noise power in dBFS. If set, noise per channel is not applied. To achieve positive SNR use values below the default gNB/nrUE amp backoff value (-36dBFS)| Example usage: ```bash diff --git a/openair1/SIMULATION/TOOLS/random_channel.c b/openair1/SIMULATION/TOOLS/random_channel.c index 4c715498a8..b9ae9e3fff 100644 --- a/openair1/SIMULATION/TOOLS/random_channel.c +++ b/openair1/SIMULATION/TOOLS/random_channel.c @@ -73,6 +73,7 @@ static telnetshell_vardef_t channelmod_vardef[] = {{"", 0, 0, NULL}}; static unsigned int max_chan; static channel_desc_t **defined_channels; static char *modellist_name; +static int noise_power_dBFS = INVALID_DBFS_VALUE; void fill_channel_desc(channel_desc_t *chan_desc, uint8_t nb_tx, @@ -2365,6 +2366,10 @@ int load_channellist(uint8_t nb_tx, uint8_t nb_rx, double sampling_rate, uint64_ return channel_list.numelt; } /* load_channelist */ +int get_noise_power_dBFS(void) { + return noise_power_dBFS; +} + #ifdef RANDOM_CHANNEL_MAIN #define sampling_rate 5.0 #define Td 2.0 diff --git a/openair1/SIMULATION/TOOLS/sim.h b/openair1/SIMULATION/TOOLS/sim.h index 8297d9c365..d6b20a9eb9 100644 --- a/openair1/SIMULATION/TOOLS/sim.h +++ b/openair1/SIMULATION/TOOLS/sim.h @@ -278,10 +278,15 @@ typedef enum { #define CHANNELMOD_MODELLIST_PARANAME "modellist" #define CHANNELMOD_HELP_MODELLIST " channel list name in config file describing the model type and its parameters\n" +#define CHANNELMOD_HELP_NOISE_POWER \ + "Noise power in dBFS. If set, noise per channel is not applied. To achieve positive SNR use values below -36dBFS\n" + +#define INVALID_DBFS_VALUE 100 // clang-format off #define CHANNELMOD_PARAMS_DESC { \ {"max_chan", "Max number of runtime models", 0, .uptr=&max_chan, .defintval=10, TYPE_UINT, 0}, \ {CHANNELMOD_MODELLIST_PARANAME, CHANNELMOD_HELP_MODELLIST, 0, .strptr=&modellist_name, .defstrval="DefaultChannelList", TYPE_STRING, 0}, \ + {"noise_power_dBFS", CHANNELMOD_HELP_NOISE_POWER, 0, .iptr=&noise_power_dBFS, .defintval=INVALID_DBFS_VALUE, TYPE_INT, 0 },\ } // clang-format on @@ -585,5 +590,6 @@ void do_DL_sig(sim_t *sim, int CC_id); void do_UL_sig(sim_t *sim, uint16_t subframe, uint8_t abstraction_flag, LTE_DL_FRAME_PARMS *frame_parms, uint32_t frame, int ru_id, uint8_t CC_id, int NB_UEs); +int get_noise_power_dBFS(void); #endif diff --git a/radio/rfsimulator/apply_channelmod.c b/radio/rfsimulator/apply_channelmod.c index cb8e75abdf..60d3afbe83 100644 --- a/radio/rfsimulator/apply_channelmod.c +++ b/radio/rfsimulator/apply_channelmod.c @@ -42,12 +42,13 @@ legacy: we regenerate each sub frame in UL, and each frame only in DL */ void rxAddInput(const c16_t *input_sig, - c16_t *after_channel_sig, + cf_t *after_channel_sig, int rxAnt, channel_desc_t *channelDesc, int nbSamples, uint64_t TS, - uint32_t CirSize) + uint32_t CirSize, + bool add_noise) { if ((channelDesc->sat_height > 0) && (channelDesc->enable_dynamic_delay || channelDesc->enable_dynamic_Doppler)) { // model for transparent satellite on circular orbit /* assumptions: @@ -127,12 +128,12 @@ void rxAddInput(const c16_t *input_sig, const double pathLossLinear = pow(10,channelDesc->path_loss_dB/20.0); // Energy in one sample to calibrate input noise // the normalized OAI value seems to be 256 as average amplitude (numerical amplification = 1) - const double noise_per_sample = pow(10,channelDesc->noise_power_dB/10.0) * 256; + const double noise_per_sample = add_noise ? pow(10,channelDesc->noise_power_dB/10.0) * 256 : 0; const uint64_t dd = channelDesc->channel_offset; const int nbTx=channelDesc->nb_tx; for (int i=0; iDoppler_phase_cur[rxAnt] += channelDesc->Doppler_phase_inc; } - out_ptr->r = lround(rx_tmp.r*pathLossLinear + noise_per_sample*gaussZiggurat(0.0,1.0)); - out_ptr->i = lround(rx_tmp.i*pathLossLinear + noise_per_sample*gaussZiggurat(0.0,1.0)); + out_ptr->r += rx_tmp.r * pathLossLinear + noise_per_sample * gaussZiggurat(0.0, 1.0); + out_ptr->i += rx_tmp.i * pathLossLinear + noise_per_sample * gaussZiggurat(0.0, 1.0); out_ptr++; } diff --git a/radio/rfsimulator/rfsimulator.h b/radio/rfsimulator/rfsimulator.h index 068250a149..11a57193ca 100644 --- a/radio/rfsimulator/rfsimulator.h +++ b/radio/rfsimulator/rfsimulator.h @@ -23,13 +23,15 @@ #ifndef __RFSIMULATOR_H #define __RFSIMULATOR_H +#include void rxAddInput( const c16_t *input_sig, - c16_t *after_channel_sig, + cf_t *after_channel_sig, int rxAnt, channel_desc_t *channelDesc, int nbSamples, uint64_t TS, - uint32_t CirSize + uint32_t CirSize, + bool apply_noise ); #endif diff --git a/radio/rfsimulator/simulator.c b/radio/rfsimulator/simulator.c index 262cb87deb..c733f4c8a2 100644 --- a/radio/rfsimulator/simulator.c +++ b/radio/rfsimulator/simulator.c @@ -177,7 +177,6 @@ typedef struct { double chan_pathloss; double chan_forgetfact; uint64_t chan_offset; - float noise_power_dB; void *telnetcmd_qid; poll_telnetcmdq_func_t poll_telnetcmdq; int wait_timeout; @@ -456,7 +455,7 @@ static int rfsimu_setchanmod_cmd(char *buff, int debug, telnet_printfunc_t prnt, t->chan_forgetfact, // forgetting_factor t->chan_offset, // propagation delay in samples t->chan_pathloss, - t->noise_power_dB); // path_loss in dB + 0); // noise_power set_channeldesc_owner(newmodel, RFSIMU_MODULEID); set_channeldesc_name(newmodel,modelname); random_channel(newmodel,false); @@ -1004,10 +1003,17 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest } while (have_to_wait); } + + struct timespec start_time; + int ret = clock_gettime(CLOCK_REALTIME, &start_time); + AssertFatal(ret == 0, "clock_gettime() failed: errno %d, %s\n", errno, strerror(errno)); + // Clear the output buffer for (int a=0; abuf[sock]; @@ -1025,12 +1031,18 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest for (int a=0; achannel_model != NULL ) { // apply a channel model - rxAddInput(ptr->circularBuf, (c16_t *) samplesVoid[a], + if (num_chanmod_channels == 0) { + memset(temp_array, 0, sizeof(temp_array)); + } + num_chanmod_channels++; + rxAddInput(ptr->circularBuf, + temp_array[a], a, ptr->channel_model, nsamps, t->nextRxTstamp, - CirSize); + CirSize, + apply_noise_per_channel); } else { // no channel modeling int nbAnt_tx = ptr->th.nbAnt; // number of Tx antennas @@ -1064,6 +1076,37 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest } // end for a (number of rx antennas) } } + if (apply_noise_per_channel && num_chanmod_channels > 0) { + // Noise is already applied through the channel model + for (int a = 0; a < nbAnt; a++) { + sample_t *out = (sample_t *)samplesVoid[a]; + for (int i = 0; i < nsamps; i++) { + out[i].r += lroundf(temp_array[a][i].r); + out[i].i += lroundf(temp_array[a][i].i); + } + } + } else if (num_chanmod_channels > 0) { + // Apply noise from global setting + int16_t noise_power = (int16_t)(32767.0 / powf(10.0, .05 * -get_noise_power_dBFS())); + for (int a = 0; a < nbAnt; a++) { + sample_t *out = (sample_t *)samplesVoid[a]; + for (int i = 0; i < nsamps; i++) { + out[i].r += lroundf(temp_array[a][i].r + noise_power * gaussZiggurat(0.0, 1.0)); + out[i].i += lroundf(temp_array[a][i].i + noise_power * gaussZiggurat(0.0, 1.0)); + } + } + } + + struct timespec end_time; + ret = clock_gettime(CLOCK_REALTIME, &end_time); + AssertFatal(ret == 0, "clock_gettime() failed: errno %d, %s\n", errno, strerror(errno)); + double diff_ns = (end_time.tv_sec - start_time.tv_sec) * 1000000000 + (end_time.tv_nsec - start_time.tv_nsec); + static double average = 0.0; + average = (average * 0.98) + ( nsamps / (diff_ns / 1e9) * 0.02); + static int calls = 0; + if (calls++ % 10000 == 0) { + LOG_D(HW, "Rfsimulator: velocity %.2f Msps, realtime requirements %.2f Msps\n", average / 1e6, t->sample_rate / 1e6); + } *ptimestamp = t->nextRxTstamp; // return the time of the first sample t->nextRxTstamp+=nsamps;