From 5d3708ac3ee7dec979204b3ae19f56a3d3fb9722 Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Wed, 5 Mar 2025 14:48:05 +0100 Subject: [PATCH] Realtime channel emulation for shm_radio --- common/utils/actor/CMakeLists.txt | 4 +- openair1/SIMULATION/TOOLS/sim.h | 1 + radio/shm_radio/CMakeLists.txt | 4 +- radio/shm_radio/README.md | 14 +- radio/shm_radio/noise_device.c | 90 +++++++++ radio/shm_radio/noise_device.h | 31 ++++ radio/shm_radio/shm_radio.c | 292 ++++++++++++++++++++++++++---- 7 files changed, 391 insertions(+), 45 deletions(-) create mode 100644 radio/shm_radio/noise_device.c create mode 100644 radio/shm_radio/noise_device.h diff --git a/common/utils/actor/CMakeLists.txt b/common/utils/actor/CMakeLists.txt index 40057d347e..182f54651d 100644 --- a/common/utils/actor/CMakeLists.txt +++ b/common/utils/actor/CMakeLists.txt @@ -1,6 +1,6 @@ add_library(actor actor.c) -target_include_directories(actor PUBLIC ./) -target_link_libraries(actor PUBLIC thread-pool) +# Only include header files due to issues with ODR in threadPool binaries +target_include_directories(actor PUBLIC ./ ../threadPool/) if (ENABLE_TESTS) add_subdirectory(tests) endif() diff --git a/openair1/SIMULATION/TOOLS/sim.h b/openair1/SIMULATION/TOOLS/sim.h index d6b20a9eb9..34cae4ba55 100644 --- a/openair1/SIMULATION/TOOLS/sim.h +++ b/openair1/SIMULATION/TOOLS/sim.h @@ -50,6 +50,7 @@ typedef enum { #define CHANMODEL_FREE_RSQRT_6 1<<1 #define CHANMODEL_FREE_RSQRT_NTAPS 1<<2 #define CHANMODEL_FREE_AMPS 1<<3 +#define SHR3 (jz = jsr, jsr ^= (jsr << 13), jsr ^= (jsr >> 17), jsr ^= (jsr << 5), jz + jsr) typedef enum { CORR_LEVEL_LOW, diff --git a/radio/shm_radio/CMakeLists.txt b/radio/shm_radio/CMakeLists.txt index dcaf3b6e1d..24f61cbd01 100644 --- a/radio/shm_radio/CMakeLists.txt +++ b/radio/shm_radio/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(shm_radio MODULE shm_radio.c) +add_library(shm_radio MODULE shm_radio.c noise_device.c) set_target_properties(shm_radio PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -target_link_libraries(shm_radio PRIVATE shm_td_iq_channel) +target_link_libraries(shm_radio PRIVATE SIMU shm_td_iq_channel actor) add_dependencies(shm_radio generate_T) diff --git a/radio/shm_radio/README.md b/radio/shm_radio/README.md index 6d66878110..fbe44c97bf 100644 --- a/radio/shm_radio/README.md +++ b/radio/shm_radio/README.md @@ -2,13 +2,14 @@ This implements a shared memory realtime and near-realtime radio interface. This is performed using `shm_td_iq_channel` which handles the shared memory -interface. +interface. # Limitations - - Only 1gNB to 1nrUE - - Only 1rx and 1tx antennas - - No channel modelling + - Only 1gNB to 1nrUE: gNB as a server, UE as a client. + - Server only accepts connections during device bringup. This means that + restarting the client will not make it reconnect, the server has to be + restarted as well. # Usage @@ -17,3 +18,8 @@ On the UE and gNB: use `device.name shm_radio` command line argument. Additionally on gNB use `shm_radio.role server` and optionally `shm_radio.timescale ` to set the timescale. Timescale 1.0 is the default and means realtime. + +Channel modelling can be enabled by adding `shm_radio.chanmod 1` to the +command line and should work the same as channel modelling in rfsimulator, +see rfsimulator [documentation](../rfsimulator/README.md), provided that your +CPU is fast enough. diff --git a/radio/shm_radio/noise_device.c b/radio/shm_radio/noise_device.c new file mode 100644 index 0000000000..2e15944d4a --- /dev/null +++ b/radio/shm_radio/noise_device.c @@ -0,0 +1,90 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ +#include +#include +#include "noise_device.h" + +// Use block size equal to cache size +#define BLOCK_SIZE (64 / sizeof(float)) // 4 bytes per float +// Use a power of 2 for block size length +#define NUM_BLOCKS (8192) +#define NOISE_VECTOR_LENGTH (BLOCK_SIZE * NUM_BLOCKS) + +static struct { + pthread_t noise_device_thread; + float noise[NOISE_VECTOR_LENGTH] __attribute__((aligned(64))); + float noise_power; + bool running; +} state; + +static uint32_t jz, jsr = 123456789; +static uint32_t random_uint(void) +{ + return SHR3; +} + +static void generate_one_block_noise(int write_block_index, float noise_power) +{ + // Generate noise vector + float block[BLOCK_SIZE]; + for (int i = 0; i < BLOCK_SIZE; i++) { + block[i] = noise_power * gaussZiggurat(0, 1); + } + // Write block to noise vector + memcpy(state.noise + write_block_index * BLOCK_SIZE, block, sizeof(block)); +} + +void *noise_device_thread_function(void *arg) +{ + while (state.running) { + // Generate noise vector + generate_one_block_noise(random_uint() % NUM_BLOCKS, state.noise_power); + usleep(10000); + } + return NULL; +} + +void init_noise_device(float noise_power) +{ + for (int i = 0; i < NUM_BLOCKS; i++) { + generate_one_block_noise(i, noise_power); + } + int ret = pthread_create(&state.noise_device_thread, NULL, noise_device_thread_function, NULL); + AssertFatal(ret == 0, "pthread_create failed: %d, errno %d (%s)\n", ret, errno, strerror(errno)); +} + +void free_noise_device(void) +{ + state.running = false; + pthread_join(state.noise_device_thread, NULL); +} + +void get_noise_vector(float *noise_vector, int length) +{ + int start_block = random_uint() % NUM_BLOCKS; + while (length > 0) { + int copy_size = min(length, (NUM_BLOCKS - start_block) * BLOCK_SIZE); + memcpy(noise_vector, &state.noise[start_block * BLOCK_SIZE], copy_size * sizeof(float)); + start_block = 0; + length -= copy_size; + noise_vector += copy_size; + } +} diff --git a/radio/shm_radio/noise_device.h b/radio/shm_radio/noise_device.h new file mode 100644 index 0000000000..421723891a --- /dev/null +++ b/radio/shm_radio/noise_device.h @@ -0,0 +1,31 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#ifndef NOISE_DEVICE_H +#define NOISE_DEVICE_H + +#include "SIMULATION/TOOLS/sim.h" + +void init_noise_device(float noise_power); +void free_noise_device(void); +void get_noise_vector(float *noise_vector, int length); + +#endif diff --git a/radio/shm_radio/shm_radio.c b/radio/shm_radio/shm_radio.c index c8779fc8a3..6dfa1179ee 100644 --- a/radio/shm_radio/shm_radio.c +++ b/radio/shm_radio/shm_radio.c @@ -42,10 +42,16 @@ #include #include "common_lib.h" #include "shm_td_iq_channel.h" +#include "SIMULATION/TOOLS/sim.h" +#include "actor.h" +#include "noise_device.h" +#include "simde/x86/avx512.h" // Simulator role typedef enum { ROLE_SERVER = 1, ROLE_CLIENT } role; +#define NUM_CHANMOD_THREADS 1 + #define ROLE_CLIENT_STRING "client" #define ROLE_SERVER_STRING "server" @@ -59,6 +65,7 @@ typedef enum { ROLE_SERVER = 1, ROLE_CLIENT } role; {"channel_name", "shared memory channel name\n", 0, .strptr = &channel_name, .defstrval = "shm_radio_channel", TYPE_STRING, 0}, \ {"role", "either client or server\n", 0, .strptr = &role, .defstrval = ROLE_CLIENT_STRING, TYPE_STRING, 0}, \ {"timescale", TIME_SCALE_HLP, 0, .dblptr = &shm_radio_state->timescale, .defdblval = 1.0, TYPE_DOUBLE, 0}, \ + {"chanmod", "Enable channel modelling", 0, .iptr = &shm_radio_state->chanmod, .defintval = 0, TYPE_INT, 0}, \ }; // clang-format on @@ -74,6 +81,14 @@ typedef struct peer_info_s { int num_rx_antennas; } peer_info_t; +typedef struct tx_timing_s { + uint64_t tx_samples_late; + uint64_t tx_early; + uint64_t tx_samples_total; + double average_tx_budget; + histogram_t tx_histogram; +} tx_timing_t; + typedef struct { int role; char channel_name[40]; @@ -83,15 +98,18 @@ typedef struct { bool run_timing_thread; double timescale; double sample_rate; - uint64_t tx_samples_late; - uint64_t tx_early; - uint64_t tx_samples_total; uint64_t rx_samples_late; uint64_t rx_early; uint64_t rx_samples_total; - double average_tx_budget; - histogram_t tx_histogram; + tx_timing_t *tx_timing; peer_info_t peer_info; + int chanmod; + double rx_freq; + double tx_bw; + int tx_num_channels; + int rx_num_channels; + channel_desc_t *channel_desc; + Actor_t *channel_modelling_actors; } shm_radio_state_t; static void histogram_add(histogram_t *histogram, double diff) @@ -114,6 +132,27 @@ static void histogram_print(histogram_t *histogram) } } +static void histogram_merge(histogram_t *dest, histogram_t *src) +{ + for (int i = 0; i < sizeofArray(dest->diff); i++) { + dest->diff[i] += src->diff[i]; + } + dest->num_samples += src->num_samples; +} + +static void load_channel_model(shm_radio_state_t *shm_radio_state) +{ + load_channellist(shm_radio_state->tx_num_channels, + shm_radio_state->peer_info.num_rx_antennas, + shm_radio_state->sample_rate, + shm_radio_state->rx_freq, + shm_radio_state->tx_bw); + char *model_name = shm_radio_state->role == ROLE_CLIENT ? "client_tx_channel_model" : "server_tx_channel_model"; + shm_radio_state->channel_desc = find_channel_desc_fromname(model_name); + random_channel(shm_radio_state->channel_desc, 0); + AssertFatal(shm_radio_state->channel_desc != NULL, "Could not find channel model %s\n", model_name); +} + static void shm_radio_readconfig(shm_radio_state_t *shm_radio_state) { char *channel_name = NULL; @@ -176,16 +215,13 @@ static void *shm_radio_timing_job(void *arg) static peer_info_t server_exchange_peer_info(peer_info_t peer_info) { int server_fd, client_fd; - struct sockaddr_un addr; - socklen_t addr_len = sizeof(addr); // Create a Unix domain socket server_fd = socket(AF_UNIX, SOCK_STREAM, 0); AssertFatal(server_fd >= 0, "socket() failed: errno: %d, %s\n", errno, strerror(errno)); // Bind the socket to a file path - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; + struct sockaddr_un addr = {.sun_family = AF_UNIX}; strncpy(addr.sun_path, "/tmp/shm_radio_socket", sizeof(addr.sun_path) - 1); unlink(addr.sun_path); // Ensure the path does not already exist int ret = bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)); @@ -196,6 +232,7 @@ static peer_info_t server_exchange_peer_info(peer_info_t peer_info) AssertFatal(ret == 0, "listen() failed: errno: %d, %s\n", errno, strerror(errno)); // Accept a connection from a client + socklen_t addr_len = sizeof(addr); client_fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len); AssertFatal(client_fd >= 0, "accept() failed: errno: %d, %s\n", errno, strerror(errno)); @@ -229,15 +266,13 @@ static peer_info_t server_exchange_peer_info(peer_info_t peer_info) static peer_info_t client_exchange_peer_info(peer_info_t peer_info) { int client_fd; - struct sockaddr_un addr; // Create a Unix domain socket client_fd = socket(AF_UNIX, SOCK_STREAM, 0); AssertFatal(client_fd >= 0, "socket() failed: errno: %d, %s\n", errno, strerror(errno)); // Connect to the server - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; + struct sockaddr_un addr = {.sun_family = AF_UNIX}; strncpy(addr.sun_path, "/tmp/shm_radio_socket", sizeof(addr.sun_path) - 1); int ret = -1; int tries = 0; @@ -266,9 +301,33 @@ static peer_info_t client_exchange_peer_info(peer_info_t peer_info) static int shm_radio_connect(openair0_device *device) { shm_radio_state_t *shm_radio_state = (shm_radio_state_t *)device->priv; + // Exchange peer info peer_info_t peer_info = {.num_rx_antennas = device->openair0_cfg[0].rx_num_channels}; if (shm_radio_state->role == ROLE_SERVER) { shm_radio_state->peer_info = server_exchange_peer_info(peer_info); + } else { + shm_radio_state->peer_info = client_exchange_peer_info(peer_info); + } + + // Handle channel modelling after number of RX antennas are known + int num_tx_stats = 1; + if (shm_radio_state->chanmod) { + shm_radio_state->channel_modelling_actors = calloc_or_fail(shm_radio_state->peer_info.num_rx_antennas, sizeof(Actor_t)); + for (int i = 0; i < shm_radio_state->peer_info.num_rx_antennas; i++) { + init_actor(&shm_radio_state->channel_modelling_actors[i], "chanmod", -1); + } + load_channel_model(shm_radio_state); + num_tx_stats = shm_radio_state->peer_info.num_rx_antennas; + } + shm_radio_state->tx_timing = calloc_or_fail(num_tx_stats, sizeof(tx_timing_t)); + for (int i = 0; i < num_tx_stats; i++) { + shm_radio_state->tx_timing[i].tx_histogram.min_samples = 100; + // Set the histogram range to 3000uS. Anything above that is not interesting + shm_radio_state->tx_timing[i].tx_histogram.range = 3000.0; + } + + // Setup a shared memory channel + if (shm_radio_state->role == ROLE_SERVER) { shm_radio_state->channel = shm_td_iq_channel_create(shm_radio_state->channel_name, shm_radio_state->peer_info.num_rx_antennas, device->openair0_cfg[0].rx_num_channels); @@ -280,12 +339,146 @@ static int shm_radio_connect(openair0_device *device) int ret = pthread_create(&shm_radio_state->timing_thread, NULL, shm_radio_timing_job, shm_radio_state); AssertFatal(ret == 0, "pthread_create() failed: errno: %d, %s\n", errno, strerror(errno)); } else { - shm_radio_state->peer_info = client_exchange_peer_info(peer_info); shm_radio_state->channel = shm_td_iq_channel_connect(shm_radio_state->channel_name, 10); } + return 0; } +static int shm_radio_write_internal(shm_radio_state_t *shm_radio_state, + openair0_timestamp timestamp, + c16_t *samples, + int nsamps, + int aarx, + int flags, + int stats_index) +{ + tx_timing_t *tx_timing = &shm_radio_state->tx_timing[stats_index]; + + uint64_t sample = shm_td_iq_channel_get_current_sample(shm_radio_state->channel); + int64_t diff = timestamp - sample; + double budget = diff / (shm_radio_state->sample_rate / 1e6); + tx_timing->average_tx_budget = .05 * budget + .95 * tx_timing->average_tx_budget; + histogram_add(&tx_timing->tx_histogram, budget); + + int ret = shm_td_iq_channel_tx(shm_radio_state->channel, timestamp, nsamps, aarx, (sample_t *)samples); + + if (ret == CHANNEL_ERROR_TOO_LATE) { + tx_timing->tx_samples_late += nsamps; + } else if (ret == CHANNEL_ERROR_TOO_EARLY) { + tx_timing->tx_early += 1; + } + tx_timing->tx_samples_total += nsamps; + + return nsamps; +} + +typedef struct { + shm_radio_state_t *shm_radio_state; + openair0_timestamp timestamp; + c16_t *samples[4]; + int nsamps; + int nbAnt; + int flags; + int aarx; +} channel_modelling_args_t; + +static void perform_channel_modelling(void *arg) +{ + channel_modelling_args_t *channel_modelling_args = arg; + int nsamps = channel_modelling_args->nsamps; + int aarx = channel_modelling_args->aarx; + int nb_tx_ant = channel_modelling_args->nbAnt; + c16_t **input_samples = (c16_t **)channel_modelling_args->samples; + + int aligned_nsamps = ceil_mod(nsamps, (512 / 8) / sizeof(cf_t)); + cf_t samples[aligned_nsamps] __attribute__((aligned(64))); + // Apply noise from global settings + get_noise_vector((float *)samples, nsamps * 2); + + channel_desc_t *channel_desc = channel_modelling_args->shm_radio_state->channel_desc; + const float pathloss_linear = powf(10, channel_desc->path_loss_dB / 20.0); + + // Convert channel impulse response to float + apply pathloss + cf_t channel_impulse_response[nb_tx_ant][channel_desc->channel_length]; + for (int aatx = 0; aatx < nb_tx_ant; aatx++) { + const struct complexd *channelModel = channel_desc->ch[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; + } + } + + for (int aatx = 0; aatx < nb_tx_ant; aatx++) { + for (int i = 0; i < nsamps; i++) { + cf_t *impulse_response = channel_impulse_response[aatx]; + for (int l = 0; l < channel_desc->channel_length; l++) { + int idx = i - l; + // TODO: Use AVX512 for this + // TODO: What are the previously sent samples (for impulse response) + if (idx >= 0) { + c16_t tx_input = input_samples[aatx][idx]; + samples[i].r += tx_input.r * impulse_response[l].r - tx_input.i * impulse_response[l].i; + samples[i].i += tx_input.i * impulse_response[l].r + tx_input.r * impulse_response[l].i; + } + } + } + } + + // Convert to c16_t + c16_t samples_out[aligned_nsamps] __attribute__((aligned(64))); +#if defined(__AVX512F__) + for (int i = 0; i < aligned_nsamps / 8; i++) { + simde__m512 *in = (simde__m512 *)&samples[i * 8]; + simde__m256i *out = (simde__m256i *)&samples_out[i * 8]; + *out = simde_mm512_cvtsepi32_epi16(simde_mm512_cvtps_epi32(*in)); + } +#elif defined(__AVX2__) + for (int i = 0; i < aligned_nsamps / 4; i++) { + simde__m256 *in = (simde__m256 *)&samples[i * 4]; + simde__m128i *out = (simde__m128i *)&samples_out[i * 4]; + *out = simde_mm256_cvtsepi32_epi16(simde_mm256_cvtps_epi32(*in)); + } +#else + for (int i = 0; i < nsamps; i++) { + samples_out[i].r = lroundf(samples[i].r); + samples_out[i].i = lroundf(samples[i].i); + } +#endif + + shm_radio_write_internal(channel_modelling_args->shm_radio_state, + channel_modelling_args->timestamp, + samples_out, + channel_modelling_args->nsamps, + aarx, + channel_modelling_args->flags, + aarx); +} + +static int shm_radio_write_with_chanmod(shm_radio_state_t *shm_radio_state, + openair0_timestamp timestamp, + void **samplesVoid, + int nsamps, + int nbAnt, + int flags) +{ + for (int aarx = 0; aarx < shm_radio_state->peer_info.num_rx_antennas; aarx++) { + notifiedFIFO_elt_t *task = newNotifiedFIFO_elt(sizeof(channel_modelling_args_t), 0, NULL, perform_channel_modelling); + channel_modelling_args_t *args = (channel_modelling_args_t *)NotifiedFifoData(task); + args->shm_radio_state = shm_radio_state; + args->timestamp = timestamp; + args->nsamps = nsamps; + args->nbAnt = nbAnt; + args->flags = flags; + args->aarx = aarx; + for (int i = 0; i < nbAnt; i++) { + args->samples[i] = samplesVoid[i]; + } + pushNotifiedFIFO(&shm_radio_state->channel_modelling_actors[aarx].fifo, task); + } + return nsamps; +} + static int shm_radio_write(openair0_device *device, openair0_timestamp timestamp, void **samplesVoid, @@ -295,20 +488,9 @@ static int shm_radio_write(openair0_device *device, { timestamp -= device->openair0_cfg->command_line_sample_advance; shm_radio_state_t *shm_radio_state = (shm_radio_state_t *)device->priv; - uint64_t sample = shm_td_iq_channel_get_current_sample(shm_radio_state->channel); - int64_t diff = timestamp - sample; - double budget = diff / (shm_radio_state->sample_rate / 1e6); - shm_radio_state->average_tx_budget = .05 * budget + .95 * shm_radio_state->average_tx_budget; - histogram_add(&shm_radio_state->tx_histogram, budget); - - int ret = shm_td_iq_channel_tx(shm_radio_state->channel, timestamp, nsamps, 0, samplesVoid[0]); - if (ret == CHANNEL_ERROR_TOO_LATE) { - shm_radio_state->tx_samples_late += nsamps; - } else if (ret == CHANNEL_ERROR_TOO_EARLY) { - shm_radio_state->tx_early += 1; - } - shm_radio_state->tx_samples_total += nsamps; - return nsamps; + return shm_radio_state->chanmod + ? shm_radio_write_with_chanmod(shm_radio_state, timestamp, samplesVoid, nsamps, nbAnt, flags) + : shm_radio_write_internal(shm_radio_state, timestamp, (c16_t *)samplesVoid[0], nsamps, 0, flags, 0); } static int shm_radio_read(openair0_device *device, openair0_timestamp *ptimestamp, void **samplesVoid, int nsamps, int nbAnt) @@ -335,17 +517,38 @@ static void shm_radio_end(openair0_device *device) int ret = pthread_join(shm_radio_state->timing_thread, NULL); AssertFatal(ret == 0, "pthread_join() failed: errno: %d, %s\n", errno, strerror(errno)); } + + tx_timing_t *tx_timing = shm_radio_state->tx_timing; + if (shm_radio_state->chanmod) { + for (int i = 0; i < shm_radio_state->peer_info.num_rx_antennas; i++) { + shutdown_actor(&shm_radio_state->channel_modelling_actors[i]); + } + free(shm_radio_state->channel_modelling_actors); + for (int i = 1; i < shm_radio_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; + tx_timing->tx_samples_late += tx_timing[i].tx_samples_late; + tx_timing->average_tx_budget += tx_timing[i].average_tx_budget; + tx_timing->tx_samples_total += tx_timing[i].tx_samples_total; + } + tx_timing->average_tx_budget /= shm_radio_state->peer_info.num_rx_antennas; + } + // produce 1 second of extra samples so threads can finish + shm_td_iq_channel_produce_samples(shm_radio_state->channel, shm_radio_state->sample_rate); + sleep(1); + shm_td_iq_channel_destroy(shm_radio_state->channel); + LOG_I(HW, "SHM_RADIO: Realtime issues: TX %.2f%%, RX %.2f%%\n", - shm_radio_state->tx_samples_late / (float)shm_radio_state->tx_samples_total * 100, + tx_timing->tx_samples_late / (float)tx_timing->tx_samples_total * 100, shm_radio_state->rx_samples_late / (float)shm_radio_state->rx_samples_total * 100); LOG_I(HW, "SHM_RADIO: Read/write too early (suspected radio implementaton error) TX: %lu, RX: %lu\n", - shm_radio_state->tx_early, + tx_timing->tx_early, shm_radio_state->rx_early); - LOG_I(HW, "SHM_RADIO: Average TX budget %.3lf uS\n", shm_radio_state->average_tx_budget); - histogram_print(&shm_radio_state->tx_histogram); - shm_td_iq_channel_destroy(shm_radio_state->channel); + LOG_I(HW, "SHM_RADIO: Average TX budget %.3lf uS\n", tx_timing->average_tx_budget); + histogram_print(&tx_timing->tx_histogram); + free(shm_radio_state->tx_timing); } static int shm_radio_stub(openair0_device *device) @@ -357,9 +560,16 @@ static int shm_radio_stub2(openair0_device *device, openair0_config_t *openair0_ return 0; } +static int shm_radio_set_freq(openair0_device *device, openair0_config_t *openair0_cfg) +{ + shm_radio_state_t *s = device->priv; + s->rx_freq = openair0_cfg->rx_freq[0]; + return 0; +} + __attribute__((__visibility__("default"))) int device_init(openair0_device *device, openair0_config_t *openair0_cfg) { - shm_radio_state_t *shm_radio_state = calloc(1, sizeof(shm_radio_state_t)); + shm_radio_state_t *shm_radio_state = calloc_or_fail(1, sizeof(shm_radio_state_t)); shm_radio_readconfig(shm_radio_state); LOG_I(HW, "Running as %s\n", @@ -369,7 +579,7 @@ __attribute__((__visibility__("default"))) int device_init(openair0_device *devi device->trx_reset_stats_func = shm_radio_stub; device->trx_end_func = shm_radio_end; device->trx_stop_func = shm_radio_stub; - device->trx_set_freq_func = shm_radio_stub2; + device->trx_set_freq_func = shm_radio_set_freq; device->trx_set_gains_func = shm_radio_stub2; device->trx_write_func = shm_radio_write; device->trx_read_func = shm_radio_read; @@ -380,8 +590,16 @@ __attribute__((__visibility__("default"))) int device_init(openair0_device *devi device->trx_write_init = shm_radio_stub; shm_radio_state->last_received_sample = 0; shm_radio_state->sample_rate = openair0_cfg->sample_rate; - shm_radio_state->tx_histogram.min_samples = 100; - // Set the histogram range to 3000uS. Anything above that is not interesting - shm_radio_state->tx_histogram.range = 3000.0; + shm_radio_state->rx_freq = openair0_cfg->rx_freq[0]; + shm_radio_state->tx_bw = openair0_cfg->tx_bw; + shm_radio_state->tx_num_channels = openair0_cfg->tx_num_channels; + shm_radio_state->rx_num_channels = openair0_cfg->rx_num_channels; + + if (shm_radio_state->chanmod) { + init_channelmod(); + int noise_power_dBFS = get_noise_power_dBFS(); + int16_t noise_power = noise_power_dBFS == INVALID_DBFS_VALUE ? 0 : (int16_t)(32767.0 / powf(10.0, .05 * -noise_power_dBFS)); + init_noise_device(noise_power); + } return 0; }