Commit 12a1c8a8 authored by Robert Schmidt's avatar Robert Schmidt

Merge remote-tracking branch 'origin/rfsim_add_prop_delay' into integration_2024_w21c

parents 87a1ff5b d5a04555
......@@ -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",
......
......@@ -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
......
......@@ -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++) {
......
......@@ -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);
......
......@@ -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);
......
......@@ -73,10 +73,11 @@ The RF simulator is using the configuration module, and its parameters are defin
|:--------------------- |:-------------------------------------------------------------------------------|----: |
|`--rfsimulator.serveraddr <addr>`| ip address to connect to, or `server` to behave as a tcp server | 127.0.0.1 |
|`--rfsimulator.serverport <port>`| 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 <file>` | 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.
......@@ -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<nbSamples; i++) {
......
......@@ -66,7 +66,7 @@
// The default value is chosen for 10ms buffering which makes 23040*20 = 460800 samples
// The previous value is kept below in comment it was computed for 100ms 1x 20MHz
// #define CirSize 6144000 // 100ms SiSo 20MHz LTE
#define CirSize 460800 // 10ms SiSo 40Mhz 3/4 sampling NR78 FR1
#define minCirSize 460800 // 10ms SiSo 40Mhz 3/4 sampling NR78 FR1
#define sampleToByte(a,b) ((a)*(b)*sizeof(sample_t))
#define byteToSample(a,b) ((a)/(sizeof(sample_t)*(b)))
......@@ -114,7 +114,8 @@ typedef enum { SIMU_ROLE_SERVER = 1, SIMU_ROLE_CLIENT } simuRole;
{"modelname", "<channel model name>\n", simOpt, .strptr=&modelname, .defstrval="AWGN", TYPE_STRING, 0 },\
{"ploss", "<channel path loss in dB>\n", simOpt, .dblptr=&(rfsimulator->chan_pathloss), .defdblval=0, TYPE_DOUBLE, 0 },\
{"forgetfact", "<channel forget factor ((0 to 1)>\n", simOpt, .dblptr=&(rfsimulator->chan_forgetfact),.defdblval=0, TYPE_DOUBLE, 0 },\
{"offset", "<channel offset in samps>\n", simOpt, .iptr=&(rfsimulator->chan_offset), .defintval=0, TYPE_INT, 0 },\
{"offset", "<channel offset in samps>\n", simOpt, .u64ptr=&(rfsimulator->chan_offset), .defint64val=0, TYPE_UINT64, 0 },\
{"prop_delay", "<propagation delay in ms>\n", simOpt, .dblptr=&(rfsimulator->prop_delay_ms), .defdblval=0.0, TYPE_DOUBLE, 0 },\
{"wait_timeout", "<wait timeout if no UE connected>\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: <default> 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",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment