Merge remote-tracking branch 'origin/develop' into threading-rk

This commit is contained in:
Raymond Knopp
2023-03-27 16:09:33 +02:00
500 changed files with 33266 additions and 20653 deletions
+4 -1
View File
@@ -110,7 +110,10 @@ int load_lib(openair0_device *device,
openair0_cfg->recplay_mode = read_recplayconfig(&(openair0_cfg->recplay_conf),&(device->recplay_state));
if ( openair0_cfg->recplay_mode == RECPLAY_REPLAYMODE ) {
if (openair0_cfg->recplay_mode == RECPLAY_RECORDMODE) {
set_softmodem_optmask(SOFTMODEM_RECRECORD_BIT); // softmodem has to know we use the iqrecorder to workaround randomized algorithms
}
if (openair0_cfg->recplay_mode == RECPLAY_REPLAYMODE) {
deflibname=OAI_IQPLAYER_LIBNAME;
shlib_fdesc[0].fname="device_init";
set_softmodem_optmask(SOFTMODEM_RECPLAY_BIT); // softmodem has to know we use the iqplayer to workaround randomized algorithms
+1 -1
View File
@@ -605,7 +605,7 @@ extern "C"
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#define DEVICE_PARAMS_DESC {\
{ CONFIG_DEVICEOPT_NAME, CONFIG_HLP_DEVICE, 0, strptr:&devname, defstrval:NULL, TYPE_STRING, 0}\
{ CONFIG_DEVICEOPT_NAME, CONFIG_HLP_DEVICE, 0, .strptr=&devname, .defstrval=NULL, TYPE_STRING, 0}\
}
+9 -16
View File
@@ -40,13 +40,11 @@
* recplay_state: store recorder or player data while the device runs
*/
int read_recplayconfig(recplay_conf_t **recplay_conf, recplay_state_t **recplay_state) {
unsigned int u_sf_record = 0; // record mode
unsigned int u_sf_replay = 0; // replay mode
*recplay_conf = calloc(sizeof(recplay_conf_t),1);
paramdef_t device_recplay_params[]=DEVICE_RECPLAY_PARAMS_DESC ;
config_get(device_recplay_params,sizeof(device_recplay_params)/sizeof(paramdef_t),DEVICE_RECPLAY_SECTION);
if (u_sf_record || u_sf_replay ) {
if ((*recplay_conf)->u_sf_record || (*recplay_conf)->u_sf_replay ) {
struct sysinfo systeminfo;
*recplay_state = calloc(sizeof(recplay_state_t),1);
@@ -62,17 +60,18 @@ int read_recplayconfig(recplay_conf_t **recplay_conf, recplay_state_t **recplay_
LOG_W(HW,"System with %f GB of mem (<6GB), mmap usage disabled\n",systeminfo.totalram/10E9);
(*recplay_conf)->use_mmap = 0;
}
} else { /* record player enabled */
} else { /* player-recorder disable */
free(*recplay_conf);
*recplay_conf=NULL;
return RECPLAY_DISABLED;
}
if (u_sf_replay == 1)
if ((*recplay_conf)->u_sf_replay == 1)
return RECPLAY_REPLAYMODE;
else if (u_sf_record == 1)
else if ((*recplay_conf)->u_sf_record == 1)
return RECPLAY_RECORDMODE;
return 0;
return RECPLAY_DISABLED;
}
/*! \brief Terminate operation of the oai iq recorder. to be called by any device
@@ -81,21 +80,15 @@ int read_recplayconfig(recplay_conf_t **recplay_conf, recplay_state_t **recplay_
*/
void iqrecorder_end(openair0_device *device) {
if (device->recplay_state != NULL) { // subframes store
iqfile_header_t fh = {device->type,device->openair0_cfg->tx_sample_advance, device->openair0_cfg->rx_bw,0,OAIIQFILE_ID};
iqfile_header_t fh = {device->type,device->openair0_cfg->tx_sample_advance, device->openair0_cfg->rx_bw,0,OAIIQFILE_ID};
recplay_state_t *rs = device->recplay_state;
recplay_conf_t *rc = device->openair0_cfg[0].recplay_conf;
recplay_conf_t *rc = device->openair0_cfg[0].recplay_conf;
rs->pFile = fopen (rc->u_sf_filename,"wb+");
if (rs->pFile == NULL) {
LOG_E(HW,"Cannot open %s\n", rc->u_sf_filename);
} else {
unsigned int i = 0;
unsigned int modu = 0;
if ((modu = rs->nbSamplesBlocks % 10) != 0) {
rs->nbSamplesBlocks -= modu; // store entire number of frames
}
fh.nbSamplesBlocks=rs->nbSamplesBlocks;
LOG_I(HW,"Writing file header to %s \n", rc->u_sf_filename );
fwrite(&fh, sizeof(fh), 1, rs->pFile);
@@ -103,7 +96,7 @@ void iqrecorder_end(openair0_device *device) {
uint8_t *ptr=(uint8_t *)rs->ms_sample;
for (i = 0; i < rs->nbSamplesBlocks; i++) {
int blockBytes=sizeof(iqrec_t)+((iqrec_t *)ptr)->nbBytes;
int blockBytes=sizeof(iqrec_t)+BELL_LABS_IQ_BYTES_PER_SF;
fwrite(ptr, sizeof(unsigned char), blockBytes, rs->pFile);
ptr+=blockBytes;
}
+19 -16
View File
@@ -44,9 +44,8 @@ extern "C"
#define RECPLAY_REPLAYMODE 2
#define BELL_LABS_IQ_HEADER 0xabababababababab
#define BELL_LABS_IQ_PER_SF 46080 // 7680 => 5MHz bw for now; 46080 => 3/4 40MHz (106 PRBs)
#define BELL_LABS_IQ_PER_SF 23040 // 23040 => 46080/2 slots => 3/4 40MHz (106 PRBs)
#define BELL_LABS_IQ_BYTES_PER_SF (BELL_LABS_IQ_PER_SF * 4)
#define MAX_BELL_LABS_IQ_BYTES_PER_SF BELL_LABS_IQ_BYTES_PER_SF*10
#define OAIIQFILE_ID {'O', 'I','Q','F'}
typedef struct {
@@ -61,7 +60,9 @@ typedef struct {
int64_t header;
int64_t ts;
int64_t nbBytes;
int64_t rfu2; // pad for 256 bits alignement required by AVX2
int64_t tv_sec; // nb of secs since EPOCH
int64_t tv_usec; // nb of µsecs since EPOCH
int64_t rfu2; // pad for 256 bits alignement required by AVX2
} iqrec_t;
#define DEF_NB_SF 120000 // default nb of sf or ms to capture (2 minutes at 5MHz)
#define DEF_SF_FILE "/tmp/iqfile" // default subframes file name
@@ -95,29 +96,31 @@ typedef struct {
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#define DEVICE_RECPLAY_PARAMS_DESC { \
{CONFIG_OPT_SF_FILE, CONFIG_HLP_SF_FILE, 0, strptr:&((*recplay_conf)->u_sf_filename), defstrval:DEF_SF_FILE, TYPE_STRING, 0}, \
{CONFIG_OPT_SF_REC, CONFIG_HLP_SF_REC, PARAMFLAG_BOOL, uptr:&(u_sf_record), defuintval:0, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_REP, CONFIG_HLP_SF_REP, PARAMFLAG_BOOL, uptr:&(u_sf_replay), defuintval:0, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_MAX, CONFIG_HLP_SF_MAX, 0, uptr:&((*recplay_conf)->u_sf_max), defintval:DEF_NB_SF, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_LOOPS, CONFIG_HLP_SF_LOOPS, 0, uptr:&((*recplay_conf)->u_sf_loops), defintval:DEF_SF_NB_LOOP, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_RDELAY, CONFIG_HLP_SF_RDELAY, 0, uptr:&((*recplay_conf)->u_sf_read_delay), defintval:DEF_SF_DELAY_READ, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_WDELAY, CONFIG_HLP_SF_WDELAY, 0, uptr:&((*recplay_conf)->u_sf_write_delay), defintval:DEF_SF_DELAY_WRITE, TYPE_UINT, 0}, \
{CONFIG_OPT_USE_MMAP, CONFIG_HLP_USE_MMAP, PARAMFLAG_BOOL, uptr:&((*recplay_conf)->use_mmap), defuintval:1, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_FILE, CONFIG_HLP_SF_FILE, 0, .strptr=&((*recplay_conf)->u_sf_filename), .defstrval=DEF_SF_FILE, TYPE_STRING, 0}, \
{CONFIG_OPT_SF_REC, CONFIG_HLP_SF_REC, PARAMFLAG_BOOL, .uptr=&((*recplay_conf)->u_sf_record), .defuintval=0, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_REP, CONFIG_HLP_SF_REP, PARAMFLAG_BOOL, .uptr=&((*recplay_conf)->u_sf_replay), .defuintval=0, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_MAX, CONFIG_HLP_SF_MAX, 0, .uptr=&((*recplay_conf)->u_sf_max), .defintval=DEF_NB_SF, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_LOOPS, CONFIG_HLP_SF_LOOPS, 0, .uptr=&((*recplay_conf)->u_sf_loops), .defintval=DEF_SF_NB_LOOP, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_RDELAY, CONFIG_HLP_SF_RDELAY, 0, .uptr=&((*recplay_conf)->u_sf_read_delay), .defintval=DEF_SF_DELAY_READ, TYPE_UINT, 0}, \
{CONFIG_OPT_SF_WDELAY, CONFIG_HLP_SF_WDELAY, 0, .uptr=&((*recplay_conf)->u_sf_write_delay), .defintval=DEF_SF_DELAY_WRITE, TYPE_UINT, 0}, \
{CONFIG_OPT_USE_MMAP, CONFIG_HLP_USE_MMAP, PARAMFLAG_BOOL, .uptr=&((*recplay_conf)->use_mmap), .defuintval=1, TYPE_UINT, 0}, \
}/*! \brief Record Player Configuration and state */
typedef struct {
char *u_sf_filename; // subframes file path
unsigned int u_sf_max ; // max number of recorded subframes
unsigned int u_sf_loops ; // number of loops in replay mode
char *u_sf_filename; // subframes file path
unsigned int u_sf_max ; // max number of recorded subframes
unsigned int u_sf_loops ; // number of loops in replay mode
unsigned int u_sf_read_delay; // read delay in replay mode
unsigned int u_sf_write_delay ; // write delay in replay mode
unsigned int use_mmap; // default is to use mmap
unsigned int use_mmap; // default is to use mmap
unsigned int u_sf_replay; // replay mode (if 1)
unsigned int u_sf_record; // record mode (if 1)
} recplay_conf_t;
typedef struct {
size_t mapsize;
FILE *pFile;
int fd;
iqrec_t *ms_sample; // memory for all subframes
iqrec_t *ms_sample; // memory for all subframes
unsigned int nbSamplesBlocks;
uint8_t *currentPtr;
uint64_t currentTs;
+36
View File
@@ -0,0 +1,36 @@
USRP device documentation
OAI works with the most common USRP models like, B200, B200mini, B210, X310, N300, N310, N320, X410. This is achieved by using the Ettus Universal Hardware Driver (UHD) (https://github.com/EttusResearch/uhd). The file usrp_lib.cpp provides an abstraction layer of UHD to OAI.
The USRP can be configured in the RU section of the config file. The field "sdr_addrs" uses the same syntax as the USRP device identification string (https://files.ettus.com/manual/page_identification.html). Here are a few examples
sdr_addrs = "addr=192.168.10.2" # uses a single 10Gb Ethernet interface on an N3x0 or X3x0 or X4x0
sdr_addrs = "addr=192.168.10.2,second_addr=192.168.20.2" # uses 2 10Gb Ethernet interfaces on a N3x0 or X3x0 or X4x0 (requires that you flashed the FPGA wth the XG image)
you can also use the multi USRP feature and specify multiple USRPs, in which case you will get the aggregated number of channels on all the devices
sdr_addrs = "addr0=192.168.10.2,addr1=192.168.30.2"
You can specify if you want to use external or interal clock or time source either by adding the parameters in the sdr_addrs field or by using the fields clock_src or time_src
sdr_addrs = "addr=192.168.10.2,clock_source=external,time_source=external"
is equivalent to
sdr_addrs = "addr=192.168.10.2"
clock_src = "external"
time_src = "external"
Valid choices for clock and time source are "internal", "external", and "gpsdo"
Careful: the USRP remembers the choice of the clock source. If you want to make sure it uses always the same, always specify the clock_source and time_source.
Last but not least you may specify that only a specfic subdevice of the USRP is used. See also https://files.ettus.com/manual/page_configuration.html#config_subdev
For example on a USRP N310 the following fields will specify that you use channel 0 of subdevice A.
tx_subdev = "A:0"
rx_subdev = "A:0"
When combining this with the multi USRP feature you can easily create a distributed antenna array with only 1 channel used at each USRP.
+39 -48
View File
@@ -659,36 +659,12 @@ int trx_usrp_write_init(openair0_device *device){
printf("end of tx write thread\n");
pthread_mutex_init(&write_thread->mutex_write, NULL);
pthread_cond_init(&write_thread->cond_write, NULL);
struct sched_param sparam={0};
sparam.sched_priority = sched_get_priority_max(SCHED_RR);
pthread_attr_t attr;
int ret=pthread_attr_init(&attr);
if (ret!=0) {
printf("error initializing USRP tx-thread attribute ret %d, errno: %d\n",ret,errno);
exit(-1);
}
else printf("USRP tx-thread attribute initialized\n");
ret=pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
if (ret!=0) {
printf("error setting USRP tx-thread inheritance ret %d, errno: %d\n",ret,errno);
exit(-1);
}
else printf("USRP tx-thread inheritance set\n");
ret=pthread_attr_setschedpolicy(&attr, SCHED_RR);
if (ret!=0) {
printf("error setting USRP tx-thread scheduling policy ret %d, errno: %d\n",ret,errno);
exit(-1);
}
else printf("USRP tx-thread scheduling policy set to SCHED_RR\n");
ret = pthread_attr_setschedparam(&attr, &sparam);
if (ret!=0) {
printf("error setting USRP tx-thread priority ret %d, errno: %d\n",ret,errno);
exit(-1);
}
else printf("USRP tx-thread priority set to %d\n",sparam.sched_priority);
pthread_create(&write_thread->pthread_write,&attr,trx_usrp_write_thread,(void *)device);
threadCreate(&write_thread->pthread_write,
trx_usrp_write_thread,
(void *)device,
(char*)"trx_usrp_write_thread",
-1,
OAI_PRIORITY_RT_MAX);
return(0);
}
@@ -727,7 +703,7 @@ static int trx_usrp_read(openair0_device *device, openair0_timestamp *ptimestamp
nsamps2 = (nsamps+3)>>2;
int16x8_t buff_tmp[cc<2 ? 2 : cc][nsamps2];
#endif
static int read_count = 0;
int rxshift;
switch (device->type) {
case USRP_B200_DEV:
@@ -798,25 +774,30 @@ static int trx_usrp_read(openair0_device *device, openair0_timestamp *ptimestamp
recplay_state_t *recPlay=device->recplay_state;
if ( recPlay != NULL) { // record mode
if (device->openair0_cfg->recplay_mode == RECPLAY_RECORDMODE) { // record mode
// Copy subframes to memory (later dump on a file)
if (recPlay->nbSamplesBlocks < device->openair0_cfg->recplay_conf->u_sf_max &&
recPlay->maxSizeBytes > (recPlay->currentPtr-(uint8_t *)recPlay->ms_sample) +
sizeof(iqrec_t) + nsamps*4 ) {
// The number of read samples might differ from BELL_LABS_IQ_BYTES_PER_SF
// The number of read samples is always stored in nbBytes but the record is always of BELL_LABS_IQ_BYTES_PER_SF size
if (recPlay->nbSamplesBlocks <= device->openair0_cfg->recplay_conf->u_sf_max &&
recPlay->maxSizeBytes >= (recPlay->currentPtr-(uint8_t *)recPlay->ms_sample) +
sizeof(iqrec_t) + BELL_LABS_IQ_BYTES_PER_SF) {
iqrec_t *hdr=(iqrec_t *)recPlay->currentPtr;
struct timespec trec;
(void) clock_gettime(CLOCK_REALTIME, &trec);
hdr->header = BELL_LABS_IQ_HEADER;
hdr->ts = *ptimestamp;
hdr->nbBytes=nsamps*4;
hdr->nbBytes=nsamps*4; // real number of samples bytes
hdr->tv_sec = trec.tv_sec; // record secs
hdr->tv_usec = trec.tv_nsec/1000; // record µsecs
memcpy(hdr+1, buff[0], nsamps*4);
recPlay->currentPtr+=sizeof(iqrec_t)+nsamps*4;
recPlay->currentPtr+=sizeof(iqrec_t)+BELL_LABS_IQ_BYTES_PER_SF; // record size is constant (BELL_LABS_IQ_BYTES_PER_SF)
recPlay->nbSamplesBlocks++;
#if 0 // BMC: this is too verbose
LOG_D(HW,"recorded %d samples, for TS %lu, shift in buffer %ld\n", nsamps, hdr->ts, recPlay->currentPtr-(uint8_t *)recPlay->ms_sample);
#endif
LOG_D(HW,"recorded %d samples, for TS %lu, shift in buffer %ld nbBytes %d nbSamplesBlocks %d\n", nsamps, hdr->ts, recPlay->currentPtr-(uint8_t *)recPlay->ms_sample, (int)hdr->nbBytes, (int)recPlay->nbSamplesBlocks);
} else
exit_function(__FILE__, __FUNCTION__, __LINE__,"Recording reaches max iq limit\n");
exit_function(__FILE__, __FUNCTION__, __LINE__, "Recording reaches max iq limit\n", OAI_EXIT_NORMAL);
}
read_count++;
LOG_D(HW,"usrp_lib: returning %d samples at ts %lu read_count %d\n", samples_received, *ptimestamp, read_count);
return samples_received;
}
@@ -1096,18 +1077,28 @@ extern "C" {
return -1;
}
LOG_I(HW,"Found USRP %s\n", device_adds[0].get("type").c_str());
std::string type_str, product_str;
if (args.find("addr0") != std::string::npos) {
type_str = "type0";
product_str = "product0";
}
else {
type_str = "type";
product_str = "product";
}
LOG_I(HW,"Found USRP %s\n", device_adds[0].get(type_str).c_str());
double usrp_master_clock;
if (device_adds[0].get("type") == "b200") {
if (device_adds[0].get(type_str) == "b200") {
device->type = USRP_B200_DEV;
usrp_master_clock = 30.72e6;
args += boost::str(boost::format(",master_clock_rate=%f") % usrp_master_clock);
args += ",num_send_frames=256,num_recv_frames=256, send_frame_size=7680, recv_frame_size=7680" ;
}
if (device_adds[0].get("type") == "n3xx") {
const std::string product = device_adds[0].get("product");
if (device_adds[0].get(type_str) == "n3xx") {
const std::string product = device_adds[0].get(product_str);
printf("Found USRP %s\n", product.c_str());
device->type=USRP_N300_DEV;
if (product == "n320")
@@ -1120,7 +1111,7 @@ extern "C" {
LOG_W(HW,"Can't set kernel parameters for N3x0\n");
}
if (device_adds[0].get("type") == "x300") {
if (device_adds[0].get(type_str) == "x300") {
printf("Found USRP x300\n");
device->type=USRP_X300_DEV;
usrp_master_clock = 184.32e6;
@@ -1131,7 +1122,7 @@ extern "C" {
LOG_W(HW,"Can't set kernel parameters for X3xx\n");
}
if (device_adds[0].get("type") == "x4xx") {
if (device_adds[0].get(type_str) == "x4xx") {
printf("Found USRP x400\n");
device->type = USRP_X400_DEV;
usrp_master_clock = 245.76e6;
+114 -27
View File
@@ -35,10 +35,33 @@
#include <time.h>
#include <sys/resource.h>
#include <errno.h>
#include <math.h>
#include <sys/time.h>
#include "common_lib.h"
#include "assertions.h"
#include "common/utils/LOG/log.h"
static void busy_wait(uint32_t usecs) {
uint32_t elapsed_usecs;
double temp;
int j;
struct timespec requestStart, requestEnd;
// Active wait
clock_gettime(CLOCK_REALTIME, &requestStart);
temp = 0;
while (1) {
for (j=0; j < 40; j++)
temp += sin(j);
clock_gettime(CLOCK_REALTIME, &requestEnd);
elapsed_usecs = ( requestEnd.tv_sec - requestStart.tv_sec ) / 1e-6
+ ( requestEnd.tv_nsec - requestStart.tv_nsec ) / 1e3;
if (elapsed_usecs >= usecs)
break;
}
//LOG_I(HW, "Expected: %d Elapsed: %d us\n", usecs, elapsed_usecs);
}
static void parse_iqfile_header(openair0_device *device, iqfile_header_t *iq_fh) {
char tmp[4]=OAIIQFILE_ID;
AssertFatal((memcmp(iq_fh->oaiid,tmp,sizeof(iq_fh->oaiid)) == 0),
@@ -92,8 +115,8 @@ static int iqplayer_loadfile(openair0_device *device, openair0_config_t *openair
fstat(s->fd, &sb);
s->mapsize=sb.st_size;
LOG_I(HW, "Loading %u subframes from %s,size=%lu bytes ...\n",s->nbSamplesBlocks, c->u_sf_filename,(uint64_t)sb.st_size);
// allocate buffer for 1 sample at a time
s->ms_sample = (iqrec_t *) malloc(sizeof(iqrec_t)+MAX_BELL_LABS_IQ_BYTES_PER_SF*4);
// allocate buffer for 1 subframe at a time
s->ms_sample = (iqrec_t *) malloc(sizeof(iqrec_t)+BELL_LABS_IQ_BYTES_PER_SF);
if (s->ms_sample == NULL) {
LOG_E(HW,"Memory allocation failed for individual subframe replay mode.\n" );
@@ -120,6 +143,56 @@ static int iqplayer_loadfile(openair0_device *device, openair0_config_t *openair
return 0;
}
/*! \brief print the device statistics
* \param device the hardware to use
* \returns 0 on success
*/
static int trx_iqplayer_get_stats(openair0_device *device)
{
LOG_I(HW, "trx_iqplayer_get_stats() called, not implemented\n");
return 0;
}
/*! \brief Reset device statistics
* \param device the hardware to use
* \returns 0 in success
*/
static int trx_iqplayer_reset_stats(openair0_device *device)
{
LOG_I(HW, "trx_iqplayer_reset_stats() called, not implemented\n");
return 0;
}
/*! \brief Stop operation of the transceiver
*/
static int trx_iqplayer_stop(openair0_device *device)
{
LOG_I(HW, "trx_iqplayer_stop() called, not implemented\n");
return 0;
}
/*! \brief Set RX frequencies
* \param device the hardware to use
* \param openair0_cfg RF frontend parameters set by application
* \returns 0 in success
*/
static int trx_iqplayer_set_freq(openair0_device *device, openair0_config_t *openair0_cfg)
{
LOG_I(HW, "trx_iqplayer_set_freq() called, not implemented\n");
return 0;
}
/*! \brief Set gains
* \param device the hardware to use
* \param openair0_cfg RF frontend parameters set by application
* \returns 0 in success
*/
static int trx_iqplayer_set_gains(openair0_device *device, openair0_config_t *openair0_cfg)
{
LOG_I(HW, "trx_iqplayer_set_gains() called, not implemented\n");
return 0;
}
/*! \brief start the oai iq player
* \param device, the hardware used
*/
@@ -181,24 +254,28 @@ static int trx_iqplayer_write(openair0_device *device, openair0_timestamp timest
*/
static int trx_iqplayer_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc) {
recplay_state_t *s = device->recplay_state;
static struct timeval tprev;
static int read_count = 0;
if (s->curSamplesBlock==0 && s->wrap_count==0) {
s->currentTs=s->ms_sample->ts;
tprev.tv_sec = s->ms_sample->tv_sec;
tprev.tv_usec = s->ms_sample->tv_usec;
LOG_I(HW, "First timestamp=%lu s->nbSamplesBlocks=%u\n", s->currentTs, s->nbSamplesBlocks);
}
if (s->curSamplesBlock == s->nbSamplesBlocks) {
LOG_I(HW, "wrapping on iq file (%ld)\n", s->wrap_count);
s->curSamplesBlock = 0;
s->wrap_count++;
if (s->wrap_count == device->openair0_cfg->recplay_conf->u_sf_loops) {
LOG_W(HW, "iqplayer device terminating subframes replay after %u iteration\n",
device->openair0_cfg->recplay_conf->u_sf_loops);
exit_function(__FILE__, __FUNCTION__, __LINE__,"replay ended, triggering process termination\n");
exit_function(__FILE__, __FUNCTION__, __LINE__,"replay ended, triggering process termination\n", OAI_EXIT_NORMAL);
}
LOG_I(HW,"go back at the beginning of IQ file");
if (s->wrap_count > 0) {
LOG_I(HW, "wrapping on iq file (%ld)\n", s->wrap_count);
}
s->curSamplesBlock = 0;
s->wrap_count++;
device->recplay_state->currentPtr=(uint8_t *)device->recplay_state->ms_sample;
if (!(device->openair0_cfg->recplay_conf->use_mmap) ) {
@@ -210,20 +287,23 @@ static int trx_iqplayer_read(openair0_device *device, openair0_timestamp *ptimes
if (!(device->openair0_cfg->recplay_conf->use_mmap)) {
// read sample from file
if (read(s->fd, s->ms_sample, sizeof(iqrec_t)) != sizeof(iqrec_t)) {
LOG_E(HW,"pb reading iqfile at index %lu %s\n",sizeof(iqrec_t)*s->curSamplesBlock, strerror(errno) );
LOG_E(HW,"pb reading iq record header at index %lu %s\n",sizeof(iqrec_t)*s->curSamplesBlock, strerror(errno) );
exit(-1);
} else {
if (read(s->fd, s->ms_sample+1, s->ms_sample->nbBytes) != s->ms_sample->nbBytes) {
LOG_E(HW,"pb reading iqfile at index %lu %s\n",sizeof(iqrec_t)*s->curSamplesBlock, strerror(errno) );
if (read(s->fd, s->ms_sample+1, BELL_LABS_IQ_BYTES_PER_SF) != BELL_LABS_IQ_BYTES_PER_SF) {
LOG_E(HW,"pb reading iq record data at index %lu %s\n",sizeof(iqrec_t)*s->curSamplesBlock, strerror(errno) );
exit(-1);
}
}
}
iqrec_t *curHeader=(iqrec_t *)s->currentPtr;
AssertFatal(curHeader->header==BELL_LABS_IQ_HEADER,"" );
if (curHeader->header != BELL_LABS_IQ_HEADER) {
LOG_I(HW,"Problem iq header nsamps %d\n", nsamps);
AssertFatal(curHeader->header==BELL_LABS_IQ_HEADER,"" );
}
// the current timestamp is the stored timestamp until we wrap on input
AssertFatal(nsamps*4==curHeader->nbBytes,"");
AssertFatal(nsamps*4==curHeader->nbBytes,"nsamps=%d curHeader->nbBytes=%d", (int)nsamps, (int)curHeader->nbBytes);
*ptimestamp = s->currentTs;
memcpy(buff[0], curHeader+1, nsamps*4);
s->curSamplesBlock++;
@@ -231,33 +311,40 @@ static int trx_iqplayer_read(openair0_device *device, openair0_timestamp *ptimes
s->currentTs+=nsamps;
if (device->openair0_cfg->recplay_conf->use_mmap)
s->currentPtr+=sizeof(iqrec_t)+s->ms_sample->nbBytes;
s->currentPtr+=sizeof(iqrec_t)+BELL_LABS_IQ_BYTES_PER_SF;
// BMC TODO: support 1 second or more subframe read delay
struct timespec req;
req.tv_sec = 0;
req.tv_nsec = (device->openair0_cfg[0].recplay_conf->u_sf_read_delay) * 1000;
nanosleep(&req, NULL);
LOG_D(HW, "returning %d samples at ts %lu\n", nsamps, *ptimestamp);
// subframe read delay is not taken into account
// Real time interval is handle to mimic RF board latcency
struct timeval tcur, tdiff;
tcur.tv_sec = curHeader->tv_sec;
tcur.tv_usec = curHeader->tv_usec;
timersub(&tcur, &tprev, &tdiff);
if (tdiff.tv_usec > 0) {
busy_wait(tdiff.tv_usec);
}
tprev = tcur;
read_count++;
LOG_D(HW, "returning %d samples at ts %lu read_count %d\n", nsamps, *ptimestamp, read_count);
return nsamps;
}
int device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
device->openair0_cfg = openair0_cfg;
device->trx_start_func = trx_iqplayer_start;
device->trx_get_stats_func = NULL;
device->trx_reset_stats_func = NULL;
device->trx_end_func = trx_iqplayer_end;
device->trx_stop_func = NULL;
device->trx_set_freq_func = NULL;
device->trx_set_gains_func = NULL;
device->trx_get_stats_func = trx_iqplayer_get_stats;
device->trx_reset_stats_func = trx_iqplayer_reset_stats;
device->trx_end_func = trx_iqplayer_end;
device->trx_stop_func = trx_iqplayer_stop;
device->trx_set_freq_func = trx_iqplayer_set_freq;
device->trx_set_gains_func = trx_iqplayer_set_gains;
// Replay subframes from from file
// openair0_cfg[0].rx_gain_calib_table = calib_table_b210_38;
// bw_gain_adjust=1;
device->trx_write_func = trx_iqplayer_write;
device->trx_read_func = trx_iqplayer_read;
iqplayer_loadfile(device, openair0_cfg);
LOG_UI(HW,"iqplayer device initialized, replay %s for %i iterations",openair0_cfg->recplay_conf->u_sf_filename,openair0_cfg->recplay_conf->u_sf_loops);
LOG_UI(HW,"iqplayer device initialized, replay %s for %i iteration(s)\n",openair0_cfg->recplay_conf->u_sf_filename,openair0_cfg->recplay_conf->u_sf_loops);
return 0;
}
/*@}*/
+1 -1
View File
@@ -115,7 +115,7 @@ make rfsimulator
### Launch gNB in one window
```bash
sudo RFSIMULATOR=server ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf --parallel-config PARALLEL_SINGLE_THREAD --rfsim --phy-test --nokrnmod 1
sudo RFSIMULATOR=server ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf --parallel-config PARALLEL_SINGLE_THREAD --rfsim --phy-test
```
### Launch UE in another window
+4 -2
View File
@@ -84,7 +84,8 @@
{"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, 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 },\
};
static void getset_currentchannels_type(char *buf, int debug, webdatadef_t *tdata, telnet_printfunc_t prnt);
@@ -141,6 +142,7 @@ typedef struct {
float noise_power_dB;
void *telnetcmd_qid;
poll_telnetcmdq_func_t poll_telnetcmdq;
int wait_timeout;
} rfsimulator_state_t;
@@ -817,7 +819,7 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest
if ( t->nextRxTstamp == 0)
LOG_W(HW,"No connected device, generating void samples...\n");
if (!flushInput(t, 1, nsamps)) {
if (!flushInput(t, t->wait_timeout, nsamps)) {
for (int x=0; x < nbAnt; x++)
memset(samplesVoid[x],0,sampleToByte(nsamps,1));