Commit 11960896 authored by Jaroslava Fiedlerova's avatar Jaroslava Fiedlerova

Merge remote-tracking branch 'origin/shm-radio' into integration_2025_w13 (!3260)

Shared memory realtime radio simulation

Realtime and near-realtime RFSimulator alternative without channel modelling.
The purpose of this library is to allow realtime simulation of gNB and nrUE.

The library is loaded using --device.name vrtsim. The server side (gNB) should
also configure --vrtsim.role server and --vrtsim.timescale according to
requirements.

Limitations:
- Only 1UE-to-1gNB connection supported

Future work:
- Telnet API
parents 0bf40318 cea02a16
......@@ -43,7 +43,7 @@
<class>Pull_Local_Registry</class>
<desc>Pull Images from Local Registry</desc>
<svr_id>0</svr_id>
<images>oai-gnb-asan oai-nr-ue-asan</images>
<images>oai-gnb oai-nr-ue</images>
</testCase>
<testCase id="800813">
<class>Create_Workspace</class>
......@@ -134,7 +134,7 @@
<always_exec>true</always_exec>
<desc>Clean Test Images on Test Server</desc>
<svr_id>0</svr_id>
<images>oai-gnb-asan oai-nr-ue-asan</images>
<images>oai-gnb oai-nr-ue</images>
</testCase>
</testCaseList>
......@@ -86,8 +86,7 @@ services:
cap_drop:
- ALL
environment:
USE_ADDITIONAL_OPTIONS: -E --rfsim --log_config.global_log_options level,nocolor,time
ASAN_OPTIONS: detect_leaks=0
USE_ADDITIONAL_OPTIONS: -E --log_config.global_log_options level,nocolor,time --device.name vrtsim --vrtsim.role server --vrtsim.timescale 0.5
depends_on:
- oai-ext-dn
networks:
......@@ -95,11 +94,13 @@ services:
ipv4_address: 192.168.71.140
volumes:
- ../../conf_files/gnb.sa.band66.106prb.rfsim.conf:/opt/oai-gnb/etc/gnb.conf
- tmp_data:/tmp/
healthcheck:
test: /bin/bash -c "pgrep nr-softmodem"
interval: 10s
timeout: 5s
retries: 5
ipc: host
oai-nr-ue:
image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop}
container_name: rfsim5g-oai-nr-ue
......@@ -109,7 +110,7 @@ services:
- NET_ADMIN # for interface bringup
- NET_RAW # for ping
environment:
USE_ADDITIONAL_OPTIONS: -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001100 --band 66 -C 2169090000 --CO -400000000 --ssb 378 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time
USE_ADDITIONAL_OPTIONS: -E -r 106 --numerology 1 --uicc0.imsi 208990100001100 --band 66 -C 2169090000 --CO -400000000 --ssb 378 --log_config.global_log_options level,nocolor,time --device.name vrtsim
depends_on:
- oai-gnb
networks:
......@@ -119,11 +120,13 @@ services:
- /dev/net/tun:/dev/net/tun
volumes:
- ../../conf_files/nrue.uicc.conf:/opt/oai-nr-ue/etc/nr-ue.conf
- tmp_data:/tmp/
healthcheck:
test: /bin/bash -c "pgrep nr-uesoftmodem"
interval: 10s
timeout: 5s
retries: 5
ipc: host
networks:
public_net:
......@@ -142,3 +145,6 @@ networks:
- subnet: 192.168.72.128/26
driver_opts:
com.docker.network.bridge.name: "rfsim5g-traffic"
volumes:
tmp_data:
......@@ -485,7 +485,7 @@ function main() {
# add some default libraries that should always be built
# for eNB, gNB, UEs, simulators
if [[ $gNB == 1 || $eNB == 1 || $UE == 1 || $nrUE == 1 || $SIMUS_PHY == 1 || $RU == 1 ]]; then
TARGET_LIST="$TARGET_LIST params_libconfig coding rfsimulator dfts params_yaml"
TARGET_LIST="$TARGET_LIST params_libconfig coding rfsimulator dfts params_yaml vrtsim"
fi
mkdir -p $DIR/$BUILD_DIR/build
......
......@@ -21,3 +21,4 @@ if (ENABLE_TESTS)
endif()
add_subdirectory(barrier)
add_subdirectory(actor)
add_subdirectory(shm_iq_channel)
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()
add_library(shm_td_iq_channel shm_td_iq_channel.c)
target_include_directories(shm_td_iq_channel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
if (ENABLE_TESTS)
add_subdirectory(tests)
endif()
This diff is collapsed.
/*
* 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 SHM_IQ_CHANNEL_H
#define SHM_IQ_CHANNEL_H
#include "../threadPool/pthread_utils.h"
#include <stdint.h>
#include <stdbool.h>
#include <semaphore.h>
#define SHM_MAGIC_NUMBER 0x12345678
/**
* ShmTdIqChannel is a shared memory bidirectional time domain IQ channel with a single clock
* source. The server (clock source) shall create the channel while the client should connect to
* it.
*
* To write samples, send them via a pointer passed to shm_td_iq_channel_tx.
* To read samples, receive them via a pointer passed to shm_td_iq_channel_rx. You can either wait until
* the sample are available using shm_td_iq_channel_wait or poll using shm_td_iq_channel_rx until the return
* value is not CHANNEL_ERROR_TOO_EARLY.
* To indicate that samples are ready to be read by the client, call shm_td_iq_channel_produce_samples
* (server only).
*
* The timestamps used in the API are in absolute samples from the time the client/server connection started.
*/
typedef enum IQChannelType { IQ_CHANNEL_TYPE_SERVER, IQ_CHANNEL_TYPE_CLIENT } IQChannelType;
typedef uint32_t sample_t;
typedef enum IQChannelErrorType {
CHANNEL_NO_ERROR,
CHANNEL_ERROR_TOO_LATE,
CHANNEL_ERROR_TOO_EARLY,
CHANNEL_ERROR_NOT_CONNECTED
} IQChannelErrorType;
typedef struct ShmTDIQChannel_s ShmTDIQChannel;
/**
* @brief Creates a shared memory IQ channel.
*
* @param name The name of the shared memory segment.
* @param num_tx_ant The number of TX antennas.
* @param num_rx_ant The number of RX antennas.
* @return A pointer to the created ShmTDIQChannel structure.
*/
ShmTDIQChannel *shm_td_iq_channel_create(const char *name, int num_tx_ant, int num_rx_ant);
/**
* @brief Connects to an existing shared memory IQ channel.
*
* @param name The name of the shared memory segment.
* @param timeout_in_seconds The timeout in seconds for the connection attempt.
* @return A pointer to the connected ShmTDIQChannel structure.
*/
ShmTDIQChannel *shm_td_iq_channel_connect(const char *name, int timeout_in_seconds);
/**
* @brief Transmit data in the channel
*
* @param channel The ShmTDIQChannel structure.
* @param timestamp The timestamp for which to get the TX IQ data slot.
* @param num_samples The number of samples to write.
* @param antenna The antenna index.
* @param tx_iq_data The TX IQ data to write.
*
* @return CHANNEL_NO_ERROR if successful, error type otherwise
*/
IQChannelErrorType shm_td_iq_channel_tx(ShmTDIQChannel *channel,
uint64_t timestamp,
uint64_t num_samples,
int antenna,
const sample_t *tx_iq_data);
/**
* @brief Receive iq data from the channel
*
* @param channel The ShmTDIQChannel structure.
* @param timestamp The timestamp for which to get the RX IQ data slot.
* @param num_samples The number of samples to read.
* @param antenna The antenna index.
* @param tx_iq_data pointer to the RX IQ data slot.
* @return CHANNEL_NO_ERROR if successful, error type otherwise
*/
IQChannelErrorType shm_td_iq_channel_rx(ShmTDIQChannel *channel,
uint64_t timestamp,
uint64_t num_samples,
int antenna,
sample_t *tx_iq_data);
/**
* @brief Advances the time in the channel by specified number of samples
*
* @param channel The ShmTDIQChannel structure.
* @param num_symbols The number of samples to produce.
*/
void shm_td_iq_channel_produce_samples(ShmTDIQChannel *channel, uint64_t num_samples);
/**
* @brief Wait until sample at the specified timestamp is available
*
* @param channel The ShmTDIQChannel structure.
*/
void shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp);
/**
* @brief Checks if the IQ channel is connected.
*
* @param channel The ShmTDIQChannel structure.
* @return True if the channel is connected, false otherwise.
*/
bool shm_td_iq_channel_is_connected(const ShmTDIQChannel *channel);
/**
* @brief Destroys the shared memory IQ channel.
*
* @param channel The ShmTDIQChannel structure.
*/
void shm_td_iq_channel_destroy(ShmTDIQChannel *channel);
/**
* @brief Returns current sample
*
* @param channel The ShmTDIQChannel structure.
*
* @return Current time as sample count since beginning of transmission
*/
uint64_t shm_td_iq_channel_get_current_sample(const ShmTDIQChannel *channel);
#endif
add_executable(test_shm_td_iq_channel test_shm_td_iq_channel.c)
target_link_libraries(test_shm_td_iq_channel shm_td_iq_channel minimal_lib pthread)
add_dependencies(tests test_shm_td_iq_channel)
# Commented out due to issues with ASAN in the unit test docker container
#add_test(test_shm_td_iq_channel ./test_shm_td_iq_channel)
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
#include "assertions.h"
#define SHM_CHANNEL_NAME "shm_iq_channel_test_file"
#include "shm_td_iq_channel.h"
void server(void);
int client(void);
enum { MODE_SERVER, MODE_CLIENT, MODE_FORK };
int main(int argc, char *argv[])
{
int mode = MODE_FORK;
if (argc == 2) {
if (strcmp(argv[1], "server") == 0) {
mode = MODE_SERVER;
} else if (strcmp(argv[1], "client") == 0) {
mode = MODE_CLIENT;
}
} else if (argc > 2) {
printf("Usage: %s [server|client]\n", argv[0]);
return 1;
}
switch (mode) {
case MODE_SERVER:
server();
break;
case MODE_CLIENT:
return client();
break;
case MODE_FORK: {
char file[256];
snprintf(file, sizeof(file), "/dev/shm/%s", SHM_CHANNEL_NAME);
remove(file);
int pid = fork();
if (pid == 0) {
server();
} else {
return client();
}
} break;
}
return 0;
}
const int num_updates = 50;
const int num_samples_per_update = 30720;
void *produce_symbols(void *arg)
{
ShmTDIQChannel *channel = (ShmTDIQChannel *)arg;
for (int i = 0; i < num_updates; i++) {
shm_td_iq_channel_produce_samples(channel, num_samples_per_update);
usleep(20000);
}
return 0;
}
void server(void)
{
int num_ant_tx = 1;
int num_ant_rx = 1;
ShmTDIQChannel *channel = shm_td_iq_channel_create(SHM_CHANNEL_NAME, num_ant_tx, num_ant_rx);
for (int i = 0; i < 10; i++) {
if (shm_td_iq_channel_is_connected(channel)) {
printf("Server connected\n");
break;
}
printf("Waiting for client\n");
sleep(1);
}
AssertFatal(shm_td_iq_channel_is_connected(channel), "Server failed to connect\n");
pthread_t producer_thread;
int ret = pthread_create(&producer_thread, NULL, produce_symbols, channel);
AssertFatal(ret == 0, "pthread_create() failed: errno %d, %s\n", errno, strerror(errno));
uint64_t timestamp = 0;
int iq_contents = 0;
while (timestamp < num_samples_per_update * num_updates) {
shm_td_iq_channel_wait(channel, timestamp + num_samples_per_update);
uint64_t target_timestamp = timestamp + num_samples_per_update;
timestamp += num_samples_per_update;
uint32_t iq_data[num_samples_per_update];
for (int i = 0; i < num_samples_per_update; i++) {
iq_data[i] = iq_contents;
}
int result = shm_td_iq_channel_tx(channel, target_timestamp, num_samples_per_update, 0, iq_data);
AssertFatal(result == CHANNEL_NO_ERROR, "Failed to write data\n");
iq_contents++;
}
printf("Finished writing data\n");
ret = pthread_join(producer_thread, NULL);
AssertFatal(ret == 0, "pthread_join() failed: errno %d, %s\n", errno, strerror(errno));
shm_td_iq_channel_destroy(channel);
}
int client(void)
{
int total_errors = 0;
ShmTDIQChannel *channel = shm_td_iq_channel_connect(SHM_CHANNEL_NAME, 10);
for (int i = 0; i < 10; i++) {
if (shm_td_iq_channel_is_connected(channel)) {
printf("Client connected\n");
break;
}
printf("Waiting for server\n");
sleep(1);
}
AssertFatal(shm_td_iq_channel_is_connected(channel), "Client failed to connect\n");
uint64_t timestamp = 0;
int iq_contents = 0;
while (timestamp < num_samples_per_update * num_updates) {
shm_td_iq_channel_wait(channel, timestamp + num_samples_per_update);
// Server starts producing from second slot
if (timestamp > num_samples_per_update) {
uint64_t target_timestamp = timestamp - num_samples_per_update;
uint32_t iq_data[num_samples_per_update];
int result = shm_td_iq_channel_rx(channel, target_timestamp, num_samples_per_update, 0, iq_data);
AssertFatal(result == CHANNEL_NO_ERROR, "Failed to read data\n");
int num_errors = 0;
for (int i = 0; i < num_samples_per_update; i++) {
if (iq_data[i] != iq_contents) {
num_errors++;
}
}
if (num_errors) {
printf("Found %d errors, value = %d, reference = %d\n", num_errors, iq_data[0], iq_contents);
total_errors += num_errors;
}
iq_contents++;
}
timestamp += num_samples_per_update;
}
printf("Finished reading data\n");
shm_td_iq_channel_destroy(channel);
return total_errors;
}
......@@ -118,6 +118,7 @@ Some directories under `radio` contain READMEs:
- [BladeRF](../radio/BLADERF/README)
- [IQPlayer](../radio/iqplayer/DOC/iqrecordplayer_usage.md), and [general documentation](./iqrecordplayer_usage.md)
- [fhi_72](../radio/fhi_72/README.md)
- [vrtsim](../radio/vrtsim/README.md)
The other SDRs (AW2S, LimeSDR, ...) have no READMEs.
......
......@@ -72,6 +72,7 @@ COPY --from=gnb-build \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_ci.so \
/oai-ran/cmake_targets/ran_build/build/libparams_yaml.so \
/oai-ran/cmake_targets/ran_build/build/libvrtsim.so \
/usr/local/lib/
COPY --from=gnb-base \
......
......@@ -69,6 +69,7 @@ COPY --from=gnb-build \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_ci.so \
/oai-ran/cmake_targets/ran_build/build/libparams_yaml.so \
/oai-ran/cmake_targets/ran_build/build/libvrtsim.so \
/usr/local/lib/
COPY --from=gnb-base \
......
......@@ -110,6 +110,7 @@ COPY --from=gnb-build \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_5Gue.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_rrc.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_o1.so \
/oai-ran/cmake_targets/ran_build/build/libvrtsim.so \
/usr/local/lib/
# Now we are copying from builder-image the UHD files.
......
......@@ -75,6 +75,7 @@ COPY --from=nr-ue-build \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_5Gue.so \
/oai-ran/cmake_targets/ran_build/build/libparams_yaml.so \
/oai-ran/cmake_targets/ran_build/build/libvrtsim.so \
/usr/local/lib/
COPY --from=nr-ue-base \
......
......@@ -72,6 +72,7 @@ COPY --from=nr-ue-build \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_5Gue.so \
/oai-ran/cmake_targets/ran_build/build/libparams_yaml.so \
/oai-ran/cmake_targets/ran_build/build/libvrtsim.so \
/usr/local/lib/
COPY --from=nr-ue-base \
......
......@@ -110,6 +110,7 @@ COPY --from=nr-ue-build \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_ciUE.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_5Gue.so \
/oai-ran/cmake_targets/ran_build/build/libparams_yaml.so \
/oai-ran/cmake_targets/ran_build/build/libvrtsim.so \
/usr/local/lib/
# Now we are copying from builder-image the UHD files.
......
......@@ -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,
......
......@@ -37,3 +37,8 @@ add_boolean_option(OAI_USRP OFF "Activate OAI's USRP driver" OFF)
if(OAI_USRP)
add_subdirectory(USRP)
endif()
add_boolean_option(VRTSIM_RADIO ON "Activate OAI's shared memory radio driver" OFF)
if(VRTSIM_RADIO)
add_subdirectory(vrtsim)
endif()
add_library(vrtsim MODULE vrtsim.c noise_device.c)
set_target_properties(vrtsim PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
target_link_libraries(vrtsim PRIVATE SIMU shm_td_iq_channel actor)
add_dependencies(vrtsim generate_T)
# Overview
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.
# Limitations
- 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
On the UE and gNB: use `device.name vrtsim` command line argument.
Additionally on gNB use `vrtsim.role server` and optionally
`vrtsim.timescale <timescale>` to set the timescale. Timescale 1.0
is the default and means realtime.
Channel modelling can be enabled by adding `vrtsim.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.
/*
* 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 <pthread.h>
#include <errno.h>
#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;
}
}
/*
* 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
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment