Merge remote-tracking branch 'origin/develop' into Dedicated-BWPs
# Conflicts: # openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c # openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c # openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
This commit is contained in:
@@ -116,9 +116,6 @@ int load_lib(openair0_device *device,
|
||||
deflibname=OAI_IQPLAYER_LIBNAME;
|
||||
shlib_fdesc[0].fname="device_init";
|
||||
set_softmodem_optmask(SOFTMODEM_RECPLAY_BIT); // softmodem has to know we use the iqplayer to workaround randomized algorithms
|
||||
} else if ( IS_SOFTMODEM_BASICSIM ) {
|
||||
deflibname=OAI_BASICSIM_LIBNAME;
|
||||
shlib_fdesc[0].fname="device_init";
|
||||
} else if (IS_SOFTMODEM_RFSIM && flag == RAU_LOCAL_RADIO_HEAD) {
|
||||
deflibname=OAI_RFSIM_LIBNAME;
|
||||
shlib_fdesc[0].fname="device_init";
|
||||
|
||||
@@ -46,8 +46,6 @@
|
||||
#define OAI_THIRDPARTY_TP_LIBNAME "thirdparty_transpro"
|
||||
/* name of shared library implementing the rf simulator */
|
||||
#define OAI_RFSIM_LIBNAME "rfsimulator"
|
||||
/* name of shared library implementing the basic simulator */
|
||||
#define OAI_BASICSIM_LIBNAME "tcp_bridge_oai"
|
||||
/* name of shared library implementing the iq player */
|
||||
#define OAI_IQPLAYER_LIBNAME "oai_iqplayer"
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
This is sort of a "TCP driver" to ease debugging. This is mostly internal
|
||||
to EURECOM. You probably don't want to use it.
|
||||
|
||||
Same license as the rest.
|
||||
@@ -1,42 +0,0 @@
|
||||
The driver tcp_bridge_oai.c is to be used with the basic simulator.
|
||||
|
||||
To build the basic simulator:
|
||||
cd [openair top directory]
|
||||
. oaienv
|
||||
cd cmake_targets
|
||||
./build_oai -I -w USRP
|
||||
./build_oai --basic-simulator
|
||||
cd ../common/utils/T/tracer
|
||||
make
|
||||
|
||||
To use it, you need to run the eNB and the UE.
|
||||
|
||||
The eNB requires the T tracer.
|
||||
Open two terminals.
|
||||
In one terminal, run:
|
||||
cd [openair top directory]
|
||||
cd common/utils/T/tracer
|
||||
./enb -d ../T_messages.txt
|
||||
In the other terminal, run:
|
||||
cd [openair top directory]
|
||||
cd cmake_targets/basic_simulator/enb
|
||||
export ENODEB=1
|
||||
sudo -E ./lte-softmodem -O [configuration file] --T_stdout 0
|
||||
[configuration file] is just a regular configuration file.
|
||||
The eNB needs an EPC.
|
||||
|
||||
To run the UE, open a terminal and run:
|
||||
cd [openair top directory]
|
||||
cd cmake_targets/basic_simulator/ue
|
||||
sudo ./lte-uesoftmodem -C 2680000000 -r 25 --ue-rxgain 140
|
||||
Adapt the value of -r, it has to match the value N_RB_DL in the configuration
|
||||
file of the eNB. (Same for -C which should match the value downlink_frequency
|
||||
in the configuration file.)
|
||||
|
||||
The UE configuration (security keys) is generated from the file
|
||||
openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf. You need to configure
|
||||
your EPC to know about the UE. If you change the file
|
||||
openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf then you need to
|
||||
regenerate the configuration files using the program targets/bin/conf2uedata.
|
||||
You run it as:
|
||||
$OPENAIR_DIR/targets/bin/conf2uedata -c $OPENAIR_DIR/openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf -o $OPENAIR_DIR/cmake_targets/basic_simulator/ue
|
||||
@@ -1,292 +0,0 @@
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
#include "common_lib.h"
|
||||
|
||||
typedef struct {
|
||||
int32_t data[30720]; /* max 20MHz */
|
||||
unsigned long timestamp;
|
||||
int size;
|
||||
} input_buffer;
|
||||
|
||||
#define BSIZE 16
|
||||
|
||||
typedef struct {
|
||||
int sock;
|
||||
int samples_per_subframe;
|
||||
input_buffer b[BSIZE];
|
||||
int bstart;
|
||||
int blen;
|
||||
unsigned long read_timestamp;
|
||||
} tcp_bridge_state_t;
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
/* buffer management */
|
||||
/* (could be simpler, we are synchronous) */
|
||||
/* maybe we should lock */
|
||||
/****************************************************************************/
|
||||
|
||||
void put_buffer(tcp_bridge_state_t *t, void *data, int size, unsigned long timestamp)
|
||||
{
|
||||
int nextpos;
|
||||
if (t->blen == BSIZE) { printf("tcp_bridge: buffer full\n"); exit(1); }
|
||||
if (size > 30720*4) abort();
|
||||
if (t->blen) {
|
||||
int lastpos = (t->bstart+t->blen-1) % BSIZE;
|
||||
if (timestamp != t->b[lastpos].timestamp + t->samples_per_subframe)
|
||||
{ printf("tcp_bridge: discontinuity\n"); exit(1); }
|
||||
}
|
||||
nextpos = (t->bstart+t->blen) % BSIZE;
|
||||
t->b[nextpos].timestamp = timestamp;
|
||||
t->b[nextpos].size = size;
|
||||
memcpy(t->b[nextpos].data, data, size);
|
||||
t->blen++;
|
||||
}
|
||||
|
||||
void get_buffer(tcp_bridge_state_t *t, void *data, int size, unsigned long timestamp)
|
||||
{
|
||||
int pos;
|
||||
if (t->blen == 0) { printf("tcp_bridge: buffer empty\n"); exit(1); }
|
||||
if (size >30720*4) abort();
|
||||
pos = t->bstart;
|
||||
if (size != t->b[pos].size) { printf("tcp_bridge: bad size\n"); exit(1); }
|
||||
memcpy(data, t->b[pos].data, size);
|
||||
t->bstart = (t->bstart + 1) % BSIZE;
|
||||
t->blen--;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* end of buffer management */
|
||||
/****************************************************************************/
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
/* network management (read/write) */
|
||||
/****************************************************************************/
|
||||
|
||||
void read_data_from_network(int sock, input_buffer *b)
|
||||
{
|
||||
if (fullread(sock, &b->timestamp, sizeof(b->timestamp)) != sizeof(b->timestamp)) goto err;
|
||||
if (fullread(sock, &b->size, sizeof(b->size)) != sizeof(b->size)) goto err;
|
||||
if (fullread(sock, b->data, b->size) != b->size) goto err;
|
||||
return;
|
||||
|
||||
err:
|
||||
printf("tcp_bridge: read_data_from_network fails\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void write_data_to_network(int sock, input_buffer *b)
|
||||
{
|
||||
if (fullwrite(sock, &b->timestamp, sizeof(b->timestamp)) != sizeof(b->timestamp)) goto err;
|
||||
if (fullwrite(sock, &b->size, sizeof(b->size)) != sizeof(b->size)) goto err;
|
||||
if (fullwrite(sock, b->data, b->size) != b->size) goto err;
|
||||
return;
|
||||
|
||||
err:
|
||||
printf("tcp_bridge: write_data_to_network fails\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* end of network management */
|
||||
/****************************************************************************/
|
||||
|
||||
|
||||
int tcp_bridge_start(openair0_device *device)
|
||||
{
|
||||
int i;
|
||||
int port = 4042;
|
||||
tcp_bridge_state_t *tcp_bridge = device->priv;
|
||||
|
||||
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); }
|
||||
|
||||
struct sockaddr_in addr = {
|
||||
sin_family: AF_INET,
|
||||
sin_port: htons(port),
|
||||
sin_addr: { s_addr: INADDR_ANY }
|
||||
};
|
||||
|
||||
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)))
|
||||
{ perror("tcp_bridge: bind"); exit(1); }
|
||||
|
||||
if (listen(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); }
|
||||
|
||||
close(sock);
|
||||
|
||||
tcp_bridge->sock = sock2;
|
||||
|
||||
/* the other end has to send 10 subframes at startup */
|
||||
for (i = 0; i < 10; i++)
|
||||
read_data_from_network(sock2, &tcp_bridge->b[i]);
|
||||
tcp_bridge->bstart = 0;
|
||||
tcp_bridge->blen = 10;
|
||||
|
||||
printf("tcp_bridge: connection established\n");
|
||||
|
||||
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; }
|
||||
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_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags)
|
||||
{
|
||||
tcp_bridge_state_t *t = device->priv;
|
||||
input_buffer out;
|
||||
int i;
|
||||
//printf("write ts %ld nsamps %d\n", timestamp, nsamps);
|
||||
|
||||
if (nsamps > 30720) abort();
|
||||
|
||||
/* read buffer timestamped with 'timestamp' if not already here */
|
||||
for (i = 0; i < t->blen; i++) {
|
||||
input_buffer *b = &t->b[(t->bstart + i) % BSIZE];
|
||||
if (b->timestamp == timestamp) break;
|
||||
}
|
||||
if (i == t->blen) {
|
||||
int nextpos = (t->bstart + t->blen) % BSIZE;
|
||||
if (t->blen == BSIZE) abort();
|
||||
read_data_from_network(t->sock, &t->b[nextpos]);
|
||||
t->blen++;
|
||||
if (t->b[nextpos].timestamp != timestamp) abort();
|
||||
}
|
||||
|
||||
memcpy(out.data, buff[0], nsamps * 4);
|
||||
out.timestamp = timestamp;
|
||||
out.size = nsamps * 4;
|
||||
write_data_to_network(t->sock, &out);
|
||||
|
||||
return nsamps;
|
||||
}
|
||||
|
||||
int tcp_bridge_read(openair0_device *device, openair0_timestamp *timestamp, void **buff, int nsamps, int cc)
|
||||
{
|
||||
tcp_bridge_state_t *t = device->priv;
|
||||
input_buffer *b;
|
||||
|
||||
if (cc != 1) abort();
|
||||
if (t->blen == 0) abort();
|
||||
|
||||
b = &t->b[t->bstart];
|
||||
|
||||
if (b->timestamp != t->read_timestamp) abort();
|
||||
if (b->size != nsamps * 4) abort();
|
||||
|
||||
*timestamp = b->timestamp;
|
||||
memcpy(buff[0], b->data, b->size);
|
||||
|
||||
t->read_timestamp += nsamps;
|
||||
t->bstart = (t->bstart + 1) % BSIZE;
|
||||
t->blen--;
|
||||
|
||||
static unsigned long ts = 0;
|
||||
//printf("read ts %ld nsamps %d\n", ts, nsamps);
|
||||
*timestamp = ts;
|
||||
ts += nsamps;
|
||||
|
||||
return nsamps;
|
||||
}
|
||||
|
||||
int tcp_bridge_write_init(openair0_device *device)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
__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));
|
||||
|
||||
/* only 25 or 50 PRBs handled for the moment */
|
||||
if (openair0_cfg[0].sample_rate != 61440000 &&
|
||||
openair0_cfg[0].sample_rate != 30720000 &&
|
||||
openair0_cfg[0].sample_rate != 15360000 &&
|
||||
openair0_cfg[0].sample_rate != 7680000) {
|
||||
printf("tcp_bridge: ERROR: only sample rate 61.44, 30.72, 15.36, and 7.68 Msps supported\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
device->trx_start_func = tcp_bridge_start;
|
||||
device->trx_get_stats_func = tcp_bridge_get_stats;
|
||||
device->trx_reset_stats_func = tcp_bridge_reset_stats;
|
||||
device->trx_end_func = tcp_bridge_end;
|
||||
device->trx_stop_func = tcp_bridge_stop;
|
||||
device->trx_set_freq_func = tcp_bridge_set_freq;
|
||||
device->trx_set_gains_func = tcp_bridge_set_gains;
|
||||
device->trx_write_func = tcp_bridge_write;
|
||||
device->trx_read_func = tcp_bridge_read;
|
||||
device->trx_write_init = tcp_bridge_write_init;
|
||||
|
||||
device->priv = tcp_bridge;
|
||||
|
||||
switch ((int)openair0_cfg[0].sample_rate) {
|
||||
case 61440000: tcp_bridge->samples_per_subframe = 61440; 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;
|
||||
}
|
||||
@@ -1,443 +0,0 @@
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
#include "common_lib.h"
|
||||
|
||||
typedef struct {
|
||||
int sock;
|
||||
int samples_per_subframe;
|
||||
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) {
|
||||
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(helpTxt);
|
||||
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 }
|
||||
};
|
||||
|
||||
if (bind(tcp_bridge->sock, (struct sockaddr *)&addr, sizeof(addr))) {
|
||||
perror("tcp_bridge: bind");
|
||||
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 sockServ = accept(tcp_bridge->sock, (struct sockaddr *)&addr, &len);
|
||||
|
||||
if ( sockServ == -1) {
|
||||
perror("tcp_bridge: accept");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
verify_connection(sockServ, tcp_bridge->is_enb);
|
||||
printf("tcp_bridge: connection established\n");
|
||||
close(tcp_bridge->sock);
|
||||
tcp_bridge->sock=sockServ;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
while(1) {
|
||||
printf("tcp_bridge: trying to connect to %s:%d\n", tcp_bridge->ip, port);
|
||||
|
||||
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");
|
||||
return 0;
|
||||
}
|
||||
|
||||
perror("tcp_bridge");
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
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_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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
return tcp_bridge_read(device, timestamp, buff, nsamps, cc);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
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();
|
||||
}
|
||||
|
||||
/* 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 *)calloc(sizeof(tcp_bridge_state_t),1);
|
||||
|
||||
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");
|
||||
|
||||
if (openair0_cfg[0].sample_rate != 61440000 &&
|
||||
openair0_cfg[0].sample_rate != 30720000 &&
|
||||
openair0_cfg[0].sample_rate != 15360000 &&
|
||||
openair0_cfg[0].sample_rate != 7680000) {
|
||||
printf("tcp_bridge: ERROR: only sample rate 61.44, 30.72, 15.36, and 7.68 Msps supported\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
device->trx_start_func = tcp_bridge_start;
|
||||
device->trx_get_stats_func = tcp_bridge_get_stats;
|
||||
device->trx_reset_stats_func = tcp_bridge_reset_stats;
|
||||
device->trx_end_func = tcp_bridge_end;
|
||||
device->trx_stop_func = tcp_bridge_stop;
|
||||
device->trx_set_freq_func = tcp_bridge_set_freq;
|
||||
device->trx_set_gains_func = tcp_bridge_set_gains;
|
||||
device->trx_write_func = tcp_bridge_write;
|
||||
|
||||
if (tcp_bridge->is_enb) {
|
||||
device->trx_read_func = tcp_bridge_read;
|
||||
} else {
|
||||
device->trx_read_func = tcp_bridge_ue_first_read;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* let's pretend to be a b2x0 */
|
||||
device->type = USRP_B200_DEV;
|
||||
device->openair0_cfg=&openair0_cfg[0];
|
||||
return 0;
|
||||
}
|
||||
@@ -1387,12 +1387,6 @@ int setup_RU_buffers(RU_t *ru) {
|
||||
else if (frame_parms->N_RB_DL == 50) ru->N_TA_offset = 624/2;
|
||||
else if (frame_parms->N_RB_DL == 25) ru->N_TA_offset = 624/4;
|
||||
|
||||
if(IS_SOFTMODEM_BASICSIM)
|
||||
/* this is required for the basic simulator in TDD mode
|
||||
* TODO: find a proper cleaner solution
|
||||
*/
|
||||
ru->N_TA_offset = 0;
|
||||
|
||||
if (frame_parms->N_RB_DL == 100) /* no scaling to do */;
|
||||
else if (frame_parms->N_RB_DL == 50) {
|
||||
ru->sf_extension /= 2;
|
||||
|
||||
@@ -360,12 +360,6 @@ void init_UE(int nb_inst,
|
||||
}
|
||||
} else UE->N_TA_offset = 0;
|
||||
|
||||
if( IS_SOFTMODEM_BASICSIM)
|
||||
/* this is required for the basic simulator in TDD mode
|
||||
* TODO: find a proper cleaner solution
|
||||
*/
|
||||
UE->N_TA_offset = 0;
|
||||
|
||||
LOG_I(PHY,"Intializing UE Threads for instance %d (%p,%p)...\n",inst,PHY_vars_UE_g[inst],PHY_vars_UE_g[inst][0]);
|
||||
init_UE_threads(inst);
|
||||
|
||||
@@ -883,7 +877,7 @@ static void *UE_thread_rxn_txnp4(void *arg)
|
||||
|
||||
proc->instance_cnt_rxtx--;
|
||||
|
||||
if ( IS_SOFTMODEM_BASICSIM || IS_SOFTMODEM_RFSIM ) {
|
||||
if (IS_SOFTMODEM_RFSIM) {
|
||||
if (pthread_cond_signal(&proc->cond_rxtx) != 0) abort();
|
||||
}
|
||||
|
||||
@@ -1998,12 +1992,6 @@ void *UE_thread(void *arg)
|
||||
log_scheduler(__func__);
|
||||
|
||||
while (!oai_exit) {
|
||||
if (IS_SOFTMODEM_BASICSIM)
|
||||
while (!(UE->proc.instance_cnt_synch < 0)) {
|
||||
printf("ue sync not ready\n");
|
||||
usleep(500*1000);
|
||||
}
|
||||
|
||||
AssertFatal ( 0== pthread_mutex_lock(&UE->proc.mutex_synch), "");
|
||||
int instance_cnt_synch = UE->proc.instance_cnt_synch;
|
||||
int is_synchronized = UE->is_synchronized;
|
||||
@@ -2131,7 +2119,7 @@ void *UE_thread(void *arg)
|
||||
// update thread index for received subframe
|
||||
UE->current_thread_id[sub_frame] = thread_idx;
|
||||
|
||||
if (IS_SOFTMODEM_BASICSIM || IS_SOFTMODEM_RFSIM ) {
|
||||
if (IS_SOFTMODEM_RFSIM ) {
|
||||
int t;
|
||||
|
||||
for (t = 0; t < 2; t++) {
|
||||
@@ -2223,8 +2211,8 @@ void *UE_thread(void *arg)
|
||||
LOG_E(PHY,"can't compensate: diff =%d\n", first_symbols);
|
||||
}
|
||||
|
||||
/* no timeout in IS_SOFTMODEM_BASICSIM or IS_SOFTMODEM_RFSIM mode */
|
||||
if (IS_SOFTMODEM_BASICSIM || IS_SOFTMODEM_RFSIM) {
|
||||
/* no timeout in IS_SOFTMODEM_RFSIM mode */
|
||||
if (IS_SOFTMODEM_RFSIM) {
|
||||
ret = pthread_mutex_lock(&proc->mutex_rxtx);
|
||||
} else {
|
||||
struct timespec tv;
|
||||
|
||||
@@ -519,7 +519,7 @@ int restart_L1L2(module_id_t enb_id) {
|
||||
static void init_pdcp(int ue_id) {
|
||||
uint32_t pdcp_initmask = (!IS_SOFTMODEM_NOS1) ? LINK_ENB_PDCP_TO_GTPV1U_BIT : (LINK_ENB_PDCP_TO_GTPV1U_BIT | PDCP_USE_NETLINK_BIT | LINK_ENB_PDCP_TO_IP_DRIVER_BIT);
|
||||
|
||||
if (IS_SOFTMODEM_BASICSIM || IS_SOFTMODEM_RFSIM || (nfapi_getmode()==NFAPI_UE_STUB_PNF)) {
|
||||
if (IS_SOFTMODEM_RFSIM || (nfapi_getmode()==NFAPI_UE_STUB_PNF)) {
|
||||
pdcp_initmask = pdcp_initmask | UE_NAS_USE_TUN_BIT;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user