Files
openairinterface5g/radio/zmq/zmq_imported.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

132 lines
3.8 KiB
C++

/*
* SPDX-License-Identifier: BSD-3-Clause-Open-MPI
* Based on zmq library in OCUDU project: ocudu/lib/radio/zmq
* Refer to https://gitlab.com/ocudu/ocudu/-/raw/dev/LICENSE?ref_type=heads
*/
#include "zmq_imported.h"
#include "log.h"
const float c16_t_to_cf_t_factor = std::numeric_limits<int16_t>::max();
static constexpr std::chrono::milliseconds TRANSMIT_TS_ALIGN_TIMEOUT = std::chrono::milliseconds(0);
static constexpr std::chrono::milliseconds RECEIVE_TS_ALIGN_TIMEOUT = std::chrono::milliseconds(100);
void zmq_tx_channel::transmit(c16_t *samples, size_t nsamps, uint64_t timestamp)
{
std::scoped_lock lock(transmit_alignment_mutex_);
size_t overflow = 0;
if (timestamp > sample_count_) {
overflow += buffer_.push_zeros(timestamp - sample_count_);
sample_count_ = timestamp;
}
cf_t samples_float[nsamps];
for (size_t i = 0; i < nsamps; i++) {
samples_float[i].r = samples[i].r / c16_t_to_cf_t_factor;
samples_float[i].i = samples[i].i / c16_t_to_cf_t_factor;
}
overflow += buffer_.push_samples(samples_float, nsamps);
sample_count_ += nsamps;
if (overflow) {
LOG_W(HW, "Overflow on ZMQ channel by %lu samples\n", overflow);
}
is_tx_enabled_ = true;
transmit_alignment_cvar_.notify_all();
}
void zmq_tx_channel::start(uint64_t init_time)
{
sample_count_ = init_time;
}
bool zmq_tx_channel::align(uint64_t timestamp, std::chrono::milliseconds timeout)
{
if (sample_count_ >= timestamp) {
return sample_count_ > timestamp;
}
std::unique_lock<std::mutex> lock(transmit_alignment_mutex_);
if (is_tx_enabled_ && (timeout.count() != 0)) {
bool is_not_timeout =
transmit_alignment_cvar_.wait_for(lock, timeout, [this, timestamp]() { return sample_count_ >= timestamp; });
if (is_not_timeout) {
return sample_count_ > timestamp;
}
LOG_W(HW, "Timeout waiting for TX path to align samples\n");
is_tx_enabled_ = false;
}
if (sample_count_ < timestamp) {
buffer_.push_zeros(timestamp - sample_count_);
sample_count_ = timestamp;
}
return false;
}
void zmq_rx_channel::receive(c16_t *samples, size_t nsamps)
{
size_t samples_popped = 0;
cf_t samples_float[nsamps];
while (samples_popped < (size_t)nsamps && !stopped_) {
size_t popped_now = buffer_.pop_samples(samples_float + samples_popped, nsamps - samples_popped);
samples_popped += popped_now;
if (popped_now == 0) {
usleep(100); // wait for more samples to arrive
}
}
for (size_t i = 0; i < nsamps; i++) {
samples[i].r = samples_float[i].r * c16_t_to_cf_t_factor + 0.5;
samples[i].i = samples_float[i].i * c16_t_to_cf_t_factor + 0.5;
}
}
void zmq_rx_channel::stop()
{
stopped_ = true;
}
void zmq_tx_stream::start(uint64_t init_time)
{
for (auto &chan : channels_) {
chan->start(init_time);
}
}
bool zmq_tx_stream::align(uint64_t timestamp, std::chrono::milliseconds timeout)
{
bool timestamp_passed = false;
for (auto &chan : channels_) {
timestamp_passed = timestamp_passed || chan->align(timestamp, timeout);
}
return timestamp_passed;
}
void zmq_tx_stream::transmit(c16_t **samples, size_t nsamps, uint64_t timestamp)
{
bool timestamp_passed = align(timestamp, TRANSMIT_TS_ALIGN_TIMEOUT);
if (timestamp_passed) {
LOG_W(HW, "Error, channel timeout\n");
return;
}
int i = 0;
for (auto chan : channels_) {
chan->transmit(samples[i++], nsamps, timestamp);
}
}
void zmq_rx_stream::start(uint64_t init_time)
{
sample_count_ = init_time;
}
void zmq_rx_stream::stop()
{
for (auto &chan : channels_) {
chan->stop();
}
}
void zmq_rx_stream::receive(c16_t **samples, size_t nsamps, uint64_t *timestamp)
{
*timestamp = sample_count_;
uint64_t passed_timestamp = sample_count_ + nsamps;
tx_stream_->align(passed_timestamp, RECEIVE_TS_ALIGN_TIMEOUT);
int i = 0;
for (auto chan : channels_) {
chan->receive(samples[i++], nsamps);
}
sample_count_ += nsamps;
}