From 2fb541a68ef788c104f92faec5dab397c221a634 Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Mon, 27 Jan 2025 17:39:19 +0100 Subject: [PATCH] Add option to use global noise power for all RX channels in RFSimulator Added new command line option to channelmod: noise_power_dBFS. This options allows the user to configure noise power in dBFS added instead of the per-channel value. This makes it so noise is not accumulated per channel reaching higher than expected values. The dBFS unit allows easy configuration. The gNB by default uses -36 dBFS setting. To reach 3dB SNR one should set the noise_power_dBFS value to -39. To configure using command line use --channelmod.noise_power_dBFS -50. Using this setting the gNB and nrUE connect without issue. --- .../TOOLS/DOC/channel_simulation.md | 1 + openair1/SIMULATION/TOOLS/random_channel.c | 5 +++ openair1/SIMULATION/TOOLS/sim.h | 6 ++++ radio/rfsimulator/apply_channelmod.c | 13 +++---- radio/rfsimulator/rfsimulator.h | 6 ++-- radio/rfsimulator/simulator.c | 34 +++++++++++++++++-- 6 files changed, 54 insertions(+), 11 deletions(-) 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 14be663d49..ecf278ffd9 100644 --- a/radio/rfsimulator/simulator.c +++ b/radio/rfsimulator/simulator.c @@ -1006,7 +1006,9 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest // Clear the output buffer for (int a=0; abuf[sock]; @@ -1024,12 +1026,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 @@ -1063,6 +1071,26 @@ 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)); + } + } + } *ptimestamp = t->nextRxTstamp; // return the time of the first sample t->nextRxTstamp+=nsamps;