Compare commits

...

1 Commits

Author SHA1 Message Date
Bartosz Podrygajlo
1b169b187c rfsim: Rewrite beamforming implementation
Rewrite rfsim beamforming simulation: Now the number of beams is equal to number
of antenna elements. Each IQ stream should have a single beam assigned. This new
requirement complicates the transport format a bit but the implementation doesn't
change that much.
2026-04-20 09:24:18 +02:00
4 changed files with 130 additions and 297 deletions

View File

@@ -419,23 +419,6 @@ struct openair0_device {
*/
int (*trx_ctlrecv_func)(openair0_device_t *device, void *msg, ssize_t msg_len);
/*! \brief Called to send samples to the 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 (3 dimensional)
@param nsamps number of samples to be sent
@param nb_antennas_tx number of antennas
@param num_beams number of beams
@param flags flags must be set to true if timestamp parameter needs to be applied
*/
int (*trx_write_beams_func)(openair0_device_t *device,
openair0_timestamp_t timestamp,
void ***buff,
int nsamps,
int nb_antennas_tx,
int num_beams,
int flags);
/*! \brief Called to send samples to the 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
@@ -550,21 +533,6 @@ struct openair0_device {
*/
int (*trx_set_gains_func)(openair0_device_t *device, openair0_config_t *openair0_cfg);
/*! \brief Set tx/rx beams
*
* Set the tx/rx beams. This has to be done in advance of the reception in order to
* allow the underlying device to change receiver configuration. The exact time depends
* on the device.
*
* NOTICE: the samples returned from trx_read_func may belong to more than one beam. It is up
* to the application to determine the beam of the received samples.
*
* \param device the hardware to use
* \param beam_map the beams to receive
* \return 0 on success
*/
int (*trx_set_beams)(openair0_device_t *device, uint64_t beam_map, openair0_timestamp_t timestamp);
/*! \brief Set tx/rx beams
*
* Set the tx/rx beams. This has to be done in advance of the reception in order to
@@ -576,10 +544,10 @@ struct openair0_device {
*
* \param device the hardware to use
* \param beams pointer to array of beam ids
* \param num_beams number of beams
* \param num_beams number of beams. Expected to be equal to number of antennas
* \return 0 on success
*/
int (*trx_set_beams2)(openair0_device_t *device, int *beams, int num_beams, openair0_timestamp_t timestamp);
int (*trx_set_beams)(openair0_device_t *device, uint16_t *beams, int num_beams, openair0_timestamp_t timestamp);
/*! \brief RRU Configuration callback
* \param idx RU index
@@ -630,7 +598,6 @@ typedef struct {
uint64_t timestamp; // Timestamp value of first sample
uint32_t option_value; // Option value
uint32_t option_flag; // Option flag
uint64_t beam_map;
} samplesBlockHeader_t;
#ifdef __cplusplus

View File

@@ -66,15 +66,12 @@ typedef enum { SIMU_ROLE_SERVER = 1, SIMU_ROLE_CLIENT } simuRole;
#define RFSIMU_SERVER_PORT "serverport"
#define RFSIMU_OPTIONS_PARAMNAME "options"
#define RFSIMU_IQFILE "IQfile"
#define RFSIMU_MODELNAME "modelname"
#define RFSIMU_PLOSS "ploss"
#define RFSIMU_FORGETFACT "forgetfact"
#define RFSIMU_OFFSET "offset"
#define RFSIMU_PROP_DELAY "prop_delay"
#define RFSIMU_WAIT_TIMEOUT "wait_timeout"
#define RFSIMU_ENABLE_BEAMS "enable_beams"
#define RFSIMU_NUM_CONCURRENT_BEAMS "num_concurrent_beams"
#define RFSIMU_BEAM_MAP "beam_map"
#define RFSIMU_BEAM_GAINS "beam_gains"
#define RFSIMU_BEAM_IDS "beam_ids"
@@ -95,15 +92,12 @@ typedef enum { SIMU_ROLE_SERVER = 1, SIMU_ROLE_CLIENT } simuRole;
UINT16PARAM(RFSIMU_SERVER_PORT, "<port to connect to>\n", simOpt, NULL, PORT), \
STRLISTPARAM(RFSIMU_OPTIONS_PARAMNAME, RFSIM_CONFIG_HELP_OPTIONS, simOpt, NULL, NULL), \
STRINGPARAM(RFSIMU_IQFILE, "<file path to use when saving IQs>\n", simOpt, NULL, "/tmp/rfsimulator.iqs"),\
STRINGPARAM(RFSIMU_MODELNAME, "<channel model name>\n", simOpt, NULL, "AWGN"), \
DOUBLEPARAM(RFSIMU_PLOSS, "<channel path loss in dB>\n", simOpt, NULL, 0), \
DOUBLEPARAM(RFSIMU_FORGETFACT, "<channel forget factor ((0 to 1)>\n", simOpt, NULL, 0), \
UINT64PARAM(RFSIMU_OFFSET, "<channel offset in samps>\n", simOpt, NULL, 0L), \
DOUBLEPARAM(RFSIMU_PROP_DELAY, "<propagation delay in ms>\n", simOpt, NULL, 0.0), \
INTPARAM(RFSIMU_WAIT_TIMEOUT, "<wait timeout if no UE connected>\n", simOpt, NULL, 1), \
BOOLPARAM(RFSIMU_ENABLE_BEAMS, "<enable simplified beam simulation>\n", simBool,NULL, 0), \
INTPARAM(RFSIMU_NUM_CONCURRENT_BEAMS, "<number of concurrent beams supported>\n", simOpt, NULL, 1), \
UINT64PARAM(RFSIMU_BEAM_MAP, "<initial beam map>\n", simOpt, NULL, 1), \
STRINGPARAM(RFSIMU_BEAM_IDS, "<initial beam ids>\n", simOpt, NULL, NULL), \
STRINGPARAM(RFSIMU_BEAM_GAINS, "<beam gain matrix in toeplitz form>\n", simOpt, NULL, NULL), \
};
@@ -113,7 +107,6 @@ static int rfsimu_setchanmod_cmd(char *buff, int debug, telnet_printfunc_t prnt,
static int rfsimu_setdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg);
static int rfsimu_getdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg);
static int rfsimu_vtime_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg);
static int rfsimu_set_beam(char *buff, int debug, telnet_printfunc_t prnt, void *arg);
static int rfsimu_set_beamids(char *buff, int debug, telnet_printfunc_t prnt, void *arg);
// clang-format off
static telnetshell_cmddef_t rfsimu_cmdarray[] = {
@@ -122,7 +115,6 @@ static telnetshell_cmddef_t rfsimu_cmdarray[] = {
{"setdistance", "<model name> <distance>", (cmdfunc_t)rfsimu_setdistance_cmd, {NULL}, TELNETSRV_CMDFLAG_PUSHINTPOOLQ | TELNETSRV_CMDFLAG_NEEDPARAM },
{"getdistance", "<model name>", (cmdfunc_t)rfsimu_getdistance_cmd, {NULL}, TELNETSRV_CMDFLAG_PUSHINTPOOLQ},
{"vtime", "", (cmdfunc_t)rfsimu_vtime_cmd, {NULL}, TELNETSRV_CMDFLAG_PUSHINTPOOLQ | TELNETSRV_CMDFLAG_AUTOUPDATE},
{"setbeam", "beam_map", (cmdfunc_t)rfsimu_set_beam, {NULL}, TELNETSRV_CMDFLAG_PUSHINTPOOLQ},
{"setbeamids", "beam_id1,beam_id2,...", (cmdfunc_t)rfsimu_set_beamids, {NULL}, TELNETSRV_CMDFLAG_PUSHINTPOOLQ},
{"", "", NULL},
};
@@ -133,12 +125,12 @@ static telnetshell_vardef_t rfsimu_vardef[] = {{"", 0, 0, NULL}};
typedef c16_t sample_t; // 2*16 bits complex number
typedef struct beam_switch_command_t {
std::vector<int> beams;
std::vector<uint16_t> beams;
openair0_timestamp_t timestamp;
} beam_switch_command_t;
typedef struct {
std::vector<int> beams;
std::vector<uint16_t> beams;
std::queue<beam_switch_command_t> cmd_queue;
std::mutex mutex;
} beam_state_t;
@@ -166,7 +158,6 @@ typedef struct buffer_s {
typedef struct {
int enable_beams;
int num_concurrent_beams;
std::vector<std::vector<float>> beam_gains;
beam_state_t tx;
beam_state_t rx;
@@ -212,10 +203,10 @@ typedef struct {
* @param nsamps_out output pointer to receive the number of samples until the next beam switch.
* @return The beam map (uint64_t) valid for the given timestamp.
*/
static std::vector<int> get_beams(beam_state_t *beam_state, openair0_timestamp_t timestamp, uint32_t nsamps, uint32_t *nsamps_out)
static std::vector<uint16_t> get_beams(beam_state_t *beam_state, openair0_timestamp_t timestamp, uint32_t nsamps, uint32_t *nsamps_out)
{
std::lock_guard<std::mutex> lock(beam_state->mutex);
std::vector<int> current_beams = beam_state->beams;
std::vector<uint16_t> current_beams = beam_state->beams;
uint32_t samples_to_next_switch = nsamps;
// Find the latest beam_switch_command_t with timestamp <= requested timestamp
@@ -243,30 +234,6 @@ static std::vector<int> get_beams(beam_state_t *beam_state, openair0_timestamp_t
return current_beams;
}
static std::vector<int> beam_map_to_beams(uint64_t beam_map)
{
int num_beams = __builtin_popcountll(beam_map);
AssertFatal(num_beams > 0, "Needs at least one beam\n");
std::vector<int> beam_ids;
for (int i = 0; i < MAX_BEAMS; i++) {
if (beam_map & (1ULL << i)) {
beam_ids.push_back(i);
}
}
return beam_ids;
}
static uint64_t beams_to_beam_map(const std::vector<int> &beam_ids)
{
uint64_t beam_map = 0;
for (size_t i = 0; i < beam_ids.size(); i++) {
beam_map |= (1ULL << beam_ids[i]);
}
return beam_map;
}
/**
* @brief Clears outdated beam switch commands from the queue and updates the current beam map.
*
@@ -466,25 +433,13 @@ static float get_rx_gain_db(rfsimulator_state_t *rfsimulator, uint rx_beam, uint
return rfsimulator->beam_ctrl->beam_gains[rx_beam][tx_beam];
}
static int rfsimulator_set_beams(openair0_device_t *device, uint64_t beam_map, openair0_timestamp_t timestamp)
static int rfsimulator_set_beams_vector(openair0_device_t *device, uint16_t *beams, int num_beams, openair0_timestamp_t timestamp)
{
rfsimulator_state_t *s = static_cast<rfsimulator_state_t *>(device->priv);
rfsim_beam_ctrl_t *beam_ctrl = s->beam_ctrl;
std::lock_guard<std::mutex> lock_tx(beam_ctrl->tx.mutex);
std::lock_guard<std::mutex> lock_rx(beam_ctrl->rx.mutex);
beam_switch_command_t command = {.beams = beam_map_to_beams(beam_map), .timestamp = timestamp};
beam_ctrl->rx.cmd_queue.emplace(command);
beam_ctrl->tx.cmd_queue.emplace(command);
return 0;
}
static int rfsimulator_set_beams_vector(openair0_device_t *device, int *beams, int num_beams, openair0_timestamp_t timestamp)
{
rfsimulator_state_t *s = static_cast<rfsimulator_state_t *>(device->priv);
rfsim_beam_ctrl_t *beam_ctrl = s->beam_ctrl;
std::lock_guard<std::mutex> lock_tx(beam_ctrl->tx.mutex);
std::lock_guard<std::mutex> lock_rx(beam_ctrl->rx.mutex);
beam_switch_command_t command = {.beams = std::vector<int>(beams, beams + num_beams), .timestamp = timestamp};
beam_switch_command_t command = {.beams = std::vector<uint16_t>(beams, beams + num_beams), .timestamp = timestamp};
beam_ctrl->rx.cmd_queue.emplace(command);
beam_ctrl->tx.cmd_queue.emplace(command);
return 0;
@@ -536,7 +491,6 @@ static void rfsimulator_readconfig(rfsimulator_state_t *rfsimulator)
rfsimulator->ip = strdup(*(gpd(rfsimuParam, sizeofArray(rfsimuParams), RFSIMU_SERVER_ADDR)->strptr));
rfsimulator->port = *(gpd(rfsimuParam, sizeofArray(rfsimuParams), RFSIMU_SERVER_PORT)->u16ptr);
char *saveF = strdup(*(gpd(rfsimuParam, sizeofArray(rfsimuParams), RFSIMU_IQFILE)->strptr));
//char *modelname = strdup(*(gpd(rfsimuParam, sizeofArray(rfsimuParams), RFSIMU_MODELNAME)->strptr));
rfsimulator->chan_pathloss = *(gpd(rfsimuParam, sizeofArray(rfsimuParams), RFSIMU_PLOSS)->dblptr);
rfsimulator->chan_forgetfact = *(gpd(rfsimuParam, sizeofArray(rfsimuParams), RFSIMU_FORGETFACT)->dblptr);
rfsimulator->chan_offset = *(gpd(rfsimuParam, sizeofArray(rfsimuParams), RFSIMU_OFFSET)->u64ptr);
@@ -545,8 +499,6 @@ static void rfsimulator_readconfig(rfsimulator_state_t *rfsimulator)
rfsim_beam_ctrl_t *beam_ctrl = rfsimulator->beam_ctrl;
beam_ctrl->enable_beams = *(gpd(rfsimuParam, sizeofArray(rfsimuParams), RFSIMU_ENABLE_BEAMS)->iptr);
beam_ctrl->num_concurrent_beams = *(gpd(rfsimuParam, sizeofArray(rfsimuParams), RFSIMU_NUM_CONCURRENT_BEAMS)->iptr);
uint64_t beam_map = *(gpd(rfsimuParam, sizeofArray(rfsimuParams), RFSIMU_BEAM_MAP)->u64ptr);
rfsimulator->saveIQfile = -1;
@@ -582,13 +534,9 @@ static void rfsimulator_readconfig(rfsimulator_state_t *rfsimulator)
process_gains(*rfsimuParam[beam_gains_param_index].strptr, beam_ctrl);
}
std::vector<int> initial_beams = beam_map_to_beams(beam_map);
beam_ctrl->rx.beams = initial_beams;
beam_ctrl->tx.beams = initial_beams;
int beam_ids_param_index = config_paramidx_fromname(rfsimuParams, sizeofArray(rfsimuParams), RFSIMU_BEAM_IDS);
if (rfsimuParam[beam_ids_param_index].strptr) {
std::vector<int> beam_ids;
std::vector<uint16_t> beam_ids;
std::stringstream ss(*rfsimuParam[beam_ids_param_index].strptr);
std::string token;
while (std::getline(ss, token, ',')) {
@@ -604,28 +552,13 @@ static void rfsimulator_readconfig(rfsimulator_state_t *rfsimulator)
rfsimulator->role = SIMU_ROLE_CLIENT;
}
static int rfsimu_set_beam(char *buff, int debug, telnet_printfunc_t prnt, void *arg)
{
UNUSED(debug);
rfsimulator_state_t *t = (rfsimulator_state_t *)arg;
rfsim_beam_ctrl_t *beam_ctrl = t->beam_ctrl;
AssertFatal(beam_ctrl->enable_beams, "Beam simualtion is disabled, cannot set beams\n");
uint64_t beam_map = strtoull(buff, NULL, 0);
std::lock_guard<std::mutex> lock_tx(beam_ctrl->tx.mutex);
std::lock_guard<std::mutex> lock_rx(beam_ctrl->rx.mutex);
beam_ctrl->rx.beams = beam_map_to_beams(beam_map);
beam_ctrl->tx.beams = beam_map_to_beams(beam_map);
prnt("Beam map set to 0x%lx\n", beam_map);
return CMDSTATUS_FOUND;
}
static int rfsimu_set_beamids(char *buff, int debug, telnet_printfunc_t prnt, void *arg)
{
UNUSED(debug);
rfsimulator_state_t *t = (rfsimulator_state_t *)arg;
rfsim_beam_ctrl_t *beam_ctrl = t->beam_ctrl;
AssertFatal(beam_ctrl->enable_beams, "Beam simualtion is disabled, cannot set beams\n");
std::vector<int> beam_ids;
std::vector<uint16_t> beam_ids;
std::stringstream ss(buff);
std::string token;
while (std::getline(ss, token, ',')) {
@@ -735,7 +668,7 @@ static int rfsimu_setdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt
rfsimulator_state_t *t = (rfsimulator_state_t *)arg;
const double sample_rate = t->sample_rate;
const double c = (double) SPEED_OF_LIGHT;
const double c = (double)SPEED_OF_LIGHT;
const uint64_t new_offset = (double)distance * sample_rate / c;
const double new_distance = (double)new_offset * c / sample_rate;
@@ -771,7 +704,7 @@ static int rfsimu_getdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt
rfsimulator_state_t *t = (rfsimulator_state_t *)arg;
const double sample_rate = t->sample_rate;
const double c = (double) SPEED_OF_LIGHT;
const double c = (double)SPEED_OF_LIGHT;
for (int i = 0; i < MAX_FD_RFSIMU; i++) {
buffer_t *b = &t->buf[i];
@@ -953,10 +886,10 @@ static int startClient(openair0_device_t *device)
static int rfsimulator_write_internal(rfsimulator_state_t *t,
openair0_timestamp_t timestamp,
void ***samplesVoid,
sample_t **samples,
int nsamps,
int nbAnt,
std::vector<int> tx_beams,
std::vector<uint16_t> tx_beams,
int flags)
{
mutexlock(t->Sockmutex);
@@ -966,20 +899,12 @@ static int rfsimulator_write_internal(rfsimulator_state_t *t,
buffer_t *b = &t->buf[i];
if (b->conn_sock >= 0) {
samplesBlockHeader_t header = {(uint32_t)nsamps, (uint32_t)nbAnt, (uint64_t)timestamp, 0, 0, beams_to_beam_map(tx_beams)};
samplesBlockHeader_t header = {(uint32_t)nsamps, (uint32_t)nbAnt, (uint64_t)timestamp, 0, 0};
fullwrite(b->conn_sock, &header, sizeof(header), t);
int num_beams = tx_beams.size();
// Send beams in order of beam index. This is required for beam_map to work correctly on the receiver side.
std::vector<size_t> indices(tx_beams.size());
std::iota(indices.begin(), indices.end(), 0);
std::sort(indices.begin(), indices.end(), [&](size_t i, size_t j) { return tx_beams[i] < tx_beams[j]; });
AssertFatal(num_beams > 0, "Must set at least one bit in beam_map\n");
for (int beam = 0; beam < num_beams; beam++) {
for (int a = 0; a < nbAnt; a++) {
sample_t *in = (sample_t *)samplesVoid[indices[beam]][a];
fullwrite(b->conn_sock, (void *)in, sampleToByte(nsamps, 1), t);
}
AssertFatal((uint)nbAnt == tx_beams.size(), "rfsim requires one beam per IQ stream\n");
fullwrite(b->conn_sock, tx_beams.data(), sizeof(uint16_t) * nbAnt, t);
for (int a = 0; a < nbAnt; a++) {
fullwrite(b->conn_sock, (void *)samples[a], sampleToByte(nsamps, 1), t);
}
}
}
@@ -1002,42 +927,40 @@ static int rfsimulator_write_internal(rfsimulator_state_t *t,
nsamps,
timestamp,
timestamp + nsamps,
signal_energy(static_cast<int32_t *>(samplesVoid[0][0]), nsamps));
signal_energy_nodc(samples[0], nsamps));
/* trace only first antenna */
T(T_USRP_TX_ANT0, T_INT(timestamp), T_BUFFER(samplesVoid[0][0], (int)sampleToByte(nsamps, 1)));
T(T_USRP_TX_ANT0, T_INT(timestamp), T_BUFFER(samples[0], (int)sampleToByte(nsamps, 1)));
return nsamps;
}
static int rfsimulator_write_beams(openair0_device_t *device,
openair0_timestamp_t timestamp,
void ***samplesVoid,
int nsamps,
int nbAnt,
int num_beams,
int flags)
static int rfsimulator_write(openair0_device_t *device,
openair0_timestamp_t timestamp,
void **samplesVoid,
int nsamps,
int nbAnt,
int flags)
{
timestamp -= device->openair0_cfg->command_line_sample_advance;
int nsamps_initial = nsamps;
rfsimulator_state_t *t = static_cast<rfsimulator_state_t *>(device->priv);
void *samples[num_beams][nbAnt];
void **samples_ptr[num_beams];
for (int beam = 0; beam < num_beams; beam++) {
samples_ptr[beam] = samples[beam];
for (int aatx = 0; aatx < nbAnt; aatx++) {
samples[beam][aatx] = samplesVoid[beam][aatx];
}
sample_t *samples[nbAnt];
for (int aatx = 0; aatx < nbAnt; aatx++) {
samples[aatx] = (sample_t *)samplesVoid[aatx];
}
while (nsamps > 0) {
uint32_t nsamps_beam_map;
std::vector<int> beams = get_beams(&t->beam_ctrl->tx, timestamp, nsamps, &nsamps_beam_map);
rfsimulator_write_internal(t, timestamp, samples_ptr, nsamps_beam_map, nbAnt, beams, flags);
for (int beam = 0; beam < num_beams; beam++) {
for (int aatx = 0; aatx < nbAnt; aatx++) {
char *ptr = (char *)samples_ptr[beam][aatx];
samples_ptr[beam][aatx] = (void *)(ptr + nsamps_beam_map * sizeof(sample_t));
}
std::vector<uint16_t> tx_beams = get_beams(&t->beam_ctrl->tx, timestamp, nsamps, &nsamps_beam_map);
if (t->beam_ctrl->enable_beams && (int)tx_beams.size() != nbAnt) {
LOG_W(HW, "Number of beams does not match application request nbAnt %d, beams %lu\n", nbAnt, tx_beams.size());
}
while ((int)tx_beams.size() < nbAnt)
tx_beams.push_back(0);
rfsimulator_write_internal(t, timestamp, samples, nsamps_beam_map, nbAnt, tx_beams, flags);
for (int aatx = 0; aatx < nbAnt; aatx++) {
samples[aatx] += nsamps_beam_map;
}
timestamp += nsamps_beam_map;
nsamps -= nsamps_beam_map;
@@ -1046,12 +969,6 @@ static int rfsimulator_write_beams(openair0_device_t *device,
return nsamps_initial;
}
static int rfsimulator_write(openair0_device_t *device, openair0_timestamp_t timestamp, void **buff, int nsamps, int cc, int flags)
{
void **tmp = buff;
return rfsimulator_write_beams(device, timestamp, &tmp, nsamps, cc, 1, flags);
}
static bool add_client(rfsimulator_state_t *t)
{
struct sockaddr_storage sa = {0};
@@ -1075,13 +992,17 @@ static bool add_client(rfsimulator_state_t *t)
getnameinfo((struct sockaddr *)&sa, socklen, ip, sizeof(ip), NULL, 0, NI_NUMERICHOST);
uint16_t port = ((struct sockaddr_in *)&sa)->sin_port;
LOG_I(HW, "Client connects from %s:%d\n", ip, port);
c16_t v = {0};
void *samplesVoid[t->tx_num_channels];
for (int i = 0; i < t->tx_num_channels; i++)
samplesVoid[i] = (void *)&v;
samplesBlockHeader_t header = {1, (uint32_t)t->tx_num_channels, (uint64_t)t->lastWroteTS, 0, 0, 1};
samplesBlockHeader_t header = {1, (uint32_t)t->tx_num_channels, (uint64_t)t->lastWroteTS, 0, 0};
fullwrite(conn_sock, &header, sizeof(header), t);
fullwrite(conn_sock, samplesVoid, sampleToByte(1, t->tx_num_channels), t);
uint16_t beam_ids[t->tx_num_channels];
for (int i = 0; i < t->tx_num_channels; i++)
beam_ids[i] = 0;
fullwrite(conn_sock, beam_ids, sizeof(beam_ids), t);
c16_t v[t->rx_num_channels];
memset(v, 0, sizeof(v));
fullwrite(conn_sock, v, sizeof(v), t);
if (new_buf->channel_model)
new_buf->channel_model->start_TS = t->lastWroteTS;
@@ -1089,7 +1010,7 @@ static bool add_client(rfsimulator_state_t *t)
return true;
}
static void process_recv_header(rfsimulator_state_t *t, buffer_t *b, bool first_time)
static void process_recv_header(buffer_t *b, bool first_time)
{
b->headerMode = false; // We got the header
AssertFatal(b->th.nbAnt != 0, "Number of antennas not set\n");
@@ -1112,14 +1033,12 @@ static void process_recv_header(rfsimulator_state_t *t, buffer_t *b, bool first_
}
}
int num_beams = __builtin_popcountll(b->th.beam_map);
AssertFatal(b->th.beam_map == 1ULL || t->beam_ctrl->enable_beams == 1,
"The transmitter has enabled beam simulation while this receiver has not\n");
size_t payload_sz = sampleToByte(b->th.size, b->th.nbAnt) * num_beams;
b->packet_ptr = static_cast<rfsim_packet_t *>(calloc_or_fail(1, payload_sz + sizeof(samplesBlockHeader_t)));
size_t beam_payload_size = b->th.nbAnt * sizeof(uint16_t);
size_t payload_sz = sampleToByte(b->th.size, b->th.nbAnt);
b->packet_ptr = static_cast<rfsim_packet_t *>(malloc(beam_payload_size + payload_sz + sizeof(samplesBlockHeader_t)));
b->packet_ptr->header = b->th;
b->transferPtr = b->packet_ptr->payload;
b->remainToTransfer = payload_sz;
b->remainToTransfer = payload_sz + beam_payload_size;
return;
}
@@ -1163,9 +1082,10 @@ static void combine_received_beams(rfsimulator_state_t *t,
}
// The beams transmitted in are ordered by beam index
std::vector<int> tx_beams = beam_map_to_beams(pkt->header.beam_map);
for (uint beam = 0; beam < tx_beams.size(); beam++) {
float gain_dB = get_rx_gain_db(t, rx_beam_id, tx_beams[beam]);
uint16_t *beams = (uint16_t *)pkt->payload;
for (uint aatx = 0; aatx < pkt->header.nbAnt; aatx++) {
int beam = beams[aatx];
float gain_dB = get_rx_gain_db(t, rx_beam_id, beam);
float gain_linear = powf(10, gain_dB / 20.0);
uint64_t overlap_start = std::max(start_timestamp, pkt->header.timestamp);
uint64_t overlap_end = std::min(start_timestamp + num_samples, pkt->header.timestamp + pkt->header.size);
@@ -1173,35 +1093,12 @@ static void combine_received_beams(rfsimulator_state_t *t,
int write_end_idx = overlap_end - start_timestamp;
int read_start_idx = overlap_start - pkt->header.timestamp;
for (int aatx = 0; aatx < num_aatx; aatx++) {
c16_t *buffer = (c16_t *)pkt->payload;
c16_t *buffer = (c16_t *)&pkt->payload[sizeof(uint16_t) * pkt->header.nbAnt];
c16_t *tx_ant_buffer_in = &buffer[(num_aatx * beam + aatx) * pkt->header.size + read_start_idx];
if (beam == 0) {
// For the first beam, we can directly copy the samples
if (gain_dB == 0.0f) {
// If gain is 0 dB, we can use memcpy for efficiency
memcpy(&samples[aatx][write_start_idx], tx_ant_buffer_in, (write_end_idx - write_start_idx) * sizeof(c16_t));
} else {
for (int s = write_start_idx; s < write_end_idx; s++) {
samples[aatx][s].r = tx_ant_buffer_in->r * gain_linear;
samples[aatx][s].i = tx_ant_buffer_in->i * gain_linear;
tx_ant_buffer_in++;
}
}
} else {
// For subsequent beams, we need to ensure we accumulate the samples
if (gain_dB == 0.0f) {
for (int s = write_start_idx; s < write_end_idx; s++) {
samples[aatx][s].r += tx_ant_buffer_in->r;
samples[aatx][s].i += tx_ant_buffer_in->i;
tx_ant_buffer_in++;
}
} else {
for (int s = write_start_idx; s < write_end_idx; s++) {
samples[aatx][s].r += tx_ant_buffer_in->r * gain_linear;
samples[aatx][s].i += tx_ant_buffer_in->i * gain_linear;
tx_ant_buffer_in++;
}
}
for (int s = write_start_idx; s < write_end_idx; s++) {
samples[aatx][s].r += tx_ant_buffer_in->r * gain_linear;
samples[aatx][s].i += tx_ant_buffer_in->i * gain_linear;
tx_ant_buffer_in++;
}
}
}
@@ -1253,7 +1150,7 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, bool first_time)
b->transferPtr += sz;
if (b->remainToTransfer == 0) {
if (b->headerMode)
process_recv_header(t, b, first_time);
process_recv_header(b, first_time);
else {
LOG_D(HW, "UEsock: %d Completed block reception: %ld\n", b->conn_sock, b->lastReceivedTS);
b->headerMode = true;
@@ -1276,17 +1173,15 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, bool first_time)
}
static void rfsimulator_read_internal(rfsimulator_state_t *t,
c16_t **samples,
sample_t **samples,
openair0_timestamp_t timestamp,
int nsamps,
int nbAnt,
int rx_beam_id,
bool is_first_beam)
std::vector<uint16_t> rx_beams)
{
cf_t temp_array[nbAnt][nsamps];
bool channel_modelling = false;
// Add all input nodes signal in the output buffer
bool is_first_peer = true;
for (int sock = 0; sock < MAX_FD_RFSIMU; sock++) {
buffer_t *ptr = &t->buf[sock];
@@ -1312,46 +1207,64 @@ static void rfsimulator_read_internal(rfsimulator_state_t *t,
input[aatx] = ant_buffers[aatx].data();
}
combine_received_beams(t,
ptr->received_packets,
timestamp - channel_offset - (channel_length - 1),
ptr->nbAnt,
nsamps + channel_length - 1,
rx_beam_id,
input);
for (int aarx = 0; aarx < nbAnt; aarx++) {
combine_received_beams(t,
ptr->received_packets,
timestamp - channel_offset - (channel_length - 1),
ptr->nbAnt,
nsamps + channel_length - 1,
rx_beams[aarx],
input);
rxAddInput(input, temp_array[aarx], aarx, ptr->channel_model, nsamps);
}
} else {
if (is_first_beam && is_first_peer && (ptr->nbAnt == 1 || nbAnt == 1)) {
// optimization: The buffer is uninitialized so samples can be written directly in the buffer
combine_received_beams(t, ptr->received_packets, timestamp - t->chan_offset, 1, nsamps, rx_beam_id, samples);
} else {
std::vector<std::vector<c16_t>> ant_buffers(ptr->nbAnt, std::vector<c16_t>(nsamps, {0, 0}));
c16_t *input[ant_buffers.size()];
for (uint aatx = 0; aatx < ant_buffers.size(); aatx++) {
input[aatx] = ant_buffers[aatx].data();
// Assume received_packets is ordered by timestamp
std::queue<rfsim_packet_t *> packets_copy = ptr->received_packets;
while (!packets_copy.empty()) {
rfsim_packet_t *pkt = packets_copy.front();
if (static_cast<int64_t>(pkt->header.timestamp + pkt->header.size) <= timestamp) {
// This packet is before the start timestamp, discard it
packets_copy.pop();
continue;
}
combine_received_beams(t, ptr->received_packets, timestamp - t->chan_offset, ptr->nbAnt, nsamps, rx_beam_id, input);
if (static_cast<int64_t>(pkt->header.timestamp) > timestamp + nsamps) {
// This packet is after the end of the buffer, stop processing
break;
}
// The beams transmitted in are ordered by beam index
uint16_t *tx_beams = (uint16_t *)pkt->payload;
uint64_t overlap_start = std::max(timestamp, static_cast<int64_t>(pkt->header.timestamp));
uint64_t overlap_end = std::min(timestamp + nsamps, static_cast<int64_t>(pkt->header.timestamp + pkt->header.size));
int write_start_idx = overlap_start - timestamp;
int write_end_idx = overlap_end - timestamp;
int read_start_idx = overlap_start - pkt->header.timestamp;
c16_t *buffer = (c16_t *)&pkt->payload[sizeof(uint16_t) * pkt->header.nbAnt];
for (int aarx = 0; aarx < nbAnt; aarx++) {
double H_awgn_mimo_coeff[ant_buffers.size()];
for (int aatx = 0; aatx < (int)ant_buffers.size(); aatx++) {
double H_awgn_mimo_coeff[pkt->header.nbAnt];
for (int aatx = 0; aatx < (int)pkt->header.nbAnt; aatx++) {
uint32_t ant_diff = std::abs(aatx - aarx);
H_awgn_mimo_coeff[aatx] = ant_diff ? (0.2 / ant_diff) : 1.0;
float gain_dB = get_rx_gain_db(t, rx_beams[aarx], tx_beams[aatx]);
float gain_linear = powf(10, gain_dB / 20.0);
H_awgn_mimo_coeff[aatx] *= gain_linear;
}
for (uint aatx = 0; aatx < ant_buffers.size(); aatx++) {
for (int i = 0; i < nsamps; i++) {
samples[aarx][i].r += ant_buffers[aatx][i].r * H_awgn_mimo_coeff[aatx];
samples[aarx][i].i += ant_buffers[aatx][i].i * H_awgn_mimo_coeff[aatx];
for (uint aatx = 0; aatx < pkt->header.nbAnt; aatx++) {
c16_t *tx_ant_buffer_in = &buffer[aatx * pkt->header.size + read_start_idx];
for (int i = write_start_idx; i < write_end_idx; i++) {
samples[aarx][i].r += tx_ant_buffer_in->r * H_awgn_mimo_coeff[aatx];
samples[aarx][i].i += tx_ant_buffer_in->i * H_awgn_mimo_coeff[aatx];
tx_ant_buffer_in++;
}
}
}
packets_copy.pop();
}
}
}
is_first_peer = false;
}
bool apply_global_noise = get_noise_power_dBFS() != INVALID_DBFS_VALUE;
@@ -1379,12 +1292,7 @@ static void rfsimulator_read_internal(rfsimulator_state_t *t,
}
}
static int rfsimulator_read_beams(openair0_device_t *device,
openair0_timestamp_t *ptimestamp,
void ***samplesVoid,
int nsamps,
int nbAnt,
int num_beams)
static int rfsimulator_read(openair0_device_t *device, openair0_timestamp_t *ptimestamp, void **samplesVoid, int nsamps, int nbAnt)
{
rfsimulator_state_t *t = static_cast<rfsimulator_state_t *>(device->priv);
LOG_D(HW,
@@ -1407,9 +1315,8 @@ static int rfsimulator_read_beams(openair0_device_t *device,
LOG_I(HW, "No connected device, generating void samples...\n");
if (!flushInput(t, t->wait_timeout, false)) {
for (int beam = 0; beam < num_beams; beam++)
for (int x = 0; x < nbAnt; x++)
memset(samplesVoid[beam][x], 0, sampleToByte(nsamps, 1));
for (int x = 0; x < nbAnt; x++)
memset(samplesVoid[x], 0, sampleToByte(nsamps, 1));
t->nextRxTstamp += nsamps;
@@ -1458,43 +1365,29 @@ static int rfsimulator_read_beams(openair0_device_t *device,
t->poll_telnetcmdq(t->telnetcmd_qid, t);
// Clear the output buffer
for (int beam = 0; beam < num_beams; beam++)
for (int a = 0; a < nbAnt; a++)
memset(samplesVoid[beam][a], 0, sampleToByte(nsamps, 1));
for (int a = 0; a < nbAnt; a++)
memset(samplesVoid[a], 0, sampleToByte(nsamps, 1));
openair0_timestamp_t timestamp = t->nextRxTstamp;
sample_t **samples = (sample_t **)samplesVoid;
int nsamps_to_process = nsamps;
while (nsamps_to_process > 0) {
uint32_t nsamps_beam_map;
std::vector<int> rx_beams = get_beams(&t->beam_ctrl->tx, timestamp, nsamps_to_process, &nsamps_beam_map);
if ((int)rx_beams.size() != num_beams) {
LOG_D(HW,
"Number of beams does not match application request num_beams %d, beam_map beams %lu\n",
num_beams,
rx_beams.size());
std::vector<uint16_t> rx_beams = get_beams(&t->beam_ctrl->tx, timestamp, nsamps_to_process, &nsamps_beam_map);
if (t->beam_ctrl->enable_beams && (int)rx_beams.size() != nbAnt) {
LOG_D(HW, "Number of beams does not match application request nbAnt %d, beams %lu\n", nbAnt, rx_beams.size());
}
for (int beam = 0; beam < num_beams && beam < (int)rx_beams.size(); beam++) {
c16_t *samples_beam[nbAnt];
for (int i = 0; i < nbAnt; i++) {
samples_beam[i] = (c16_t *)samplesVoid[beam][i] + timestamp - t->nextRxTstamp;
}
rfsimulator_read_internal(t, samples_beam, timestamp, nsamps_beam_map, nbAnt, rx_beams[beam], beam == 0);
while ((int)rx_beams.size() < nbAnt)
rx_beams.push_back(0);
rfsimulator_read_internal(t, samples, timestamp, nsamps_beam_map, nbAnt, rx_beams);
for (int a = 0; a < nbAnt; a++) {
samples[0] += timestamp - t->nextRxTstamp;
}
timestamp += nsamps_beam_map;
nsamps_to_process -= nsamps_beam_map;
}
struct timespec end_time;
ret = clock_gettime(CLOCK_REALTIME, &end_time);
AssertFatal(ret == 0, "clock_gettime() failed: errno %d, %s\n", errno, strerror(errno));
double diff_ns = (end_time.tv_sec - start_time.tv_sec) * 1000000000 + (end_time.tv_nsec - start_time.tv_nsec);
static double average = 0.0;
average = (average * 0.98) + (nsamps / (diff_ns / 1e9) * 0.02);
static int calls = 0;
if (calls++ % 10000 == 0) {
LOG_D(HW, "Rfsimulator: velocity %.2f Msps, realtime requirements %.2f Msps\n", average / 1e6, t->sample_rate / 1e6);
}
*ptimestamp = t->nextRxTstamp; // return the time of the first sample
t->nextRxTstamp += nsamps;
clear_beam_queue(&t->beam_ctrl->rx, t->nextRxTstamp);
@@ -1503,7 +1396,7 @@ static int rfsimulator_read_beams(openair0_device_t *device,
nsamps,
*ptimestamp,
t->nextRxTstamp,
signal_energy(static_cast<int32_t *>(samplesVoid[0][0]), nsamps));
signal_energy_nodc(samples[0], nsamps));
/* trace only first antenna */
T(T_USRP_RX_ANT0, T_INT(t->nextRxTstamp), T_BUFFER(samplesVoid[0], (int)sampleToByte(nsamps, 1)));
@@ -1526,21 +1419,18 @@ static int rfsimulator_read_beams(openair0_device_t *device,
return nsamps;
}
static int rfsimulator_read(openair0_device_t *device, openair0_timestamp_t *ptimestamp, void **samplesVoid, int nsamps, int nbAnt)
{
return rfsimulator_read_beams(device, ptimestamp, &samplesVoid, nsamps, nbAnt, 1);
}
static int rfsimulator_get_stats(openair0_device_t *device)
{
UNUSED(device);
return 0;
}
static int rfsimulator_reset_stats(openair0_device_t *device)
{
UNUSED(device);
return 0;
}
static void rfsimulator_end(openair0_device_t *device)
{
rfsimulator_state_t *s = static_cast<rfsimulator_state_t *>(device->priv);
@@ -1621,18 +1511,14 @@ extern "C" __attribute__((__visibility__("default"))) int device_init(openair0_d
device->trx_set_gains_func = rfsimulator_set_gains;
device->trx_write_func = rfsimulator_write;
device->trx_read_func = rfsimulator_read;
if (rfsimulator->beam_ctrl->enable_beams) {
device->trx_write_beams_func = rfsimulator_write_beams;
device->trx_read_beams_func = rfsimulator_read_beams;
}
/* let's pretend to be a b2x0 */
device->type = RFSIMULATOR;
openair0_cfg->rx_gain[0] = 0;
device->openair0_cfg = openair0_cfg;
device->priv = rfsimulator;
device->trx_write_init = rfsimulator_write_init;
device->trx_set_beams = rfsimulator_set_beams;
device->trx_set_beams2 = rfsimulator_set_beams_vector;
device->trx_set_beams = rfsimulator_set_beams_vector;
for (int i = 0; i < MAX_FD_RFSIMU; i++)
rfsimulator->buf[i].conn_sock = -1;

View File

@@ -158,7 +158,6 @@ int main(int argc, char *argv[]) {
timestamp+=blockSize;
header.option_value=0;
header.option_flag=0;
header.beam_map = 1;
} else {
AssertFatal(read(fd,&header,sizeof(header)), "");
}

View File

@@ -766,18 +766,6 @@ static int vrtsim_write(openair0_device_t *device,
}
}
static int vrtsim_write_beams(openair0_device_t *device,
openair0_timestamp_t timestamp,
void ***buff,
int nsamps,
int nb_antennas_tx,
int num_beams,
int flags)
{
vrtsim_write(device, timestamp, (void **)buff[0], nsamps, nb_antennas_tx, flags);
return nsamps;
}
static int vrtsim_read(openair0_device_t *device, openair0_timestamp_t *ptimestamp, void **samplesVoid, int nsamps, int nbAnt)
{
vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv;
@@ -929,12 +917,7 @@ static int vrtsim_set_freq(openair0_device_t *device, openair0_config_t *openair
return 0;
}
static int vrtsim_set_beams(openair0_device_t *device, uint64_t beam_map, openair0_timestamp_t timestamp)
{
return 0;
}
static int vrtsim_set_beams2(openair0_device_t *device, int *beam_ids, int num_beams, openair0_timestamp_t timestamp)
static int vrtsim_set_beams(openair0_device_t *device, uint16_t *beam_ids, int num_beams, openair0_timestamp_t timestamp)
{
return 0;
}
@@ -955,9 +938,7 @@ __attribute__((__visibility__("default"))) int device_init(openair0_device_t *de
device->trx_set_gains_func = vrtsim_stub2;
device->trx_write_func = vrtsim_write;
device->trx_read_func = vrtsim_read;
device->trx_write_beams_func = vrtsim_write_beams;
device->trx_set_beams = vrtsim_set_beams;
device->trx_set_beams2 = vrtsim_set_beams2;
device->type = RFSIMULATOR;
device->openair0_cfg = &openair0_cfg[0];