Files
openairinterface5g/radio/oc/oc_lib.cpp
2026-05-19 14:56:42 +02:00

808 lines
24 KiB
C++

/*
* Licensed by open cells project
*/
#include <iostream>
#include <complex>
#include <fstream>
#include <cmath>
#include <cassert>
#include <queue>
#include <condition_variable>
#include <mutex>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#ifdef OAI_INTEGRATION
#include "common_lib.h"
#include "assertions.h"
#else
// #define LOG_E(m, a...) printf(a)
#include "common_lib.h"
#endif
#include "system.h"
#include <sys/resource.h>
#include "common/platform_types.h"
#include "openair1/PHY/sse_intrin.h"
#include "common/utils/LOG/log.h"
#include "common/utils/time_meas.h"
// Thread-safe queue
template <typename T>
class TSQueue {
private:
// Underlying queue
// mutex for thread synchronization
std::mutex m_mutex;
// Condition variable for signaling
std::condition_variable m_cond;
public:
std::queue<T> m_queue;
// Pushes an element to the queue
void push(T item)
{
// Acquire lock
std::unique_lock<std::mutex> lock(m_mutex);
// Add item
m_queue.push(item);
// Notify one thread that
// is waiting
m_cond.notify_one();
}
// Pops an element off the queue
T pop()
{
// acquire lock
std::unique_lock<std::mutex> lock(m_mutex);
// wait until queue is not empty
m_cond.wait(lock, [this]() { return !m_queue.empty(); });
// retrieve item
T item = m_queue.front();
m_queue.pop();
// return item
return item;
}
};
#define DEVICE_WRITE_DEFAULT "/dev/xdma0_h2c_0"
#define DEVICE_READ_DEFAULT "/dev/xdma0_c2h_0"
#define NB_BLOCKS_PER_READ 8
#define READ_BLOCK_NB_SAMPLES 2048
#define NB_BLOCKS_PER_WRITE 8
#define WRITE_BLOCK_NB_SAMPLES 2048
#define PKT_HEADER_NB_SAMPLES 7
#define PKT_FOOTER_NB_SAMPLES 1
#define PKT_OVERHEAD_NB_SAMPLES (PKT_HEADER_NB_SAMPLES + PKT_FOOTER_NB_SAMPLES)
static const uint64_t magic_tx = 0xA5A50be3A5A5A5A5LL;
static const uint64_t magic_rx = 0xA5A50be3A5A5A5A5LL;
static const uint32_t magic_footer1 = 0xce11;
static const uint32_t magic_footer2 = 0x5A;
static const uint64_t tx_ahead = WRITE_BLOCK_NB_SAMPLES * 24;
typedef struct {
uint64_t control;
uint32_t packetSeqNum: 16;
uint32_t packetSz: 16;
uint32_t seqId: 2;
uint32_t filler: 6;
uint32_t markers: 8;
uint32_t filler2: 16;
uint32_t txGain: 24;
uint32_t filler3: 8;
uint32_t ppsOffset;
uint64_t timestamp;
} __attribute__((packed)) headerTx_t;
typedef struct {
uint64_t control;
uint16_t packetSeqNum;
uint16_t packetSz;
uint8_t RXen : 1;
uint8_t TXen : 1;
uint8_t PLLlocked: 1;
uint8_t ADCsync : 1;
uint8_t DACsync : 1;
uint8_t TXlate : 1;
uint8_t TXseqerr : 1;
uint8_t filler : 1;
uint8_t InbandReadAddr;
uint16_t InbandReadValue;
uint32_t ppsOffset: 30;
uint32_t ppsAlive: 1;
uint32_t gpsLock: 1;
uint64_t timestamp;
} __attribute__((packed)) headerRx_t;
typedef struct {
uint16_t control1;
uint8_t control2;
uint8_t atomicPacket: 1;
uint8_t timerOverflow: 1;
uint8_t filler: 6;
} __attribute__((packed)) footer_t;
typedef struct {
headerRx_t h;
c16_t b[READ_BLOCK_NB_SAMPLES];
footer_t f;
} __attribute__((packed)) rx_packet_t;
typedef struct {
headerTx_t h;
c16_t b[WRITE_BLOCK_NB_SAMPLES];
// footer_t f;
} __attribute__((packed)) tx_packet_t;
static inline void dumpHD(std::string ctx, headerRx_t h)
{
printf("header dump, %s\n", ctx.c_str());
uint8_t *z = (uint8_t *)&h;
for (int i = 0; i < PKT_HEADER_NB_SAMPLES; i++)
printf(" %02x:%02x %02x:%02x\n", z[i * 4 + 0], z[i * 4 + 1], z[i * 4 + 2], z[i * 4 + 3]);
printf(
"decoded magic: %lx\n"
" dataSz(words): %u, packetSeq:%u\n"
" InbandReadValue %x, InbandReadAddr:%x, DACsync %u, ADCsync: %u, PLLlocked:%u, TXen:%u, RXen:%u\n"
" gpslocked %d, ppsalive %d, pps offset %d\n"
" timestamp: %lu\n",
h.control,
h.packetSz,
h.packetSeqNum,
h.InbandReadValue,
h.InbandReadAddr,
h.DACsync,
h.ADCsync,
h.PLLlocked,
h.TXen,
h.RXen,
h.gpsLock,
h.ppsAlive,
h.ppsOffset,
h.timestamp);
}
typedef struct {
char filename_write[FILENAME_MAX];
char filename_read[FILENAME_MAX];
int fd_write;
int fd_read;
int num_underflows;
int num_overflows;
int num_seq_errors;
int64_t tx_count;
int64_t rx_count;
int wait_for_first_pps;
int use_gps;
openair0_timestamp_t rx_timestamp;
openair0_timestamp_t rx_ts_interface;
openair0_timestamp_t tx_ts;
uint txSeq;
uint64_t gap;
tx_packet_t *tx_block;
size_t tx_block_pos;
TSQueue<tx_packet_t *> *ready_tx;
TSQueue<uint64_t> *last_rx;
TSQueue<rx_packet_t *> *read_queue;
bool first_tx;
bool rxMagicFound;
uint seqNum;
uint lastPpsOffset;
int nb_blocks_per_read;
int remain_samples;
rx_packet_t *rx_live;
uint txLate;
uint txErr;
uint timerOverflow;
uint atomicPacket;
} oc_state_t;
typedef struct {
openair0_device_t *rfdevice;
int antennas;
int dft_sz;
} threads_t;
static int check_ref_locked(oc_state_t *s)
{
return 0;
}
static int sync_to_gps(openair0_device_t *device)
{
return 0;
}
void *write_thread(void *arg)
{
oc_state_t *s = (oc_state_t *)arg;
uint64_t last_rx = s->last_rx->pop();
FILE* fd=fopen("/tmp/headers", "w");
fprintf(fd,"time before call xdma, nano sec in xdma write, packet seq num, timestamp\n");
char * log_headers=getenv("LOGHEADERS");
do {
tx_packet_t *p = s->ready_tx->pop();
if (last_rx + tx_ahead < p->h.timestamp)
LOG_D(HW, "tx is too ahead, waiting, %lu, %ld\n", p->h.timestamp, p->h.timestamp - last_rx);
while (last_rx + tx_ahead < p->h.timestamp) {
last_rx = s->last_rx->pop();
LOG_D(HW, "pop rx: %lu, rx q sz %lu, tx q sz %lu\n", last_rx, s->last_rx->m_queue.size(), s->ready_tx->m_queue.size());
}
struct timespec b,e;
clock_gettime(CLOCK_REALTIME,&b);
size_t wrote = write(s->fd_write, p, sizeof(tx_packet_t) * NB_BLOCKS_PER_WRITE);
clock_gettime(CLOCK_REALTIME,&e);
if (wrote != sizeof(tx_packet_t) * NB_BLOCKS_PER_WRITE)
LOG_E(HW, "write to SDR failed, request: %lu, wrote %ld\n", sizeof(tx_packet_t) * NB_BLOCKS_PER_WRITE, wrote);
if (wrote < 0)
LOG_E(HW, "write to %s failed, errno %d:%s\n", s->filename_write, errno, strerror(errno));
LOG_D(HW, "wrote: %lu\n", p->h.timestamp);
if (log_headers) {
char str[60];
memset(str,' ', sizeof(str));
snprintf(str,sizeof(str), "%lu.%lu, %lu, %u, %lu\n", b.tv_sec, b.tv_nsec,(e.tv_sec-b.tv_sec)*1000*1000*1000+e.tv_nsec-b.tv_nsec,p->h.packetSeqNum,p->h.timestamp);
fwrite(str, sizeof(str),1,fd);
}
free(p);
} while (true);
return NULL;
}
struct energy_s {
float mean;
float papr_dB;
float peak;
};
struct energy_s compute_papr_db(const c16_t *x, size_t N)
{
float p_max = 0.0f;
float p_sum = 0.0f;
for (size_t n = 0; n < N; n++) {
float p = (float)x[n].r * x[n].r + (float)x[n].i * x[n].i;
p_sum += p;
if (p > p_max)
p_max = p;
}
float p_avg = p_sum / N;
float papr = p_max / p_avg;
return (struct energy_s){p_avg, 10.0f * log10f(papr), sqrt(p_max)};
}
static int32_t signalEnergy(c16_t *input, uint32_t length)
{
// init
simde__m128 mm0 = simde_mm_setzero_ps();
// Acc
for (uint32_t i = 0; i < (length >> 2); i++) {
simde__m128i in = simde_mm_loadu_si128((simde__m128i *)input);
mm0 = simde_mm_add_ps(mm0, simde_mm_cvtepi32_ps(simde_mm_madd_epi16(in, in)));
input += 4;
}
// leftover
float leftover_sum = 0;
c16_t *leftover_input = input + (length & ~3);
uint16_t lefover_count = length & 3;
for (int32_t i = 0; i < lefover_count; i++) {
leftover_sum += leftover_input[i].r * leftover_input[i].r + leftover_input[i].i * leftover_input[i].i;
}
// Ave
float sums[4];
simde_mm_store_ps(sums, mm0);
return (uint32_t)((sums[0] + sums[1] + sums[2] + sums[3] + leftover_sum) / (float)length);
}
// DC-filter: 0 will be done in FPGA after seeing 128-consecutive samples having the same value
static inline int write_block(oc_state_t *s, c16_t *samples, uint sz)
{
if (!s->tx_block) {
s->tx_block = (tx_packet_t *)malloc16(NB_BLOCKS_PER_WRITE * sizeof(tx_packet_t));
}
tx_packet_t *ant0 = s->tx_block + s->tx_block_pos;
ant0->h = (headerTx_t){.control = magic_tx,
.packetSeqNum = s->txSeq++,
.packetSz = WRITE_BLOCK_NB_SAMPLES,
.seqId = 1,
.filler = 0x02,
.markers = 0xb1,
.filler2 = 0xabcd,
.txGain = 0x112233,
.filler3 = 0xf0,
.ppsOffset = 0x28272625,
.timestamp = (uint64_t)s->tx_ts-170};
for (uint i = 0; i < sz; i++)
ant0->b[i] = (c16_t){(int16_t)(samples[i].r << 4), (int16_t)(samples[i].i << 4)};
// memcpy(ant0->b, samples, sz * sizeof(c16_t));
s->tx_ts += sz;
s->tx_block_pos++;
s->tx_count++;
if (s->tx_block_pos == NB_BLOCKS_PER_WRITE) {
s->ready_tx->push(s->tx_block);
s->tx_block_pos = 0;
s->tx_block = NULL;
}
return sz;
}
static int oc_write(openair0_device_t *device, openair0_timestamp_t timestamp, void **buff, int nsamps, int cc, int flags)
{
oc_state_t *s = (oc_state_t *)device->priv;
timestamp -= device->openair0_cfg->command_line_sample_advance + device->openair0_cfg->tx_sample_advance;
if (s->first_tx) {
s->tx_ts = timestamp;
s->first_tx = false;
}
int64_t gap = timestamp - s->tx_ts;
if (gap < 0) {
LOG_E(HW, "out of sequence\n");
gap = 0;
}
if (gap)
LOG_I(HW, "gap of %ld\n", gap);
else
LOG_D(HW, ".\n");
int wr_sz = nsamps;
// LOG_E(HW, "ask to write %d\n", wr_sz);
while (wr_sz > 0) {
int tmp = std::min(wr_sz, WRITE_BLOCK_NB_SAMPLES);
if (tmp != WRITE_BLOCK_NB_SAMPLES)
LOG_E(HW, "Error block size: %d\n", nsamps);
int sz = write_block(s, ((c16_t *)buff[0]) + nsamps - wr_sz, tmp);
if (sz != tmp)
LOG_E(HW, "ask to write %d, res is %d\n", tmp, sz);
wr_sz -= sz;
}
if (s->tx_ts != timestamp + nsamps)
LOG_E(HW,"tx samples count error\n");
s->tx_ts = timestamp + nsamps;
#if 0
static uint nsamps0 = 0;
struct energy_s e = compute_papr_db((c16_t *)*buff, nsamps);
if (e.mean > 1) {
LOG_I(HW, "sent power: %f, papr %f, max %f after %d samples with no energy\n", e.mean, e.papr_dB, e.peak, nsamps0);
nsamps0 = 0;
} else {
nsamps0 += nsamps;
}
#endif
return nsamps;
}
static void initial_block_align (oc_state_t *s) {
LOG_I(HW, "Synchronizing rx\n");
__attribute__ ((aligned(32))) uint32_t b[READ_BLOCK_NB_SAMPLES + PKT_OVERHEAD_NB_SAMPLES];
int idx = 0;
int old = 0;
uint64_t bytes = 0;
//while (1) {
ssize_t ret = read(s->fd_read, b, sizeof(b));
if (ret != sizeof(b)) {
LOG_E(HW, "Error reading %ld bytes: %ld (%s)\n", sizeof(b), ret, strerror(errno));
usleep(10000);
return;
}
int i;
headerRx_t *rx = NULL;
for (i = 0; i < READ_BLOCK_NB_SAMPLES + PKT_HEADER_NB_SAMPLES; i++)
if (b[i] == (magic_rx & UINT32_MAX) && b[i + 1] == ((magic_rx >> 32) & UINT32_MAX)) {
LOG_D(HW,
"found first magic at %d (inblock: %d), dist: %d, bytes %lu\n",
idx * (READ_BLOCK_NB_SAMPLES + PKT_OVERHEAD_NB_SAMPLES) + i,
i,
idx * (READ_BLOCK_NB_SAMPLES + PKT_OVERHEAD_NB_SAMPLES) + i - old,
bytes);
rx = (headerRx_t *)(b + i);
dumpHD("first header:", *rx);
break;
}
if (i == (READ_BLOCK_NB_SAMPLES + PKT_HEADER_NB_SAMPLES)) {
LOG_E(HW, "%%error magic not found\n");
return;
}
ret = read(s->fd_read, b, i * sizeof(*b));
s->seqNum = rx->packetSeqNum + 1;
s->rx_timestamp = (int64_t)rx->timestamp + rx->packetSz;
s->rx_ts_interface = rx->timestamp;
s->rx_count = 1;
}
static bool get_blocks(oc_state_t *s, rx_packet_t *p)
{
static struct timespec last_second={}, origin={};
static struct timespec now={};
static uint64_t tot_samples= 0;
int readSz = sizeof(*s->rx_live) * s->nb_blocks_per_read;
ssize_t ret = read(s->fd_read, p, readSz);
if (ret != readSz || p[0].h.control != magic_rx) {
LOG_E(HW, "Error reading header asked for %d bytes, got %ld, magic: %lx\n", readSz, ret, p[0].h.control);
dumpHD("lost good header:", p[0].h);
// abort();
s->rx_count = -1;
return false;
}
clock_gettime(CLOCK_REALTIME, &now);
if ( last_second.tv_sec==0) {
last_second=now;
origin=now;
}
tot_samples+= NB_BLOCKS_PER_READ * READ_BLOCK_NB_SAMPLES;
if (now.tv_sec != last_second.tv_sec) {
LOG_I(HW,
"driver avg read rate:%f\n errors during last second: txLate %u, txSeqerr %u, timerOverflow %u, atomicPacket %u, present "
"tx seq %u\n\n ",
(float)tot_samples / (now.tv_sec * 1000000 - origin.tv_sec * 1000000 + now.tv_nsec / 1000.0 - origin.tv_nsec / 1000.0),
s->txLate,
s->txErr,
s->timerOverflow,
s->atomicPacket,
s->txSeq);
s->txLate = s->txErr = s->timerOverflow = s->atomicPacket = 0;
last_second.tv_sec++;
}
for (int i = 0; i <s->nb_blocks_per_read ; i++) {
if (s->rx_timestamp != (int64_t)p[i].h.timestamp)
LOG_W(HW,
"expected ts: %lu got %lu, diff %ld, seq num %d, atomicPacket %d, timerOverflow %d\n",
s->rx_timestamp,
p[i].h.timestamp,
(int64_t)p[i].h.timestamp - s->rx_timestamp,
s->seqNum,
(~p[i].f.atomicPacket & 0x01),
p[i].f.timerOverflow);
s->rx_timestamp=p[i].h.timestamp + READ_BLOCK_NB_SAMPLES;
/*
if (llabs((int64_t)s->lastPpsOffset - (int64_t)p[i].h.ppsOffset) > 12)
// pps_in is based on a real pulse from the GPS. It may have some jitter and drift during time.
// Thus it is normal to have some diff between theoretical (expected) and real (measured) values.
// >12 ->l means that we flag errors higher than +/- 0.1ppm.
// When the board's FPGA starts, the vcxo is not yet discplined to the GPS, thus we may observe
// some errors until the frequency error algorithm converges
printf("expected pps offset %u got %u, diff %d, seq num %d\n",
s->lastPpsOffset,
p[i].h.ppsOffset,
p[i].h.ppsOffset- s->lastPpsOffset,
s->seqNum);
s->lastPpsOffset = (p[i].h.ppsOffset + READ_BLOCK_NB_SAMPLES )% 122880000 ;
if (!p[i].h.ppsAlive)
printf("pps not alive\n");
*/
if (p[i].h.TXlate) {
LOG_D(HW, "TXlate\n");
s->txLate++;
}
if (p[i].h.TXseqerr) {
LOG_W(HW, "TXseqerr, current tx seq is %u\n", s->txSeq);
s->txErr++;
}
if (p[i].f.control1 != magic_footer1 || p[i].f.control2 != magic_footer2)
LOG_W(HW, "footer error\n");
if (p[i].f.timerOverflow) {
LOG_D(HW, "timerOverflow\n");
s->timerOverflow++;
}
if (p[i].f.atomicPacket) {
s->atomicPacket++;
LOG_D(HW, "Not atomic\n");
}
static int last_filler = 0;
if (last_filler != p[i].f.filler)
LOG_I(HW, "filler changed to %x\n", p[i].f.filler);
last_filler = p[i].f.filler;
if (s->seqNum % 65536 != p[i].h.packetSeqNum) {
LOG_W(HW,
"expected rx packet sequence number %u got %u, diff %d\n",
s->seqNum,
p[i].h.packetSeqNum,
p[i].h.packetSeqNum - s->seqNum);
s->seqNum = p[i].h.packetSeqNum;
//s->rx_count = -1;
//return false;
}
s->seqNum++;
}
s->last_rx->push(s->rx_timestamp);
LOG_D(HW, "read: %lu\n", s->rx_timestamp);
return true;
}
void *read_thread(void *arg)
{
oc_state_t *s = (oc_state_t *)arg;
while (true) {
if (s->rx_count == -1)
initial_block_align(s);
if (s->rx_count == -1) {
usleep(10);
continue;
}
if (s->read_queue->m_queue.size() > 100) {
LOG_W(HW, "rx consumer is too slow, trashing rx queue\n");
while (s->read_queue->m_queue.size())
free(s->read_queue->pop());
}
rx_packet_t *tmp = (rx_packet_t *)malloc(sizeof(*s->rx_live) * s->nb_blocks_per_read);
if (!get_blocks(s, tmp)) {
printf("getblocks returned bad\n");
free(tmp);
} else
s->read_queue->push(tmp);
}
return NULL;
}
static int oc_read(openair0_device_t *device, openair0_timestamp_t *ptimestamp, void **buff, int nsamps, int cc)
{
oc_state_t *s = (oc_state_t *)device->priv;
c16_t **output=(c16_t**)buff;
int remain_to_get=nsamps;
while (remain_to_get > 0) {
while (s->remain_samples > 0 && remain_to_get > 0) {
if (s->gap) {
c16_t *out = output[0] + nsamps - remain_to_get;
while (s->gap && remain_to_get) {
*out++ = {};
remain_to_get--;
s->gap--;
s->rx_ts_interface++;
}
}
rx_packet_t *last_rx = s->rx_live;
int nb_samples_per_packet = sizeof(last_rx[0].b) / sizeof(last_rx[0].b[0]);
int nb_samples=nb_samples_per_packet*s->nb_blocks_per_read;
int consumed_samples = nb_samples - s->remain_samples;
int nb_consumed_samples_in_block= consumed_samples%nb_samples_per_packet;
int bloc = consumed_samples / nb_samples_per_packet;
rx_packet_t *cur_pkt = last_rx + bloc;
if (nb_consumed_samples_in_block == 0 && cur_pkt->h.timestamp != (uint64_t)s->rx_ts_interface) {
s->gap = cur_pkt->h.timestamp - s->rx_ts_interface;
if (s->gap > 0 && s->gap < 1228800) {
LOG_W(HW, "gap of %ld, we fill \n", s->gap);
continue;
} else {
LOG_W(HW, "gap of %ld, we break timestamp sequence %lu != %lu\n", s->gap, s->rx_ts_interface, cur_pkt->h.timestamp);
s->rx_ts_interface = cur_pkt->h.timestamp;
s->gap = 0;
}
}
int remaing_samples_in_block= nb_samples_per_packet-nb_consumed_samples_in_block;
int toCopy=std::min(remaing_samples_in_block,remain_to_get );
memcpy(output[0] + nsamps - remain_to_get, cur_pkt->b + nb_consumed_samples_in_block, toCopy * sizeof(cur_pkt->b[0]));
s->remain_samples-=toCopy;
remain_to_get -= toCopy;
s->rx_ts_interface += toCopy;
}
if( remain_to_get > 0 ) {
free(s->rx_live);
s->rx_live = s->read_queue->pop();
s->remain_samples = sizeof(s->rx_live->b) * s->nb_blocks_per_read / sizeof(*s->rx_live->b);
}
}
*ptimestamp = s->rx_ts_interface-nsamps;
return nsamps;
}
static int oc_set_freq(openair0_device_t *device, openair0_config_t *openair0_cfg)
{
// oc_state_t *s = (oc_state_t *)device->priv;
LOG_I(HW,
"Setting TX Freq %f, RX Freq %f, tune_offset: %f\n",
openair0_cfg[0].tx_freq[0],
openair0_cfg[0].rx_freq[0],
openair0_cfg[0].tune_offset);
return 0;
}
static int oc_set_gains(openair0_device_t *device, openair0_config_t *openair0_cfg)
{
// oc_state_t *s = (oc_state_t *)device->priv;
LOG_I(HW, "Setting RX gain to %f \n", openair0_cfg[0].rx_gain[0]);
return 0;
}
static int oc_stop(openair0_device_t *device)
{
return 0;
}
void set_rx_gain_offset(openair0_config_t *openair0_cfg, int chain_index, int bw_gain_adjust)
{
int i = 0;
// loop through calibration table to find best adjustment factor for RX frequency
double min_diff = 6e9, diff, gain_adj = 0.0;
if (bw_gain_adjust == 1) {
switch ((int)openair0_cfg[0].sample_rate) {
case 46080000:
break;
case 30720000:
break;
case 23040000:
gain_adj = 1.25;
break;
case 15360000:
gain_adj = 3.0;
break;
case 7680000:
gain_adj = 6.0;
break;
case 3840000:
gain_adj = 9.0;
break;
case 1920000:
gain_adj = 12.0;
break;
default:
LOG_E(HW, "unknown sampling rate %d\n", (int)openair0_cfg[0].sample_rate);
// exit(-1);
break;
}
}
while (openair0_cfg->rx_gain_calib_table[i].freq > 0) {
diff = fabs(openair0_cfg->rx_freq[chain_index] - openair0_cfg->rx_gain_calib_table[i].freq);
LOG_I(HW,
"cal %d: freq %f, offset %f, diff %f\n",
i,
openair0_cfg->rx_gain_calib_table[i].freq,
openair0_cfg->rx_gain_calib_table[i].offset,
diff);
if (min_diff > diff) {
min_diff = diff;
openair0_cfg->rx_gain_offset[chain_index] = openair0_cfg->rx_gain_calib_table[i].offset + gain_adj;
}
i++;
}
}
static int oc_get_stats(openair0_device_t *device)
{
return (0);
}
static int oc_reset_stats(openair0_device_t *device)
{
return (0);
}
int oc_write_init(openair0_device_t *device)
{
LOG_E(HW, "trx_write_init should not be called, and is a design error even for USRP\n");
return -1;
}
static int oc_start(openair0_device_t *device)
{
oc_state_t *s = (oc_state_t *)device->priv;
s->rx_count = -1;
s->wait_for_first_pps = 1;
s->first_tx = true;
s->nb_blocks_per_read = NB_BLOCKS_PER_READ;
s->rx_live = (rx_packet_t *)malloc16(s->nb_blocks_per_read * sizeof(*s->rx_live));
s->ready_tx = new TSQueue<tx_packet_t *>;
s->read_queue = new TSQueue<rx_packet_t *>;
s->last_rx = new TSQueue<uint64_t>;
s->fd_write = open(s->filename_write, O_WRONLY);
if (s->fd_write < 0) {
LOG_E(HW, "Open %s failed, errno %d:%s\n", s->filename_write, errno, strerror(errno));
exit(1);
}
s->fd_read = open(s->filename_read, O_RDONLY);
if (s->fd_read < 0) {
LOG_E(HW, "Open %s failed, errno %d:%s\n", s->filename_read, errno, strerror(errno));
exit(1);
}
pthread_t w_thread;
threadCreate(&w_thread, write_thread, s, (char *)"write_thr", -1, OAI_PRIORITY_RT);
pthread_t r_thread;
threadCreate(&r_thread, read_thread, s, (char *)"read_thr", -1, OAI_PRIORITY_RT);
oc_set_gains(device, device->openair0_cfg);
oc_set_freq(device, device->openair0_cfg);
sync_to_gps(device);
check_ref_locked(s);
// Fixme: set sampling rate, lack of API in OAI
return 0;
}
static void oc_end(openair0_device_t *device)
{
if (device == NULL)
return;
// oc_state_t *s = (oc_state_t *)device->priv;
}
extern "C" {
int device_init(openair0_device_t *device, openair0_config_t *openair0_cfg)
{
LOG_I(HW, "openair0_cfg->clock_source == '%d' (internal = %d, external = %d)\n", openair0_cfg->clock_source, internal, external);
oc_state_t *st;
if (device->priv == NULL) {
st = (oc_state_t *)calloc(1, sizeof(oc_state_t));
device->priv = st;
strcpy(st->filename_write, DEVICE_WRITE_DEFAULT);
strcpy(st->filename_read, DEVICE_READ_DEFAULT);
AssertFatal(st != NULL, "OC device: memory allocation failure\n");
} else {
LOG_E(HW, "multiple calls to device init detected\n");
return 0;
}
device->openair0_cfg = openair0_cfg;
device->trx_start_func = oc_start;
device->trx_get_stats_func = oc_get_stats;
device->trx_reset_stats_func = oc_reset_stats;
device->trx_end_func = oc_end;
device->trx_stop_func = oc_stop;
device->trx_set_freq_func = oc_set_freq;
device->trx_set_gains_func = oc_set_gains;
device->trx_write_init = oc_write_init;
device->trx_write_func = oc_write;
device->trx_read_func = oc_read;
if (device->openair0_cfg->recplay_mode == RECPLAY_RECORDMODE) {
std::cerr << "OC device initialized in subframes record mode" << std::endl;
}
device->type = USRP_X300_DEV;
struct {
int sample_rate;
int tx_sample_advance;
double tx_bw;
double rx_bw;
} config_table[] = {{245760000, 15, 200e6, 200e6},
{184320000, 15, 100e6, 100e6},
{122880000, 15, 80e6, 80e6},
{92160000, 15, 60e6, 60e6},
{61440000, 15, 40e6, 40e6},
{46080000, 15, 40e6, 40e6},
{30720000, 15, 40e6, 40e6},
{23040000, 15, 20e6, 20e6},
{15360000, 15, 10e6, 10e6},
{7680000, 50, 5e6, 5e6},
{1920000, 50, 1.25e6, 1.25e6}};
size_t i = 0;
for (; i < sizeofArray(config_table); i++)
if (config_table[i].sample_rate == (int)openair0_cfg[0].sample_rate) {
openair0_cfg[0].tx_sample_advance = config_table[i].tx_sample_advance;
openair0_cfg[0].tx_bw = config_table[i].tx_bw;
openair0_cfg[0].rx_bw = config_table[i].rx_bw;
break;
}
if (i == sizeofArray(config_table)) {
LOG_E(HW, "unknown sampling rate: %d\n", (int)openair0_cfg[0].sample_rate);
exit(-1);
}
return 0;
}
}