diff --git a/common/config/config_common.c b/common/config/config_common.c index 5559cc2076..45edcac6df 100644 --- a/common/config/config_common.c +++ b/common/config/config_common.c @@ -141,7 +141,7 @@ int config_setdefault_int64(configmodule_interface_t *cfg, paramdef_t *cfgoption config_check_valptr(cfg, cfgoptions, sizeof(*cfgoptions->i64ptr), 1); if (((cfgoptions->paramflags & PARAMFLAG_MANDATORY) == 0)) { - *(cfgoptions->u64ptr) = cfgoptions->defuintval; + *(cfgoptions->u64ptr) = cfgoptions->defint64val; status=1; printf_params(cfg, "[CONFIG] %s.%s set to default value %llu\n", diff --git a/openair1/SIMULATION/TOOLS/multipath_channel.c b/openair1/SIMULATION/TOOLS/multipath_channel.c index 55bc8d70b8..41b756b54d 100644 --- a/openair1/SIMULATION/TOOLS/multipath_channel.c +++ b/openair1/SIMULATION/TOOLS/multipath_channel.c @@ -55,12 +55,12 @@ void __attribute__ ((no_sanitize_address)) multipath_channel(channel_desc_t *des tx128_re, tx128_im, ch128_x, ch128_y, pathloss128; double path_loss = pow(10,desc->path_loss_dB/20); - int dd = abs(desc->channel_offset); + uint64_t dd = desc->channel_offset; pathloss128 = simde_mm_set1_pd(path_loss); #ifdef DEBUG_CH - printf("[CHANNEL] keep = %d : path_loss = %g (%f), nb_rx %d, nb_tx %d, dd %d, len %d \n",keep_channel,path_loss,desc->path_loss_dB,desc->nb_rx,desc->nb_tx,dd,desc->channel_length); + printf("[CHANNEL] keep = %d : path_loss = %g (%f), nb_rx %d, nb_tx %d, dd %lu, len %d \n",keep_channel,path_loss,desc->path_loss_dB,desc->nb_rx,desc->nb_tx,dd,desc->channel_length); #endif if (keep_channel) { @@ -184,11 +184,10 @@ void __attribute__ ((no_sanitize_address)) multipath_channel(channel_desc_t *des { double path_loss = pow(10,desc->path_loss_dB/20); - int dd; - dd = abs(desc->channel_offset); + uint64_t dd = desc->channel_offset; #ifdef DEBUG_CH - printf("[CHANNEL] keep = %d : path_loss = %g (%f), nb_rx %d, nb_tx %d, dd %d, len %d \n", + printf("[CHANNEL] keep = %d : path_loss = %g (%f), nb_rx %d, nb_tx %d, dd %lu, len %d \n", keep_channel, path_loss, desc->path_loss_dB, desc->nb_rx, desc->nb_tx, dd, desc->channel_length); #endif diff --git a/openair1/SIMULATION/TOOLS/multipath_tv_channel.c b/openair1/SIMULATION/TOOLS/multipath_tv_channel.c index ece1f81335..a9967a6eaf 100644 --- a/openair1/SIMULATION/TOOLS/multipath_tv_channel.c +++ b/openair1/SIMULATION/TOOLS/multipath_tv_channel.c @@ -29,7 +29,7 @@ void tv_channel(channel_desc_t *desc,double complex ***H,uint32_t length); double frand_a_b(double a, double b); -void tv_conv(double complex **h, double complex *x, double complex *y, uint32_t nb_samples, uint8_t nb_taps, int delay); +void tv_conv(double complex **h, double complex *x, double complex *y, uint32_t nb_samples, uint8_t nb_taps, uint64_t delay); void multipath_tv_channel(channel_desc_t *desc, double **tx_sig_re, @@ -42,10 +42,10 @@ void multipath_tv_channel(channel_desc_t *desc, double complex **tx,**rx,***H_t; double path_loss = pow(10,desc->path_loss_dB/20); - int i,j,dd; - dd = abs(desc->channel_offset); + int i,j; + uint64_t dd = desc->channel_offset; #ifdef DEBUG_CH - printf("[TV CHANNEL] keep = %d : path_loss = %g (%f), nb_rx %d, nb_tx %d, dd %d, len %d max_doppler %g\n",keep_channel,path_loss,desc->path_loss_dB,desc->nb_rx,desc->nb_tx,dd,desc->channel_length, + printf("[TV CHANNEL] keep = %d : path_loss = %g (%f), nb_rx %d, nb_tx %d, dd %lu, len %d max_doppler %g\n",keep_channel,path_loss,desc->path_loss_dB,desc->nb_rx,desc->nb_tx,dd,desc->channel_length, desc->max_Doppler); #endif tx = (double complex **)malloc(desc->nb_tx*sizeof(double complex *)); @@ -190,7 +190,7 @@ void tv_channel(channel_desc_t *desc,double complex ***H,uint32_t length){ } // time varying convolution -void tv_conv(double complex **h, double complex *x, double complex *y, uint32_t nb_samples, uint8_t nb_taps, int dd) +void tv_conv(double complex **h, double complex *x, double complex *y, uint32_t nb_samples, uint8_t nb_taps, uint64_t dd) { for(int i = 0; i < ((int)nb_samples-dd); i++) { for(int j = 0; j < nb_taps; j++) { diff --git a/openair1/SIMULATION/TOOLS/random_channel.c b/openair1/SIMULATION/TOOLS/random_channel.c index 8fc8604696..e9c26e9abd 100644 --- a/openair1/SIMULATION/TOOLS/random_channel.c +++ b/openair1/SIMULATION/TOOLS/random_channel.c @@ -76,7 +76,6 @@ static unsigned int max_chan; static channel_desc_t **defined_channels; static char *modellist_name; - void fill_channel_desc(channel_desc_t *chan_desc, uint8_t nb_tx, uint8_t nb_rx, @@ -92,9 +91,10 @@ void fill_channel_desc(channel_desc_t *chan_desc, double aoa, double forgetting_factor, double max_Doppler, - int32_t channel_offset, + uint64_t channel_offset, double path_loss_dB, - uint8_t random_aoa) { + uint8_t random_aoa) +{ uint16_t i,j; double delta_tau; LOG_I(OCM,"[CHANNEL] Getting new channel descriptor, nb_tx %d, nb_rx %d, nb_taps %d, channel_length %d\n", @@ -568,7 +568,7 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx, double maxDoppler, const corr_level_t corr_level, double forgetting_factor, - int32_t channel_offset, + uint64_t channel_offset, double path_loss_dB, float noise_power_dB) { @@ -2063,8 +2063,12 @@ static void display_channelmodel(channel_desc_t *cd,int debug, telnet_printfunc_ prnt("nb_tx: %i nb_rx: %i taps: %i bandwidth: %lf sampling: %lf\n",cd->nb_tx, cd->nb_rx, cd->nb_taps, cd->channel_bandwidth, cd->sampling_rate); prnt("channel length: %i Max path delay: %lf ricean fact.: %lf angle of arrival: %lf (randomized:%s)\n", cd->channel_length, cd->Td, cd->ricean_factor, cd->aoa, (cd->random_aoa?"Yes":"No")); - prnt("max Doppler: %lf path loss: %lf noise: %lf rchannel offset: %i forget factor; %lf\n", - cd->max_Doppler, cd->path_loss_dB, cd->noise_power_dB, cd->channel_offset, cd->forgetting_factor); + prnt("max Doppler: %lf path loss: %lf noise: %lf rchannel offset: %lu forget factor; %lf\n", + cd->max_Doppler, + cd->path_loss_dB, + cd->noise_power_dB, + cd->channel_offset, + cd->forgetting_factor); prnt("Initial phase: %lf nb_path: %i \n", cd->ip, cd->nb_paths); diff --git a/openair1/SIMULATION/TOOLS/sim.h b/openair1/SIMULATION/TOOLS/sim.h index 33c3f0f9ee..3b55e7c4e5 100644 --- a/openair1/SIMULATION/TOOLS/sim.h +++ b/openair1/SIMULATION/TOOLS/sim.h @@ -101,7 +101,7 @@ typedef struct { ///path loss including shadow fading in dB double path_loss_dB; ///additional delay of channel in samples. - int32_t channel_offset; + uint64_t channel_offset; float noise_power_dB; ///This parameter (0...1) allows for simple 1st order temporal variation. 0 means a new channel every call, 1 means keep channel constant all the time double forgetting_factor; @@ -307,7 +307,6 @@ typedef struct { double ru_amp[NUMBER_OF_RU_MAX]; } sim_t; - channel_desc_t *new_channel_desc_scm(uint8_t nb_tx, uint8_t nb_rx, SCM_t channel_model, @@ -318,7 +317,7 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx, double maxDoppler, const corr_level_t corr_level, double forgetting_factor, - int32_t channel_offset, + uint64_t channel_offset, double path_loss_dB, float noise_power_dB); diff --git a/radio/rfsimulator/README.md b/radio/rfsimulator/README.md index 376255d282..679b922d2d 100644 --- a/radio/rfsimulator/README.md +++ b/radio/rfsimulator/README.md @@ -73,10 +73,11 @@ The RF simulator is using the configuration module, and its parameters are defin |:--------------------- |:-------------------------------------------------------------------------------|----: | |`--rfsimulator.serveraddr `| ip address to connect to, or `server` to behave as a tcp server | 127.0.0.1 | |`--rfsimulator.serverport `| port number to connect to or to listen on (eNB, which behaves as a tcp server) | 4043 | -| `--rfsimulator.options` | list of comma separated run-time options, two are supported: `chanmod`, `saviq`| all options disabled | -| `--rfsimulator.options saviq` | store IQs to a file for future replay | disabled | +|`--rfsimulator.options` | list of comma separated run-time options, two are supported: `chanmod`, `saviq`| all options disabled | +|`--rfsimulator.options saviq` | store IQs to a file for future replay | disabled | |`--rfsimulator.options chanmod` | enable the channel model | disabled | |`--rfsimulator.IQfile ` | path to a file to store the IQ samples to (only with `saviq`) | `/tmp/rfsimulator.iqs` | +|`--rfsimulator.prop_delay` | simulated receive-path (gNB: UL, UE: DL) propagation delay in ms | 0 | |`--rfsimulator.wait_timeout` | wait timeout when no UE is connected | 1 | Please refer to this document [`SIMULATION/TOOLS/DOC/channel_simulation.md`](../../openair1/SIMULATION/TOOLS/DOC/channel_simulation.md) for information about using the RFSimulator options to run the simulator with a channel model. @@ -152,6 +153,24 @@ The format intends to be compatible with the OAI store/replay feature on USRP. Please refer to this document [`channel_simulation.md`](../../openair1/SIMULATION/TOOLS/DOC/channel_simulation.md) to get familiar with channel simulation in RFSIM and to see the list of commands for real-time usage with telnet. +## How to simulate a simple GEO satellite channel model + +A simple channel model for satellites on a geostationary orbit (GEO) simulates simply one line-of-sight propagation channel. + +The most basic version is to simply simulate a constant propagation delay, without any other effects. + +In case of a transparent GEO satellite, the minumum one-way propagation delay (DL: gNB -> satellite -> UE, or UL: UE -> satellite -> gNB) is 238.74 ms. + +So, additionally to other parameters, this parameter should be given when executing the gNB and the UE executables: + +``` +--rfsimulator.prop_delay 238.74 +``` + +Note: + +To successfully establish a connection with such a GEO satellite channel, both gNB and UE need to have the NTN support configured. + # Caveats There are issues in power control: txgain/rxgain setting is not supported. diff --git a/radio/rfsimulator/apply_channelmod.c b/radio/rfsimulator/apply_channelmod.c index 18ab7e29e9..917a22924c 100644 --- a/radio/rfsimulator/apply_channelmod.c +++ b/radio/rfsimulator/apply_channelmod.c @@ -68,7 +68,7 @@ void rxAddInput( const c16_t *input_sig, // 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 int dd = abs(channelDesc->channel_offset); + const uint64_t dd = channelDesc->channel_offset; const int nbTx=channelDesc->nb_tx; for (int i=0; i\n", simOpt, .strptr=&modelname, .defstrval="AWGN", TYPE_STRING, 0 },\ {"ploss", "\n", simOpt, .dblptr=&(rfsimulator->chan_pathloss), .defdblval=0, TYPE_DOUBLE, 0 },\ {"forgetfact", "\n", simOpt, .dblptr=&(rfsimulator->chan_forgetfact),.defdblval=0, TYPE_DOUBLE, 0 },\ - {"offset", "\n", simOpt, .iptr=&(rfsimulator->chan_offset), .defintval=0, TYPE_INT, 0 },\ + {"offset", "\n", simOpt, .u64ptr=&(rfsimulator->chan_offset), .defint64val=0, TYPE_UINT64, 0 },\ + {"prop_delay", "\n", simOpt, .dblptr=&(rfsimulator->prop_delay_ms), .defdblval=0.0, TYPE_DOUBLE, 0 },\ {"wait_timeout", "\n", simOpt, .iptr=&(rfsimulator->wait_timeout), .defintval=1, TYPE_INT, 0 },\ }; @@ -138,6 +139,8 @@ static telnetshell_vardef_t rfsimu_vardef[] = {{"", 0, 0, NULL}}; pthread_mutex_t Sockmutex; unsigned int nb_ue = 0; +static uint64_t CirSize = minCirSize; + typedef c16_t sample_t; // 2*16 bits complex number typedef struct buffer_s { @@ -169,11 +172,12 @@ typedef struct { int channelmod; double chan_pathloss; double chan_forgetfact; - int chan_offset; + uint64_t chan_offset; float noise_power_dB; void *telnetcmd_qid; poll_telnetcmdq_func_t poll_telnetcmdq; int wait_timeout; + double prop_delay_ms; } rfsimulator_state_t; static int allocCirBuf(rfsimulator_state_t *bridge, int sock) @@ -403,7 +407,7 @@ static int rfsimu_setchanmod_cmd(char *buff, int debug, telnet_printfunc_t prnt, 0.0, CORR_LEVEL_LOW, t->chan_forgetfact, // forgetting_factor - t->chan_offset, // maybe used for TA + t->chan_offset, // propagation delay in samples t->chan_pathloss, t->noise_power_dB); // path_loss in dB set_channeldesc_owner(newmodel, RFSIMU_MODULEID); @@ -460,60 +464,6 @@ static void getset_currentchannels_type(char *buf, int debug, webdatadef_t *tdat // printf("\n"); //} -static void rfsimu_offset_change_cirBuf(struct complex16 *circularBuf, - uint64_t firstSample, - uint32_t cirSize, - int old_offset, - int new_offset, - int nbTx) -{ - //int start = max(new_offset, old_offset) + 10; - //int end = 10; - //printf("new_offset %d old_offset %d start %d end %d\n", new_offset, old_offset, start, end); - //printf("ringbuffer before:\n"); - //print_cirBuf(circularBuf, firstSample, cirSize, start, end, nbTx); - - int doffset = new_offset - old_offset; - if (doffset > 0) { - /* Moving away, creating a gap. We need to insert "zero" samples between - * the previous (end of the) slot and the new slot (at the ringbuffer - * index) to prevent that the receiving side detects things that are not - * in the channel (e.g., samples that have already been delivered). */ - for (int i = new_offset; i > 0; --i) { - for (int txAnt = 0; txAnt < nbTx; txAnt++) { - const int newidx = ((firstSample - i) * nbTx + txAnt + cirSize) % cirSize; - if (i > doffset) { - // shift samples not read yet - const int oldidx = (newidx + doffset) % cirSize; - circularBuf[newidx] = circularBuf[oldidx]; - } else { - // create zero samples between slots - const struct complex16 nullsample = {0, 0}; - circularBuf[newidx] = nullsample; - } - } - } - } else { - /* Moving closer, creating overlap between samples. For simplicity, we - * simply drop `doffset` samples at the end of the previous slot - * (this is, in a sense, arbitrary). In a real channel, there would be - * some overlap between samples, e.g., for `doffset == 1` we could add - * two samples. I think that we cannot do that for multiple samples, - * though, and so we just drop some */ - // drop the last -doffset samples of the previous slot - for (int i = old_offset; i > -doffset; --i) { - for (int txAnt = 0; txAnt < nbTx; txAnt++) { - const int oldidx = ((firstSample - i) * nbTx + txAnt + cirSize) % cirSize; - const int newidx = (oldidx - doffset) % cirSize; - circularBuf[newidx] = circularBuf[oldidx]; - } - } - } - - //printf("ringbuffer after:\n"); - //print_cirBuf(circularBuf, firstSample, cirSize, start, end, nbTx); -} - static int rfsimu_setdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg) { if (debug) @@ -531,10 +481,13 @@ static int rfsimu_setdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt const double sample_rate = t->sample_rate; const double c = 299792458; /* 3e8 */ - const int new_offset = (double) distance * sample_rate / c; + const uint64_t new_offset = (double) distance * sample_rate / c; const double new_distance = (double) new_offset * c / sample_rate; + const double new_delay_ms = new_offset * 1000.0 / sample_rate; - prnt("\n%s: new_offset %d new (exact) distance %.3f m\n", __func__, new_offset, new_distance); + prnt("\n%s: new_offset %lu, new (exact) distance %.3f m, new delay %f ms\n", __func__, new_offset, new_distance, new_delay_ms); + t->prop_delay_ms = new_delay_ms; + t->chan_offset = new_offset; /* Set distance in rfsim and channel model, update channel and ringbuffer */ for (int i = 0; i < MAX_FD_RFSIMU; i++) { @@ -546,12 +499,7 @@ static int rfsimu_setdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt } channel_desc_t *cd = b->channel_model; - const int old_offset = cd->channel_offset; cd->channel_offset = new_offset; - - const int nbTx = cd->nb_tx; - prnt(" %s: Modifying model %s...\n", __func__, modelname); - rfsimu_offset_change_cirBuf(b->circularBuf, t->nextRxTstamp, CirSize, old_offset, new_offset, nbTx); } free(modelname); @@ -574,10 +522,11 @@ static int rfsimu_getdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt continue; channel_desc_t *cd = b->channel_model; - const int offset = cd->channel_offset; + const uint64_t offset = cd->channel_offset; const double distance = (double) offset * c / sample_rate; - prnt("%s: \%s offset %d distance %.3f m\n", __func__, cd->model_name, offset, distance); + prnt("%s: %s offset %lu distance %.3f m\n", __func__, cd->model_name, offset, distance); } + prnt("%s: offset %lu delay %f ms\n", __func__, t->chan_offset, t->prop_delay_ms); return CMDSTATUS_FOUND; } @@ -726,9 +675,16 @@ static int rfsimulator_write_internal(rfsimulator_state_t *t, openair0_timestamp if ( t->lastWroteTS != 0 && fabs((double)t->lastWroteTS-timestamp) > (double)CirSize) LOG_W(HW, "Discontinuous TX gap too large Tx:%lu, %lu\n", t->lastWroteTS, timestamp); - if (t->lastWroteTS > timestamp+nsamps) + if (t->lastWroteTS > timestamp) LOG_W(HW, "Not supported to send Tx out of order %lu, %lu\n", t->lastWroteTS, timestamp); + if ((flags != TX_BURST_START) && (flags != TX_BURST_START_AND_END) && (t->lastWroteTS < timestamp)) + LOG_W(HW, + "Gap in writing to USRP: last written %lu, now %lu, gap %lu\n", + t->lastWroteTS, + timestamp, + timestamp - t->lastWroteTS); + t->lastWroteTS=timestamp+nsamps; if (!alreadyLocked) @@ -983,16 +939,16 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest } else { // no channel modeling int nbAnt_tx = ptr->th.nbAnt; // number of Tx antennas + int firstIndex = (CirSize + t->nextRxTstamp - t->chan_offset) % CirSize; + sample_t *out = (sample_t *)samplesVoid[a]; if ((nbAnt_tx == 1) && ((nb_ue == 1) || (t->role == SIMU_ROLE_CLIENT))) { // optimized for 1 Tx and 1 UE - sample_t *out = (sample_t *)samplesVoid[a]; - int firstIndex = t->nextRxTstamp % CirSize; sample_t *firstSample = (sample_t *)&(ptr->circularBuf[firstIndex]); if (firstIndex + nsamps > CirSize) { int tailSz = CirSize - firstIndex; - memcpy(out, firstSample, sampleToByte(tailSz, nbAnt_tx)); - memcpy(out + tailSz, &ptr->circularBuf[0], sampleToByte(nsamps - tailSz, nbAnt_tx)); + memcpy(out, firstSample, sampleToByte(tailSz, 1)); + memcpy(out + tailSz, &ptr->circularBuf[0], sampleToByte(nsamps - tailSz, 1)); } else { - memcpy(out, firstSample, nsamps * 4); + memcpy(out, firstSample, sampleToByte(nsamps, 1)); } } else { // SIMD (with simde) optimization might be added here later @@ -1001,13 +957,11 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest {0.1, 0.2, 1.0, 0.2}, // rx 2 {0.05, 0.1, 0.2, 1.0}}; // rx 3 - sample_t *out = (sample_t *)samplesVoid[a]; - LOG_D(HW, "nbAnt_tx %d\n", nbAnt_tx); for (int i = 0; i < nsamps; i++) { // loop over nsamps for (int a_tx = 0; a_tx < nbAnt_tx; a_tx++) { // sum up signals from nbAnt_tx antennas - out[i].r += (short)(ptr->circularBuf[((t->nextRxTstamp + i) * nbAnt_tx + a_tx) % CirSize].r * H_awgn_mimo[a][a_tx]); - out[i].i += (short)(ptr->circularBuf[((t->nextRxTstamp + i) * nbAnt_tx + a_tx) % CirSize].i * H_awgn_mimo[a][a_tx]); + out[i].r += (short)(ptr->circularBuf[((firstIndex + i) * nbAnt_tx + a_tx) % CirSize].r * H_awgn_mimo[a][a_tx]); + out[i].i += (short)(ptr->circularBuf[((firstIndex + i) * nbAnt_tx + a_tx) % CirSize].i * H_awgn_mimo[a][a_tx]); } // end for a_tx } // end for i (number of samps) } // end of 1 tx antenna optimization @@ -1066,6 +1020,16 @@ int device_init(openair0_device *device, openair0_config_t *openair0_cfg) { rfsimulator->tx_bw=openair0_cfg->tx_bw; rfsimulator_readconfig(rfsimulator); LOG_W(HW, "sample_rate %f\n", rfsimulator->sample_rate); + if (rfsimulator->prop_delay_ms > 0.0) + rfsimulator->chan_offset = rfsimulator->sample_rate * rfsimulator->prop_delay_ms / 1000; + if (rfsimulator->chan_offset != 0) { + if (CirSize < minCirSize + rfsimulator->chan_offset) { + CirSize = minCirSize + rfsimulator->chan_offset; + LOG_I(HW, "CirSize = %lu\n", CirSize); + } + rfsimulator->prop_delay_ms = rfsimulator->chan_offset * 1000 / rfsimulator->sample_rate; + LOG_I(HW, "propagation delay %f ms, %lu samples\n", rfsimulator->prop_delay_ms, rfsimulator->chan_offset); + } pthread_mutex_init(&Sockmutex, NULL); LOG_I(HW, "Running as %s\n",