simulator.c 43.1 KB
Newer Older
1
/*
laurent's avatar
laurent committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
* 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
*
* Author and copyright: Laurent Thomas, open-cells.com
*
* 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
22 23
*/

laurent's avatar
laurent committed
24

laurent's avatar
laurent committed
25 26 27 28 29 30
/*
 * Open issues and limitations
 * The read and write should be called in the same thread, that is not new USRP UHD design
 * When the opposite side switch from passive reading to active R+Write, the synchro is not fully deterministic
 */

31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <errno.h>
#include <sys/epoll.h>
#include <string.h>

#include <common/utils/assertions.h>
#include <common/utils/LOG/log.h>
46
#include <common/utils/load_module_shlib.h>
frtabu's avatar
frtabu committed
47
#include <common/utils/telnetsrv/telnetsrv.h>
48
#include <common/config/config_userapi.h>
49 50 51
#include "common_lib.h"
#include <openair1/PHY/defs_eNB.h>
#include "openair1/PHY/defs_UE.h"
52
#define CHANNELMOD_DYNAMICLOAD
53
#include <openair1/SIMULATION/TOOLS/sim.h>
francescomani's avatar
francescomani committed
54
#include "rfsimulator.h"
55

56
#define PORT 4043 //default TCP port for this simulator
57 58 59 60 61 62 63 64 65 66 67 68 69
//
// CirSize defines the number of samples inquired for a read cycle
// It is bounded by a slot read capability (which depends on bandwidth and numerology)
// up to multiple slots read to allow I/Q buffering of the I/Q TCP stream
//
// As a rule of thumb:
// -it can't be less than the number of samples for a slot
// -it can range up to multiple slots
//
// 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
70 71 72
#define sampleToByte(a,b) ((a)*(b)*sizeof(sample_t))
#define byteToSample(a,b) ((a)/(sizeof(sample_t)*(b)))

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
#define GENERATE_CHANNEL 10 // each frame (or slot?) in DL

// This needs to be re-architected in the future
//
// File Descriptors management in rfsimulator is not optimized
// Relying on FD_SETSIZE (actually 1024) is not appropriated
// Also the use of fd value as returned by Linux as an index for buf[] structure is not appropriated
// especially for client (UE) side since only 1 fd per connection to a gNB is needed. On the server
// side the value should be tuned to the maximum number of connections with UE's which corresponds
// to the maximum number of UEs hosted by a gNB which is unlikely to be in the order of thousands
// since all I/Q's would flow through the same TCP transport.
// Until a convenient management is implemented, the MAX_FD_RFSIMU is used everywhere (instead of
// FD_SETSIE) and reduced to 125. This should allow for around 20 simultaeous UEs.
//
// #define MAX_FD_RFSIMU FD_SETSIZE
88
#define MAX_FD_RFSIMU 250
89 90
#define SYSCTL_MEM_VALUE 134217728 // Kernel network buffer size
#define SEND_BUFF_SIZE SYSCTL_MEM_VALUE // Socket buffer size
91

92 93
// Simulator role
typedef enum { SIMU_ROLE_SERVER = 1, SIMU_ROLE_CLIENT } simuRole;
94

95
//
96

97 98
#define RFSIMU_SECTION    "rfsimulator"
#define RFSIMU_OPTIONS_PARAMNAME "options"
99 100 101


#define RFSIM_CONFIG_HELP_OPTIONS     " list of comma separated options to enable rf simulator functionalities. Available options: \n"\
102 103
  "        chanmod:   enable channel modelisation\n"\
  "        saviq:     enable saving written iqs to a file\n"
104 105 106 107
/*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*                                            configuration parameters for the rfsimulator device                                                                              */
/*   optname                     helpstr                     paramflags           XXXptr                               defXXXval                          type         numelt  */
/*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
Laurent Thomas's avatar
Laurent Thomas committed
108 109
#define simOpt PARAMFLAG_NOFREE|PARAMFLAG_CMDLINE_NOPREFIXENABLED
#define RFSIMULATOR_PARAMS_DESC {					\
110 111 112 113 114 115 116 117 118
    {"serveraddr",             "<ip address to connect to>\n",        simOpt,  .strptr=&rfsimulator->ip,               .defstrval="127.0.0.1",           TYPE_STRING,    0 },\
    {"serverport",             "<port to connect to>\n",              simOpt,  .u16ptr=&(rfsimulator->port),           .defuintval=PORT,                 TYPE_UINT16,    0 },\
    {RFSIMU_OPTIONS_PARAMNAME, RFSIM_CONFIG_HELP_OPTIONS,             0,       .strlistptr=NULL,                       .defstrlistval=NULL,              TYPE_STRINGLIST,0 },\
    {"IQfile",                 "<file path to use when saving IQs>\n",simOpt,  .strptr=&saveF,                         .defstrval="/tmp/rfsimulator.iqs",TYPE_STRING,    0 },\
    {"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 },\
    {"wait_timeout",           "<wait timeout if no UE connected>\n", simOpt,  .iptr=&(rfsimulator->wait_timeout),     .defintval=1,                     TYPE_INT,       0 },\
119
  };
120

frtabu's avatar
frtabu committed
121 122
static void getset_currentchannels_type(char *buf, int debug, webdatadef_t *tdata, telnet_printfunc_t prnt);
extern int get_currentchannels_type(char *buf, int debug, webdatadef_t *tdata, telnet_printfunc_t prnt); // in random_channel.c
123
static int rfsimu_setchanmod_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg);
124 125
static int rfsimu_setdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg);
static int rfsimu_getdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg);
126
static int rfsimu_vtime_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg);
frtabu's avatar
frtabu committed
127
static telnetshell_cmddef_t rfsimu_cmdarray[] = {
frtabu's avatar
frtabu committed
128 129
    {"show models", "", (cmdfunc_t)rfsimu_setchanmod_cmd, {(webfunc_t)getset_currentchannels_type}, TELNETSRV_CMDFLAG_WEBSRVONLY | TELNETSRV_CMDFLAG_GETWEBTBLDATA, NULL},
    {"setmodel", "<model name> <model type>", (cmdfunc_t)rfsimu_setchanmod_cmd, {NULL}, TELNETSRV_CMDFLAG_PUSHINTPOOLQ | TELNETSRV_CMDFLAG_TELNETONLY, NULL},
130
    {"setdistance", "<model name> <distance>", (cmdfunc_t)rfsimu_setdistance_cmd, {NULL}, TELNETSRV_CMDFLAG_PUSHINTPOOLQ | TELNETSRV_CMDFLAG_NEEDPARAM },
frtabu's avatar
frtabu committed
131 132 133
    {"getdistance", "<model name>", (cmdfunc_t)rfsimu_getdistance_cmd, {NULL}, TELNETSRV_CMDFLAG_PUSHINTPOOLQ},
    {"vtime", "", (cmdfunc_t)rfsimu_vtime_cmd, {NULL}, TELNETSRV_CMDFLAG_PUSHINTPOOLQ | TELNETSRV_CMDFLAG_AUTOUPDATE},
    {"", "", NULL},
frtabu's avatar
frtabu committed
134
};
frtabu's avatar
frtabu committed
135
static telnetshell_cmddef_t *setmodel_cmddef = &(rfsimu_cmdarray[1]);
frtabu's avatar
frtabu committed
136

frtabu's avatar
frtabu committed
137
static telnetshell_vardef_t rfsimu_vardef[] = {{"", 0, 0, NULL}};
138
pthread_mutex_t Sockmutex;
139
unsigned int nb_ue = 0;
140

141
typedef c16_t sample_t; // 2*16 bits complex number
142

143 144
typedef struct buffer_s {
  int conn_sock;
laurent's avatar
laurent committed
145
  openair0_timestamp lastReceivedTS;
146
  bool headerMode;
147
  bool trashingPacket;
148
  samplesBlockHeader_t th;
149 150 151 152
  char *transferPtr;
  uint64_t remainToTransfer;
  char *circularBufEnd;
  sample_t *circularBuf;
153
  channel_desc_t *channel_model;
154 155 156 157
} buffer_t;

typedef struct {
  int listen_sock, epollfd;
158
  openair0_timestamp nextRxTstamp;
159
  openair0_timestamp lastWroteTS;
160
  simuRole role;
161
  char *ip;
162
  uint16_t port;
163
  int saveIQfile;
164
  buffer_t buf[MAX_FD_RFSIMU];
165 166 167 168
  int rx_num_channels;
  int tx_num_channels;
  double sample_rate;
  double tx_bw;
169
  int channelmod;
170 171
  double chan_pathloss;
  double chan_forgetfact;
172
  int    chan_offset;
Laurent THOMAS's avatar
Laurent THOMAS committed
173
  float  noise_power_dB;
frtabu's avatar
frtabu committed
174 175
  void *telnetcmd_qid;
  poll_telnetcmdq_func_t poll_telnetcmdq;
176
  int wait_timeout;
177 178
} rfsimulator_state_t;

179 180
static int allocCirBuf(rfsimulator_state_t *bridge, int sock)
{
181
  buffer_t *ptr=&bridge->buf[sock];
182 183 184 185 186
  ptr->circularBuf = malloc(sampleToByte(CirSize, 1));
  if (ptr->circularBuf == NULL) {
    LOG_E(HW, "malloc(%lu) failed\n", sampleToByte(CirSize, 1));
    return -1;
  }
187 188
  ptr->circularBufEnd=((char *)ptr->circularBuf)+sampleToByte(CirSize,1);
  ptr->conn_sock=sock;
189
  ptr->lastReceivedTS=0;
190
  ptr->headerMode=true;
191
  ptr->trashingPacket=false;
192
  ptr->transferPtr=(char *)&ptr->th;
193
  ptr->remainToTransfer=sizeof(samplesBlockHeader_t);
194 195 196 197 198
  int sendbuff = SEND_BUFF_SIZE;
  if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sendbuff, sizeof(sendbuff)) != 0) {
    LOG_E(HW, "setsockopt(SO_SNDBUF) failed\n");
    return -1;
  }
199 200 201
  struct epoll_event ev= {0};
  ev.events = EPOLLIN | EPOLLRDHUP;
  ev.data.fd = sock;
202 203 204 205
  if (epoll_ctl(bridge->epollfd, EPOLL_CTL_ADD, sock, &ev) != 0) {
    LOG_E(HW, "epoll_ctl(EPOLL_CTL_ADD) failed\n");
    return -1;
  }
206

207
  if ( bridge->channelmod > 0) {
208 209 210 211 212 213 214 215 216 217 218 219
    // create channel simulation model for this mode reception
    // snr_dB is pure global, coming from configuration paramter "-s"
    // Fixme: referenceSignalPower should come from the right place
    // but the datamodel is inconsistant
    // legacy: RC.ru[ru_id]->frame_parms.pdsch_config_common.referenceSignalPower
    // (must not come from ru[]->frame_parms as it doesn't belong to ru !!!)
    // Legacy sets it as:
    // ptr->channel_model->path_loss_dB = -132.24 + snr_dB - RC.ru[0]->frame_parms->pdsch_config_common.referenceSignalPower;
    // we use directly the paramter passed on the command line ("-s")
    // the value channel_model->path_loss_dB seems only a storage place (new_channel_desc_scm() only copy the passed value)
    // Legacy changes directlty the variable channel_model->path_loss_dB place to place
    // while calling new_channel_desc_scm() with path losses = 0
laurent's avatar
laurent committed
220
    static bool init_done=false;
laurent's avatar
laurent committed
221

laurent's avatar
laurent committed
222
    if (!init_done) {
223 224 225
      uint64_t rand;
      FILE *h=fopen("/dev/random","r");

226
      if ( 1 != fread(&rand,sizeof(rand),1,h) )
227
        LOG_W(HW, "Can't read /dev/random\n");
228 229

      fclose(h);
laurent's avatar
laurent committed
230
      randominit(rand);
laurent's avatar
laurent committed
231
      tableNor(rand);
laurent's avatar
laurent committed
232 233
      init_done=true;
    }
234 235
    char *modelname = (bridge->role == SIMU_ROLE_SERVER) ? "rfsimu_channel_ue0" : "rfsimu_channel_enB0";
    ptr->channel_model = find_channel_desc_fromname(modelname); // path_loss in dB
236
    if (!ptr->channel_model) {
237 238 239 240
      LOG_E(HW, "Channel model %s not found, check config file\n", modelname);
      return -1;
    }

241
    set_channeldesc_owner(ptr->channel_model, RFSIMU_MODULEID);
242
    random_channel(ptr->channel_model,false);
243
    LOG_I(HW, "Random channel %s in rfsimulator activated\n", modelname);
244
  }
245
  return 0;
246 247
}

Laurent Thomas's avatar
Laurent Thomas committed
248
static void removeCirBuf(rfsimulator_state_t *bridge, int sock) {
249 250 251
  if (epoll_ctl(bridge->epollfd, EPOLL_CTL_DEL, sock, NULL) != 0) {
    LOG_E(HW, "epoll_ctl(EPOLL_CTL_DEL) failed\n");
  }
252 253
  close(sock);
  free(bridge->buf[sock].circularBuf);
254 255
  // Fixme: no free_channel_desc_scm(bridge->buf[sock].channel_model) implemented
  // a lot of mem leaks
256
  //free(bridge->buf[sock].channel_model);
257 258
  memset(&bridge->buf[sock], 0, sizeof(buffer_t));
  bridge->buf[sock].conn_sock=-1;
259
  nb_ue--;
260 261
}

Laurent Thomas's avatar
Laurent Thomas committed
262
static void socketError(rfsimulator_state_t *bridge, int sock) {
263
  if (bridge->buf[sock].conn_sock!=-1) {
264
    LOG_W(HW, "Lost socket\n");
265 266
    removeCirBuf(bridge, sock);

267
    if (bridge->role == SIMU_ROLE_CLIENT)
268 269 270 271 272 273 274 275 276
      exit(1);
  }
}

enum  blocking_t {
  notBlocking,
  blocking
};

277 278
static int setblocking(int sock, enum blocking_t active)
{
279
  int opts = fcntl(sock, F_GETFL);
280 281 282 283
  if (opts < 0) {
    LOG_E(HW, "fcntl(F_GETFL) failed, errno(%d)\n", errno);
    return -1;
  }
284 285 286 287 288 289

  if (active==blocking)
    opts = opts & ~O_NONBLOCK;
  else
    opts = opts | O_NONBLOCK;

290
  opts = fcntl(sock, F_SETFL, opts);
291 292 293 294 295
  if (opts < 0) {
    LOG_E(HW, "fcntl(F_SETFL) failed, errno(%d)\n", errno);
    return -1;
  }
  return 0;
296 297
}

298
static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps);
299

Laurent Thomas's avatar
Laurent Thomas committed
300
static void fullwrite(int fd, void *_buf, ssize_t count, rfsimulator_state_t *t) {
301 302
  if (t->saveIQfile != -1) {
    if (write(t->saveIQfile, _buf, count) != count )
303
      LOG_E(HW, "write() in save iq file failed (%d)\n", errno);
304
  }
305 306

  char *buf = _buf;
307
  ssize_t l;
308 309 310 311 312 313 314 315

  while (count) {
    l = write(fd, buf, count);

    if (l <= 0) {
      if (errno==EINTR)
        continue;

316
      if (errno == EAGAIN) {
317
        LOG_D(HW, "write() failed, errno(%d)\n", errno);
318
        usleep(250);
319 320 321 322 323 324 325 326 327
        continue;
      } else
        return;
    }

    count -= l;
    buf += l;
  }
}
328

Laurent Thomas's avatar
Laurent Thomas committed
329
static void rfsimulator_readconfig(rfsimulator_state_t *rfsimulator) {
330
  char *saveF=NULL;
331
  char *modelname=NULL;
332
  paramdef_t rfsimu_params[] = RFSIMULATOR_PARAMS_DESC;
333 334 335
  int p = config_paramidx_fromname(rfsimu_params, sizeofArray(rfsimu_params), RFSIMU_OPTIONS_PARAMNAME);
  int ret = config_get(config_get_if(), rfsimu_params, sizeofArray(rfsimu_params), RFSIMU_SECTION);
  AssertFatal(ret >= 0, "configuration couldn't be performed\n");
336

337 338
  rfsimulator->saveIQfile = -1;

339
  for(int i=0; i<rfsimu_params[p].numelt ; i++) {
340 341 342 343
    if (strcmp(rfsimu_params[p].strlistptr[i],"saviq") == 0) {
      rfsimulator->saveIQfile=open(saveF,O_APPEND| O_CREAT|O_TRUNC | O_WRONLY, 0666);

      if ( rfsimulator->saveIQfile != -1 )
344 345 346 347 348
        LOG_I(HW, "Will save written IQ samples in %s\n", saveF);
      else {
        LOG_E(HW, "open(%s) failed for IQ saving, errno(%d)\n", saveF, errno);
        exit(-1);
      }
349 350 351

      break;
    } else if (strcmp(rfsimu_params[p].strlistptr[i],"chanmod") == 0) {
352
      init_channelmod();
353 354
      load_channellist(rfsimulator->tx_num_channels, rfsimulator->rx_num_channels, rfsimulator->sample_rate, rfsimulator->tx_bw);
      rfsimulator->channelmod=true;
355
    } else {
356
      fprintf(stderr, "unknown rfsimulator option: %s\n", rfsimu_params[p].strlistptr[i]);
357 358
      exit(-1);
    }
359
  }
360

361 362
  /* for compatibility keep environment variable usage */
  if ( getenv("RFSIMULATOR") != NULL ) {
363
    rfsimulator->ip=getenv("RFSIMULATOR");
364 365 366
    LOG_W(HW, "The RFSIMULATOR environment variable is deprecated and support will be removed in the future. Instead, add parameter --rfsimulator.serveraddr %s to set the server address. Note: the default is \"server\"; for the gNB/eNB, you don't have to set any configuration.\n", rfsimulator->ip);
    LOG_I(HW, "Remove RFSIMULATOR environment variable to get rid of this message and the sleep.\n");
    sleep(10);
367
  }
368

369
  if ( strncasecmp(rfsimulator->ip,"enb",3) == 0 ||
370
       strncasecmp(rfsimulator->ip,"server",3) == 0 )
371
    rfsimulator->role = SIMU_ROLE_SERVER;
372
  else
373
    rfsimulator->role = SIMU_ROLE_CLIENT;
374
}
375

376
static int rfsimu_setchanmod_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg) {
377
  char *modelname=NULL;
378
  char *modeltype=NULL;
frtabu's avatar
frtabu committed
379 380
  rfsimulator_state_t *t = (rfsimulator_state_t *)arg;
  if (t->channelmod == false) {
381
    prnt("%s: ERROR channel modelisation disabled...\n", __func__);
frtabu's avatar
frtabu committed
382 383 384
    return 0;
  }
  if (buff == NULL) {
385
    prnt("%s: ERROR wrong rfsimu setchannelmod command...\n", __func__);
frtabu's avatar
frtabu committed
386 387
    return 0;
  }
388
  if (debug)
389
    prnt("%s: rfsimu_setchanmod_cmd buffer \"%s\"\n", __func__, buff);
390
  int s = sscanf(buff,"%m[^ ] %ms\n",&modelname, &modeltype);
391

392 393
  if (s == 2) {
    int channelmod=modelid_fromstrtype(modeltype);
394

395
    if (channelmod<0)
396
      prnt("%s: ERROR: model type %s unknown\n", __func__, modeltype);
397
    else {
398
      rfsimulator_state_t *t = (rfsimulator_state_t *)arg;
399
      int found=0;
400
      for (int i = 0; i < MAX_FD_RFSIMU; i++) {
401
        buffer_t *b=&t->buf[i];
402 403 404 405 406
        if ( b->channel_model==NULL)
          continue;
        if (b->channel_model->model_name==NULL)
          continue;
        if (b->conn_sock >= 0 && (strcmp(b->channel_model->model_name,modelname)==0)) {
407 408 409 410 411 412 413 414 415 416 417 418 419
          channel_desc_t *newmodel = new_channel_desc_scm(t->tx_num_channels,
                                                          t->rx_num_channels,
                                                          channelmod,
                                                          t->sample_rate,
                                                          0,
                                                          t->tx_bw,
                                                          30e-9, // TDL delay-spread parameter
                                                          0.0,
                                                          CORR_LEVEL_LOW,
                                                          t->chan_forgetfact, // forgetting_factor
                                                          t->chan_offset, // maybe used for TA
                                                          t->chan_pathloss,
                                                          t->noise_power_dB); // path_loss in dB
420
          set_channeldesc_owner(newmodel, RFSIMU_MODULEID);
421
          set_channeldesc_name(newmodel,modelname);
422 423 424 425
          random_channel(newmodel,false);
          channel_desc_t *oldmodel=b->channel_model;
          b->channel_model=newmodel;
          free_channel_desc_scm(oldmodel);
426
          prnt("%s: New model type %s applied to channel %s connected to sock %d\n", __func__, modeltype, modelname, i);
427 428
          found=1;
          break;
429
        }
430 431
      } /* for */
      if (found==0)
432
        prnt("%s: Channel %s not found or not currently used\n", __func__, modelname);
433 434
    }
  } else {
435
    prnt("%s: ERROR: 2 parameters required: model name and model type (%i found)\n", __func__, s);
frtabu's avatar
frtabu committed
436
  }
437

438
  free(modelname);
439
  free(modeltype);
frtabu's avatar
frtabu committed
440 441 442
  return CMDSTATUS_FOUND;
}

frtabu's avatar
frtabu committed
443 444 445 446 447 448 449 450 451 452 453 454 455
static void getset_currentchannels_type(char *buf, int debug, webdatadef_t *tdata, telnet_printfunc_t prnt)
{
  if (strncmp(buf, "set", 3) == 0) {
    char cmd[256];
    snprintf(cmd, sizeof(cmd), "setmodel %s %s", tdata->lines[0].val[1], tdata->lines[0].val[3]);
    push_telnetcmd_func_t push_telnetcmd = (push_telnetcmd_func_t)get_shlibmodule_fptr("telnetsrv", TELNET_PUSHCMD_FNAME);
    push_telnetcmd(setmodel_cmddef, cmd, prnt);
  } else {
    get_currentchannels_type("modify type", debug, tdata, prnt);
  }
}
/*getset_currentchannels_type */

456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
//static void print_cirBuf(struct complex16 *circularBuf,
//                         uint64_t firstSample,
//                         uint32_t cirSize,
//                         int neg,
//                         int pos,
//                         int nbTx)
//{
//  for (int i = -neg; i < pos ; ++i) {
//    for (int txAnt = 0; txAnt < nbTx; txAnt++) {
//      const int idx = ((firstSample + i) * nbTx + txAnt + cirSize) % cirSize;
//      if (i == 0)
//        printf("->");
//      printf("%08x%08x\n", circularBuf[idx].r, circularBuf[idx].i);
//    }
//  }
//  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)
    prnt("%s() buffer \"%s\"\n", __func__, buff);

  char *modelname;
  int distance;
  int s = sscanf(buff,"%m[^ ] %d\n", &modelname, &distance);
  if (s != 2) {
537
    prnt("%s: require exact two parameters\n", __func__);
538 539 540 541 542 543 544 545 546 547
    return CMDSTATUS_VARNOTFOUND;
  }

  rfsimulator_state_t *t = (rfsimulator_state_t *)arg;
  const double sample_rate = t->sample_rate;
  const double c = 299792458; /* 3e8 */

  const int new_offset = (double) distance * sample_rate / c;
  const double new_distance = (double) new_offset * c / sample_rate;

548
  prnt("\n%s: new_offset %d new (exact) distance %.3f m\n", __func__, new_offset, new_distance);
549 550

  /* Set distance in rfsim and channel model, update channel and ringbuffer */
551
  for (int i = 0; i < MAX_FD_RFSIMU; i++) {
552
    buffer_t *b=&t->buf[i];
frtabu's avatar
frtabu committed
553 554
    if (b->conn_sock <= 0 || b->channel_model == NULL || b->channel_model->model_name == NULL || strcmp(b->channel_model->model_name, modelname) != 0) {
      if (b->channel_model != NULL && b->channel_model->model_name != NULL)
555
        prnt("  %s: model %s unmodified\n", __func__, b->channel_model->model_name);
556
      continue;
frtabu's avatar
frtabu committed
557
    }
558 559 560 561 562 563

    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;
564
    prnt("  %s: Modifying model %s...\n", __func__, modelname);
565 566 567 568 569 570 571 572 573 574 575
    rfsimu_offset_change_cirBuf(b->circularBuf, t->nextRxTstamp, CirSize, old_offset, new_offset, nbTx);
  }

  free(modelname);

  return CMDSTATUS_FOUND;
}

static int rfsimu_getdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg)
{
  if (debug)
frtabu's avatar
frtabu committed
576
    prnt("%s() buffer \"%s\"\n", __func__, (buff != NULL) ? buff : "NULL");
577 578 579 580 581

  rfsimulator_state_t *t = (rfsimulator_state_t *)arg;
  const double sample_rate = t->sample_rate;
  const double c = 299792458; /* 3e8 */

582
  for (int i = 0; i < MAX_FD_RFSIMU; i++) {
583
    buffer_t *b=&t->buf[i];
frtabu's avatar
frtabu committed
584
    if (b->conn_sock <= 0 || b->channel_model == NULL || b->channel_model->model_name == NULL)
585 586 587 588 589
      continue;

    channel_desc_t *cd = b->channel_model;
    const int offset = cd->channel_offset;
    const double distance = (double) offset * c / sample_rate;
590
    prnt("%s: \%s offset %d distance %.3f m\n", __func__, cd->model_name, offset, distance);
591 592 593 594 595
  }

  return CMDSTATUS_FOUND;
}

596 597 598 599 600
static int rfsimu_vtime_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg)
{
  rfsimulator_state_t *t = (rfsimulator_state_t *)arg;
  const openair0_timestamp ts = t->nextRxTstamp;
  const double sample_rate = t->sample_rate;
601
  prnt("%s: vtime measurement: TS %llu sample_rate %.3f\n", __func__, ts, sample_rate);
602 603 604
  return CMDSTATUS_FOUND;
}

605 606 607 608 609 610
static void customNetForPerf()
{
  int res = 0;
  char sysctlmem[256];
  memset(sysctlmem, 0, 256);
  sprintf(sysctlmem, "/sbin/sysctl -n -e -q -w net.core.rmem_default=%d", SYSCTL_MEM_VALUE);
611
  LOG_W(HW, "running command \"%s\" to increase RFsim performance\n", sysctlmem);
612 613 614 615 616 617
  res = system(sysctlmem);
  if (res != 0) {
    LOG_W(HW, "Cannot set net.core.rmem_default to %d\n", SYSCTL_MEM_VALUE);
  }
  memset(sysctlmem, 0, 256);
  sprintf(sysctlmem, "/sbin/sysctl -n -e -q -w net.core.rmem_max=%d", SYSCTL_MEM_VALUE);
618
  LOG_W(HW, "running command \"%s\" to increase RFsim performance\n", sysctlmem);
619 620 621 622 623 624
  res = system(sysctlmem);
  if (res != 0) {
    LOG_W(HW, "Cannot set net.core.rmem_max to %d\n", SYSCTL_MEM_VALUE);
  }
  memset(sysctlmem, 0, 256);
  sprintf(sysctlmem, "/sbin/sysctl -n -e -q -w net.core.wmem_default=%d", SYSCTL_MEM_VALUE);
625
  LOG_W(HW, "running command \"%s\" to increase RFsim performance\n", sysctlmem);
626 627 628 629 630 631
  res = system(sysctlmem);
  if (res != 0) {
    LOG_W(HW, "Cannot set net.core.wmem_default to %d\n", SYSCTL_MEM_VALUE);
  }
  memset(sysctlmem, 0, 256);
  sprintf(sysctlmem, "/sbin/sysctl -n -e -q -w net.core.wmem_max=%d", SYSCTL_MEM_VALUE);
632
  LOG_W(HW, "running command \"%s\" to increase RFsim performance\n", sysctlmem);
633 634 635 636 637 638
  res = system(sysctlmem);
  if (res != 0) {
    LOG_W(HW, "Cannot set net.core.wmem_max to %d\n", SYSCTL_MEM_VALUE);
  }
}

Laurent Thomas's avatar
Laurent Thomas committed
639
static int startServer(openair0_device *device) {
640
  rfsimulator_state_t *t = (rfsimulator_state_t *) device->priv;
641 642 643 644 645 646
  t->role = SIMU_ROLE_SERVER;
  t->listen_sock = socket(AF_INET, SOCK_STREAM, 0);
  if (t->listen_sock < 0) {
    LOG_E(HW, "socket(SOCK_STREAM) failed, errno(%d)\n", errno);
    return -1;
  }
647
  int enable = 1;
648 649 650 651 652 653
  if (setsockopt(t->listen_sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) != 0) {
    LOG_E(HW, "setsockopt(SO_REUSEADDR) failed, errno(%d)\n", errno);
    return -1;
  }
  struct sockaddr_in addr = {.sin_family = AF_INET, .sin_port = htons(t->port), .sin_addr = {.s_addr = INADDR_ANY}};
  customNetForPerf();
654
  int rc = bind(t->listen_sock, (struct sockaddr *)&addr, sizeof(addr));
655 656 657 658 659 660 661 662
  if (rc < 0) {
    LOG_E(HW, "bind() failed, errno(%d)\n", errno);
    return -1;
  }
  if (listen(t->listen_sock, 5) != 0) {
    LOG_E(HW, "listen() failed, errno(%d)\n", errno);
    return -1;
  }
663
  struct epoll_event ev= {0};
664 665
  ev.events = EPOLLIN;
  ev.data.fd = t->listen_sock;
666 667 668 669
  if (epoll_ctl(t->epollfd, EPOLL_CTL_ADD, t->listen_sock, &ev) != 0) {
    LOG_E(HW, "epoll_ctl(EPOLL_CTL_ADD) failed, errno(%d)\n", errno);
    return -1;
  }
670 671 672
  return 0;
}

Laurent Thomas's avatar
Laurent Thomas committed
673
static int startClient(openair0_device *device) {
674
  rfsimulator_state_t *t = device->priv;
675
  t->role = SIMU_ROLE_CLIENT;
676
  int sock;
677 678 679 680 681
  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    LOG_E(HW, "socket(SOCK_STREAM) failed, errno(%d)\n", errno);
    return -1;
  }
  struct sockaddr_in addr = {.sin_family = AF_INET, .sin_port = htons(t->port), .sin_addr = {.s_addr = INADDR_ANY}};
682 683 684
  addr.sin_addr.s_addr = inet_addr(t->ip);
  bool connected=false;

685
  customNetForPerf();
686
  while(!connected) {
687
    LOG_I(HW, "Trying to connect to %s:%d\n", t->ip, t->port);
688 689

    if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
690
      LOG_I(HW, "Connection to %s:%d established\n", t->ip, t->port);
691 692 693
      connected=true;
    }

694
    LOG_I(HW, "connect() to %s:%d failed, errno(%d)\n", t->ip, t->port, errno);
695 696 697
    sleep(1);
  }

698 699 700 701
  if (setblocking(sock, notBlocking) == -1) {
    return -1;
  }
  return allocCirBuf(t, sock);
702 703
}

704 705 706 707
static int rfsimulator_write_internal(rfsimulator_state_t *t, openair0_timestamp timestamp, void **samplesVoid, int nsamps, int nbAnt, int flags, bool alreadyLocked) {
  if (!alreadyLocked)
    pthread_mutex_lock(&Sockmutex);

708
  LOG_D(HW, "Sending %d samples at time: %ld, nbAnt %d\n", nsamps, timestamp, nbAnt);
709

710
  for (int i = 0; i < MAX_FD_RFSIMU; i++) {
laurent's avatar
laurent committed
711
    buffer_t *b=&t->buf[i];
712

laurent's avatar
laurent committed
713
    if (b->conn_sock >= 0 ) {
714
      samplesBlockHeader_t header = {nsamps, nbAnt, timestamp};
laurent's avatar
laurent committed
715
      fullwrite(b->conn_sock,&header, sizeof(header), t);
716 717
      sample_t tmpSamples[nsamps][nbAnt];

718 719 720 721 722 723 724
      if (nbAnt == 1) {
        if (b->conn_sock >= 0) {
          fullwrite(b->conn_sock, samplesVoid[0], sampleToByte(nsamps, nbAnt), t);
        }
      } else {
        for (int a = 0; a < nbAnt; a++) {
          sample_t *in = (sample_t *)samplesVoid[a];
725

726 727 728
          for (int s = 0; s < nsamps; s++)
            tmpSamples[s][a] = in[s];
        }
729

730 731 732
        if (b->conn_sock >= 0) {
          fullwrite(b->conn_sock, (void *)tmpSamples, sampleToByte(nsamps, nbAnt), t);
        }
laurent's avatar
laurent committed
733
      }
734 735 736
    }
  }

737
  if ( t->lastWroteTS != 0 && fabs((double)t->lastWroteTS-timestamp) > (double)CirSize)
738
    LOG_W(HW, "Discontinuous TX gap too large Tx:%lu, %lu\n", t->lastWroteTS, timestamp);
739

laurent's avatar
laurent committed
740
  if (t->lastWroteTS > timestamp+nsamps)
741
    LOG_W(HW, "Not supported to send Tx out of order %lu, %lu\n", t->lastWroteTS, timestamp);
laurent's avatar
laurent committed
742

743 744 745 746 747
  t->lastWroteTS=timestamp+nsamps;

  if (!alreadyLocked)
    pthread_mutex_unlock(&Sockmutex);

748 749 750 751 752 753
  LOG_D(HW,
        "Sent %d samples at time: %ld->%ld, energy in first antenna: %d\n",
        nsamps,
        timestamp,
        timestamp + nsamps,
        signal_energy(samplesVoid[0], nsamps));
754 755 756
  return nsamps;
}

Laurent Thomas's avatar
Laurent Thomas committed
757
static int rfsimulator_write(openair0_device *device, openair0_timestamp timestamp, void **samplesVoid, int nsamps, int nbAnt, int flags) {
758 759
  return rfsimulator_write_internal(device->priv, timestamp, samplesVoid, nsamps, nbAnt, flags, false); // false = with lock
  // return rfsimulator_write_internal(device->priv, timestamp, samplesVoid, nsamps, nbAnt, flags, true);
760 761 762
}

static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initial) {
763 764
  // Process all incoming events on sockets
  // store the data in lists
765 766
  struct epoll_event events[MAX_FD_RFSIMU] = {{0}};
  int nfds = epoll_wait(t->epollfd, events, MAX_FD_RFSIMU, timeout);
767 768

  if ( nfds==-1 ) {
769
    if ( errno==EINTR || errno==EAGAIN ) {
770
      return false;
771 772 773 774
    } else {
      LOG_W(HW, "epoll_wait() failed, errno(%d)\n", errno);
      return false;
    }
775 776 777 778 779 780 781
  }

  for (int nbEv = 0; nbEv < nfds; ++nbEv) {
    int fd=events[nbEv].data.fd;

    if (events[nbEv].events & EPOLLIN && fd == t->listen_sock) {
      int conn_sock;
782 783 784 785 786 787 788 789 790 791 792 793
      conn_sock = accept(t->listen_sock, NULL, NULL);
      if (conn_sock == -1) {
        LOG_E(HW, "accept() failed, errno(%d)\n", errno);
        return false;
      }
      if (setblocking(conn_sock, notBlocking)) {
        return false;
      }
      if (allocCirBuf(t, conn_sock) == -1) {
        return false;
      }
      LOG_I(HW, "A client connects, sending the current time\n");
794
      c16_t v= {0};
795
      nb_ue++;
796 797 798 799 800
      void *samplesVoid[t->tx_num_channels];

      for ( int i=0; i < t->tx_num_channels; i++)
        samplesVoid[i]=(void *)&v;

801
      rfsimulator_write_internal(t, t->lastWroteTS > 1 ? t->lastWroteTS - 1 : 0, samplesVoid, 1, t->tx_num_channels, 1, false);
802 803 804 805 806 807 808 809 810
    } else {
      if ( events[nbEv].events & (EPOLLHUP | EPOLLERR | EPOLLRDHUP) ) {
        socketError(t,fd);
        continue;
      }

      buffer_t *b=&t->buf[fd];

      if ( b->circularBuf == NULL ) {
811
        LOG_E(HW, "Received data on not connected socket %d\n", events[nbEv].data.fd);
812 813 814
        continue;
      }

815
      ssize_t blockSz;
816 817 818 819

      if ( b->headerMode)
        blockSz=b->remainToTransfer;
      else
820
        blockSz= b->transferPtr + b->remainToTransfer <= b->circularBufEnd ?
821
                 b->remainToTransfer :
822
                 b->circularBufEnd - b->transferPtr ;
823

824
      ssize_t sz=recv(fd, b->transferPtr, blockSz, MSG_DONTWAIT);
825 826 827

      if ( sz < 0 ) {
        if ( errno != EAGAIN ) {
828
          LOG_E(HW, "recv() failed, errno(%d)\n", errno);
829
          //abort();
830 831 832 833
        }
      } else if ( sz == 0 )
        continue;

834
      LOG_D(HW, "Socket rcv %zd bytes\n", sz);
835
      b->remainToTransfer -= sz;
836 837
      b->transferPtr+=sz;

838
      if (b->transferPtr==b->circularBufEnd )
839 840 841 842
        b->transferPtr=(char *)b->circularBuf;

      // check the header and start block transfer
      if ( b->headerMode==true && b->remainToTransfer==0) {
843
        b->headerMode = false;
844

845
        if (t->nextRxTstamp == 0) { // First block in UE, resync with the gNB current TS
846
          t->nextRxTstamp=b->th.timestamp> nsamps_for_initial ?
847 848 849 850 851
                           b->th.timestamp -  nsamps_for_initial :
                           0;
          b->lastReceivedTS=b->th.timestamp> nsamps_for_initial ?
                            b->th.timestamp :
                            nsamps_for_initial;
852
          LOG_D(HW, "UE got first timestamp: starting at %lu\n", t->nextRxTstamp);
853
          b->trashingPacket=true;
854
        } else if (b->lastReceivedTS < b->th.timestamp) {
855
          int nbAnt= b->th.nbAnt;
laurent's avatar
laurent committed
856

857
          if ( b->th.timestamp-b->lastReceivedTS < CirSize ) {
laurent's avatar
laurent committed
858 859 860 861 862
            for (uint64_t index=b->lastReceivedTS; index < b->th.timestamp; index++ ) {
              for (int a=0; a < nbAnt; a++) {
                b->circularBuf[(index*nbAnt+a)%CirSize].r = 0;
                b->circularBuf[(index*nbAnt+a)%CirSize].i = 0;
              }
863
            }
864
          } else {
865 866
            memset(b->circularBuf, 0, sampleToByte(CirSize,1));
          }
laurent's avatar
laurent committed
867

868
          b->lastReceivedTS=b->th.timestamp;
869 870
        } else if (b->lastReceivedTS > b->th.timestamp && b->th.size == 1) {
          LOG_W(HW, "Received Rx/Tx synchro out of order\n");
871
          b->trashingPacket=true;
872
        } else if (b->lastReceivedTS == b->th.timestamp) {
873 874
          // normal case
        } else {
875
          LOG_W(HW, "Received data in past: current is %lu, new reception: %lu!\n", b->lastReceivedTS, b->th.timestamp);
876
          b->trashingPacket=true;
laurent's avatar
laurent committed
877
        }
878

879 880
        pthread_mutex_lock(&Sockmutex);

881
        if (t->lastWroteTS != 0 && (fabs((double)t->lastWroteTS-b->lastReceivedTS) > (double)CirSize))
882
          LOG_W(HW, "UEsock(%d) Tx/Rx shift too large Tx:%lu, Rx:%lu\n", fd, t->lastWroteTS, b->lastReceivedTS);
883 884

        pthread_mutex_unlock(&Sockmutex);
885
        b->transferPtr=(char *)&b->circularBuf[(b->lastReceivedTS*b->th.nbAnt)%CirSize];
886 887 888 889
        b->remainToTransfer=sampleToByte(b->th.size, b->th.nbAnt);
      }

      if ( b->headerMode==false ) {
890 891
        if ( ! b->trashingPacket ) {
          b->lastReceivedTS=b->th.timestamp+b->th.size-byteToSample(b->remainToTransfer,b->th.nbAnt);
892
          LOG_D(HW, "UEsock: %d Set b->lastReceivedTS %ld\n", fd, b->lastReceivedTS);
893
        }
894

laurent's avatar
laurent committed
895
        if ( b->remainToTransfer==0) {
896
          LOG_D(HW, "UEsock: %d Completed block reception: %ld\n", fd, b->lastReceivedTS);
laurent's avatar
laurent committed
897 898
          b->headerMode=true;
          b->transferPtr=(char *)&b->th;
899
          b->remainToTransfer = sizeof(samplesBlockHeader_t);
900
          b->trashingPacket=false;
901 902 903 904 905 906 907 908
        }
      }
    }
  }

  return nfds>0;
}

909 910
static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimestamp, void **samplesVoid, int nsamps, int nbAnt)
{
911
  rfsimulator_state_t *t = device->priv;
912
  LOG_D(HW, "Enter rfsimulator_read, expect %d samples, will release at TS: %ld, nbAnt %d\n", nsamps, t->nextRxTstamp+nsamps, nbAnt);
913

914 915 916 917
  // deliver data from received data
  // check if a UE is connected
  int first_sock;

918
  for (first_sock = 0; first_sock < MAX_FD_RFSIMU; first_sock++)
919 920 921
    if (t->buf[first_sock].circularBuf != NULL )
      break;

922
  if (first_sock == MAX_FD_RFSIMU) {
923
    // no connected device (we are eNB, no UE is connected)
924
    if ( t->nextRxTstamp == 0)
925
      LOG_W(HW, "No connected device, generating void samples...\n");
926

927
    if (!flushInput(t, t->wait_timeout,  nsamps)) {
928 929 930
      for (int x=0; x < nbAnt; x++)
        memset(samplesVoid[x],0,sampleToByte(nsamps,1));

931
      t->nextRxTstamp+=nsamps;
932

933
      if ( ((t->nextRxTstamp/nsamps)%100) == 0)
934
        LOG_D(HW, "No UE, Generating void samples for Rx: %ld\n", t->nextRxTstamp);
935

936
      *ptimestamp = t->nextRxTstamp-nsamps;
937 938 939 940 941 942 943 944
      return nsamps;
    }
  } else {
    bool have_to_wait;

    do {
      have_to_wait=false;

945 946 947
      buffer_t *b = NULL;
      for (int sock = 0; sock < MAX_FD_RFSIMU; sock++) {
        b = &t->buf[sock];
948

laurent's avatar
laurent committed
949
        if ( b->circularBuf )
950
          if ( t->nextRxTstamp+nsamps > b->lastReceivedTS ) {
laurent's avatar
laurent committed
951 952
            have_to_wait=true;
            break;
953
          }
laurent's avatar
laurent committed
954
      }
955

956 957 958 959 960
      if (have_to_wait) {
        LOG_D(HW,
              "Waiting on socket, current last ts: %ld, expected at least : %ld\n",
              b->lastReceivedTS,
              t->nextRxTstamp + nsamps);
961
        flushInput(t, 3, nsamps);
962
      }
963 964 965 966 967 968 969
    } while (have_to_wait);
  }

  // Clear the output buffer
  for (int a=0; a<nbAnt; a++)
    memset(samplesVoid[a],0,sampleToByte(nsamps,1));

laurent's avatar
laurent committed
970
  // Add all input nodes signal in the output buffer
971
  for (int sock = 0; sock < MAX_FD_RFSIMU; sock++) {
972 973
    buffer_t *ptr=&t->buf[sock];

laurent's avatar
laurent committed
974
    if ( ptr->circularBuf ) {
975 976 977 978 979 980
      bool reGenerateChannel=false;

      //fixme: when do we regenerate
      // it seems legacy behavior is: never in UL, each frame in DL
      if (reGenerateChannel)
        random_channel(ptr->channel_model,0);
981

982
      if (t->poll_telnetcmdq)
983
        t->poll_telnetcmdq(t->telnetcmd_qid,t);
984

985
      for (int a=0; a<nbAnt; a++) {//loop over number of Rx antennas
986 987 988 989 990 991 992
        if ( ptr->channel_model != NULL ) { // apply a channel model
          rxAddInput(ptr->circularBuf, (c16_t *) samplesVoid[a],
                     a,
                     ptr->channel_model,
                     nsamps,
                     t->nextRxTstamp,
                     CirSize);
993
        }
994
        else { // no channel modeling
995
          int nbAnt_tx = ptr->th.nbAnt; // number of Tx antennas
996
          if ((nbAnt_tx == 1) && ((nb_ue == 1) || (t->role == SIMU_ROLE_CLIENT))) { // optimized for 1 Tx and 1 UE
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
            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));
            } else {
              memcpy(out, firstSample, nsamps * 4);
            }
          } else {
            // SIMD (with simde) optimization might be added here later
            double H_awgn_mimo[4][4] = {{1.0, 0.2, 0.1, 0.05}, // rx 0
                                        {0.2, 1.0, 0.2, 0.1}, // rx 1
                                        {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]);
              } // end for a_tx
            } // end for i (number of samps)
          } // end of 1 tx antenna optimization
1024
        } // end of no channel modeling
1025
      } // end for a (number of rx antennas)
1026 1027 1028
    }
  }

1029 1030
  *ptimestamp = t->nextRxTstamp; // return the time of the first sample
  t->nextRxTstamp+=nsamps;
1031 1032
  LOG_D(HW,
        "Rx to upper layer: %d from %ld to %ld, energy in first antenna %d\n",
1033
        nsamps,
1034 1035
        *ptimestamp,
        t->nextRxTstamp,
1036 1037 1038
        signal_energy(samplesVoid[0], nsamps));
  return nsamps;
}
Laurent Thomas's avatar
Laurent Thomas committed
1039 1040

static int rfsimulator_get_stats(openair0_device *device) {
1041 1042
  return 0;
}
Laurent Thomas's avatar
Laurent Thomas committed
1043
static int rfsimulator_reset_stats(openair0_device *device) {
1044 1045
  return 0;
}
Robert Schmidt's avatar
Robert Schmidt committed
1046 1047
static void rfsimulator_end(openair0_device *device) {
  rfsimulator_state_t* s = device->priv;
1048
  for (int i = 0; i < MAX_FD_RFSIMU; i++) {
Robert Schmidt's avatar
Robert Schmidt committed
1049 1050 1051 1052 1053 1054
    buffer_t *b = &s->buf[i];
    if (b->conn_sock >= 0 )
      close(b->conn_sock);
  }
  close(s->epollfd);
}
Laurent Thomas's avatar
Laurent Thomas committed
1055
static int rfsimulator_stop(openair0_device *device) {
1056 1057
  return 0;
}
Robert Schmidt's avatar
Robert Schmidt committed
1058
static int rfsimulator_set_freq(openair0_device *device, openair0_config_t *openair0_cfg) {
1059 1060
  return 0;
}
Laurent Thomas's avatar
Laurent Thomas committed
1061
static int rfsimulator_set_gains(openair0_device *device, openair0_config_t *openair0_cfg) {
1062 1063
  return 0;
}
Laurent Thomas's avatar
Laurent Thomas committed
1064
static int rfsimulator_write_init(openair0_device *device) {
WANG Tsu-Han's avatar
WANG Tsu-Han committed
1065 1066
  return 0;
}
1067 1068
__attribute__((__visibility__("default")))
int device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
1069 1070
  // to change the log level, use this on command line
  // --log_config.hw_log_level debug
1071
  rfsimulator_state_t *rfsimulator = calloc(sizeof(rfsimulator_state_t), 1);
1072 1073 1074 1075 1076
  // initialize channel simulation
  rfsimulator->tx_num_channels=openair0_cfg->tx_num_channels;
  rfsimulator->rx_num_channels=openair0_cfg->rx_num_channels;
  rfsimulator->sample_rate=openair0_cfg->sample_rate;
  rfsimulator->tx_bw=openair0_cfg->tx_bw;  
1077
  rfsimulator_readconfig(rfsimulator);
1078
  LOG_W(HW, "sample_rate %f\n", rfsimulator->sample_rate);
1079
  pthread_mutex_init(&Sockmutex, NULL);
1080 1081 1082 1083 1084
  LOG_I(HW,
        "Running as %s\n",
        rfsimulator->role == SIMU_ROLE_SERVER ? "server waiting opposite rfsimulators to connect"
                                              : "client: will connect to a rfsimulator server side");
  device->trx_start_func = rfsimulator->role == SIMU_ROLE_SERVER ? startServer : startClient;
1085 1086 1087 1088 1089 1090 1091 1092 1093
  device->trx_get_stats_func   = rfsimulator_get_stats;
  device->trx_reset_stats_func = rfsimulator_reset_stats;
  device->trx_end_func         = rfsimulator_end;
  device->trx_stop_func        = rfsimulator_stop;
  device->trx_set_freq_func    = rfsimulator_set_freq;
  device->trx_set_gains_func   = rfsimulator_set_gains;
  device->trx_write_func       = rfsimulator_write;
  device->trx_read_func      = rfsimulator_read;
  /* let's pretend to be a b2x0 */
Laurent Thomas's avatar
Laurent Thomas committed
1094
  device->type = RFSIMULATOR;
francescomani's avatar
francescomani committed
1095
  openair0_cfg[0].rx_gain[0] = 0;
1096 1097
  device->openair0_cfg=&openair0_cfg[0];
  device->priv = rfsimulator;
WANG Tsu-Han's avatar
WANG Tsu-Han committed
1098
  device->trx_write_init = rfsimulator_write_init;
1099

1100
  for (int i = 0; i < MAX_FD_RFSIMU; i++)
1101 1102
    rfsimulator->buf[i].conn_sock=-1;

1103
  AssertFatal((rfsimulator->epollfd = epoll_create1(0)) != -1, "epoll_create1() failed, errno(%d)", errno);
1104
  // we need to call randominit() for telnet server (use gaussdouble=>uniformrand)
1105
  randominit(0);
1106
  set_taus_seed(0);
1107
  /* look for telnet server, if it is loaded, add the channel modeling commands to it */
frtabu's avatar
frtabu committed
1108
  add_telnetcmd_func_t addcmd = (add_telnetcmd_func_t)get_shlibmodule_fptr("telnetsrv", TELNET_ADDCMD_FNAME);
1109

frtabu's avatar
frtabu committed
1110
  if (addcmd != NULL) {
1111
    rfsimulator->poll_telnetcmdq =  (poll_telnetcmdq_func_t)get_shlibmodule_fptr("telnetsrv", TELNET_POLLCMDQ_FNAME);
frtabu's avatar
frtabu committed
1112
    addcmd("rfsimu",rfsimu_vardef,rfsimu_cmdarray);
1113

frtabu's avatar
frtabu committed
1114
    for(int i=0; rfsimu_cmdarray[i].cmdfunc != NULL; i++) {
1115
      if (  rfsimu_cmdarray[i].qptr != NULL) {
frtabu's avatar
frtabu committed
1116 1117 1118
        rfsimulator->telnetcmd_qid = rfsimu_cmdarray[i].qptr;
        break;
      }
1119 1120 1121
    }
  }

1122 1123 1124 1125 1126 1127
  /* write on a socket fails if the other end is closed and we get SIGPIPE */
  if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
    perror("SIGPIPE");
    exit(1);
  }

1128 1129
  return 0;
}