Files
openairinterface5g/radio/zmq/tests/test_zmq_radio.cpp
Bartosz Podrygajlo 46c120651f ZMQ radio
This commit introduces ZMQ-based radio library. Each pair of RX/TX antennas is
represented by a ZMQ REQ/REP socket pair which streams continuous IQ samples
from radio start until stop.

Usage:
Simplest configuration is to connect OAI NR UE to OAI GNB with the same number
of antennas - by inverting the RX and TX channels in ZMQ radio configuration the
gNBs TX is mapped to UEs RX antennas and vice versa.
2026-04-22 22:34:02 +02:00

163 lines
4.5 KiB
C++

/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#include "common_lib.h"
#include <gtest/gtest.h>
#include "common/config/config_userapi.h"
#include <zmq.h>
extern "C" {
#include "common/config/config_userapi.h"
#include "openair1/SIMULATION/TOOLS/sim.h"
extern int device_init(openair0_device_t *device, openair0_config_t *openair0_cfg);
static softmodem_params_t softmodem_params;
softmodem_params_t *get_softmodem_params(void)
{
return &softmodem_params;
}
}
#include "common/platform_types.h"
#include <thread>
configmodule_interface_t *uniqCfg = NULL;
extern "C" void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
{
fprintf(stderr, "FATAL: %s at %s:%s:%d\n", s, file, function, line);
exit(EXIT_FAILURE);
}
class ZMQTest : public ::testing::Test {
protected:
configmodule_interface_t *cfg1 = nullptr;
configmodule_interface_t *cfg2 = nullptr;
openair0_device_t device1 = {0};
openair0_device_t device2 = {0};
openair0_config_t config1 = {0};
openair0_config_t config2 = {0};
char *argv[4];
void SetUp() override
{
argv[0] = strdup("--zmq.[0].tx_channels");
argv[1] = strdup("tcp://127.0.0.1:5555");
argv[2] = strdup("--zmq.[0].rx_channels");
argv[3] = strdup("tcp://127.0.0.1:5556");
cfg1 = load_configmodule(sizeofArray(argv), argv, CONFIG_ENABLECMDLINEONLY);
uniqCfg = cfg1;
// 2. Initialize the ZMQ device
config1.tx_num_channels = 1;
config1.rx_num_channels = 1;
config1.sample_rate = 30;
ASSERT_EQ(device_init(&device1, &config1), 0);
ASSERT_EQ(device1.trx_start_func(&device1), 0);
// Swap the RX with TX for second device
char *tmp = argv[0];
argv[0] = argv[2];
argv[2] = tmp;
cfg2 = load_configmodule(sizeofArray(argv), argv, CONFIG_ENABLECMDLINEONLY);
uniqCfg = cfg2;
config2.tx_num_channels = 1;
config2.rx_num_channels = 1;
config2.sample_rate = 1500;
ASSERT_EQ(device_init(&device2, &config2), 0);
ASSERT_EQ(device2.trx_start_func(&device2), 0);
}
void TearDown() override
{
if (device1.trx_end_func) {
device1.trx_end_func(&device1);
}
if (device2.trx_end_func) {
device2.trx_end_func(&device2);
}
if (cfg1) {
end_configmodule(cfg1);
}
if (cfg2) {
end_configmodule(cfg2);
}
for (auto i = 0U; i < sizeofArray(argv); i++) {
free(argv[i]);
}
}
};
TEST_F(ZMQTest, RXSamples)
{
std::thread t1([this]() {
c16_t rx_samples[10];
openair0_timestamp_t rx_timestamp;
void *samples[1] = {rx_samples};
ASSERT_EQ(device1.trx_read_func(&device1, &rx_timestamp, samples, 10, 1), 10);
for (int i = 0; i < 10; i++) {
ASSERT_EQ(rx_samples[i].r, 0);
ASSERT_EQ(rx_samples[i].i, 0);
}
});
std::thread t2([this]() {
c16_t rx_samples[10];
openair0_timestamp_t rx_timestamp;
void *samples[1] = {rx_samples};
ASSERT_EQ(device2.trx_read_func(&device2, &rx_timestamp, samples, 10, 1), 10);
for (int i = 0; i < 10; i++) {
ASSERT_EQ(rx_samples[i].r, 0);
ASSERT_EQ(rx_samples[i].i, 0);
}
});
t1.join();
t2.join();
}
TEST_F(ZMQTest, TxRxSamples)
{
std::thread t1([this]() {
c16_t rx_samples[10];
openair0_timestamp_t rx_timestamp;
void *samples[1] = {rx_samples};
ASSERT_EQ(device1.trx_read_func(&device1, &rx_timestamp, samples, 10, 1), 10);
for (int i = 0; i < 10; i++) {
ASSERT_EQ(rx_samples[i].r, 0);
ASSERT_EQ(rx_samples[i].i, 0);
}
c16_t tx_samples[10];
openair0_timestamp_t tx_timestamp = rx_timestamp + 10;
for (int i = 0; i < 10; i++) {
tx_samples[i].r = i;
tx_samples[i].i = i + 1;
}
samples[0] = tx_samples;
ASSERT_EQ(device1.trx_write_func(&device1, tx_timestamp, samples, 10, 1, 0), 10);
});
std::thread t2([this]() {
c16_t rx_samples[10];
openair0_timestamp_t rx_timestamp;
void *samples[1] = {rx_samples};
ASSERT_EQ(device2.trx_read_func(&device2, &rx_timestamp, samples, 10, 1), 10);
for (int i = 0; i < 10; i++) {
ASSERT_EQ(rx_samples[i].r, 0);
ASSERT_EQ(rx_samples[i].i, 0);
}
openair0_timestamp_t rx_timestamp2;
ASSERT_EQ(device2.trx_read_func(&device2, &rx_timestamp2, samples, 10, 1), 10);
for (int i = 0; i < 10; i++) {
ASSERT_EQ(rx_samples[i].r, i);
ASSERT_EQ(rx_samples[i].i, i + 1);
}
ASSERT_EQ(rx_timestamp + 10, rx_timestamp2);
});
t1.join();
t2.join();
}
int main(int argc, char **argv)
{
logInit();
g_log->log_component[HW].level = OAILOG_DEBUG;
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}