Merge branch 'develop' into feature-127-protocol-split
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
BladeRF documentation
|
||||
=====================
|
||||
|
||||
As of 2018-11-06, the bladeRF support is not fully automatic and requires
|
||||
some manual settings before use. This documentation is the ultimate source
|
||||
of information. If something described in this file does not work or does
|
||||
not correspond to the reality, then contact us so we can fix the problems
|
||||
and update this documentation.
|
||||
|
||||
1. Install bladeRF 2.0 libraries.
|
||||
|
||||
As of now, it's better to install from source.
|
||||
So, do not run: ./build_oai -I -w BLADERF
|
||||
(That is: do not include '-w BLADERF'.)
|
||||
|
||||
Instead, follow the instructions at: https://github.com/Nuand/bladeRF
|
||||
|
||||
If you already had some bladeRF software installed using automatic
|
||||
methods, first remove it by hand ('apt-get purge bladeRF' or something
|
||||
similar, you can get the list of installed bladeRF packages by running
|
||||
'dpkg -l|grep -i blade', remove them all).
|
||||
|
||||
2. Update the device.
|
||||
|
||||
Download the latest FX3 firmware and FPGA images from Nuand's website.
|
||||
As of writing, this is:
|
||||
|
||||
https://github.com/Nuand/bladeRF/wiki
|
||||
|
||||
That points to the following pages.
|
||||
For FX3:
|
||||
|
||||
http://www.nuand.com/fx3_images/
|
||||
|
||||
For FPGA:
|
||||
|
||||
http://www.nuand.com/fpga_images/
|
||||
|
||||
Install FX3 firmware:
|
||||
|
||||
sudo bladeRF-cli -f bladeRF_fw_latest.img
|
||||
|
||||
Install FPGA image (this is for BladeRF x40):
|
||||
|
||||
sudo bladeRF-cli -L hostedx40-latest.rbf
|
||||
|
||||
Retrieve calibration information:
|
||||
|
||||
sudo bladeRF-cli -i
|
||||
info
|
||||
|
||||
That outputs the serial number of your device.
|
||||
|
||||
Go to:
|
||||
|
||||
https://www.nuand.com/calibration
|
||||
|
||||
And enter your serial number.
|
||||
The website tells you to run something like:
|
||||
|
||||
sudo bladeRF-cli -i
|
||||
flash_init_cal 40 0x9271
|
||||
|
||||
Actual values depend on your device and serial number.
|
||||
|
||||
3. Calibrate the bladeRF device.
|
||||
|
||||
We will work with band 7 on 2.68GHz with a bandwidth of 5 MHz (25 RBs).
|
||||
|
||||
Plug the bladeRF device, then run:
|
||||
|
||||
sudo bladeRF-cli -i
|
||||
set frequency tx 2680000000
|
||||
set frequency rx 2560000000
|
||||
set gain rx 60
|
||||
set gain tx 60
|
||||
set bandwidth 5000000
|
||||
set samplerate 7680000
|
||||
cal lms
|
||||
cal lms
|
||||
cal lms
|
||||
cal dc rxtx
|
||||
cal dc rxtx
|
||||
cal dc rxtx
|
||||
|
||||
4. Tune the RX gain using the enb tracer.
|
||||
|
||||
Run the softmodem and the 'enb' tracer. For instructions, see:
|
||||
|
||||
https://gitlab.eurecom.fr/oai/openairinterface5g/wikis/T/basic
|
||||
|
||||
In the enb window, check the 'input signal'. You should see some blue
|
||||
signal as seen at:
|
||||
|
||||
https://gitlab.eurecom.fr/oai/openairinterface5g/wikis/T/enb
|
||||
|
||||
(the 'Time signal power' plot).
|
||||
|
||||
The level should be around 30.
|
||||
|
||||
If it's not around 30 then edit your configuration file and modify
|
||||
the value 'max_rxgain' in the section 'RUs'.
|
||||
|
||||
The configuration file to use is:
|
||||
|
||||
configuration/bladeRF/enb-band7-5mhz.conf
|
||||
|
||||
In the configuration file, you also need to set the correct values for:
|
||||
|
||||
- tracking_area_code
|
||||
- plmn_list: mcc, mnc, mnc_length
|
||||
- mme_ip_address: this is the IP address used by the computer running
|
||||
the softmodem to connect to the EPC
|
||||
- NETWORK_INTERFACES: all the ENB*ADDRESS* variables have to point
|
||||
to the IP address of the EPC machine
|
||||
|
||||
5. You're good to go.
|
||||
|
||||
You can now connect a UE and pass some traffic. If everything is well
|
||||
configured you can expect more than 16 Mb/s of throughput in the downlink
|
||||
using iperf and more than 8 Mb/s in the uplink. Looking at the logs, you
|
||||
should find lines containing 'PHR 40' and 'CQI 15'. If your values are
|
||||
lower then your setup may need some adjustments.
|
||||
|
||||
6. In case of problems.
|
||||
|
||||
If the performance of the softmodem is very bad, you can stop it and
|
||||
run the calibration again, without setting the parameters (frequencies,
|
||||
gains, etc.). Just run:
|
||||
|
||||
sudo bladeRF-cli -i
|
||||
cal lms
|
||||
cal dc rxtx
|
||||
|
||||
That may help.
|
||||
|
||||
Be sure to use proper radio equipment (duplexer, antennas, clean
|
||||
environment without interferences).
|
||||
File diff suppressed because it is too large
Load Diff
@@ -48,7 +48,7 @@ int set_device(openair0_device *device) {
|
||||
case USRP_B200_DEV:
|
||||
printf("[%s] has loaded USRP B200 device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"));
|
||||
break;
|
||||
case USRP_X300_DEV:
|
||||
case USRP_X300_DEV:
|
||||
printf("[%s] has loaded USRP X300 device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"));
|
||||
break;
|
||||
case BLADERF_DEV:
|
||||
@@ -57,6 +57,9 @@ case USRP_X300_DEV:
|
||||
case LMSSDR_DEV:
|
||||
printf("[%s] has loaded LMSSDR device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"));
|
||||
break;
|
||||
case IRIS_DEV:
|
||||
printf("[%s] has loaded Iris device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"));
|
||||
break;
|
||||
case NONE_DEV:
|
||||
printf("[%s] has not loaded a HW device.\n",((device->host_type == RAU_HOST) ? "RAU": "RRU"));
|
||||
break;
|
||||
|
||||
@@ -95,6 +95,8 @@ typedef enum {
|
||||
BLADERF_DEV,
|
||||
/*!\brief device is LMSSDR (SoDeRa)*/
|
||||
LMSSDR_DEV,
|
||||
/*!\brief device is Iris */
|
||||
IRIS_DEV,
|
||||
/*!\brief device is NONE*/
|
||||
NONE_DEV,
|
||||
MAX_RF_DEV_TYPE
|
||||
|
||||
@@ -244,7 +244,6 @@ int trx_eth_write_udp_IF4p5(openair0_device *device, openair0_timestamp timestam
|
||||
}
|
||||
|
||||
eth->tx_nsamps = nblocks;
|
||||
// printf("Sending %d bytes to %s:%d\n",packet_size,str,ntohs(eth->local_addrd.sin_port));
|
||||
|
||||
bytes_sent = sendto(eth->sockfdd,
|
||||
buff[0],
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/*! \file ethernet_lib.c
|
||||
/*! \file ethernet_lib.c
|
||||
* \brief API to stream I/Q samples over standard ethernet
|
||||
* \author add alcatel Katerina Trilyraki, Navid Nikaein, Pedro Dinis, Lucio Ferreira, Raymond Knopp
|
||||
* \date 2015
|
||||
@@ -55,113 +55,113 @@ int dest_addr_len[MAX_INST];
|
||||
|
||||
int trx_eth_start(openair0_device *device) {
|
||||
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
|
||||
/* initialize socket */
|
||||
if (eth->flags == ETH_RAW_MODE) {
|
||||
printf("Setting ETHERNET to ETH_RAW_IF5_MODE\n");
|
||||
if (eth_socket_init_raw(device)!=0) return -1;
|
||||
/* RRU gets device configuration - RAU sets device configuration*/
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
|
||||
printf("Setting Timenout to 999999 usecs\n");
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
/* initialize socket */
|
||||
if (eth->flags == ETH_RAW_MODE) {
|
||||
printf("Setting ETHERNET to ETH_RAW_IF5_MODE\n");
|
||||
if (eth_socket_init_raw(device)!=0) return -1;
|
||||
/* RRU gets device configuration - RAU sets device configuration*/
|
||||
|
||||
printf("Setting Timenout to 999999 usecs\n");
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
|
||||
/*
|
||||
if (device->host_type == RAU_HOST) {
|
||||
if(eth_set_dev_conf_raw(device)!=0) return -1;
|
||||
} else {
|
||||
if(eth_get_dev_conf_raw(device)!=0) return -1;
|
||||
}*/
|
||||
|
||||
/* adjust MTU wrt number of samples per packet */
|
||||
if(eth->compression == ALAW_COMPRESS) {
|
||||
if(ethernet_tune (device,MTU_SIZE,RAW_PACKET_SIZE_BYTES_ALAW(device->openair0_cfg->samples_per_packet))!=0) return -1;
|
||||
} else {
|
||||
if(ethernet_tune (device,MTU_SIZE,RAW_PACKET_SIZE_BYTES(device->openair0_cfg->samples_per_packet))!=0) return -1;
|
||||
}
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
} else if (eth->flags == ETH_RAW_IF4p5_MODE) {
|
||||
|
||||
printf("Setting ETHERNET to ETH_RAW_IF4p5_MODE\n");
|
||||
if (eth_socket_init_raw(device)!=0) return -1;
|
||||
|
||||
printf("Setting Timenout to 999999 usecs\n");
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
|
||||
/*
|
||||
if (device->host_type == RAU_HOST) {
|
||||
if(eth_set_dev_conf_raw_IF4p5(device)!=0) return -1;
|
||||
} else {
|
||||
if(eth_get_dev_conf_raw_IF4p5(device)!=0) return -1;
|
||||
}
|
||||
*/
|
||||
|
||||
/* adjust MTU wrt number of samples per packet */
|
||||
if(ethernet_tune (device,MTU_SIZE,RAW_IF4p5_PRACH_SIZE_BYTES)!=0) return -1;
|
||||
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
} else if (eth->flags == ETH_UDP_IF4p5_MODE) {
|
||||
printf("Setting ETHERNET to UDP_IF4p5_MODE\n");
|
||||
if (eth_socket_init_udp(device)!=0) return -1;
|
||||
|
||||
printf("Setting Timeout to 999999 usecs\n");
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
|
||||
/*
|
||||
if (device->host_type == RAU_HOST) {
|
||||
if(eth_set_dev_conf_udp(device)!=0) return -1;
|
||||
} else {
|
||||
if(eth_get_dev_conf_udp(device)!=0) return -1;
|
||||
}
|
||||
*/
|
||||
|
||||
/* adjust MTU wrt number of samples per packet */
|
||||
if(ethernet_tune (device,MTU_SIZE,UDP_IF4p5_PRACH_SIZE_BYTES)!=0) return -1;
|
||||
|
||||
|
||||
|
||||
|
||||
} else if (eth->flags == ETH_RAW_IF5_MOBIPASS) {
|
||||
printf("Setting ETHERNET to RAW_IF5_MODE\n");
|
||||
if (eth_socket_init_raw(device)!=0) return -1;
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
|
||||
/*
|
||||
if (device->host_type == RAU_HOST) {
|
||||
if(eth_set_dev_conf_raw(device)!=0) return -1;
|
||||
} else {
|
||||
if(eth_get_dev_conf_raw(device)!=0) return -1;
|
||||
}*/
|
||||
printf("Setting ETHERNET to UDP_IF5_MODE\n");
|
||||
if (eth_socket_init_udp(device)!=0) return -1;
|
||||
|
||||
/* adjust MTU wrt number of samples per packet */
|
||||
if(eth->compression == ALAW_COMPRESS) {
|
||||
if(ethernet_tune (device,MTU_SIZE,RAW_PACKET_SIZE_BYTES_ALAW(device->openair0_cfg->samples_per_packet))!=0) return -1;
|
||||
} else {
|
||||
if(ethernet_tune (device,MTU_SIZE,RAW_PACKET_SIZE_BYTES(device->openair0_cfg->samples_per_packet))!=0) return -1;
|
||||
/*
|
||||
if (device->host_type == RAU_HOST) {
|
||||
if(eth_set_dev_conf_udp(device)!=0) return -1;
|
||||
} else {
|
||||
if(eth_get_dev_conf_udp(device)!=0) return -1;
|
||||
}*/
|
||||
|
||||
/* adjust MTU wrt number of samples per packet */
|
||||
if(ethernet_tune (device,MTU_SIZE,UDP_IF4p5_PRACH_SIZE_BYTES)!=0) return -1;
|
||||
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
}
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
} else if (eth->flags == ETH_RAW_IF4p5_MODE) {
|
||||
/* apply additional configuration */
|
||||
if(ethernet_tune (device, SND_BUF_SIZE,2000000000)!=0) return -1;
|
||||
if(ethernet_tune (device, RCV_BUF_SIZE,2000000000)!=0) return -1;
|
||||
if(ethernet_tune (device, KERNEL_SND_BUF_MAX_SIZE, 200000000)!=0) return -1;
|
||||
if(ethernet_tune (device, KERNEL_RCV_BUF_MAX_SIZE, 200000000)!=0) return -1;
|
||||
|
||||
printf("Setting ETHERNET to ETH_RAW_IF4p5_MODE\n");
|
||||
if (eth_socket_init_raw(device)!=0) return -1;
|
||||
|
||||
printf("Setting Timenout to 999999 usecs\n");
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
|
||||
/*
|
||||
if (device->host_type == RAU_HOST) {
|
||||
if(eth_set_dev_conf_raw_IF4p5(device)!=0) return -1;
|
||||
} else {
|
||||
if(eth_get_dev_conf_raw_IF4p5(device)!=0) return -1;
|
||||
}
|
||||
*/
|
||||
|
||||
/* adjust MTU wrt number of samples per packet */
|
||||
if(ethernet_tune (device,MTU_SIZE,RAW_IF4p5_PRACH_SIZE_BYTES)!=0) return -1;
|
||||
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
} else if (eth->flags == ETH_UDP_IF4p5_MODE) {
|
||||
printf("Setting ETHERNET to UDP_IF4p5_MODE\n");
|
||||
if (eth_socket_init_udp(device)!=0) return -1;
|
||||
|
||||
printf("Setting Timeout to 999999 usecs\n");
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
|
||||
/*
|
||||
if (device->host_type == RAU_HOST) {
|
||||
if(eth_set_dev_conf_udp(device)!=0) return -1;
|
||||
} else {
|
||||
if(eth_get_dev_conf_udp(device)!=0) return -1;
|
||||
}
|
||||
*/
|
||||
|
||||
/* adjust MTU wrt number of samples per packet */
|
||||
if(ethernet_tune (device,MTU_SIZE,UDP_IF4p5_PRACH_SIZE_BYTES)!=0) return -1;
|
||||
|
||||
|
||||
|
||||
|
||||
} else if (eth->flags == ETH_RAW_IF5_MOBIPASS) {
|
||||
printf("Setting ETHERNET to RAW_IF5_MODE\n");
|
||||
if (eth_socket_init_raw(device)!=0) return -1;
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
|
||||
} else {
|
||||
printf("Setting ETHERNET to UDP_IF5_MODE\n");
|
||||
if (eth_socket_init_udp(device)!=0) return -1;
|
||||
|
||||
/*
|
||||
if (device->host_type == RAU_HOST) {
|
||||
if(eth_set_dev_conf_udp(device)!=0) return -1;
|
||||
} else {
|
||||
if(eth_get_dev_conf_udp(device)!=0) return -1;
|
||||
}*/
|
||||
|
||||
/* adjust MTU wrt number of samples per packet */
|
||||
if(ethernet_tune (device,MTU_SIZE,UDP_IF4p5_PRACH_SIZE_BYTES)!=0) return -1;
|
||||
|
||||
if(ethernet_tune (device,RCV_TIMEOUT,999999)!=0) return -1;
|
||||
}
|
||||
/* apply additional configuration */
|
||||
if(ethernet_tune (device, SND_BUF_SIZE,2000000000)!=0) return -1;
|
||||
if(ethernet_tune (device, RCV_BUF_SIZE,2000000000)!=0) return -1;
|
||||
if(ethernet_tune (device, KERNEL_SND_BUF_MAX_SIZE, 200000000)!=0) return -1;
|
||||
if(ethernet_tune (device, KERNEL_RCV_BUF_MAX_SIZE, 200000000)!=0) return -1;
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void trx_eth_end(openair0_device *device) {
|
||||
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
/* destroys socket only for the processes that call the eth_end fuction-- shutdown() for beaking the pipe */
|
||||
if ( close(eth->sockfdd) <0 ) {
|
||||
perror("ETHERNET: Failed to close socket");
|
||||
exit(0);
|
||||
} else {
|
||||
printf("[%s] socket has been successfully closed.\n",(device->host_type == RAU_HOST)? "RAU":"RRU");
|
||||
}
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
/* destroys socket only for the processes that call the eth_end fuction-- shutdown() for beaking the pipe */
|
||||
if ( close(eth->sockfdd) <0 ) {
|
||||
perror("ETHERNET: Failed to close socket");
|
||||
exit(0);
|
||||
} else {
|
||||
printf("[%s] socket has been successfully closed.\n",(device->host_type == RAU_HOST)? "RAU":"RRU");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -187,282 +187,282 @@ int trx_eth_reset_stats(openair0_device* device) {
|
||||
|
||||
|
||||
int ethernet_tune(openair0_device *device, unsigned int option, int value) {
|
||||
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
struct timeval timeout;
|
||||
struct ifreq ifr;
|
||||
char system_cmd[256];
|
||||
int rname[] = { CTL_NET, NET_CORE, NET_CORE_RMEM_MAX };
|
||||
int wname[] = { CTL_NET, NET_CORE, NET_CORE_WMEM_MAX };
|
||||
int namelen=3;
|
||||
int newval[1];
|
||||
int newlen=sizeof(newval);
|
||||
int ret=0;
|
||||
// int i=0;
|
||||
|
||||
/****************** socket level options ************************/
|
||||
switch(option) {
|
||||
case SND_BUF_SIZE: /* transmit socket buffer size */
|
||||
if (setsockopt(eth->sockfdd,
|
||||
SOL_SOCKET,
|
||||
SO_SNDBUF,
|
||||
&value,sizeof(value))) {
|
||||
perror("[ETHERNET] setsockopt()");
|
||||
} else {
|
||||
printf("send buffer size= %d bytes\n",value);
|
||||
}
|
||||
break;
|
||||
|
||||
case RCV_BUF_SIZE: /* receive socket buffer size */
|
||||
if (setsockopt(eth->sockfdd,
|
||||
SOL_SOCKET,
|
||||
SO_RCVBUF,
|
||||
&value,sizeof(value))) {
|
||||
perror("[ETHERNET] setsockopt()");
|
||||
} else {
|
||||
printf("receive bufffer size= %d bytes\n",value);
|
||||
}
|
||||
break;
|
||||
|
||||
case RCV_TIMEOUT:
|
||||
timeout.tv_sec = value/1000000;
|
||||
timeout.tv_usec = value%1000000;//less than rt_period?
|
||||
if (setsockopt(eth->sockfdc,
|
||||
SOL_SOCKET,
|
||||
SO_RCVTIMEO,
|
||||
(char *)&timeout,sizeof(timeout))) {
|
||||
perror("[ETHERNET] setsockopt()");
|
||||
} else {
|
||||
printf( "receive timeout= %u usec\n",(unsigned int)timeout.tv_usec);
|
||||
}
|
||||
if (setsockopt(eth->sockfdd,
|
||||
SOL_SOCKET,
|
||||
SO_RCVTIMEO,
|
||||
(char *)&timeout,sizeof(timeout))) {
|
||||
perror("[ETHERNET] setsockopt()");
|
||||
} else {
|
||||
printf( "receive timeout= %u usec\n",(unsigned int)timeout.tv_usec);
|
||||
}
|
||||
break;
|
||||
|
||||
case SND_TIMEOUT:
|
||||
timeout.tv_sec = value/1000000000;
|
||||
timeout.tv_usec = value%1000000000;//less than rt_period?
|
||||
if (setsockopt(eth->sockfdc,
|
||||
SOL_SOCKET,
|
||||
SO_SNDTIMEO,
|
||||
(char *)&timeout,sizeof(timeout))) {
|
||||
perror("[ETHERNET] setsockopt()");
|
||||
} else {
|
||||
printf( "send timeout= %d,%d sec\n",(int)timeout.tv_sec,(int)timeout.tv_usec);
|
||||
}
|
||||
if (setsockopt(eth->sockfdd,
|
||||
SOL_SOCKET,
|
||||
SO_SNDTIMEO,
|
||||
(char *)&timeout,sizeof(timeout))) {
|
||||
perror("[ETHERNET] setsockopt()");
|
||||
} else {
|
||||
printf( "send timeout= %d,%d sec\n",(int)timeout.tv_sec,(int)timeout.tv_usec);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
|
||||
eth_state_t *eth = (eth_state_t*)device->priv;
|
||||
struct timeval timeout;
|
||||
struct ifreq ifr;
|
||||
char system_cmd[256];
|
||||
int rname[] = { CTL_NET, NET_CORE, NET_CORE_RMEM_MAX };
|
||||
int wname[] = { CTL_NET, NET_CORE, NET_CORE_WMEM_MAX };
|
||||
int namelen=3;
|
||||
int newval[1];
|
||||
int newlen=sizeof(newval);
|
||||
int ret=0;
|
||||
// int i=0;
|
||||
|
||||
/****************** socket level options ************************/
|
||||
switch(option) {
|
||||
case SND_BUF_SIZE: /* transmit socket buffer size */
|
||||
if (setsockopt(eth->sockfdd,
|
||||
SOL_SOCKET,
|
||||
SO_SNDBUF,
|
||||
&value,sizeof(value))) {
|
||||
perror("[ETHERNET] setsockopt()");
|
||||
} else {
|
||||
printf("send buffer size= %d bytes\n",value);
|
||||
}
|
||||
break;
|
||||
|
||||
case RCV_BUF_SIZE: /* receive socket buffer size */
|
||||
if (setsockopt(eth->sockfdd,
|
||||
SOL_SOCKET,
|
||||
SO_RCVBUF,
|
||||
&value,sizeof(value))) {
|
||||
perror("[ETHERNET] setsockopt()");
|
||||
} else {
|
||||
printf("receive bufffer size= %d bytes\n",value);
|
||||
}
|
||||
break;
|
||||
|
||||
case RCV_TIMEOUT:
|
||||
timeout.tv_sec = value/1000000;
|
||||
timeout.tv_usec = value%1000000;//less than rt_period?
|
||||
if (setsockopt(eth->sockfdc,
|
||||
SOL_SOCKET,
|
||||
SO_RCVTIMEO,
|
||||
(char *)&timeout,sizeof(timeout))) {
|
||||
perror("[ETHERNET] setsockopt()");
|
||||
} else {
|
||||
printf( "receive timeout= %u usec\n",(unsigned int)timeout.tv_usec);
|
||||
}
|
||||
if (setsockopt(eth->sockfdd,
|
||||
SOL_SOCKET,
|
||||
SO_RCVTIMEO,
|
||||
(char *)&timeout,sizeof(timeout))) {
|
||||
perror("[ETHERNET] setsockopt()");
|
||||
} else {
|
||||
printf( "receive timeout= %u usec\n",(unsigned int)timeout.tv_usec);
|
||||
}
|
||||
break;
|
||||
|
||||
case SND_TIMEOUT:
|
||||
timeout.tv_sec = value/1000000000;
|
||||
timeout.tv_usec = value%1000000000;//less than rt_period?
|
||||
if (setsockopt(eth->sockfdc,
|
||||
SOL_SOCKET,
|
||||
SO_SNDTIMEO,
|
||||
(char *)&timeout,sizeof(timeout))) {
|
||||
perror("[ETHERNET] setsockopt()");
|
||||
} else {
|
||||
printf( "send timeout= %d,%d sec\n",(int)timeout.tv_sec,(int)timeout.tv_usec);
|
||||
}
|
||||
if (setsockopt(eth->sockfdd,
|
||||
SOL_SOCKET,
|
||||
SO_SNDTIMEO,
|
||||
(char *)&timeout,sizeof(timeout))) {
|
||||
perror("[ETHERNET] setsockopt()");
|
||||
} else {
|
||||
printf( "send timeout= %d,%d sec\n",(int)timeout.tv_sec,(int)timeout.tv_usec);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
/******************* interface level options *************************/
|
||||
case MTU_SIZE: /* change MTU of the eth interface */
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
strncpy(ifr.ifr_name,eth->if_name, sizeof(ifr.ifr_name));
|
||||
ifr.ifr_mtu =value;
|
||||
if (ioctl(eth->sockfdd,SIOCSIFMTU,(caddr_t)&ifr) < 0 )
|
||||
perror ("[ETHERNET] Can't set the MTU");
|
||||
else
|
||||
printf("[ETHERNET] %s MTU size has changed to %d\n",eth->if_name,ifr.ifr_mtu);
|
||||
break;
|
||||
|
||||
case TX_Q_LEN: /* change TX queue length of eth interface */
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
strncpy(ifr.ifr_name,eth->if_name, sizeof(ifr.ifr_name));
|
||||
ifr.ifr_qlen =value;
|
||||
if (ioctl(eth->sockfdd,SIOCSIFTXQLEN,(caddr_t)&ifr) < 0 )
|
||||
perror ("[ETHERNET] Can't set the txqueuelen");
|
||||
else
|
||||
printf("[ETHERNET] %s txqueuelen size has changed to %d\n",eth->if_name,ifr.ifr_qlen);
|
||||
break;
|
||||
|
||||
case MTU_SIZE: /* change MTU of the eth interface */
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
strncpy(ifr.ifr_name,eth->if_name, sizeof(ifr.ifr_name));
|
||||
ifr.ifr_mtu =value;
|
||||
if (ioctl(eth->sockfdd,SIOCSIFMTU,(caddr_t)&ifr) < 0 )
|
||||
perror ("[ETHERNET] Can't set the MTU");
|
||||
else
|
||||
printf("[ETHERNET] %s MTU size has changed to %d\n",eth->if_name,ifr.ifr_mtu);
|
||||
break;
|
||||
|
||||
case TX_Q_LEN: /* change TX queue length of eth interface */
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
strncpy(ifr.ifr_name,eth->if_name, sizeof(ifr.ifr_name));
|
||||
ifr.ifr_qlen =value;
|
||||
if (ioctl(eth->sockfdd,SIOCSIFTXQLEN,(caddr_t)&ifr) < 0 )
|
||||
perror ("[ETHERNET] Can't set the txqueuelen");
|
||||
else
|
||||
printf("[ETHERNET] %s txqueuelen size has changed to %d\n",eth->if_name,ifr.ifr_qlen);
|
||||
break;
|
||||
|
||||
/******************* device level options *************************/
|
||||
case COALESCE_PAR:
|
||||
ret=snprintf(system_cmd,sizeof(system_cmd),"ethtool -C %s rx-usecs %d",eth->if_name,value);
|
||||
if (ret > 0) {
|
||||
ret=system(system_cmd);
|
||||
if (ret == -1) {
|
||||
fprintf (stderr,"[ETHERNET] Can't start shell to execute %s %s",system_cmd, strerror(errno));
|
||||
} else {
|
||||
printf ("[ETHERNET] status of %s is %d\n", system_cmd, WEXITSTATUS(ret));
|
||||
}
|
||||
printf("[ETHERNET] Coalesce parameters %s\n",system_cmd);
|
||||
} else {
|
||||
perror("[ETHERNET] Can't set coalesce parameters\n");
|
||||
case COALESCE_PAR:
|
||||
ret=snprintf(system_cmd,sizeof(system_cmd),"ethtool -C %s rx-usecs %d",eth->if_name,value);
|
||||
if (ret > 0) {
|
||||
ret=system(system_cmd);
|
||||
if (ret == -1) {
|
||||
fprintf (stderr,"[ETHERNET] Can't start shell to execute %s %s",system_cmd, strerror(errno));
|
||||
} else {
|
||||
printf ("[ETHERNET] status of %s is %d\n", system_cmd, WEXITSTATUS(ret));
|
||||
}
|
||||
printf("[ETHERNET] Coalesce parameters %s\n",system_cmd);
|
||||
} else {
|
||||
perror("[ETHERNET] Can't set coalesce parameters\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case PAUSE_PAR:
|
||||
if (value==1) ret=snprintf(system_cmd,sizeof(system_cmd),"ethtool -A %s autoneg off rx off tx off",eth->if_name);
|
||||
else if (value==0) ret=snprintf(system_cmd,sizeof(system_cmd),"ethtool -A %s autoneg on rx on tx on",eth->if_name);
|
||||
else break;
|
||||
if (ret > 0) {
|
||||
ret=system(system_cmd);
|
||||
if (ret == -1) {
|
||||
fprintf (stderr,"[ETHERNET] Can't start shell to execute %s %s",system_cmd, strerror(errno));
|
||||
} else {
|
||||
printf ("[ETHERNET] status of %s is %d\n", system_cmd, WEXITSTATUS(ret));
|
||||
}
|
||||
printf("[ETHERNET] Pause parameters %s\n",system_cmd);
|
||||
} else {
|
||||
perror("[ETHERNET] Can't set pause parameters\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case RING_PAR:
|
||||
ret=snprintf(system_cmd,sizeof(system_cmd),"ethtool -G %s val %d",eth->if_name,value);
|
||||
if (ret > 0) {
|
||||
ret=system(system_cmd);
|
||||
if (ret == -1) {
|
||||
fprintf (stderr,"[ETHERNET] Can't start shell to execute %s %s",system_cmd, strerror(errno));
|
||||
} else {
|
||||
printf ("[ETHERNET] status of %s is %d\n", system_cmd, WEXITSTATUS(ret));
|
||||
}
|
||||
printf("[ETHERNET] Ring parameters %s\n",system_cmd);
|
||||
} else {
|
||||
perror("[ETHERNET] Can't set ring parameters\n");
|
||||
}
|
||||
break;
|
||||
case KERNEL_RCV_BUF_MAX_SIZE:
|
||||
newval[0] = value;
|
||||
ret=sysctl(rname, namelen, NULL, 0, newval, newlen);
|
||||
if (ret) {
|
||||
fprintf(stderr,"[ETHERNET] Error using sysctl():%s\n",strerror(errno));
|
||||
} else {
|
||||
printf("[ETHERNET] Kernel network receive buffer max size is set to %u\n",(unsigned int)newval[0]);
|
||||
}
|
||||
break;
|
||||
case KERNEL_SND_BUF_MAX_SIZE:
|
||||
newval[0] = value;
|
||||
ret=sysctl(wname, namelen, NULL, 0, newval, newlen);
|
||||
if (ret) {
|
||||
fprintf(stderr,"[ETHERNET] Error using sysctl():%s\n",strerror(errno));
|
||||
} else {
|
||||
printf("[ETHERNET] Kernel network send buffer max size is set to %u\n",(unsigned int)newval[0]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case PAUSE_PAR:
|
||||
if (value==1) ret=snprintf(system_cmd,sizeof(system_cmd),"ethtool -A %s autoneg off rx off tx off",eth->if_name);
|
||||
else if (value==0) ret=snprintf(system_cmd,sizeof(system_cmd),"ethtool -A %s autoneg on rx on tx on",eth->if_name);
|
||||
else break;
|
||||
if (ret > 0) {
|
||||
ret=system(system_cmd);
|
||||
if (ret == -1) {
|
||||
fprintf (stderr,"[ETHERNET] Can't start shell to execute %s %s",system_cmd, strerror(errno));
|
||||
} else {
|
||||
printf ("[ETHERNET] status of %s is %d\n", system_cmd, WEXITSTATUS(ret));
|
||||
}
|
||||
printf("[ETHERNET] Pause parameters %s\n",system_cmd);
|
||||
} else {
|
||||
perror("[ETHERNET] Can't set pause parameters\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case RING_PAR:
|
||||
ret=snprintf(system_cmd,sizeof(system_cmd),"ethtool -G %s val %d",eth->if_name,value);
|
||||
if (ret > 0) {
|
||||
ret=system(system_cmd);
|
||||
if (ret == -1) {
|
||||
fprintf (stderr,"[ETHERNET] Can't start shell to execute %s %s",system_cmd, strerror(errno));
|
||||
} else {
|
||||
printf ("[ETHERNET] status of %s is %d\n", system_cmd, WEXITSTATUS(ret));
|
||||
}
|
||||
printf("[ETHERNET] Ring parameters %s\n",system_cmd);
|
||||
} else {
|
||||
perror("[ETHERNET] Can't set ring parameters\n");
|
||||
}
|
||||
break;
|
||||
case KERNEL_RCV_BUF_MAX_SIZE:
|
||||
newval[0] = value;
|
||||
ret=sysctl(rname, namelen, NULL, 0, newval, newlen);
|
||||
if (ret) {
|
||||
fprintf(stderr,"[ETHERNET] Error using sysctl():%s\n",strerror(errno));
|
||||
} else{
|
||||
printf("[ETHERNET] Kernel network receive buffer max size is set to %u\n",newval[0]);
|
||||
}
|
||||
break;
|
||||
case KERNEL_SND_BUF_MAX_SIZE:
|
||||
newval[0] = value;
|
||||
ret=sysctl(wname, namelen, NULL, 0, newval, newlen);
|
||||
if (ret) {
|
||||
fprintf(stderr,"[ETHERNET] Error using sysctl():%s\n",strerror(errno));
|
||||
} else{
|
||||
printf("[ETHERNET] Kernel network send buffer max size is set to %u\n",newval[0]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int transport_init(openair0_device *device, openair0_config_t *openair0_cfg, eth_params_t * eth_params ) {
|
||||
|
||||
eth_state_t *eth = (eth_state_t*)malloc(sizeof(eth_state_t));
|
||||
memset(eth, 0, sizeof(eth_state_t));
|
||||
|
||||
if (eth_params->transp_preference == 1) {
|
||||
eth->flags = ETH_RAW_MODE;
|
||||
} else if (eth_params->transp_preference == 0) {
|
||||
eth->flags = ETH_UDP_MODE;
|
||||
} else if (eth_params->transp_preference == 3) {
|
||||
eth->flags = ETH_RAW_IF4p5_MODE;
|
||||
} else if (eth_params->transp_preference == 2) {
|
||||
eth->flags = ETH_UDP_IF4p5_MODE;
|
||||
} else if (eth_params->transp_preference == 4) {
|
||||
eth->flags = ETH_RAW_IF5_MOBIPASS;
|
||||
} else {
|
||||
printf("transport_init: Unknown transport preference %d - default to RAW", eth_params->transp_preference);
|
||||
eth->flags = ETH_RAW_MODE;
|
||||
}
|
||||
eth_state_t *eth = (eth_state_t*)malloc(sizeof(eth_state_t));
|
||||
memset(eth, 0, sizeof(eth_state_t));
|
||||
|
||||
if (eth_params->if_compress == 0) {
|
||||
eth->compression = NO_COMPRESS;
|
||||
} else if (eth_params->if_compress == 1) {
|
||||
eth->compression = ALAW_COMPRESS;
|
||||
} else {
|
||||
printf("transport_init: Unknown compression scheme %d - default to ALAW", eth_params->if_compress);
|
||||
eth->compression = ALAW_COMPRESS;
|
||||
}
|
||||
|
||||
printf("[ETHERNET]: Initializing openair0_device for %s ...\n", ((device->host_type == RAU_HOST) ? "RAU": "RRU"));
|
||||
device->Mod_id = 0;//num_devices_eth++;
|
||||
device->transp_type = ETHERNET_TP;
|
||||
device->trx_start_func = trx_eth_start;
|
||||
device->trx_get_stats_func = trx_eth_get_stats;
|
||||
device->trx_reset_stats_func = trx_eth_reset_stats;
|
||||
device->trx_end_func = trx_eth_end;
|
||||
device->trx_stop_func = trx_eth_stop;
|
||||
device->trx_set_freq_func = trx_eth_set_freq;
|
||||
device->trx_set_gains_func = trx_eth_set_gains;
|
||||
|
||||
if (eth->flags == ETH_RAW_MODE) {
|
||||
device->trx_write_func = trx_eth_write_raw;
|
||||
device->trx_read_func = trx_eth_read_raw;
|
||||
} else if (eth->flags == ETH_UDP_MODE) {
|
||||
device->trx_write_func = trx_eth_write_udp;
|
||||
device->trx_read_func = trx_eth_read_udp;
|
||||
device->trx_ctlsend_func = trx_eth_ctlsend_udp;
|
||||
device->trx_ctlrecv_func = trx_eth_ctlrecv_udp;
|
||||
} else if (eth->flags == ETH_RAW_IF4p5_MODE) {
|
||||
device->trx_write_func = trx_eth_write_raw_IF4p5;
|
||||
device->trx_read_func = trx_eth_read_raw_IF4p5;
|
||||
} else if (eth->flags == ETH_UDP_IF4p5_MODE) {
|
||||
device->trx_write_func = trx_eth_write_udp_IF4p5;
|
||||
device->trx_read_func = trx_eth_read_udp_IF4p5;
|
||||
device->trx_ctlsend_func = trx_eth_ctlsend_udp;
|
||||
device->trx_ctlrecv_func = trx_eth_ctlrecv_udp;
|
||||
} else if (eth->flags == ETH_RAW_IF5_MOBIPASS) {
|
||||
device->trx_write_func = trx_eth_write_raw_IF4p5;
|
||||
device->trx_read_func = trx_eth_read_raw_IF5_mobipass;
|
||||
} else {
|
||||
//device->trx_write_func = trx_eth_write_udp_IF4p5;
|
||||
//device->trx_read_func = trx_eth_read_udp_IF4p5;
|
||||
}
|
||||
|
||||
eth->if_name = eth_params->local_if_name;
|
||||
device->priv = eth;
|
||||
|
||||
/* device specific */
|
||||
// openair0_cfg[0].iq_rxrescale = 15;//rescale iqs
|
||||
// openair0_cfg[0].iq_txshift = eth_params->iq_txshift;// shift
|
||||
// openair0_cfg[0].tx_sample_advance = eth_params->tx_sample_advance;
|
||||
|
||||
/* RRU does not have any information to make this configuration atm */
|
||||
/*
|
||||
if (device->host_type == RAU_HOST) {
|
||||
|
||||
switch ((int)openair0_cfg[0].sample_rate) {
|
||||
case 30720000:
|
||||
openair0_cfg[0].samples_per_packet = 3840;
|
||||
break;
|
||||
case 23040000:
|
||||
openair0_cfg[0].samples_per_packet = 2880;
|
||||
break;
|
||||
case 15360000:
|
||||
openair0_cfg[0].samples_per_packet = 1920;
|
||||
break;
|
||||
case 7680000:
|
||||
openair0_cfg[0].samples_per_packet = 960;
|
||||
break;
|
||||
case 1920000:
|
||||
openair0_cfg[0].samples_per_packet = 240;
|
||||
break;
|
||||
default:
|
||||
printf("Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
|
||||
exit(-1);
|
||||
break;
|
||||
if (eth_params->transp_preference == 1) {
|
||||
eth->flags = ETH_RAW_MODE;
|
||||
} else if (eth_params->transp_preference == 0) {
|
||||
eth->flags = ETH_UDP_MODE;
|
||||
} else if (eth_params->transp_preference == 3) {
|
||||
eth->flags = ETH_RAW_IF4p5_MODE;
|
||||
} else if (eth_params->transp_preference == 2) {
|
||||
eth->flags = ETH_UDP_IF4p5_MODE;
|
||||
} else if (eth_params->transp_preference == 4) {
|
||||
eth->flags = ETH_RAW_IF5_MOBIPASS;
|
||||
} else {
|
||||
printf("transport_init: Unknown transport preference %d - default to RAW", eth_params->transp_preference);
|
||||
eth->flags = ETH_RAW_MODE;
|
||||
}
|
||||
}*/
|
||||
|
||||
device->openair0_cfg=&openair0_cfg[0];
|
||||
return 0;
|
||||
if (eth_params->if_compress == 0) {
|
||||
eth->compression = NO_COMPRESS;
|
||||
} else if (eth_params->if_compress == 1) {
|
||||
eth->compression = ALAW_COMPRESS;
|
||||
} else {
|
||||
printf("transport_init: Unknown compression scheme %d - default to ALAW", eth_params->if_compress);
|
||||
eth->compression = ALAW_COMPRESS;
|
||||
}
|
||||
|
||||
printf("[ETHERNET]: Initializing openair0_device for %s ...\n", ((device->host_type == RAU_HOST) ? "RAU": "RRU"));
|
||||
device->Mod_id = 0;//num_devices_eth++;
|
||||
device->transp_type = ETHERNET_TP;
|
||||
device->trx_start_func = trx_eth_start;
|
||||
device->trx_get_stats_func = trx_eth_get_stats;
|
||||
device->trx_reset_stats_func = trx_eth_reset_stats;
|
||||
device->trx_end_func = trx_eth_end;
|
||||
device->trx_stop_func = trx_eth_stop;
|
||||
device->trx_set_freq_func = trx_eth_set_freq;
|
||||
device->trx_set_gains_func = trx_eth_set_gains;
|
||||
|
||||
if (eth->flags == ETH_RAW_MODE) {
|
||||
device->trx_write_func = trx_eth_write_raw;
|
||||
device->trx_read_func = trx_eth_read_raw;
|
||||
} else if (eth->flags == ETH_UDP_MODE) {
|
||||
device->trx_write_func = trx_eth_write_udp;
|
||||
device->trx_read_func = trx_eth_read_udp;
|
||||
device->trx_ctlsend_func = trx_eth_ctlsend_udp;
|
||||
device->trx_ctlrecv_func = trx_eth_ctlrecv_udp;
|
||||
} else if (eth->flags == ETH_RAW_IF4p5_MODE) {
|
||||
device->trx_write_func = trx_eth_write_raw_IF4p5;
|
||||
device->trx_read_func = trx_eth_read_raw_IF4p5;
|
||||
} else if (eth->flags == ETH_UDP_IF4p5_MODE) {
|
||||
device->trx_write_func = trx_eth_write_udp_IF4p5;
|
||||
device->trx_read_func = trx_eth_read_udp_IF4p5;
|
||||
device->trx_ctlsend_func = trx_eth_ctlsend_udp;
|
||||
device->trx_ctlrecv_func = trx_eth_ctlrecv_udp;
|
||||
} else if (eth->flags == ETH_RAW_IF5_MOBIPASS) {
|
||||
device->trx_write_func = trx_eth_write_raw_IF4p5;
|
||||
device->trx_read_func = trx_eth_read_raw_IF5_mobipass;
|
||||
} else {
|
||||
//device->trx_write_func = trx_eth_write_udp_IF4p5;
|
||||
//device->trx_read_func = trx_eth_read_udp_IF4p5;
|
||||
}
|
||||
|
||||
eth->if_name = eth_params->local_if_name;
|
||||
device->priv = eth;
|
||||
|
||||
/* device specific */
|
||||
// openair0_cfg[0].iq_rxrescale = 15;//rescale iqs
|
||||
// openair0_cfg[0].iq_txshift = eth_params->iq_txshift;// shift
|
||||
// openair0_cfg[0].tx_sample_advance = eth_params->tx_sample_advance;
|
||||
|
||||
/* RRU does not have any information to make this configuration atm */
|
||||
/*
|
||||
if (device->host_type == RAU_HOST) {
|
||||
|
||||
switch ((int)openair0_cfg[0].sample_rate) {
|
||||
case 30720000:
|
||||
openair0_cfg[0].samples_per_packet = 3840;
|
||||
break;
|
||||
case 23040000:
|
||||
openair0_cfg[0].samples_per_packet = 2880;
|
||||
break;
|
||||
case 15360000:
|
||||
openair0_cfg[0].samples_per_packet = 1920;
|
||||
break;
|
||||
case 7680000:
|
||||
openair0_cfg[0].samples_per_packet = 960;
|
||||
break;
|
||||
case 1920000:
|
||||
openair0_cfg[0].samples_per_packet = 240;
|
||||
break;
|
||||
default:
|
||||
printf("Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
|
||||
exit(-1);
|
||||
break;
|
||||
}
|
||||
}*/
|
||||
|
||||
device->openair0_cfg=&openair0_cfg[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Executable → Regular
Executable → Regular
@@ -444,7 +444,7 @@ static void *watchdog_thread(void *arg) {
|
||||
(diff > 16)&&
|
||||
(first_acquisition==0)) {// we're too late so exit
|
||||
exm->watchdog_exit = 1;
|
||||
printf("exiting, too late to keep up - diff = %d\n", diff);
|
||||
printf("exiting, too late to keep up - diff = %u\n", diff);
|
||||
}
|
||||
first_acquisition=0;
|
||||
|
||||
@@ -625,7 +625,7 @@ int trx_exmimo_read(openair0_device *device, openair0_timestamp *ptimestamp, voi
|
||||
if (cfg->mmapped_dma == 0) { // if buff is not the dma buffer, do a memcpy, otherwise do nothing
|
||||
for (i=0;i<cc;i++) {
|
||||
#ifdef DEBUG_EXMIMO
|
||||
printf("copying to %p (%lu), from %llu\n",buff[i]+(ntot*sizeof(int)),ntot*sizeof(int),(exm->last_ts_rx % exm->samples_per_frame));
|
||||
printf("copying to %p (%zu), from %llu\n",buff[i]+(ntot*sizeof(int)),ntot*sizeof(int),(exm->last_ts_rx % exm->samples_per_frame));
|
||||
#endif
|
||||
if ((n+(exm->last_ts_rx%exm->samples_per_frame))<exm->samples_per_frame) {
|
||||
memcpy(buff[i]+(ntot*sizeof(int)),
|
||||
@@ -733,7 +733,7 @@ int device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
|
||||
|
||||
if (ret == -3)
|
||||
printf("Error mapping RX or TX buffer");
|
||||
|
||||
free(exm);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
@@ -752,6 +752,7 @@ int device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
|
||||
// check if the software matches firmware
|
||||
if (p_exmimo_id->board_swrev!=BOARD_SWREV_CNTL2) {
|
||||
printf("Software revision %d and firmware revision %d do not match. Please update either the firmware or the software!\n",BOARD_SWREV_CNTL2,p_exmimo_id->board_swrev);
|
||||
free(exm);
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
@@ -905,7 +906,7 @@ int openair0_config(openair0_config_t *openair0_cfg, int UE_flag)
|
||||
p_exmimo_config->rf.rx_gain[ant][0] = 30 - (rxg_max[ant] - (int)openair0_cfg[card].rx_gain[ant]); //was measured at rxgain=30;
|
||||
}
|
||||
else {
|
||||
printf("openair0: RX RF gain too high, reduce by %d dB\n", (int)openair0_cfg[card].rx_gain[ant]-rxg_max[ant]);
|
||||
printf("openair0: RX RF gain too high, reduce by %u dB\n", (int)openair0_cfg[card].rx_gain[ant]-rxg_max[ant]);
|
||||
exit(-1);
|
||||
}
|
||||
break;
|
||||
@@ -916,7 +917,7 @@ int openair0_config(openair0_config_t *openair0_cfg, int UE_flag)
|
||||
p_exmimo_config->rf.rx_gain[ant][0] = 30 - (rxg_med[ant] - (int)openair0_cfg[card].rx_gain[ant]); //was measured at rxgain=30;
|
||||
}
|
||||
else {
|
||||
printf("openair0: RX RF gain too high, reduce by %d dB\n", (int)openair0_cfg[card].rx_gain[ant]-rxg_med[ant]);
|
||||
printf("openair0: RX RF gain too high, reduce by %u dB\n", (int)openair0_cfg[card].rx_gain[ant]-rxg_med[ant]);
|
||||
exit(-1);
|
||||
}
|
||||
break;
|
||||
@@ -927,7 +928,7 @@ int openair0_config(openair0_config_t *openair0_cfg, int UE_flag)
|
||||
p_exmimo_config->rf.rx_gain[ant][0] = 30 - (rxg_byp[ant] - (int)openair0_cfg[card].rx_gain[ant]); //was measured at rxgain=30;
|
||||
}
|
||||
else {
|
||||
printf("openair0: RX RF gain too high, reduce by %d dB\n", (int)openair0_cfg[card].rx_gain[ant]-rxg_byp[ant]);
|
||||
printf("openair0: RX RF gain too high, reduce by %u dB\n", (int)openair0_cfg[card].rx_gain[ant]-rxg_byp[ant]);
|
||||
exit(-1);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -211,22 +211,25 @@ void find_and_transfer_section(char* section_name, unsigned int verboselevel) {
|
||||
int nbread;
|
||||
unsigned int secnb = 0;
|
||||
|
||||
if(p_elfimage == NULL) {
|
||||
fprintf(stderr, "%s %d:Error, firmware file not opened\n",__FILE__,__LINE__);
|
||||
}
|
||||
for (secnb = 0 ; secnb < elf_Ehdr.e_shnum; secnb++) {
|
||||
get_elf_section_header(&elf_Shdr, p_elfimage, secnb);
|
||||
|
||||
if (!strcmp(SecNameStnTable + elf_Shdr.sh_name, section_name)) {
|
||||
if (verboselevel >= VERBOSE_LEVEL_SECTION_DETAILS)
|
||||
printf("Info: ok, found section %s (as section nb. %d)\n", SecNameStnTable + elf_Shdr.sh_name, secnb);
|
||||
printf("Info: ok, found section %s (as section nb. %u)\n", SecNameStnTable + elf_Shdr.sh_name, secnb);
|
||||
|
||||
/* Check that section size is a multiple of 4 bytes. */
|
||||
if (elf_Shdr.sh_size % 4) {
|
||||
fprintf(stderr, "Error: section %s has a non-multiple-of-4-bytes size (%d).\n",
|
||||
SecNameStnTable + elf_Shdr.sh_name, elf_Shdr.sh_size);
|
||||
fprintf(stderr, "Error: section %s has a non-multiple-of-4-bytes size (%lu).\n",
|
||||
SecNameStnTable + elf_Shdr.sh_name, (unsigned long)elf_Shdr.sh_size);
|
||||
fclose(p_elfimage);
|
||||
exit(-1);
|
||||
} else if (verboselevel >= VERBOSE_LEVEL_SECTION_DETAILS) {
|
||||
printf("Info: ok, section %s has size %d bytes (multiple of 4 bytes).\n",
|
||||
SecNameStnTable + elf_Shdr.sh_name, elf_Shdr.sh_size);
|
||||
printf("Info: ok, section %s has size %lu bytes (multiple of 4 bytes).\n",
|
||||
SecNameStnTable + elf_Shdr.sh_name, (unsigned long)elf_Shdr.sh_size);
|
||||
}
|
||||
|
||||
/* Dynamically allocate a chunk of memory to store the section into. */
|
||||
@@ -245,13 +248,13 @@ void find_and_transfer_section(char* section_name, unsigned int verboselevel) {
|
||||
}
|
||||
|
||||
if (!section_content) {
|
||||
fprintf(stderr, "Error: could not dynamically allocate %d bytes for section %s.\n",
|
||||
elf_Shdr.sh_size, SecNameStnTable + elf_Shdr.sh_name);
|
||||
fprintf(stderr, "Error: could not dynamically allocate %lu bytes for section %s.\n",
|
||||
(unsigned long)elf_Shdr.sh_size, (SecNameStnTable + elf_Shdr.sh_name));
|
||||
fclose(p_elfimage);
|
||||
exit(-1);
|
||||
} else if (verboselevel >= VERBOSE_LEVEL_IOCTL) {
|
||||
printf("Info: ok, dynamically allocated a %d bytes buffer for section %s.\n",
|
||||
elf_Shdr.sh_size, SecNameStnTable + elf_Shdr.sh_name);
|
||||
printf("Info: ok, dynamically allocated a %lu bytes buffer for section %s.\n",
|
||||
(unsigned long)elf_Shdr.sh_size, SecNameStnTable + elf_Shdr.sh_name);
|
||||
}
|
||||
|
||||
/* Position the file cursor at the begining of proper section. */
|
||||
@@ -260,13 +263,13 @@ void find_and_transfer_section(char* section_name, unsigned int verboselevel) {
|
||||
nbread = fread(section_content, elf_Shdr.sh_size, 1, p_elfimage);
|
||||
|
||||
if (nbread != 1) {
|
||||
fprintf(stderr, "Error: could not read %d bytes from ELF file into dynamic buffer.\n", elf_Shdr.sh_size);
|
||||
fprintf(stderr, "Error: could not read %lu bytes from ELF file into dynamic buffer.\n", (unsigned long)elf_Shdr.sh_size);
|
||||
free(section_content);
|
||||
fclose(p_elfimage);
|
||||
exit(-1);
|
||||
} else if (verboselevel >= VERBOSE_LEVEL_IOCTL) {
|
||||
printf("Info: ok, copied content of section %s into dynamic buffer (%d bytes copied).\n",
|
||||
SecNameStnTable + elf_Shdr.sh_name, elf_Shdr.sh_size);
|
||||
printf("Info: ok, copied content of section %s into dynamic buffer (%lu bytes copied).\n",
|
||||
SecNameStnTable + elf_Shdr.sh_name, (unsigned long)elf_Shdr.sh_size);
|
||||
}
|
||||
|
||||
/* Open the special device file. */
|
||||
@@ -324,12 +327,15 @@ void find_and_clear_section_bss(unsigned int verboselevel) {
|
||||
int ifile;
|
||||
unsigned int secnb = 0;
|
||||
|
||||
if(p_elfimage == NULL) {
|
||||
fprintf(stderr, "%s %d:Error, firmware file not opened\n",__FILE__,__LINE__);
|
||||
}
|
||||
for (secnb = 0 ; secnb < elf_Ehdr.e_shnum; secnb++) {
|
||||
get_elf_section_header(&elf_Shdr, p_elfimage, secnb);
|
||||
|
||||
if (!strcmp(SecNameStnTable + elf_Shdr.sh_name, ".bss")) {
|
||||
if (verboselevel >= VERBOSE_LEVEL_SECTION_DETAILS)
|
||||
printf("Info: ok, found section %s (as section nb. %d)\n", SecNameStnTable + elf_Shdr.sh_name, secnb);
|
||||
printf("Info: ok, found section %s (as section nb. %u)\n", SecNameStnTable + elf_Shdr.sh_name, secnb);
|
||||
|
||||
/* Open the special device file. */
|
||||
if (!pflag) {
|
||||
@@ -491,6 +497,10 @@ int main(int argc, char** argv) {
|
||||
|
||||
/* Open firmware file in READ_ONLY mode. */
|
||||
filename = p_str_fwn;
|
||||
if (filename == NULL) {
|
||||
fprintf(stderr, "%s %d: No filename specified\n", __FILE__, __LINE__);
|
||||
exit(-1);
|
||||
}
|
||||
p_elfimage = fopen(filename, READ_FILE_MODE);
|
||||
|
||||
if (p_elfimage == NULL) {
|
||||
@@ -505,6 +515,7 @@ int main(int argc, char** argv) {
|
||||
/* Get informations from header file */
|
||||
if (!(get_elf_header(&elf_Ehdr, p_elfimage))) {
|
||||
fprintf(stderr, "Error : file doesn't match expected format.\n");
|
||||
fclose(p_elfimage);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
@@ -522,9 +533,10 @@ int main(int argc, char** argv) {
|
||||
|
||||
/* copy the string table into global variable */
|
||||
if (StringSec_size > MAX_SIZE_STRING_TABLE) {
|
||||
fprintf(stderr, "Error: section name string table too big (%d > %d);"
|
||||
fprintf(stderr, "Error: section name string table too big (%ld > %d);"
|
||||
" increase max. allowed value in source code and recompile\n",
|
||||
StringSec_size, MAX_SIZE_STRING_TABLE);
|
||||
fclose(p_elfimage);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
@@ -626,7 +638,7 @@ int main(int argc, char** argv) {
|
||||
if (verboselevel >= VERBOSE_LEVEL_MAIN_STEPS) printf("Info: entering action #5 (Jump to firmware/set stack-pointer).\n");
|
||||
|
||||
if (verboselevel >= VERBOSE_LEVEL_SECTION_DETAILS)
|
||||
printf("Info: Firmware entry point = 0x%08x, setting stack pointer = 0x%08x.\n", elf_Ehdr.e_entry, stackpointer);
|
||||
printf("Info: Firmware entry point = 0x%08x, setting stack pointer = 0x%08x.\n", (unsigned int)elf_Ehdr.e_entry, stackpointer);
|
||||
|
||||
/* Open the special device file. */
|
||||
if (!pflag) {
|
||||
|
||||
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
@@ -116,7 +116,7 @@ DEFUN_DLD (oarf_send_frame, args, nargout,"Send frame")
|
||||
|
||||
for (i=0;i<4;i++) {
|
||||
resampling_factor[i] = (openair0_exmimo_pci[card].exmimo_config_ptr)->framing.resampling_factor[i];
|
||||
printf("card %d, ant %d, resampling %d\n",card,i,resampling_factor[i]);
|
||||
printf("card %d, ant %d, resampling %u\n",card,i,resampling_factor[i]);
|
||||
}
|
||||
|
||||
for (i=0;i<numcols;i++){
|
||||
|
||||
Executable → Regular
Executable → Regular
Executable → Regular
@@ -0,0 +1,3 @@
|
||||
IRIS_OBJ += $(OPENAIR_TARGETS)/ARCH/IRIS/USERSPACE/LIB/iris_lib.o
|
||||
IRIS_FILE_OBJ += $(OPENAIR_TARGETS)/ARCH/IRIS/USERSPACE/LIB/iris_lib.cpp
|
||||
IRIS_CFLAGS += -I$(OPENAIR_TARGETS)/ARCH/COMMON -I$(OPENAIR_TARGETS)/ARCH/IRIS/USERSPACE/LIB/ -I$(OPENAIR_TARGETS)/COMMON
|
||||
@@ -0,0 +1,850 @@
|
||||
|
||||
/** iris_lib.cpp
|
||||
*
|
||||
* \authors: Rahman Doost-Mohammady : doost@rice.edu
|
||||
* Clay Shepard : cws@rice.edu
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <SoapySDR/Device.hpp>
|
||||
#include <SoapySDR/Formats.hpp>
|
||||
#include <SoapySDR/Time.hpp>
|
||||
//#include <boost/format.hpp>
|
||||
#include <iostream>
|
||||
#include <complex>
|
||||
#include <fstream>
|
||||
#include <cmath>
|
||||
#include <time.h>
|
||||
#include <limits>
|
||||
#include "common/utils/LOG/log_extern.h"
|
||||
#include "common_lib.h"
|
||||
#include <chrono>
|
||||
|
||||
#ifdef __SSE4_1__
|
||||
# include <smmintrin.h>
|
||||
#endif
|
||||
|
||||
#ifdef __AVX2__
|
||||
# include <immintrin.h>
|
||||
#endif
|
||||
|
||||
#define MOVE_DC
|
||||
#define SAMPLE_RATE_DOWN 1
|
||||
|
||||
/*! \brief Iris Configuration */
|
||||
extern "C" {
|
||||
typedef struct {
|
||||
|
||||
// --------------------------------
|
||||
// variables for Iris configuration
|
||||
// --------------------------------
|
||||
//! Iris device pointer
|
||||
std::vector<SoapySDR::Device *> iris;
|
||||
int device_num;
|
||||
int rx_num_channels;
|
||||
int tx_num_channels;
|
||||
//create a send streamer and a receive streamer
|
||||
//! Iris TX Stream
|
||||
std::vector<SoapySDR::Stream *> txStream;
|
||||
//! Iris RX Stream
|
||||
std::vector<SoapySDR::Stream *> rxStream;
|
||||
|
||||
//! Sampling rate
|
||||
double sample_rate;
|
||||
|
||||
//! time offset between transmiter timestamp and receiver timestamp;
|
||||
double tdiff;
|
||||
|
||||
//! TX forward samples.
|
||||
int tx_forward_nsamps; //166 for 20Mhz
|
||||
|
||||
|
||||
// --------------------------------
|
||||
// Debug and output control
|
||||
// --------------------------------
|
||||
//! Number of underflows
|
||||
int num_underflows;
|
||||
//! Number of overflows
|
||||
int num_overflows;
|
||||
|
||||
//! Number of sequential errors
|
||||
int num_seq_errors;
|
||||
//! tx count
|
||||
int64_t tx_count;
|
||||
//! rx count
|
||||
int64_t rx_count;
|
||||
//! timestamp of RX packet
|
||||
openair0_timestamp rx_timestamp;
|
||||
|
||||
} iris_state_t;
|
||||
}
|
||||
/*! \brief Called to start the Iris lime transceiver. Return 0 if OK, < 0 if error
|
||||
@param device pointer to the device structure specific to the RF hardware target
|
||||
*/
|
||||
static int trx_iris_start(openair0_device *device) {
|
||||
iris_state_t *s = (iris_state_t *) device->priv;
|
||||
|
||||
long long timeNs = s->iris[0]->getHardwareTime("") + 500000;
|
||||
int flags = 0;
|
||||
//flags |= SOAPY_SDR_HAS_TIME;
|
||||
int r;
|
||||
for (r = 0; r < s->device_num; r++) {
|
||||
int ret = s->iris[r]->activateStream(s->rxStream[r], flags, timeNs, 0);
|
||||
int ret2 = s->iris[r]->activateStream(s->txStream[r]);
|
||||
if (ret < 0 | ret2 < 0)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Stop Iris
|
||||
* \param card refers to the hardware index to use
|
||||
*/
|
||||
int trx_iris_stop(openair0_device *device) {
|
||||
iris_state_t *s = (iris_state_t *) device->priv;
|
||||
int r;
|
||||
for (r = 0; r < s->device_num; r++) {
|
||||
s->iris[r]->deactivateStream(s->txStream[r]);
|
||||
s->iris[r]->deactivateStream(s->rxStream[r]);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*! \brief Terminate operation of the Iris lime transceiver -- free all associated resources
|
||||
* \param device the hardware to use
|
||||
*/
|
||||
static void trx_iris_end(openair0_device *device) {
|
||||
LOG_I(HW, "Closing Iris device.\n");
|
||||
trx_iris_stop(device);
|
||||
iris_state_t *s = (iris_state_t *) device->priv;
|
||||
int r;
|
||||
for (r = 0; r < s->device_num; r++) {
|
||||
s->iris[r]->closeStream(s->txStream[r]);
|
||||
s->iris[r]->closeStream(s->rxStream[r]);
|
||||
SoapySDR::Device::unmake(s->iris[r]);
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Called to send samples to the Iris RF target
|
||||
@param device pointer to the device structure specific to the RF hardware target
|
||||
@param timestamp The timestamp at whicch the first sample MUST be sent
|
||||
@param buff Buffer which holds the samples
|
||||
@param nsamps number of samples to be sent
|
||||
@param antenna_id index of the antenna if the device has multiple anteannas
|
||||
@param flags flags must be set to TRUE if timestamp parameter needs to be applied
|
||||
*/
|
||||
|
||||
|
||||
static int
|
||||
trx_iris_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags) {
|
||||
using namespace std::chrono;
|
||||
static long long int loop = 0;
|
||||
static long time_min = 0, time_max = 0, time_avg = 0;
|
||||
struct timespec tp_start, tp_end;
|
||||
long time_diff;
|
||||
|
||||
int ret = 0, ret_i = 0;
|
||||
int flag = 0;
|
||||
|
||||
iris_state_t *s = (iris_state_t *) device->priv;
|
||||
int nsamps2; // aligned to upper 32 or 16 byte boundary
|
||||
#if defined(__x86_64) || defined(__i386__)
|
||||
#ifdef __AVX2__
|
||||
nsamps2 = (nsamps+7)>>3;
|
||||
__m256i buff_tx[2][nsamps2];
|
||||
#else
|
||||
nsamps2 = (nsamps+3)>>2;
|
||||
__m128i buff_tx[2][nsamps2];
|
||||
#endif
|
||||
#else
|
||||
#error unsupported CPU architecture, iris device cannot be built
|
||||
#endif
|
||||
|
||||
// bring RX data into 12 LSBs for softmodem RX
|
||||
for (int i=0; i<cc; i++) {
|
||||
for (int j=0; j<nsamps2; j++) {
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
#ifdef __AVX2__
|
||||
buff_tx[i][j] = _mm256_slli_epi16(((__m256i *)buff[i])[j],4);
|
||||
#else
|
||||
buff_tx[i][j] = _mm_slli_epi16(((__m128i *)buff[i])[j],4);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &tp_start);
|
||||
|
||||
// This hack was added by cws to help keep packets flowing
|
||||
|
||||
if (flags)
|
||||
flag |= SOAPY_SDR_HAS_TIME;
|
||||
else {
|
||||
long long tempHack = s->iris[0]->getHardwareTime("");
|
||||
return nsamps;
|
||||
}
|
||||
|
||||
if (flags == 2 || flags == 1) { // start of burst
|
||||
|
||||
} else if (flags == 3 | flags == 4) {
|
||||
flag |= SOAPY_SDR_END_BURST;
|
||||
}
|
||||
|
||||
|
||||
long long timeNs = SoapySDR::ticksToTimeNs(timestamp, s->sample_rate / SAMPLE_RATE_DOWN);
|
||||
uint32_t *samps[2]; //= (uint32_t **)buff;
|
||||
int r;
|
||||
int m = s->tx_num_channels;
|
||||
for (r = 0; r < s->device_num; r++) {
|
||||
int samples_sent = 0;
|
||||
samps[0] = (uint32_t *) buff_tx[m * r];
|
||||
|
||||
if (cc % 2 == 0)
|
||||
samps[1] = (uint32_t *) buff_tx[m * r + 1]; //cws: it seems another thread can clobber these, so we need to save them locally.
|
||||
#ifdef IRIS_DEBUG
|
||||
int i;
|
||||
for (i = 200; i < 216; i++)
|
||||
printf("%d, ",((int16_t)(samps[0][i]>>16))>>4);
|
||||
printf("\n");
|
||||
//printf("\nHardware time before write: %lld, tx_time_stamp: %lld\n", s->iris[0]->getHardwareTime(""), timeNs);
|
||||
#endif
|
||||
ret = s->iris[r]->writeStream(s->txStream[r], (void **) samps, (size_t)(nsamps), flag, timeNs, 1000000);
|
||||
|
||||
if (ret < 0)
|
||||
printf("Unable to write stream!\n");
|
||||
else
|
||||
samples_sent = ret;
|
||||
|
||||
|
||||
if (samples_sent != nsamps)
|
||||
printf("[xmit] tx samples %d != %d\n", samples_sent, nsamps);
|
||||
|
||||
}
|
||||
|
||||
return nsamps;
|
||||
}
|
||||
|
||||
/*! \brief Receive samples from hardware.
|
||||
* Read \ref nsamps samples from each channel to buffers. buff[0] is the array for
|
||||
* the first channel. *ptimestamp is the time at which the first sample
|
||||
* was received.
|
||||
* \param device the hardware to use
|
||||
* \param[out] ptimestamp the time at which the first sample was received.
|
||||
* \param[out] buff An array of pointers to buffers for received samples. The buffers must be large enough to hold the number of samples \ref nsamps.
|
||||
* \param nsamps Number of samples. One sample is 2 byte I + 2 byte Q => 4 byte.
|
||||
* \param antenna_id Index of antenna for which to receive samples
|
||||
* \returns the number of sample read
|
||||
*/
|
||||
static int trx_iris_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc) {
|
||||
int ret = 0;
|
||||
static long long nextTime;
|
||||
static bool nextTimeValid = false;
|
||||
iris_state_t *s = (iris_state_t *) device->priv;
|
||||
bool time_set = false;
|
||||
long long timeNs = 0;
|
||||
int flags;
|
||||
int samples_received;
|
||||
uint32_t *samps[2]; //= (uint32_t **)buff;
|
||||
|
||||
int r;
|
||||
int m = s->rx_num_channels;
|
||||
int nsamps2; // aligned to upper 32 or 16 byte boundary
|
||||
#if defined(__x86_64) || defined(__i386__)
|
||||
#ifdef __AVX2__
|
||||
nsamps2 = (nsamps+7)>>3;
|
||||
__m256i buff_tmp[2][nsamps2];
|
||||
#else
|
||||
nsamps2 = (nsamps+3)>>2;
|
||||
__m128i buff_tmp[2][nsamps2];
|
||||
#endif
|
||||
#endif
|
||||
|
||||
for (r = 0; r < s->device_num; r++) {
|
||||
flags = 0;
|
||||
samples_received = 0;
|
||||
samps[0] = (uint32_t *) buff_tmp[m * r];
|
||||
if (cc % 2 == 0)
|
||||
samps[1] = (uint32_t *) buff_tmp[m * r + 1];
|
||||
|
||||
flags = 0;
|
||||
ret = s->iris[r]->readStream(s->rxStream[r], (void **) samps, (size_t)(nsamps), flags,
|
||||
timeNs, 1000000);
|
||||
if (ret < 0) {
|
||||
if (ret == SOAPY_SDR_TIME_ERROR)
|
||||
printf("[recv] Time Error in tx stream!\n");
|
||||
else if (ret == SOAPY_SDR_OVERFLOW | (flags & SOAPY_SDR_END_ABRUPT))
|
||||
printf("[recv] Overflow occured!\n");
|
||||
else if (ret == SOAPY_SDR_TIMEOUT)
|
||||
printf("[recv] Timeout occured!\n");
|
||||
else if (ret == SOAPY_SDR_STREAM_ERROR)
|
||||
printf("[recv] Stream (tx) error occured!\n");
|
||||
else if (ret == SOAPY_SDR_CORRUPTION)
|
||||
printf("[recv] Bad packet occured!\n");
|
||||
break;
|
||||
} else
|
||||
samples_received = ret;
|
||||
|
||||
|
||||
if (r == 0) {
|
||||
if (samples_received == ret) // first batch
|
||||
{
|
||||
if (flags & SOAPY_SDR_HAS_TIME) {
|
||||
s->rx_timestamp = SoapySDR::timeNsToTicks(timeNs, s->sample_rate / SAMPLE_RATE_DOWN);
|
||||
*ptimestamp = s->rx_timestamp;
|
||||
nextTime = timeNs;
|
||||
nextTimeValid = true;
|
||||
time_set = true;
|
||||
//printf("1) time set %llu \n", *ptimestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (r == 0) {
|
||||
if (samples_received == nsamps) {
|
||||
|
||||
if (flags & SOAPY_SDR_HAS_TIME) {
|
||||
s->rx_timestamp = SoapySDR::timeNsToTicks(nextTime, s->sample_rate / SAMPLE_RATE_DOWN);
|
||||
*ptimestamp = s->rx_timestamp;
|
||||
nextTime = timeNs;
|
||||
nextTimeValid = true;
|
||||
time_set = true;
|
||||
}
|
||||
} else if (samples_received < nsamps)
|
||||
printf("[recv] received %d samples out of %d\n", samples_received, nsamps);
|
||||
|
||||
s->rx_count += samples_received;
|
||||
|
||||
if (s->sample_rate != 0 && nextTimeValid) {
|
||||
if (!time_set) {
|
||||
s->rx_timestamp = SoapySDR::timeNsToTicks(nextTime, s->sample_rate / SAMPLE_RATE_DOWN);
|
||||
*ptimestamp = s->rx_timestamp;
|
||||
//printf("2) time set %llu, nextTime will be %llu \n", *ptimestamp, nextTime);
|
||||
}
|
||||
nextTime += SoapySDR::ticksToTimeNs(samples_received, s->sample_rate / SAMPLE_RATE_DOWN);
|
||||
}
|
||||
}
|
||||
|
||||
// bring RX data into 12 LSBs for softmodem RX
|
||||
for (int i=0; i<cc; i++) {
|
||||
for (int j=0; j<nsamps2; j++) {
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
#ifdef __AVX2__
|
||||
((__m256i *)buff[i])[j] = _mm256_srai_epi16(buff_tmp[i][j],4);
|
||||
#else
|
||||
((__m128i *)buff[i])[j] = _mm_srai_epi16(buff_tmp[i][j],4);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
return samples_received;
|
||||
}
|
||||
|
||||
/*! \brief Get current timestamp of Iris
|
||||
* \param device the hardware to use
|
||||
*/
|
||||
openair0_timestamp get_iris_time(openair0_device *device) {
|
||||
iris_state_t *s = (iris_state_t *) device->priv;
|
||||
return SoapySDR::timeNsToTicks(s->iris[0]->getHardwareTime(""), s->sample_rate);
|
||||
}
|
||||
|
||||
/*! \brief Compares two variables within precision
|
||||
* \param a first variable
|
||||
* \param b second variable
|
||||
*/
|
||||
static bool is_equal(double a, double b) {
|
||||
return std::fabs(a - b) < std::numeric_limits<double>::epsilon();
|
||||
}
|
||||
|
||||
void *set_freq_thread(void *arg) {
|
||||
|
||||
openair0_device *device = (openair0_device *) arg;
|
||||
iris_state_t *s = (iris_state_t *) device->priv;
|
||||
int r, i;
|
||||
printf("Setting Iris TX Freq %f, RX Freq %f\n", device->openair0_cfg[0].tx_freq[0],
|
||||
device->openair0_cfg[0].rx_freq[0]);
|
||||
// add check for the number of channels in the cfg
|
||||
for (r = 0; r < s->device_num; r++) {
|
||||
for (i = 0; i < s->iris[r]->getNumChannels(SOAPY_SDR_RX); i++) {
|
||||
if (i < s->rx_num_channels)
|
||||
s->iris[r]->setFrequency(SOAPY_SDR_RX, i, "RF", device->openair0_cfg[0].rx_freq[i]);
|
||||
}
|
||||
for (i = 0; i < s->iris[r]->getNumChannels(SOAPY_SDR_TX); i++) {
|
||||
if (i < s->tx_num_channels)
|
||||
s->iris[r]->setFrequency(SOAPY_SDR_TX, i, "RF", device->openair0_cfg[0].tx_freq[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Set frequencies (TX/RX)
|
||||
* \param device the hardware to use
|
||||
* \param openair0_cfg RF frontend parameters set by application
|
||||
* \param dummy dummy variable not used
|
||||
* \returns 0 in success
|
||||
*/
|
||||
int trx_iris_set_freq(openair0_device *device, openair0_config_t *openair0_cfg, int dont_block) {
|
||||
iris_state_t *s = (iris_state_t *) device->priv;
|
||||
pthread_t f_thread;
|
||||
if (dont_block)
|
||||
pthread_create(&f_thread, NULL, set_freq_thread, (void *) device);
|
||||
else {
|
||||
int r, i;
|
||||
for (r = 0; r < s->device_num; r++) {
|
||||
printf("Setting Iris TX Freq %f, RX Freq %f\n", openair0_cfg[0].tx_freq[0], openair0_cfg[0].rx_freq[0]);
|
||||
for (i = 0; i < s->iris[r]->getNumChannels(SOAPY_SDR_RX); i++) {
|
||||
if (i < s->rx_num_channels) {
|
||||
s->iris[r]->setFrequency(SOAPY_SDR_RX, i, "RF", openair0_cfg[0].rx_freq[i]);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < s->iris[r]->getNumChannels(SOAPY_SDR_TX); i++) {
|
||||
if (i < s->tx_num_channels) {
|
||||
s->iris[r]->setFrequency(SOAPY_SDR_TX, i, "RF", openair0_cfg[0].tx_freq[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Set Gains (TX/RX)
|
||||
* \param device the hardware to use
|
||||
* \param openair0_cfg RF frontend parameters set by application
|
||||
* \returns 0 in success
|
||||
*/
|
||||
int trx_iris_set_gains(openair0_device *device,
|
||||
openair0_config_t *openair0_cfg) {
|
||||
iris_state_t *s = (iris_state_t *) device->priv;
|
||||
int r;
|
||||
for (r = 0; r < s->device_num; r++) {
|
||||
s->iris[r]->setGain(SOAPY_SDR_RX, 0, openair0_cfg[0].rx_gain[0]);
|
||||
s->iris[r]->setGain(SOAPY_SDR_TX, 0, openair0_cfg[0].tx_gain[0]);
|
||||
s->iris[r]->setGain(SOAPY_SDR_RX, 1, openair0_cfg[0].rx_gain[1]);
|
||||
s->iris[r]->setGain(SOAPY_SDR_TX, 1, openair0_cfg[0].tx_gain[1]);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*! \brief Iris RX calibration table */
|
||||
rx_gain_calib_table_t calib_table_iris[] = {
|
||||
{3500000000.0, 83},
|
||||
{2660000000.0, 83},
|
||||
{2580000000.0, 83},
|
||||
{2300000000.0, 83},
|
||||
{1880000000.0, 83},
|
||||
{816000000.0, 83},
|
||||
{-1, 0}};
|
||||
|
||||
|
||||
/*! \brief Set RX gain offset
|
||||
* \param openair0_cfg RF frontend parameters set by application
|
||||
* \param chain_index RF chain to apply settings to
|
||||
* \returns 0 in success
|
||||
*/
|
||||
void set_rx_gain_offset(openair0_config_t *openair0_cfg, int chain_index, int bw_gain_adjust) {
|
||||
|
||||
int i = 0;
|
||||
// loop through calibration table to find best adjustment factor for RX frequency
|
||||
double min_diff = 6e9, diff, gain_adj = 0.0;
|
||||
if (bw_gain_adjust == 1) {
|
||||
switch ((int) openair0_cfg[0].sample_rate) {
|
||||
case 30720000:
|
||||
break;
|
||||
case 23040000:
|
||||
gain_adj = 1.25;
|
||||
break;
|
||||
case 15360000:
|
||||
gain_adj = 3.0;
|
||||
break;
|
||||
case 7680000:
|
||||
gain_adj = 6.0;
|
||||
break;
|
||||
case 3840000:
|
||||
gain_adj = 9.0;
|
||||
break;
|
||||
case 1920000:
|
||||
gain_adj = 12.0;
|
||||
break;
|
||||
default:
|
||||
printf("unknown sampling rate %d\n", (int) openair0_cfg[0].sample_rate);
|
||||
exit(-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (openair0_cfg->rx_gain_calib_table[i].freq > 0) {
|
||||
diff = fabs(openair0_cfg->rx_freq[chain_index] - openair0_cfg->rx_gain_calib_table[i].freq);
|
||||
printf("cal %d: freq %f, offset %f, diff %f\n",
|
||||
i,
|
||||
openair0_cfg->rx_gain_calib_table[i].freq,
|
||||
openair0_cfg->rx_gain_calib_table[i].offset, diff);
|
||||
if (min_diff > diff) {
|
||||
min_diff = diff;
|
||||
openair0_cfg->rx_gain_offset[chain_index] = openair0_cfg->rx_gain_calib_table[i].offset + gain_adj;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*! \brief print the Iris statistics
|
||||
* \param device the hardware to use
|
||||
* \returns 0 on success
|
||||
*/
|
||||
int trx_iris_get_stats(openair0_device *device) {
|
||||
|
||||
return (0);
|
||||
|
||||
}
|
||||
|
||||
/*! \brief Reset the Iris statistics
|
||||
* \param device the hardware to use
|
||||
* \returns 0 on success
|
||||
*/
|
||||
int trx_iris_reset_stats(openair0_device *device) {
|
||||
|
||||
return (0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
extern "C" {
|
||||
/*! \brief Initialize Openair Iris target. It returns 0 if OK
|
||||
* \param device the hardware to use
|
||||
* \param openair0_cfg RF frontend parameters set by application
|
||||
*/
|
||||
int device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
|
||||
|
||||
size_t i, r, card;
|
||||
int bw_gain_adjust = 0;
|
||||
openair0_cfg[0].rx_gain_calib_table = calib_table_iris;
|
||||
iris_state_t *s = (iris_state_t *) malloc(sizeof(iris_state_t));
|
||||
memset(s, 0, sizeof(iris_state_t));
|
||||
|
||||
std::string devFE("DEV");
|
||||
std::string cbrsFE("CBRS");
|
||||
std::string wireFormat("WIRE");
|
||||
|
||||
// Initialize Iris device
|
||||
device->openair0_cfg = openair0_cfg;
|
||||
SoapySDR::Kwargs args;
|
||||
args["driver"] = "iris";
|
||||
char *iris_addrs = device->openair0_cfg[0].sdr_addrs;
|
||||
if (iris_addrs == NULL)
|
||||
{
|
||||
s->iris.push_back(SoapySDR::Device::make(args));
|
||||
}
|
||||
else
|
||||
{
|
||||
char *serial = strtok(iris_addrs, ",");
|
||||
while (serial != NULL) {
|
||||
LOG_I(HW, "Attempting to open Iris device %s\n", serial);
|
||||
args["serial"] = serial;
|
||||
s->iris.push_back(SoapySDR::Device::make(args));
|
||||
serial = strtok(NULL, ",");
|
||||
}
|
||||
}
|
||||
|
||||
s->device_num = s->iris.size();
|
||||
device->type = IRIS_DEV;
|
||||
|
||||
|
||||
switch ((int) openair0_cfg[0].sample_rate) {
|
||||
case 30720000:
|
||||
//openair0_cfg[0].samples_per_packet = 1024;
|
||||
openair0_cfg[0].tx_sample_advance = 115;
|
||||
openair0_cfg[0].tx_bw = 20e6;
|
||||
openair0_cfg[0].rx_bw = 20e6;
|
||||
break;
|
||||
case 23040000:
|
||||
//openair0_cfg[0].samples_per_packet = 1024;
|
||||
openair0_cfg[0].tx_sample_advance = 113;
|
||||
openair0_cfg[0].tx_bw = 15e6;
|
||||
openair0_cfg[0].rx_bw = 15e6;
|
||||
break;
|
||||
case 15360000:
|
||||
//openair0_cfg[0].samples_per_packet = 1024;
|
||||
openair0_cfg[0].tx_sample_advance = 60;
|
||||
openair0_cfg[0].tx_bw = 10e6;
|
||||
openair0_cfg[0].rx_bw = 10e6;
|
||||
break;
|
||||
case 7680000:
|
||||
//openair0_cfg[0].samples_per_packet = 1024;
|
||||
openair0_cfg[0].tx_sample_advance = 30;
|
||||
openair0_cfg[0].tx_bw = 5e6;
|
||||
openair0_cfg[0].rx_bw = 5e6;
|
||||
break;
|
||||
case 1920000:
|
||||
//openair0_cfg[0].samples_per_packet = 1024;
|
||||
openair0_cfg[0].tx_sample_advance = 20;
|
||||
openair0_cfg[0].tx_bw = 1.4e6;
|
||||
openair0_cfg[0].rx_bw = 1.4e6;
|
||||
break;
|
||||
default:
|
||||
printf("Error: unknown sampling rate %f\n", openair0_cfg[0].sample_rate);
|
||||
exit(-1);
|
||||
break;
|
||||
}
|
||||
|
||||
printf("tx_sample_advance %d\n", openair0_cfg[0].tx_sample_advance);
|
||||
s->rx_num_channels = openair0_cfg[0].rx_num_channels;
|
||||
s->tx_num_channels = openair0_cfg[0].tx_num_channels;
|
||||
if ((s->rx_num_channels == 1 || s->rx_num_channels == 2) && (s->tx_num_channels == 1 || s->tx_num_channels == 2))
|
||||
printf("Enabling %d rx and %d tx channel(s) on each device...\n", s->rx_num_channels, s->tx_num_channels);
|
||||
else {
|
||||
printf("Invalid rx or tx number of channels (%d, %d)\n", s->rx_num_channels, s->tx_num_channels);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
for (r = 0; r < s->device_num; r++) {
|
||||
//this is unnecessary -- it will set the correct master clock based on sample rate
|
||||
/*switch ((int) openair0_cfg[0].sample_rate) {
|
||||
case 1920000:
|
||||
s->iris[r]->setMasterClockRate(256 * openair0_cfg[0].sample_rate);
|
||||
break;
|
||||
case 3840000:
|
||||
s->iris[r]->setMasterClockRate(128 * openair0_cfg[0].sample_rate);
|
||||
break;
|
||||
case 7680000:
|
||||
s->iris[r]->setMasterClockRate(64 * openair0_cfg[0].sample_rate);
|
||||
break;
|
||||
case 15360000:
|
||||
s->iris[r]->setMasterClockRate(32 * openair0_cfg[0].sample_rate);
|
||||
break;
|
||||
case 30720000:
|
||||
s->iris[r]->setMasterClockRate(16 * openair0_cfg[0].sample_rate);
|
||||
break;
|
||||
default:
|
||||
printf("Error: unknown sampling rate %f\n", openair0_cfg[0].sample_rate);
|
||||
exit(-1);
|
||||
break;
|
||||
}*/
|
||||
|
||||
for (i = 0; i < s->iris[r]->getNumChannels(SOAPY_SDR_RX); i++) {
|
||||
if (i < s->rx_num_channels) {
|
||||
s->iris[r]->setSampleRate(SOAPY_SDR_RX, i, openair0_cfg[0].sample_rate / SAMPLE_RATE_DOWN);
|
||||
#ifdef MOVE_DC
|
||||
printf("Moving DC out of main carrier for rx...\n");
|
||||
s->iris[r]->setFrequency(SOAPY_SDR_RX, i, "RF", openair0_cfg[0].rx_freq[i]-.75*openair0_cfg[0].sample_rate);
|
||||
s->iris[r]->setFrequency(SOAPY_SDR_RX, i, "BB", .75*openair0_cfg[0].sample_rate);
|
||||
#else
|
||||
s->iris[r]->setFrequency(SOAPY_SDR_RX, i, "RF", openair0_cfg[0].rx_freq[i]);
|
||||
#endif
|
||||
|
||||
set_rx_gain_offset(&openair0_cfg[0], i, bw_gain_adjust);
|
||||
//s->iris[r]->setGain(SOAPY_SDR_RX, i, openair0_cfg[0].rx_gain[i] - openair0_cfg[0].rx_gain_offset[i]);
|
||||
printf("rx gain offset: %f, rx_gain: %f, tx_tgain: %f\n", openair0_cfg[0].rx_gain_offset[i], openair0_cfg[0].rx_gain[i], openair0_cfg[0].tx_gain[i]);
|
||||
if (s->iris[r]->getHardwareInfo()["frontend"].compare(devFE) != 0) {
|
||||
s->iris[r]->setGain(SOAPY_SDR_RX, i, "LNA", openair0_cfg[0].rx_gain[i] - openair0_cfg[0].rx_gain_offset[i]);
|
||||
//s->iris[r]->setGain(SOAPY_SDR_RX, i, "LNA", 0);
|
||||
s->iris[r]->setGain(SOAPY_SDR_RX, i, "LNA1", 30);
|
||||
s->iris[r]->setGain(SOAPY_SDR_RX, i, "LNA2", 17);
|
||||
s->iris[r]->setGain(SOAPY_SDR_RX, i, "TIA", 7);
|
||||
s->iris[r]->setGain(SOAPY_SDR_RX, i, "PGA", 18);
|
||||
s->iris[r]->setGain(SOAPY_SDR_RX, i, "ATTN", 0);
|
||||
} else {
|
||||
s->iris[r]->setGain(SOAPY_SDR_RX, i, "LNA", openair0_cfg[0].rx_gain[i] - openair0_cfg[0].rx_gain_offset[i]); // [0,30]
|
||||
s->iris[r]->setGain(SOAPY_SDR_RX, i, "TIA", 7); // [0,12,6]
|
||||
s->iris[r]->setGain(SOAPY_SDR_RX, i, "PGA", 18); // [-12,19,1]
|
||||
//s->iris[r]->setGain(SOAPY_SDR_RX, i, 50); // [-12,19,1]
|
||||
|
||||
}
|
||||
|
||||
s->iris[r]->setDCOffsetMode(SOAPY_SDR_RX, i, true); // move somewhere else
|
||||
}
|
||||
}
|
||||
for (i = 0; i < s->iris[r]->getNumChannels(SOAPY_SDR_TX); i++) {
|
||||
if (i < s->tx_num_channels) {
|
||||
s->iris[r]->setSampleRate(SOAPY_SDR_TX, i, openair0_cfg[0].sample_rate / SAMPLE_RATE_DOWN);
|
||||
#ifdef MOVE_DC
|
||||
printf("Moving DC out of main carrier for tx...\n");
|
||||
s->iris[r]->setFrequency(SOAPY_SDR_TX, i, "RF", openair0_cfg[0].tx_freq[i]-.75*openair0_cfg[0].sample_rate);
|
||||
s->iris[r]->setFrequency(SOAPY_SDR_TX, i, "BB", .75*openair0_cfg[0].sample_rate);
|
||||
#else
|
||||
s->iris[r]->setFrequency(SOAPY_SDR_TX, i, "RF", openair0_cfg[0].tx_freq[i]);
|
||||
#endif
|
||||
|
||||
if (s->iris[r]->getHardwareInfo()["frontend"].compare(devFE) == 0) {
|
||||
s->iris[r]->setGain(SOAPY_SDR_TX, i, "PAD", openair0_cfg[0].tx_gain[i]);
|
||||
//s->iris[r]->setGain(SOAPY_SDR_TX, i, "PAD", 50);
|
||||
s->iris[r]->setGain(SOAPY_SDR_TX, i, "IAMP", 12);
|
||||
//s->iris[r]->writeSetting("TX_ENABLE_DELAY", "0");
|
||||
//s->iris[r]->writeSetting("TX_DISABLE_DELAY", "100");
|
||||
} else {
|
||||
s->iris[r]->setGain(SOAPY_SDR_TX, i, "PAD", openair0_cfg[0].tx_gain[i]);
|
||||
s->iris[r]->setGain(SOAPY_SDR_TX, i, "ATTN", 0); // [-18, 0, 6] dB
|
||||
s->iris[r]->setGain(SOAPY_SDR_TX, i, "IAMP", 6); // [-12, 12, 1] dB
|
||||
//s->iris[r]->setGain(SOAPY_SDR_TX, i, "PAD", 44); //openair0_cfg[0].tx_gain[i]);
|
||||
//s->iris[r]->setGain(SOAPY_SDR_TX, i, "PAD", 35); // [0, 52, 1] dB
|
||||
//s->iris[r]->setGain(SOAPY_SDR_TX, i, "PA1", 17); // 17 ??? dB
|
||||
s->iris[r]->setGain(SOAPY_SDR_TX, i, "PA2", 0); // [0, 17, 17] dB
|
||||
//s->iris[r]->setGain(SOAPY_SDR_TX, i, "PA3", 20); // 33 ??? dB
|
||||
s->iris[r]->writeSetting("TX_ENABLE_DELAY", "0");
|
||||
s->iris[r]->writeSetting("TX_DISABLE_DELAY", "100");
|
||||
}
|
||||
|
||||
// if (openair0_cfg[0].duplex_mode == 0) {
|
||||
// printf("\nFDD: Enable TX antenna override\n");
|
||||
// s->iris[r]->writeSetting(SOAPY_SDR_TX, i, "TX_ENB_OVERRIDE",
|
||||
// "true"); // From Josh: forces tx switching to be on always transmit regardless of bursts
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
printf("Actual master clock: %fMHz...\n", (s->iris[r]->getMasterClockRate() / 1e6));
|
||||
|
||||
int tx_filt_bw = openair0_cfg[0].tx_bw;
|
||||
int rx_filt_bw = openair0_cfg[0].rx_bw;
|
||||
#ifdef MOVE_DC //the filter is centered around the carrier, so we have to expand it if we have moved the DC tone.
|
||||
tx_filt_bw *= 3;
|
||||
rx_filt_bw *= 3;
|
||||
#endif
|
||||
/* Setting TX/RX BW */
|
||||
for (i = 0; i < s->tx_num_channels; i++) {
|
||||
if (i < s->iris[r]->getNumChannels(SOAPY_SDR_TX)) {
|
||||
s->iris[r]->setBandwidth(SOAPY_SDR_TX, i, tx_filt_bw);
|
||||
printf("Setting tx bandwidth on channel %zu/%lu: BW %f (readback %f)\n", i,
|
||||
s->iris[r]->getNumChannels(SOAPY_SDR_TX), tx_filt_bw / 1e6,
|
||||
s->iris[r]->getBandwidth(SOAPY_SDR_TX, i) / 1e6);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < s->rx_num_channels; i++) {
|
||||
if (i < s->iris[r]->getNumChannels(SOAPY_SDR_RX)) {
|
||||
s->iris[r]->setBandwidth(SOAPY_SDR_RX, i, rx_filt_bw);
|
||||
printf("Setting rx bandwidth on channel %zu/%lu : BW %f (readback %f)\n", i,
|
||||
s->iris[r]->getNumChannels(SOAPY_SDR_RX), rx_filt_bw / 1e6,
|
||||
s->iris[r]->getBandwidth(SOAPY_SDR_RX, i) / 1e6);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < s->iris[r]->getNumChannels(SOAPY_SDR_TX); i++) {
|
||||
if (i < s->tx_num_channels) {
|
||||
printf("\nUsing SKLK calibration...\n");
|
||||
s->iris[r]->writeSetting(SOAPY_SDR_TX, i, "CALIBRATE", "SKLK");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (i = 0; i < s->iris[r]->getNumChannels(SOAPY_SDR_RX); i++) {
|
||||
if (i < s->rx_num_channels) {
|
||||
printf("\nUsing SKLK calibration...\n");
|
||||
s->iris[r]->writeSetting(SOAPY_SDR_RX, i, "CALIBRATE", "SKLK");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (s->iris[r]->getHardwareInfo()["frontend"].compare(devFE) == 0) {
|
||||
for (i = 0; i < s->iris[r]->getNumChannels(SOAPY_SDR_RX); i++) {
|
||||
if (openair0_cfg[0].duplex_mode == 0) {
|
||||
printf("\nFDD: Setting receive antenna to %s\n", s->iris[r]->listAntennas(SOAPY_SDR_RX, i)[1].c_str());
|
||||
if (i < s->rx_num_channels)
|
||||
s->iris[r]->setAntenna(SOAPY_SDR_RX, i, "RX");
|
||||
} else {
|
||||
printf("\nTDD: Setting receive antenna to %s\n", s->iris[r]->listAntennas(SOAPY_SDR_RX, i)[0].c_str());
|
||||
if (i < s->rx_num_channels)
|
||||
s->iris[r]->setAntenna(SOAPY_SDR_RX, i, "TRX");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//s->iris[r]->writeSetting("TX_SW_DELAY", std::to_string(
|
||||
// -openair0_cfg[0].tx_sample_advance)); //should offset switching to compensate for RF path (Lime) delay -- this will eventually be automated
|
||||
|
||||
// create tx & rx streamer
|
||||
//const SoapySDR::Kwargs &arg = SoapySDR::Kwargs();
|
||||
std::map <std::string, std::string> rxStreamArgs;
|
||||
rxStreamArgs["WIRE"] = SOAPY_SDR_CS16;
|
||||
|
||||
std::vector <size_t> channels;
|
||||
for (i = 0; i < s->rx_num_channels; i++)
|
||||
if (i < s->iris[r]->getNumChannels(SOAPY_SDR_RX))
|
||||
channels.push_back(i);
|
||||
s->rxStream.push_back(s->iris[r]->setupStream(SOAPY_SDR_RX, SOAPY_SDR_CS16, channels));//, rxStreamArgs));
|
||||
|
||||
std::vector <size_t> tx_channels = {};
|
||||
for (i = 0; i < s->tx_num_channels; i++)
|
||||
if (i < s->iris[r]->getNumChannels(SOAPY_SDR_TX))
|
||||
tx_channels.push_back(i);
|
||||
s->txStream.push_back(s->iris[r]->setupStream(SOAPY_SDR_TX, SOAPY_SDR_CS16, tx_channels)); //, arg));
|
||||
//s->iris[r]->setHardwareTime(0, "");
|
||||
|
||||
std::cout << "Front end detected: " << s->iris[r]->getHardwareInfo()["frontend"] << "\n";
|
||||
for (i = 0; i < s->rx_num_channels; i++) {
|
||||
if (i < s->iris[r]->getNumChannels(SOAPY_SDR_RX)) {
|
||||
printf("RX Channel %zu\n", i);
|
||||
printf("Actual RX sample rate: %fMSps...\n", (s->iris[r]->getSampleRate(SOAPY_SDR_RX, i) / 1e6));
|
||||
printf("Actual RX frequency: %fGHz...\n", (s->iris[r]->getFrequency(SOAPY_SDR_RX, i) / 1e9));
|
||||
printf("Actual RX gain: %f...\n", (s->iris[r]->getGain(SOAPY_SDR_RX, i)));
|
||||
printf("Actual RX LNA gain: %f...\n", (s->iris[r]->getGain(SOAPY_SDR_RX, i, "LNA")));
|
||||
printf("Actual RX PGA gain: %f...\n", (s->iris[r]->getGain(SOAPY_SDR_RX, i, "PGA")));
|
||||
printf("Actual RX TIA gain: %f...\n", (s->iris[r]->getGain(SOAPY_SDR_RX, i, "TIA")));
|
||||
if (s->iris[r]->getHardwareInfo()["frontend"].compare(devFE) != 0) {
|
||||
printf("Actual RX LNA1 gain: %f...\n", (s->iris[r]->getGain(SOAPY_SDR_RX, i, "LNA1")));
|
||||
printf("Actual RX LNA2 gain: %f...\n", (s->iris[r]->getGain(SOAPY_SDR_RX, i, "LNA2")));
|
||||
}
|
||||
printf("Actual RX bandwidth: %fM...\n", (s->iris[r]->getBandwidth(SOAPY_SDR_RX, i) / 1e6));
|
||||
printf("Actual RX antenna: %s...\n", (s->iris[r]->getAntenna(SOAPY_SDR_RX, i).c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < s->tx_num_channels; i++) {
|
||||
if (i < s->iris[r]->getNumChannels(SOAPY_SDR_TX)) {
|
||||
printf("TX Channel %zu\n", i);
|
||||
printf("Actual TX sample rate: %fMSps...\n", (s->iris[r]->getSampleRate(SOAPY_SDR_TX, i) / 1e6));
|
||||
printf("Actual TX frequency: %fGHz...\n", (s->iris[r]->getFrequency(SOAPY_SDR_TX, i) / 1e9));
|
||||
printf("Actual TX gain: %f...\n", (s->iris[r]->getGain(SOAPY_SDR_TX, i)));
|
||||
printf("Actual TX PAD gain: %f...\n", (s->iris[r]->getGain(SOAPY_SDR_TX, i, "PAD")));
|
||||
printf("Actual TX IAMP gain: %f...\n", (s->iris[r]->getGain(SOAPY_SDR_TX, i, "IAMP")));
|
||||
if (s->iris[r]->getHardwareInfo()["frontend"].compare(devFE) != 0) {
|
||||
printf("Actual TX PA1 gain: %f...\n", (s->iris[r]->getGain(SOAPY_SDR_TX, i, "PA1")));
|
||||
printf("Actual TX PA2 gain: %f...\n", (s->iris[r]->getGain(SOAPY_SDR_TX, i, "PA2")));
|
||||
printf("Actual TX PA3 gain: %f...\n", (s->iris[r]->getGain(SOAPY_SDR_TX, i, "PA3")));
|
||||
}
|
||||
printf("Actual TX bandwidth: %fM...\n", (s->iris[r]->getBandwidth(SOAPY_SDR_TX, i) / 1e6));
|
||||
printf("Actual TX antenna: %s...\n", (s->iris[r]->getAntenna(SOAPY_SDR_TX, i).c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
s->iris[0]->writeSetting("SYNC_DELAYS", "");
|
||||
for (r = 0; r < s->device_num; r++)
|
||||
s->iris[r]->setHardwareTime(0, "TRIGGER");
|
||||
s->iris[0]->writeSetting("TRIGGER_GEN", "");
|
||||
for (r = 0; r < s->device_num; r++)
|
||||
printf("Device timestamp: %f...\n", (s->iris[r]->getHardwareTime("TRIGGER") / 1e9));
|
||||
|
||||
device->priv = s;
|
||||
device->trx_start_func = trx_iris_start;
|
||||
device->trx_write_func = trx_iris_write;
|
||||
device->trx_read_func = trx_iris_read;
|
||||
device->trx_get_stats_func = trx_iris_get_stats;
|
||||
device->trx_reset_stats_func = trx_iris_reset_stats;
|
||||
device->trx_end_func = trx_iris_end;
|
||||
device->trx_stop_func = trx_iris_stop;
|
||||
device->trx_set_freq_func = trx_iris_set_freq;
|
||||
device->trx_set_gains_func = trx_iris_set_gains;
|
||||
device->openair0_cfg = openair0_cfg;
|
||||
|
||||
s->sample_rate = openair0_cfg[0].sample_rate;
|
||||
// TODO:
|
||||
// init tx_forward_nsamps based iris_time_offset ex
|
||||
if (is_equal(s->sample_rate, (double) 30.72e6))
|
||||
s->tx_forward_nsamps = 176;
|
||||
if (is_equal(s->sample_rate, (double) 15.36e6))
|
||||
s->tx_forward_nsamps = 90;
|
||||
if (is_equal(s->sample_rate, (double) 7.68e6))
|
||||
s->tx_forward_nsamps = 50;
|
||||
|
||||
LOG_I(HW, "Finished initializing %d Iris device(s).\n", s->device_num);
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/*@}*/
|
||||
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
@@ -193,7 +193,7 @@ int trx_lms_start(openair0_device *device){
|
||||
LMS_EnableChannel(lms_device,LMS_CH_RX,1,true);
|
||||
LMS_EnableChannel(lms_device,LMS_CH_TX,1,true);
|
||||
} */
|
||||
LMS_VCTCXOWrite(lms_device,129);
|
||||
LMS_VCTCXOWrite(lms_device,129, true);
|
||||
|
||||
if (LMS_SetSampleRate(lms_device,device->openair0_cfg->sample_rate,2)!=0)
|
||||
{
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
USRP_OBJ += $(OPENAIR_TARGETS)/ARCH/USRP/USERSPACE/LIB/usrp_lib.o
|
||||
USRP_FILE_OBJ += $(OPENAIR_TARGETS)/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp
|
||||
USRP_CFLAGS += -I$(OPENAIR_TARGETS)/ARCH/COMMON -I$(OPENAIR_TARGETS)/ARCH/USRP/USERSPACE/LIB/ -I$(OPENAIR_TARGETS)/COMMON
|
||||
|
||||
@@ -346,6 +346,7 @@ static void trx_usrp_end(openair0_device *device) {
|
||||
|
||||
done = 1;
|
||||
|
||||
|
||||
if (u_sf_mode != 2) { // not subframes replay
|
||||
#endif
|
||||
usrp_state_t *s = (usrp_state_t *)device->priv;
|
||||
@@ -435,17 +436,20 @@ static int trx_usrp_write(openair0_device *device, openair0_timestamp timestamp,
|
||||
#endif
|
||||
usrp_state_t *s = (usrp_state_t *)device->priv;
|
||||
int nsamps2; // aligned to upper 32 or 16 byte boundary
|
||||
|
||||
#if defined(__x86_64) || defined(__i386__)
|
||||
#ifdef __AVX2__
|
||||
nsamps2 = (nsamps+7)>>3;
|
||||
__m256i buff_tx[2][nsamps2];
|
||||
#else
|
||||
#ifdef __AVX2__
|
||||
nsamps2 = (nsamps+7)>>3;
|
||||
__m256i buff_tx[2][nsamps2];
|
||||
#else
|
||||
nsamps2 = (nsamps+3)>>2;
|
||||
__m128i buff_tx[2][nsamps2];
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(__arm__)
|
||||
nsamps2 = (nsamps+3)>>2;
|
||||
int16x8_t buff_tx[2][nsamps2];
|
||||
#else
|
||||
#error Unsupported CPU architecture, USRP device cannot be built
|
||||
#endif
|
||||
|
||||
// bring RX data into 12 LSBs for softmodem RX
|
||||
|
||||
@@ -8,33 +8,47 @@
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
int fullread(int fd, void *_buf, int count)
|
||||
{
|
||||
const int port = 4043;
|
||||
#define helpTxt "\
|
||||
\x1b[31m\
|
||||
tcp_bridge: error: you have to run one UE and one eNB\n\
|
||||
For this, export TCPBRIDGE=enb (eNB case) or \n\
|
||||
TCPBRIDGE=<an ip address> (UE case)\n\
|
||||
\x1b[m"
|
||||
|
||||
int fullread(int fd, void *_buf, int count) {
|
||||
char *buf = _buf;
|
||||
int ret = 0;
|
||||
int l;
|
||||
|
||||
while (count) {
|
||||
l = read(fd, buf, count);
|
||||
|
||||
if (l <= 0) return -1;
|
||||
|
||||
count -= l;
|
||||
buf += l;
|
||||
ret += l;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fullwrite(int fd, void *_buf, int count)
|
||||
{
|
||||
int fullwrite(int fd, void *_buf, int count) {
|
||||
char *buf = _buf;
|
||||
int ret = 0;
|
||||
int l;
|
||||
|
||||
while (count) {
|
||||
l = write(fd, buf, count);
|
||||
|
||||
if (l <= 0) return -1;
|
||||
|
||||
count -= l;
|
||||
buf += l;
|
||||
ret += l;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -46,155 +60,208 @@ typedef struct {
|
||||
uint64_t timestamp;
|
||||
uint64_t next_tx_timestamp;
|
||||
int is_enb;
|
||||
char *ip;
|
||||
} tcp_bridge_state_t;
|
||||
|
||||
void verify_connection(int fd, int is_enb)
|
||||
{
|
||||
void verify_connection(int fd, int is_enb) {
|
||||
char c = is_enb;
|
||||
|
||||
if (fullwrite(fd, &c, 1) != 1) exit(1);
|
||||
|
||||
if (fullread(fd, &c, 1) != 1) exit(1);
|
||||
|
||||
if (c == is_enb) {
|
||||
printf("\x1b[31mtcp_bridge: error: you have to run one UE and one eNB"
|
||||
" (did you run 'export ENODEB=1' in the eNB terminal?)\x1b[m\n");
|
||||
printf(helpTxt);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int tcp_bridge_start(openair0_device *device)
|
||||
{
|
||||
int port = 4043;
|
||||
tcp_bridge_state_t *tcp_bridge = device->priv;
|
||||
int try;
|
||||
int max_try = 5;
|
||||
|
||||
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock == -1) { perror("tcp_bridge: socket"); exit(1); }
|
||||
|
||||
int enable = 1;
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)))
|
||||
{ perror("tcp_bridge: SO_REUSEADDR"); exit(1); }
|
||||
|
||||
int start_enb(tcp_bridge_state_t *tcp_bridge) {
|
||||
struct sockaddr_in addr = {
|
||||
sin_family: AF_INET,
|
||||
sin_port: htons(port),
|
||||
sin_addr: { s_addr: INADDR_ANY }
|
||||
sin_family:
|
||||
AF_INET,
|
||||
sin_port:
|
||||
htons(port),
|
||||
sin_addr:
|
||||
{ s_addr: INADDR_ANY }
|
||||
};
|
||||
|
||||
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr))) {
|
||||
if (errno == EADDRINUSE) goto client_mode;
|
||||
{ perror("tcp_bridge: bind"); exit(1); }
|
||||
if (bind(tcp_bridge->sock, (struct sockaddr *)&addr, sizeof(addr))) {
|
||||
perror("tcp_bridge: bind");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (listen(sock, 5))
|
||||
{ perror("tcp_bridge: listen"); exit(1); }
|
||||
if (listen(tcp_bridge->sock, 5)) {
|
||||
perror("tcp_bridge: listen");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("tcp_bridge: wait for connection on port %d\n", port);
|
||||
|
||||
socklen_t len = sizeof(addr);
|
||||
int sock2 = accept(sock, (struct sockaddr *)&addr, &len);
|
||||
if (sock2 == -1)
|
||||
{ perror("tcp_bridge: accept"); exit(1); }
|
||||
int sockServ = accept(tcp_bridge->sock, (struct sockaddr *)&addr, &len);
|
||||
|
||||
close(sock);
|
||||
|
||||
tcp_bridge->sock = sock2;
|
||||
if ( sockServ == -1) {
|
||||
perror("tcp_bridge: accept");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
verify_connection(sockServ, tcp_bridge->is_enb);
|
||||
printf("tcp_bridge: connection established\n");
|
||||
|
||||
verify_connection(sock2, tcp_bridge->is_enb);
|
||||
|
||||
close(tcp_bridge->sock);
|
||||
tcp_bridge->sock=sockServ;
|
||||
return 0;
|
||||
}
|
||||
|
||||
client_mode:
|
||||
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
int start_ue(tcp_bridge_state_t *tcp_bridge) {
|
||||
struct sockaddr_in addr = {sin_family:
|
||||
AF_INET,
|
||||
sin_port:
|
||||
htons(port),
|
||||
sin_addr:
|
||||
{ s_addr: INADDR_ANY }
|
||||
};
|
||||
addr.sin_addr.s_addr = inet_addr(tcp_bridge->ip);
|
||||
|
||||
for (try = 0; try < max_try; try++) {
|
||||
if (try != 0) sleep(1);
|
||||
while(1) {
|
||||
printf("tcp_bridge: trying to connect to %s:%d\n", tcp_bridge->ip, port);
|
||||
|
||||
printf("tcp_bridge: trying to connect to 127.0.0.1:%d (attempt %d/%d)\n",
|
||||
port, try+1, max_try);
|
||||
|
||||
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
|
||||
if (connect(tcp_bridge->sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
|
||||
verify_connection(tcp_bridge->sock, tcp_bridge->is_enb);
|
||||
printf("tcp_bridge: connection established\n");
|
||||
tcp_bridge->sock = sock;
|
||||
verify_connection(sock, tcp_bridge->is_enb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
perror("tcp_bridge");
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
printf("tcp_bridge: connection failed\n");
|
||||
|
||||
exit(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp_bridge_request(openair0_device *device, void *msg, ssize_t msg_len) { abort(); return 0; }
|
||||
int tcp_bridge_reply(openair0_device *device, void *msg, ssize_t msg_len) { abort(); return 0; }
|
||||
int tcp_bridge_get_stats(openair0_device* device) { return 0; }
|
||||
int tcp_bridge_reset_stats(openair0_device* device) { return 0; }
|
||||
int tcp_bridge_start(openair0_device *device) {
|
||||
tcp_bridge_state_t *tcp_bridge = device->priv;
|
||||
tcp_bridge->sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (tcp_bridge->sock == -1) {
|
||||
perror("tcp_bridge: socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int enable = 1;
|
||||
|
||||
if (setsockopt(tcp_bridge->sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int))) {
|
||||
perror("tcp_bridge: SO_REUSEADDR");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( tcp_bridge->is_enb )
|
||||
return start_enb(tcp_bridge);
|
||||
else
|
||||
return start_ue(tcp_bridge);
|
||||
}
|
||||
|
||||
int tcp_bridge_request(openair0_device *device, void *msg, ssize_t msg_len) {
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
int tcp_bridge_reply(openair0_device *device, void *msg, ssize_t msg_len) {
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
int tcp_bridge_get_stats(openair0_device *device) {
|
||||
return 0;
|
||||
}
|
||||
int tcp_bridge_reset_stats(openair0_device *device) {
|
||||
return 0;
|
||||
}
|
||||
void tcp_bridge_end(openair0_device *device) {}
|
||||
int tcp_bridge_stop(openair0_device *device) { return 0; }
|
||||
int tcp_bridge_set_freq(openair0_device* device, openair0_config_t *openair0_cfg,int exmimo_dump_config) { return 0; }
|
||||
int tcp_bridge_set_gains(openair0_device* device, openair0_config_t *openair0_cfg) { return 0; }
|
||||
int tcp_bridge_stop(openair0_device *device) {
|
||||
return 0;
|
||||
}
|
||||
int tcp_bridge_set_freq(openair0_device *device, openair0_config_t *openair0_cfg,int exmimo_dump_config) {
|
||||
return 0;
|
||||
}
|
||||
int tcp_bridge_set_gains(openair0_device *device, openair0_config_t *openair0_cfg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp_bridge_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags) {
|
||||
if (cc != 1) {
|
||||
printf("tcp_bridge: only 1 antenna supported\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int tcp_bridge_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags)
|
||||
{
|
||||
if (cc != 1) { printf("tcp_bridge: only 1 antenna supported\n"); exit(1); }
|
||||
tcp_bridge_state_t *t = device->priv;
|
||||
|
||||
/* deal with discontinuities in output (think: eNB in TDD mode) */
|
||||
if (t->next_tx_timestamp && timestamp != t->next_tx_timestamp) {
|
||||
uint32_t b[4096];
|
||||
uint64_t to_send = timestamp - t->next_tx_timestamp;
|
||||
memset(b, 0, 4096 * sizeof(uint32_t));
|
||||
|
||||
while (to_send) {
|
||||
int len = to_send > 4096 ? 4096 : to_send;
|
||||
int n = fullwrite(t->sock, b, len * 4);
|
||||
|
||||
if (n != len * 4) {
|
||||
printf("tcp_bridge: write error ret %d error %s\n", n, strerror(errno));
|
||||
abort();
|
||||
}
|
||||
|
||||
to_send -= len;
|
||||
}
|
||||
}
|
||||
|
||||
int n = fullwrite(t->sock, buff[0], nsamps * 4);
|
||||
|
||||
if (n != nsamps * 4) {
|
||||
printf("tcp_bridge: write error ret %d (wanted %d) error %s\n", n, nsamps*4, strerror(errno));
|
||||
abort();
|
||||
}
|
||||
|
||||
t->next_tx_timestamp = timestamp + nsamps;
|
||||
return nsamps;
|
||||
}
|
||||
|
||||
int tcp_bridge_read(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc)
|
||||
{
|
||||
if (cc != 1) { printf("tcp_bridge: only 1 antenna supported\n"); exit(1); }
|
||||
int tcp_bridge_read(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc) {
|
||||
if (cc != 1) {
|
||||
printf("tcp_bridge: only 1 antenna supported\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
tcp_bridge_state_t *t = device->priv;
|
||||
int n = fullread(t->sock, buff[0], nsamps * 4);
|
||||
|
||||
if (n != nsamps * 4) {
|
||||
printf("tcp_bridge: read error ret %d nsamps*4 %d error %s\n", n, nsamps * 4, strerror(errno));
|
||||
abort();
|
||||
}
|
||||
|
||||
*timestamp = t->timestamp;
|
||||
t->timestamp += nsamps;
|
||||
return nsamps;
|
||||
}
|
||||
|
||||
int tcp_bridge_read_ue(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc)
|
||||
{
|
||||
if (cc != 1) { printf("tcp_bridge: only 1 antenna supported\n"); exit(1); }
|
||||
int tcp_bridge_read_ue(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc) {
|
||||
if (cc != 1) {
|
||||
printf("tcp_bridge: only 1 antenna supported\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
tcp_bridge_state_t *t = device->priv;
|
||||
int n;
|
||||
|
||||
/* In synch mode, UE does not write, but we need to
|
||||
* send something to the eNodeB.
|
||||
* We know that UE is in synch mode when it reads
|
||||
* 10 subframes at a time.
|
||||
*/
|
||||
send something to the eNodeB.
|
||||
We know that UE is in synch mode when it reads
|
||||
10 subframes at a time.
|
||||
*/
|
||||
if (nsamps == t->samples_per_subframe * 10) {
|
||||
uint32_t b[nsamps];
|
||||
memset(b, 0, nsamps * 4);
|
||||
n = fullwrite(t->sock, b, nsamps * 4);
|
||||
|
||||
if (n != nsamps * 4) {
|
||||
printf("tcp_bridge: write error ret %d error %s\n", n, strerror(errno));
|
||||
abort();
|
||||
@@ -205,96 +272,129 @@ int tcp_bridge_read_ue(openair0_device *device, openair0_timestamp *timestamp, v
|
||||
}
|
||||
|
||||
/* To startup proper communcation between eNB and UE,
|
||||
* we need to understand that:
|
||||
* - eNodeB starts reading subframe 0
|
||||
* - then eNodeB starts sending subframe 4
|
||||
* and then repeats read/write for each subframe.
|
||||
* The UE:
|
||||
* - reads 10 subframes at a time until it is synchronized
|
||||
* - then reads subframe n and writes subframe n+2
|
||||
* We also want to enforce that the subframe 0 is read
|
||||
* at the beginning of the UE RX buffer, not in the middle
|
||||
* of it.
|
||||
* So it means:
|
||||
* - for the eNodeB: let it run as in normal mode (as with a B210)
|
||||
* - for the UE, on its very first read:
|
||||
* - we want this read to get data from subframe 0
|
||||
* but the first write of eNodeB is subframe 4
|
||||
* so we first need to read and ignore 6 subframes
|
||||
* - the UE will start its TX only at the subframe 2
|
||||
* corresponding to the subframe 0 it just read,
|
||||
* so we need to write 12 subframes before anything
|
||||
* (the function tcp_bridge_read_ue takes care to
|
||||
* insert dummy TX data during the synch phase)
|
||||
*
|
||||
* Here is a drawing of the beginning of things to make
|
||||
* this logic clearer.
|
||||
*
|
||||
* We see that eNB starts RX at subframe 0, starts TX at subfram 4,
|
||||
* and that UE starts RX at subframe 10 and TX at subframe 12.
|
||||
*
|
||||
* We understand that the UE has to transmit 12 empty
|
||||
* subframes for the eNodeB to start its processing.
|
||||
*
|
||||
* And because the eNodeB starts its TX at subframe 4 and we
|
||||
* want the UE to start its RX at subframe 10, we need to
|
||||
* read and ignore 6 subframes in the UE.
|
||||
*
|
||||
* -------------------------------------------------------------------------
|
||||
* eNB RX: | *0* | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 ...
|
||||
* -------------------------------------------------------------------------
|
||||
*
|
||||
* -------------------------------------------------------------------------
|
||||
* eNB TX: | 0 | 1 | 2 | 3 | *4* | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 ...
|
||||
* -------------------------------------------------------------------------
|
||||
*
|
||||
* -------------------------------------------------------------------------
|
||||
* UE RX: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | *10* | 11 | 12 | 13 | 14 ...
|
||||
* -------------------------------------------------------------------------
|
||||
*
|
||||
* -------------------------------------------------------------------------
|
||||
* UE TX: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | *12* | 13 | 14 ...
|
||||
* -------------------------------------------------------------------------
|
||||
*
|
||||
* As a final note, we do TX before RX to ensure that the eNB will
|
||||
* get some data and send us something so there is no deadlock
|
||||
* at the beginning of things. Hopefully the kernel buffers for
|
||||
* the sockets are big enough so that the first (big) TX can
|
||||
* return to user mode before the buffers are full. If this
|
||||
* is wrong in some environment, we will need to work by smaller
|
||||
* units of data at a time.
|
||||
*/
|
||||
int tcp_bridge_ue_first_read(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc)
|
||||
{
|
||||
if (cc != 1) { printf("tcp_bridge: only 1 antenna supported\n"); exit(1); }
|
||||
tcp_bridge_state_t *t = device->priv;
|
||||
we need to understand that:
|
||||
- eNodeB starts reading subframe 0
|
||||
- then eNodeB starts sending subframe 4
|
||||
and then repeats read/write for each subframe.
|
||||
The UE:
|
||||
- reads 10 subframes at a time until it is synchronized
|
||||
- then reads subframe n and writes subframe n+2
|
||||
We also want to enforce that the subframe 0 is read
|
||||
at the beginning of the UE RX buffer, not in the middle
|
||||
of it.
|
||||
So it means:
|
||||
- for the eNodeB: let it run as in normal mode (as with a B210)
|
||||
- for the UE, on its very first read:
|
||||
- we want this read to get data from subframe 0
|
||||
but the first write of eNodeB is subframe 4
|
||||
so we first need to read and ignore 6 subframes
|
||||
- the UE will start its TX only at the subframe 2
|
||||
corresponding to the subframe 0 it just read,
|
||||
so we need to write 12 subframes before anything
|
||||
(the function tcp_bridge_read_ue takes care to
|
||||
insert dummy TX data during the synch phase)
|
||||
|
||||
Here is a drawing of the beginning of things to make
|
||||
this logic clearer.
|
||||
|
||||
We see that eNB starts RX at subframe 0, starts TX at subfram 4,
|
||||
and that UE starts RX at subframe 10 and TX at subframe 12.
|
||||
|
||||
We understand that the UE has to transmit 12 empty
|
||||
subframes for the eNodeB to start its processing.
|
||||
|
||||
And because the eNodeB starts its TX at subframe 4 and we
|
||||
want the UE to start its RX at subframe 10, we need to
|
||||
read and ignore 6 subframes in the UE.
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
eNB RX: | *0* | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 ...
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
eNB TX: | 0 | 1 | 2 | 3 | *4* | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 ...
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
UE RX: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | *10* | 11 | 12 | 13 | 14 ...
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
UE TX: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | *12* | 13 | 14 ...
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
As a final note, we do TX before RX to ensure that the eNB will
|
||||
get some data and send us something so there is no deadlock
|
||||
at the beginning of things. Hopefully the kernel buffers for
|
||||
the sockets are big enough so that the first (big) TX can
|
||||
return to user mode before the buffers are full. If this
|
||||
is wrong in some environment, we will need to work by smaller
|
||||
units of data at a time.
|
||||
*/
|
||||
int tcp_bridge_ue_first_read(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc) {
|
||||
if (cc != 1) {
|
||||
printf("tcp_bridge: only 1 antenna supported\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
tcp_bridge_state_t *t = device->priv;
|
||||
uint32_t b[t->samples_per_subframe * 12];
|
||||
memset(b, 0, t->samples_per_subframe * 12 * 4);
|
||||
int n = fullwrite(t->sock, b, t->samples_per_subframe * 12 * 4);
|
||||
|
||||
if (n != t->samples_per_subframe * 12 * 4) {
|
||||
printf("tcp_bridge: write error ret %d error %s\n", n, strerror(errno));
|
||||
abort();
|
||||
}
|
||||
|
||||
n = fullread(t->sock, b, t->samples_per_subframe * 6 * 4);
|
||||
|
||||
if (n != t->samples_per_subframe * 6 * 4) {
|
||||
printf("tcp_bridge: read error ret %d error %s\n", n, strerror(errno));
|
||||
abort();
|
||||
}
|
||||
|
||||
device->trx_read_func = tcp_bridge_read_ue;
|
||||
/* Due to some unknown bug in the eNB (or UE, or both), the first frames
|
||||
* are not correctly generated (or handled), which leads to a bad behavior
|
||||
* of the simulator in some cases (seen with 100 RBs: the UE reads a bad
|
||||
* MIB and switches to 25 RBs, which results in a deadlock in this driver).
|
||||
* Let's skip 10 frames to avoid this issue.
|
||||
*/
|
||||
for (int i = 0; i < 10 * 10; i++) {
|
||||
memset(b, 0, t->samples_per_subframe * 4);
|
||||
n = fullwrite(t->sock, b, t->samples_per_subframe * 4);
|
||||
|
||||
if (n != t->samples_per_subframe * 4) {
|
||||
printf("tcp_bridge: write error ret %d error %s\n", n, strerror(errno));
|
||||
abort();
|
||||
}
|
||||
|
||||
n = fullread(t->sock, b, t->samples_per_subframe * 4);
|
||||
|
||||
if (n != t->samples_per_subframe * 4) {
|
||||
printf("tcp_bridge: read error ret %d error %s\n", n, strerror(errno));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
device->trx_read_func = tcp_bridge_read_ue;
|
||||
return tcp_bridge_read_ue(device, timestamp, buff, nsamps, cc);
|
||||
}
|
||||
|
||||
__attribute__((__visibility__("default")))
|
||||
int device_init(openair0_device* device, openair0_config_t *openair0_cfg)
|
||||
{
|
||||
tcp_bridge_state_t *tcp_bridge = (tcp_bridge_state_t*)malloc(sizeof(tcp_bridge_state_t));
|
||||
memset(tcp_bridge, 0, sizeof(tcp_bridge_state_t));
|
||||
int device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
|
||||
tcp_bridge_state_t *tcp_bridge = (tcp_bridge_state_t *)calloc(sizeof(tcp_bridge_state_t),1);
|
||||
|
||||
tcp_bridge->is_enb = getenv("ENODEB") != NULL;
|
||||
if ((tcp_bridge->ip=getenv("TCPBRIDGE")) == NULL ) {
|
||||
/* for compatibility, we test the ENB environment variable */
|
||||
if ((tcp_bridge->ip=getenv("ENODEB")) != NULL ) {
|
||||
tcp_bridge->ip=strdup("enb");
|
||||
} else {
|
||||
tcp_bridge->ip=strdup("127.0.0.1");
|
||||
}
|
||||
}
|
||||
|
||||
tcp_bridge->is_enb = strncasecmp(tcp_bridge->ip,"enb",3) == 0;
|
||||
printf("tcp_bridge: running as %s\n", tcp_bridge->is_enb ? "eNB" : "UE");
|
||||
|
||||
/* only 25, 50 or 100 PRBs handled for the moment */
|
||||
@@ -323,15 +423,21 @@ int device_init(openair0_device* device, openair0_config_t *openair0_cfg)
|
||||
device->priv = tcp_bridge;
|
||||
|
||||
switch ((int)openair0_cfg[0].sample_rate) {
|
||||
case 30720000: tcp_bridge->samples_per_subframe = 30720; break;
|
||||
case 15360000: tcp_bridge->samples_per_subframe = 15360; break;
|
||||
case 7680000: tcp_bridge->samples_per_subframe = 7680; break;
|
||||
case 30720000:
|
||||
tcp_bridge->samples_per_subframe = 30720;
|
||||
break;
|
||||
|
||||
case 15360000:
|
||||
tcp_bridge->samples_per_subframe = 15360;
|
||||
break;
|
||||
|
||||
case 7680000:
|
||||
tcp_bridge->samples_per_subframe = 7680;
|
||||
break;
|
||||
}
|
||||
|
||||
/* let's pretend to be a b2x0 */
|
||||
device->type = USRP_B200_DEV;
|
||||
|
||||
device->openair0_cfg=&openair0_cfg[0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user