add multi-ue support to vrtsim
- Add num_ues and ue_id parameters for server and client configuration - Implement ul combining and dl distribution to all ues - Each ue uses dedicated antenna offsets based on ue_id - Works with no-chanmod mode only and with uniform antenna dimensions across all ues
This commit is contained in:
+372
-39
@@ -44,6 +44,7 @@
|
||||
#include <common/utils/load_module_shlib.h>
|
||||
#include <common/utils/telnetsrv/telnetsrv.h>
|
||||
#include <common/config/config_userapi.h>
|
||||
#include <common/openairinterface5g_limits.h>
|
||||
#include "common_lib.h"
|
||||
#include "shm_td_iq_channel.h"
|
||||
#include "SIMULATION/TOOLS/sim.h"
|
||||
@@ -59,6 +60,7 @@ typedef enum { ROLE_SERVER = 1, ROLE_CLIENT } role;
|
||||
|
||||
#define MAX_NUM_ANTENNAS_TX 4
|
||||
#define SAVED_SAMPLES_LEN 256
|
||||
#define MAX_NUM_UES MAX_MOBILES_PER_GNB
|
||||
|
||||
#define ROLE_CLIENT_STRING "client"
|
||||
#define ROLE_SERVER_STRING "server"
|
||||
@@ -90,6 +92,8 @@ typedef enum { ROLE_SERVER = 1, ROLE_CLIENT } role;
|
||||
{"cirdb_model_id", "Preferred TDL model id 0..4", 0, .iptr = &vrtsim_state->cirdb_model_id, .defintval = 0, TYPE_INT, 0}, \
|
||||
{"cirdb_ds_ns", "Desired RMS delay spread in ns", 0, .dblptr = &vrtsim_state->cirdb_ds_ns, .defdblval = 10.0, TYPE_DOUBLE, 0}, \
|
||||
{"cirdb_speed_mps", "Desired speed in m/s", 0, .dblptr = &vrtsim_state->cirdb_speed_mps, .defdblval = 1.5, TYPE_DOUBLE, 0}, \
|
||||
{"num_ues", "Number of UE slots (server only)\n", 0, .iptr = &vrtsim_state->num_ues, .defintval = 1, TYPE_INT, 0}, \
|
||||
{"ue_id", "UE slot index 0..num_ues-1 (client only)\n", 0, .iptr = &vrtsim_state->ue_id, .defintval = 0, TYPE_INT, 0}, \
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -113,6 +117,20 @@ typedef struct tx_timing_s {
|
||||
histogram_t tx_histogram;
|
||||
} tx_timing_t;
|
||||
|
||||
typedef struct {
|
||||
int model_id;
|
||||
double ds_ns;
|
||||
double speed_mps;
|
||||
} cirdb_conf_t;
|
||||
|
||||
typedef struct {
|
||||
int tx_ant;
|
||||
int rx_ant;
|
||||
int tx_offset;
|
||||
int rx_offset;
|
||||
cirdb_conf_t cir_conf;
|
||||
} ue_conf_t;
|
||||
|
||||
typedef struct {
|
||||
int role;
|
||||
char *connection_descriptor;
|
||||
@@ -133,6 +151,8 @@ typedef struct {
|
||||
int tx_num_channels;
|
||||
int rx_num_channels;
|
||||
channel_desc_t *channel_desc;
|
||||
channel_desc_t *channel_desc_per_ue[MAX_NUM_UES];
|
||||
pthread_mutex_t cirdb_mutex;
|
||||
Actor_t *channel_modelling_actors;
|
||||
char *taps_socket;
|
||||
int client_num_rx_antennas;
|
||||
@@ -145,6 +165,18 @@ typedef struct {
|
||||
int cirdb_model_id;
|
||||
double cirdb_ds_ns;
|
||||
double cirdb_speed_mps;
|
||||
// Multi-UE support
|
||||
int num_ues;
|
||||
int ue_id;
|
||||
|
||||
ue_conf_t ue_conf[MAX_NUM_UES];
|
||||
ue_conf_t ue;
|
||||
|
||||
// Totals
|
||||
int total_ul_streams;
|
||||
int total_dl_streams;
|
||||
sample_t *ul_combine_buffer;
|
||||
size_t ul_combine_buffer_len;
|
||||
} vrtsim_state_t;
|
||||
|
||||
static void histogram_add(histogram_t *histogram, double diff)
|
||||
@@ -251,10 +283,11 @@ static void *vrtsim_timing_job(void *arg)
|
||||
}
|
||||
|
||||
typedef struct client_info_s {
|
||||
int server_num_rx_antennas;
|
||||
int client_num_rx_antennas;
|
||||
int num_ues;
|
||||
int gnb_num_tx_ant;
|
||||
int gnb_num_rx_ant;
|
||||
ue_conf_t ues[MAX_NUM_UES];
|
||||
} client_info_t;
|
||||
|
||||
/**
|
||||
* @brief Publishes the client information information to a file for the client to read.
|
||||
*
|
||||
@@ -293,43 +326,174 @@ static client_info_t client_read_info(char *descriptor_file)
|
||||
return client_info;
|
||||
}
|
||||
|
||||
static void parse_ue_config(vrtsim_state_t *vrtsim_state)
|
||||
{
|
||||
AssertFatal(vrtsim_state->num_ues > 0 && vrtsim_state->num_ues <= MAX_NUM_UES,
|
||||
"num_ues=%d out of range (1..%d)\n", vrtsim_state->num_ues, MAX_NUM_UES);
|
||||
|
||||
for (int i = 0; i < vrtsim_state->num_ues; i++) {
|
||||
char prefix[64];
|
||||
snprintf(prefix, sizeof(prefix), "%s.ue_config.[%d]", VRTSIM_SECTION, i);
|
||||
|
||||
char *antennas = NULL;
|
||||
int model_id = vrtsim_state->cirdb_model_id;
|
||||
double ds_ns = vrtsim_state->cirdb_ds_ns;
|
||||
double speed_mps = vrtsim_state->cirdb_speed_mps;
|
||||
|
||||
paramdef_t ue_params[] = {
|
||||
{"antennas", "Antenna config e.g. \"1x2\"", 0, .strptr = &antennas, .defstrval = NULL, TYPE_STRING, 0},
|
||||
{"model_id", "TDL model id 0..4", 0, .iptr = &model_id, .defintval = vrtsim_state->cirdb_model_id, TYPE_INT, 0},
|
||||
{"ds_ns", "Delay spread in ns", 0, .dblptr = &ds_ns, .defdblval = vrtsim_state->cirdb_ds_ns, TYPE_DOUBLE, 0},
|
||||
{"speed_mps", "Speed in m/s", 0, .dblptr = &speed_mps, .defdblval = vrtsim_state->cirdb_speed_mps, TYPE_DOUBLE, 0},
|
||||
};
|
||||
config_get(config_get_if(), ue_params, sizeofArray(ue_params), prefix);
|
||||
|
||||
if (antennas != NULL) {
|
||||
int tx_ant, rx_ant;
|
||||
AssertFatal(sscanf(antennas, "%dx%d", &tx_ant, &rx_ant) == 2,
|
||||
"Invalid antenna format '%s' for UE %d, use e.g. '1x2'\n", antennas, i);
|
||||
AssertFatal(tx_ant > 0 && tx_ant <= MAX_NUM_ANTENNAS_TX,
|
||||
"Invalid TX antenna count %d for UE %d\n", tx_ant, i);
|
||||
AssertFatal(rx_ant > 0 && rx_ant <= MAX_NUM_ANTENNAS_TX,
|
||||
"Invalid RX antenna count %d for UE %d\n", rx_ant, i);
|
||||
vrtsim_state->ue_conf[i].tx_ant = tx_ant;
|
||||
vrtsim_state->ue_conf[i].rx_ant = rx_ant;
|
||||
} else {
|
||||
vrtsim_state->ue_conf[i].tx_ant = 1;
|
||||
vrtsim_state->ue_conf[i].rx_ant = 1;
|
||||
}
|
||||
|
||||
AssertFatal(model_id >= 0 && model_id <= 4,
|
||||
"Invalid model_id %d for UE %d (must be 0-4)\n", model_id, i);
|
||||
AssertFatal(ds_ns > 0,
|
||||
"Invalid ds_ns %.1f for UE %d (must be > 0)\n", ds_ns, i);
|
||||
AssertFatal(speed_mps >= 0,
|
||||
"Invalid speed_mps %.1f for UE %d (must be >= 0)\n", speed_mps, i);
|
||||
|
||||
vrtsim_state->ue_conf[i].cir_conf.model_id = model_id;
|
||||
vrtsim_state->ue_conf[i].cir_conf.ds_ns = ds_ns;
|
||||
vrtsim_state->ue_conf[i].cir_conf.speed_mps = speed_mps;
|
||||
|
||||
LOG_I(HW, "VRTSIM: UE %d - %dx%d antennas, Model %d (TDL-%c), DS %.1fns, Speed %.1fm/s\n",
|
||||
i,
|
||||
vrtsim_state->ue_conf[i].tx_ant,
|
||||
vrtsim_state->ue_conf[i].rx_ant,
|
||||
model_id, 'A' + model_id, ds_ns, speed_mps);
|
||||
}
|
||||
}
|
||||
|
||||
static void compute_ue_antenna_offsets(vrtsim_state_t *vrtsim_state)
|
||||
{
|
||||
int tx_offset = 0;
|
||||
int rx_offset = 0;
|
||||
|
||||
for (int i = 0; i < vrtsim_state->num_ues; i++) {
|
||||
vrtsim_state->ue_conf[i].tx_offset = tx_offset;
|
||||
vrtsim_state->ue_conf[i].rx_offset = rx_offset;
|
||||
|
||||
LOG_I(HW,
|
||||
"VRTSIM: UE %d - TX: %d antennas (offset %d), RX: %d antennas (offset %d)\n",
|
||||
i,
|
||||
vrtsim_state->ue_conf[i].tx_ant,
|
||||
tx_offset,
|
||||
vrtsim_state->ue_conf[i].rx_ant,
|
||||
rx_offset);
|
||||
|
||||
tx_offset += vrtsim_state->ue_conf[i].tx_ant;
|
||||
rx_offset += vrtsim_state->ue_conf[i].rx_ant;
|
||||
}
|
||||
|
||||
vrtsim_state->total_ul_streams = tx_offset;
|
||||
vrtsim_state->total_dl_streams = rx_offset;
|
||||
|
||||
LOG_I(HW, "VRTSIM: Total streams - UL: %d, DL: %d\n", vrtsim_state->total_ul_streams, vrtsim_state->total_dl_streams);
|
||||
}
|
||||
|
||||
static int vrtsim_connect(openair0_device_t *device)
|
||||
{
|
||||
vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv;
|
||||
|
||||
// Setup a shared memory channel
|
||||
if (vrtsim_state->role == ROLE_SERVER) {
|
||||
vrtsim_state->peer_info.num_rx_antennas = vrtsim_state->client_num_rx_antennas;
|
||||
vrtsim_state->channel = shm_td_iq_channel_create(DEFAULT_CHANNEL_NAME,
|
||||
vrtsim_state->peer_info.num_rx_antennas,
|
||||
device->openair0_cfg[0].rx_num_channels);
|
||||
// Exchange peer info
|
||||
client_info_t client_info = {
|
||||
.server_num_rx_antennas = device->openair0_cfg[0].rx_num_channels,
|
||||
.client_num_rx_antennas = vrtsim_state->client_num_rx_antennas,
|
||||
};
|
||||
server_publish_client_info(client_info, vrtsim_state->connection_descriptor);
|
||||
parse_ue_config(vrtsim_state);
|
||||
compute_ue_antenna_offsets(vrtsim_state);
|
||||
|
||||
int ul_streams = (vrtsim_state->chanmod || vrtsim_state->taps_socket || vrtsim_state->use_cirdb)
|
||||
? vrtsim_state->num_ues * device->openair0_cfg[0].rx_num_channels
|
||||
: vrtsim_state->total_ul_streams;
|
||||
vrtsim_state->channel =
|
||||
shm_td_iq_channel_create(DEFAULT_CHANNEL_NAME, ul_streams, vrtsim_state->total_dl_streams);
|
||||
size_t max_nsamps = 7680 * 2;
|
||||
vrtsim_state->ul_combine_buffer = calloc_or_fail(max_nsamps, sizeof(sample_t));
|
||||
vrtsim_state->ul_combine_buffer_len = max_nsamps;
|
||||
// Exchange peer info
|
||||
|
||||
client_info_t client_info = {
|
||||
.num_ues = vrtsim_state->num_ues,
|
||||
.gnb_num_tx_ant = device->openair0_cfg[0].tx_num_channels,
|
||||
.gnb_num_rx_ant = device->openair0_cfg[0].rx_num_channels,
|
||||
};
|
||||
|
||||
for (int i = 0; i < vrtsim_state->num_ues; i++)
|
||||
client_info.ues[i] = vrtsim_state->ue_conf[i];
|
||||
|
||||
server_publish_client_info(client_info, vrtsim_state->connection_descriptor);
|
||||
vrtsim_state->peer_info.num_rx_antennas = vrtsim_state->client_num_rx_antennas;
|
||||
vrtsim_state->last_received_sample = shm_td_iq_channel_get_current_sample(vrtsim_state->channel);
|
||||
vrtsim_state->run_timing_thread = true;
|
||||
threadCreate(&vrtsim_state->timing_thread, vrtsim_timing_job, vrtsim_state, "vrtsim_timing", -1, OAI_PRIORITY_RT_MAX);
|
||||
} else {
|
||||
client_info_t client_info = client_read_info(vrtsim_state->connection_descriptor);
|
||||
AssertFatal(client_info.server_num_rx_antennas > 0, "Server did not publish valid client info, aborting client connection\n");
|
||||
AssertFatal(
|
||||
client_info.client_num_rx_antennas == device->openair0_cfg[0].rx_num_channels,
|
||||
"Server expects different number of RX antennas. %d != %d. Use server command line option --client-num-rx-antennas\n",
|
||||
client_info.client_num_rx_antennas,
|
||||
device->openair0_cfg[0].rx_num_channels);
|
||||
AssertFatal(client_info.num_ues > 0, "Server did not publish valid num_ues\n");
|
||||
AssertFatal(vrtsim_state->ue_id < client_info.num_ues, "ue_id %d >= num_ues %d\n", vrtsim_state->ue_id, client_info.num_ues);
|
||||
|
||||
vrtsim_state->ue = client_info.ues[vrtsim_state->ue_id];
|
||||
|
||||
AssertFatal(vrtsim_state->ue.tx_ant == device->openair0_cfg[0].tx_num_channels,
|
||||
"Server expects UE %d to have %d TX antennas, this UE has %d\n",
|
||||
vrtsim_state->ue_id,
|
||||
vrtsim_state->ue.tx_ant,
|
||||
device->openair0_cfg[0].tx_num_channels);
|
||||
AssertFatal(vrtsim_state->ue.rx_ant == device->openair0_cfg[0].rx_num_channels,
|
||||
"Server expects UE %d to have %d RX antennas, this UE has %d\n",
|
||||
vrtsim_state->ue_id,
|
||||
vrtsim_state->ue.rx_ant,
|
||||
device->openair0_cfg[0].rx_num_channels);
|
||||
|
||||
LOG_I(HW,
|
||||
"VRTSIM: UE %d/%d - %dx%d antennas, UL offset=%d, DL offset=%d\n",
|
||||
vrtsim_state->ue_id,
|
||||
client_info.num_ues,
|
||||
vrtsim_state->ue.tx_ant,
|
||||
vrtsim_state->ue.rx_ant,
|
||||
vrtsim_state->ue.tx_offset,
|
||||
vrtsim_state->ue.rx_offset);
|
||||
if (vrtsim_state->use_cirdb) {
|
||||
vrtsim_state->cirdb_model_id = vrtsim_state->ue.cir_conf.model_id;
|
||||
vrtsim_state->cirdb_ds_ns = vrtsim_state->ue.cir_conf.ds_ns;
|
||||
vrtsim_state->cirdb_speed_mps = vrtsim_state->ue.cir_conf.speed_mps;
|
||||
|
||||
LOG_I(HW,
|
||||
"VRTSIM: UE %d channel - Model %d (TDL-%c), DS %.1fns, Speed %.1fm/s\n",
|
||||
vrtsim_state->ue_id,
|
||||
vrtsim_state->cirdb_model_id,
|
||||
'A' + vrtsim_state->cirdb_model_id,
|
||||
vrtsim_state->cirdb_ds_ns,
|
||||
vrtsim_state->cirdb_speed_mps);
|
||||
}
|
||||
vrtsim_state->channel = shm_td_iq_channel_connect(DEFAULT_CHANNEL_NAME, 10);
|
||||
vrtsim_state->peer_info.num_rx_antennas = client_info.server_num_rx_antennas;
|
||||
vrtsim_state->peer_info.num_rx_antennas = client_info.gnb_num_rx_ant;
|
||||
vrtsim_state->last_received_sample = shm_td_iq_channel_get_current_sample(vrtsim_state->channel);
|
||||
}
|
||||
|
||||
// Handle channel modelling after number of RX antennas are known
|
||||
int num_tx_stats = 1;
|
||||
if (vrtsim_state->chanmod || vrtsim_state->taps_socket || vrtsim_state->use_cirdb) {
|
||||
vrtsim_state->channel_modelling_actors = calloc_or_fail(vrtsim_state->peer_info.num_rx_antennas, sizeof(Actor_t));
|
||||
for (int i = 0; i < vrtsim_state->peer_info.num_rx_antennas; i++) {
|
||||
int num_actors = (vrtsim_state->role == ROLE_SERVER && vrtsim_state->num_ues > 1) ? vrtsim_state->total_dl_streams
|
||||
: vrtsim_state->peer_info.num_rx_antennas;
|
||||
|
||||
vrtsim_state->channel_modelling_actors = calloc_or_fail(num_actors, sizeof(Actor_t));
|
||||
for (int i = 0; i < num_actors; i++) {
|
||||
init_actor(&vrtsim_state->channel_modelling_actors[i], "chanmod", -1);
|
||||
}
|
||||
if (vrtsim_state->taps_socket) {
|
||||
@@ -379,16 +543,55 @@ static int vrtsim_connect(openair0_device_t *device)
|
||||
sel.yaml_path ? sel.yaml_path : "(auto)",
|
||||
sel.bin_path ? sel.bin_path : "(auto)");
|
||||
|
||||
cirdb_connect(0,
|
||||
device->openair0_cfg[0].tx_num_channels,
|
||||
vrtsim_state->peer_info.num_rx_antennas,
|
||||
&sel,
|
||||
&vrtsim_state->channel_desc);
|
||||
LOG_A(HW, "VRTSIM: channel taps via CIR DB\n");
|
||||
// Multi-UE server: create per-UE channel descriptors
|
||||
if (vrtsim_state->role == ROLE_SERVER && vrtsim_state->num_ues > 1) {
|
||||
for (int u = 0; u < vrtsim_state->num_ues; u++) {
|
||||
cirdb_select_opts_t ue_sel = sel;
|
||||
ue_sel.want_model_id = vrtsim_state->ue_conf[u].cir_conf.model_id;
|
||||
ue_sel.want_ds_ns = vrtsim_state->ue_conf[u].cir_conf.ds_ns;
|
||||
ue_sel.want_speed_mps = vrtsim_state->ue_conf[u].cir_conf.speed_mps;
|
||||
|
||||
AssertFatal(ue_sel.want_model_id >= 0 && ue_sel.want_model_id <= 4,
|
||||
"Invalid CIRDB model_id=%d for UE %d\n",
|
||||
ue_sel.want_model_id,
|
||||
u);
|
||||
|
||||
cirdb_connect(u,
|
||||
device->openair0_cfg[0].tx_num_channels,
|
||||
vrtsim_state->ue_conf[u].rx_ant,
|
||||
&ue_sel,
|
||||
&vrtsim_state->channel_desc_per_ue[u]);
|
||||
|
||||
channel_desc_t *cd = vrtsim_state->channel_desc_per_ue[u];
|
||||
AssertFatal(cd != NULL, "CIRDB failed to create channel_desc for UE %d\n", u);
|
||||
AssertFatal(cd->nb_tx == device->openair0_cfg[0].tx_num_channels,
|
||||
"CIRDB shape mismatch UE%d: nb_tx=%d expected %d\n",
|
||||
u, cd->nb_tx, device->openair0_cfg[0].tx_num_channels);
|
||||
LOG_I(HW, "VRTSIM: UE %d channel_desc=%p ch_ps=%p ch=%p nb_tx=%d nb_rx=%d\n",
|
||||
u, cd, cd->ch_ps, cd->ch, cd->nb_tx, cd->nb_rx);
|
||||
LOG_I(HW, "VRTSIM: UE %d CIRDB - Model %d (TDL-%c), antennas %dx%d, nb_rx=%d\n",
|
||||
u, ue_sel.want_model_id, 'A' + ue_sel.want_model_id,
|
||||
device->openair0_cfg[0].tx_num_channels, vrtsim_state->ue_conf[u].rx_ant, cd->nb_rx);
|
||||
}
|
||||
vrtsim_state->channel_desc = vrtsim_state->channel_desc_per_ue[0];
|
||||
pthread_mutex_init(&vrtsim_state->cirdb_mutex, NULL);
|
||||
LOG_A(HW, "VRTSIM: Multi-UE channel taps via CIR DB\n");
|
||||
} else {
|
||||
cirdb_connect(0,
|
||||
device->openair0_cfg[0].tx_num_channels,
|
||||
vrtsim_state->peer_info.num_rx_antennas,
|
||||
&sel,
|
||||
&vrtsim_state->channel_desc);
|
||||
LOG_A(HW, "VRTSIM: channel taps via CIR DB\n");
|
||||
}
|
||||
} else {
|
||||
load_channel_model(vrtsim_state);
|
||||
}
|
||||
num_tx_stats = vrtsim_state->peer_info.num_rx_antennas;
|
||||
if (vrtsim_state->role == ROLE_SERVER && vrtsim_state->num_ues > 1) {
|
||||
vrtsim_state->peer_info.num_rx_antennas = vrtsim_state->total_dl_streams;
|
||||
num_tx_stats = vrtsim_state->total_dl_streams;
|
||||
}
|
||||
}
|
||||
vrtsim_state->tx_timing = calloc_or_fail(num_tx_stats, sizeof(tx_timing_t));
|
||||
for (int i = 0; i < num_tx_stats; i++) {
|
||||
@@ -451,15 +654,69 @@ static void perform_channel_modelling(void *arg)
|
||||
c16_t **input_samples = (c16_t **)channel_modelling_args->samples;
|
||||
|
||||
channel_desc_t *channel_desc = vrtsim_state->channel_desc;
|
||||
int local_aarx = aarx;
|
||||
int global_aarx = aarx;
|
||||
int target_ue = 0;
|
||||
|
||||
if (vrtsim_state->role == ROLE_CLIENT) {
|
||||
global_aarx = vrtsim_state->ue_id * vrtsim_state->peer_info.num_rx_antennas + aarx;
|
||||
}
|
||||
|
||||
if (vrtsim_state->role == ROLE_SERVER && vrtsim_state->num_ues > 1) {
|
||||
if (aarx >= vrtsim_state->total_dl_streams) {
|
||||
LOG_E(HW, "VRTSIM: aarx=%d >= total_dl_streams=%d\n", aarx, vrtsim_state->total_dl_streams);
|
||||
return;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
for (int u = 0; u < vrtsim_state->num_ues; u++) {
|
||||
int offset = vrtsim_state->ue_conf[u].rx_offset;
|
||||
int count = vrtsim_state->ue_conf[u].rx_ant;
|
||||
if (aarx >= offset && aarx < offset + count) {
|
||||
target_ue = u;
|
||||
local_aarx = aarx - offset;
|
||||
channel_desc = vrtsim_state->channel_desc_per_ue[u];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
LOG_E(HW, "VRTSIM: aarx=%d could not map to any UE (total_dl_streams=%d)\n", aarx, vrtsim_state->total_dl_streams);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (channel_desc == NULL) {
|
||||
LOG_E(HW, "VRTSIM: channel_desc is NULL for aarx=%d\n", aarx);
|
||||
return;
|
||||
}
|
||||
|
||||
// Bounds check: ensure local_aarx is within channel_desc bounds
|
||||
if (local_aarx >= channel_desc->nb_rx) {
|
||||
LOG_E(HW,
|
||||
"VRTSIM: UE%d local_aarx=%d >= nb_rx=%d (global aarx=%d). CIRDB shape mismatch.\n",
|
||||
target_ue,
|
||||
local_aarx,
|
||||
channel_desc->nb_rx,
|
||||
aarx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vrtsim_state->use_cirdb) {
|
||||
double seconds = (double)channel_modelling_args->timestamp / vrtsim_state->sample_rate;
|
||||
uint64_t elapsed_ns = (uint64_t)(seconds * 1e9 + 0.5);
|
||||
|
||||
// Lock for multi-UE to prevent concurrent CIRDB updates
|
||||
if (vrtsim_state->role == ROLE_SERVER && vrtsim_state->num_ues > 1) {
|
||||
pthread_mutex_lock(&vrtsim_state->cirdb_mutex);
|
||||
}
|
||||
|
||||
cirdb_update(elapsed_ns);
|
||||
|
||||
if (vrtsim_state->role == ROLE_SERVER && vrtsim_state->num_ues > 1) {
|
||||
pthread_mutex_unlock(&vrtsim_state->cirdb_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
cf_t channel_impulse_response[nb_tx_ant][channel_desc->channel_length];
|
||||
@@ -468,7 +725,7 @@ static void perform_channel_modelling(void *arg)
|
||||
const float pathloss_linear = powf(10, channel_desc->path_loss_dB / 20.0);
|
||||
// Convert channel impulse response to float + apply pathloss
|
||||
for (int aatx = 0; aatx < nb_tx_ant; aatx++) {
|
||||
const struct complexd *channelModel = channel_desc->ch[aarx + (aatx * channel_desc->nb_rx)];
|
||||
const struct complexd *channelModel = channel_desc->ch[local_aarx + (aatx * channel_desc->nb_rx)];
|
||||
for (int i = 0; i < channel_desc->channel_length; i++) {
|
||||
channel_impulse_response[aatx][i].r = channelModel[i].r * pathloss_linear;
|
||||
channel_impulse_response[aatx][i].i = channelModel[i].i * pathloss_linear;
|
||||
@@ -477,7 +734,7 @@ static void perform_channel_modelling(void *arg)
|
||||
}
|
||||
} else {
|
||||
for (int aatx = 0; aatx < nb_tx_ant; aatx++) {
|
||||
struct complexf *channelModel = channel_desc->ch_ps[aarx + (aatx * channel_desc->nb_rx)];
|
||||
struct complexf *channelModel = channel_desc->ch_ps[local_aarx + (aatx * channel_desc->nb_rx)];
|
||||
channel_impulse_response_p[aatx] = channelModel;
|
||||
}
|
||||
}
|
||||
@@ -537,7 +794,7 @@ static void perform_channel_modelling(void *arg)
|
||||
channel_modelling_args->timestamp + start_sample,
|
||||
samples_out,
|
||||
num_samples,
|
||||
aarx,
|
||||
global_aarx,
|
||||
channel_modelling_args->flags,
|
||||
aarx);
|
||||
}
|
||||
@@ -623,8 +880,27 @@ static int vrtsim_write(openair0_device_t *device,
|
||||
timestamp -= device->openair0_cfg->command_line_sample_advance;
|
||||
vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv;
|
||||
bool channel_modelling = vrtsim_state->chanmod || vrtsim_state->taps_socket || vrtsim_state->use_cirdb;
|
||||
return channel_modelling ? vrtsim_write_with_chanmod(vrtsim_state, timestamp, samplesVoid, nsamps, nbAnt, flags)
|
||||
: vrtsim_write_internal(vrtsim_state, timestamp, (c16_t *)samplesVoid[0], nsamps, 0, flags, 0);
|
||||
if (channel_modelling) {
|
||||
return vrtsim_write_with_chanmod(vrtsim_state, timestamp, samplesVoid, nsamps, nbAnt, flags);
|
||||
}
|
||||
if (vrtsim_state->role == ROLE_CLIENT) {
|
||||
for (int aatx = 0; aatx < nbAnt; aatx++) {
|
||||
int global_ul_ant = vrtsim_state->ue.tx_offset + aatx;
|
||||
vrtsim_write_internal(vrtsim_state, timestamp, (c16_t *)samplesVoid[aatx], nsamps, global_ul_ant, flags, 0);
|
||||
}
|
||||
return nsamps;
|
||||
} else {
|
||||
AssertFatal(nbAnt == 1, "Multi-TX gNB not yet supported in multi-UE no-chanmod mode\n");
|
||||
for (int u = 0; u < vrtsim_state->num_ues; u++) {
|
||||
int rx_offset = vrtsim_state->ue_conf[u].rx_offset;
|
||||
int num_rx_ant = vrtsim_state->ue_conf[u].rx_ant;
|
||||
for (int aarx = 0; aarx < num_rx_ant; aarx++) {
|
||||
int global_dl_ant = rx_offset + aarx;
|
||||
vrtsim_write_internal(vrtsim_state, timestamp, (c16_t *)samplesVoid[0], nsamps, global_dl_ant, flags, 0);
|
||||
}
|
||||
}
|
||||
return nsamps;
|
||||
}
|
||||
}
|
||||
|
||||
static int vrtsim_write_beams(openair0_device_t *device,
|
||||
@@ -665,12 +941,62 @@ static int vrtsim_read(openair0_device_t *device, openair0_timestamp_t *ptimesta
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ret = shm_td_iq_channel_rx(vrtsim_state->channel, vrtsim_state->last_received_sample, nsamps, 0, samplesVoid[0]);
|
||||
if (ret == CHANNEL_ERROR_TOO_LATE) {
|
||||
vrtsim_state->rx_samples_late += nsamps;
|
||||
} else if (ret == CHANNEL_ERROR_TOO_EARLY) {
|
||||
vrtsim_state->rx_early += 1;
|
||||
if (vrtsim_state->role == ROLE_SERVER) {
|
||||
if (vrtsim_state->num_ues > 1) {
|
||||
/* Multi-UE UL combining */
|
||||
if (nsamps > vrtsim_state->ul_combine_buffer_len) {
|
||||
sample_t *tmp = realloc(vrtsim_state->ul_combine_buffer, nsamps * sizeof(sample_t));
|
||||
AssertFatal(tmp != NULL, "Failed to realloc UL combine buffer\n");
|
||||
vrtsim_state->ul_combine_buffer = tmp;
|
||||
vrtsim_state->ul_combine_buffer_len = nsamps;
|
||||
}
|
||||
for (int aarx = 0; aarx < nbAnt; aarx++)
|
||||
memset(samplesVoid[aarx], 0, nsamps * sizeof(sample_t));
|
||||
for (int u = 0; u < vrtsim_state->num_ues; u++) {
|
||||
for (int aarx = 0; aarx < nbAnt; aarx++) {
|
||||
int stream = u * nbAnt + aarx;
|
||||
int ret = shm_td_iq_channel_rx(vrtsim_state->channel,
|
||||
vrtsim_state->last_received_sample,
|
||||
nsamps,
|
||||
stream,
|
||||
vrtsim_state->ul_combine_buffer);
|
||||
if (ret == CHANNEL_ERROR_TOO_LATE) {
|
||||
vrtsim_state->rx_samples_late += nsamps;
|
||||
} else if (ret == CHANNEL_ERROR_TOO_EARLY) {
|
||||
vrtsim_state->rx_early += 1;
|
||||
}
|
||||
int16_t *out = (int16_t *)samplesVoid[aarx];
|
||||
int16_t *in = (int16_t *)vrtsim_state->ul_combine_buffer;
|
||||
for (int i = 0; i < nsamps * 2; i++) {
|
||||
int32_t sum = (int32_t)out[i] + (int32_t)in[i];
|
||||
out[i] = (int16_t)((sum > 32767) ? 32767 : (sum < -32768) ? -32768 : sum);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Single-UE server UL read */
|
||||
int ret = shm_td_iq_channel_rx(vrtsim_state->channel, vrtsim_state->last_received_sample, nsamps, 0, samplesVoid[0]);
|
||||
if (ret == CHANNEL_ERROR_TOO_LATE) {
|
||||
vrtsim_state->rx_samples_late += nsamps;
|
||||
} else if (ret == CHANNEL_ERROR_TOO_EARLY) {
|
||||
vrtsim_state->rx_early += 1;
|
||||
}
|
||||
for (int aarx = 1; aarx < nbAnt; aarx++) {
|
||||
if (samplesVoid[aarx] != NULL)
|
||||
memcpy(samplesVoid[aarx], samplesVoid[0], nsamps * sizeof(sample_t));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int aarx = 0; aarx < nbAnt; aarx++) {
|
||||
int global_dl_ant = vrtsim_state->ue.rx_offset + aarx;
|
||||
int ret =
|
||||
shm_td_iq_channel_rx(vrtsim_state->channel, vrtsim_state->last_received_sample, nsamps, global_dl_ant, samplesVoid[aarx]);
|
||||
if (ret == CHANNEL_ERROR_TOO_LATE) {
|
||||
vrtsim_state->rx_samples_late += nsamps;
|
||||
} else if (ret == CHANNEL_ERROR_TOO_EARLY) {
|
||||
vrtsim_state->rx_early += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
vrtsim_state->rx_samples_total += nsamps;
|
||||
*ptimestamp = vrtsim_state->last_received_sample;
|
||||
@@ -693,6 +1019,9 @@ static void vrtsim_end(openair0_device_t *device)
|
||||
shutdown_actor(&vrtsim_state->channel_modelling_actors[i]);
|
||||
}
|
||||
free(vrtsim_state->channel_modelling_actors);
|
||||
if (vrtsim_state->use_cirdb && vrtsim_state->role == ROLE_SERVER && vrtsim_state->num_ues > 1) {
|
||||
pthread_mutex_destroy(&vrtsim_state->cirdb_mutex);
|
||||
}
|
||||
for (int i = 1; i < vrtsim_state->peer_info.num_rx_antennas; i++) {
|
||||
histogram_merge(&tx_timing->tx_histogram, &tx_timing[i].tx_histogram);
|
||||
tx_timing->tx_early += tx_timing[i].tx_early;
|
||||
@@ -708,6 +1037,10 @@ static void vrtsim_end(openair0_device_t *device)
|
||||
taps_client_stop();
|
||||
}
|
||||
}
|
||||
if (vrtsim_state->role == ROLE_SERVER && vrtsim_state->ul_combine_buffer) {
|
||||
free(vrtsim_state->ul_combine_buffer);
|
||||
vrtsim_state->ul_combine_buffer = NULL;
|
||||
}
|
||||
shm_td_iq_channel_abort(vrtsim_state->channel);
|
||||
sleep(1);
|
||||
shm_td_iq_channel_destroy(vrtsim_state->channel);
|
||||
|
||||
Reference in New Issue
Block a user