first trial

This commit is contained in:
laurent
2025-01-21 15:21:22 +01:00
committed by Laurent THOMAS
parent 215941da12
commit a13f34d3d6
14 changed files with 1009 additions and 284 deletions

View File

@@ -1976,6 +1976,9 @@ target_link_libraries(nr-uesoftmodem PRIVATE
add_executable(rftest
${OPENAIR_DIR}/openair1/PHY/TOOLS/calibration_test.c
${OPENAIR_DIR}/openair1/PHY/TOOLS/calibration_scope.c
${OPENAIR_DIR}/radio/COMMON/common_lib.c
${OPENAIR1_DIR}/PHY/TOOLS/dfts_load.c
${OPENAIR_DIR}/executables/softmodem-common.c
)
target_link_libraries(rftest PRIVATE
minimal_lib PHY_NR_COMMON UTIL forms pthread dl m ${T_LIB}

View File

@@ -223,8 +223,8 @@ function main() {
shift 2;;
-w | --hardware)
case "$2" in
"USRP" | "BLADERF" | "LMSSDR" | "IRIS" | "ZMQ")
HWs+=" OAI_"$2
"USRP" | "BLADERF" | "LMSSDR" | "IRIS" | "ZMQ" | "OC")
HWs+=" OAI_$2"
TARGET_LIST="$TARGET_LIST oai_${2,,}devif" # ,, makes lowercase
CMAKE_CMD="$CMAKE_CMD -DOAI_$2=ON"
;;

View File

@@ -131,7 +131,14 @@ void *L1_tx_thread(void *arg) {
PHY_VARS_gNB *gNB = (PHY_VARS_gNB*)arg;
while (oai_exit == 0) {
notifiedFIFO_elt_t *res = pullNotifiedFIFO(&gNB->L1_tx_out);
notifiedFIFO_elt_t *res = NULL;
do {
res = pollNotifiedFIFO(&gNB->L1_tx_out);
if (!res) {
LOG_W(HW, "possible underrun\n");
usleep(300);
}
} while (!res);
if (res == NULL) // stopping condition, happens only when queue is freed
break;
processingData_L1tx_t *info = (processingData_L1tx_t *)NotifiedFifoData(res);

View File

@@ -367,7 +367,7 @@ static void rx_rf(RU_t *ru, int *frame, int *slot)
uint32_t samples_per_slot_prev = get_samples_per_slot((*slot - 1) % fp->slots_per_frame, fp);
if (proc->timestamp_rx - old_ts != samples_per_slot_prev) {
LOG_D(PHY,
LOG_W(PHY,
"rx_rf: rfdevice timing drift of %" PRId64 " samples (ts_off %" PRId64 ")\n",
proc->timestamp_rx - old_ts - samples_per_slot_prev,
ru->ts_offset);

View File

@@ -41,8 +41,6 @@
#define SSS_START_IDX (3) /* [0:PSBCH 1:PSS0 2:PSS1 3:SSS0 4:SSS1] */
#define NUM_SSS_SYMBOLS (2)
#define SSS_METRIC_FLOOR_NR (30000)
void init_context_sss_nr(int amp);
void free_context_sss_nr(void);

View File

@@ -226,6 +226,7 @@ bool rx_sss_nr(const NR_DL_FRAME_PARMS *frame_parms,
/* Computation of signal with shift phase is based on below formula */
/* cosinus cos(x + y) = cos(x)cos(y) - sin(x)sin(y) */
/* sinus sin(x + y) = sin(x)cos(y) + cos(x)sin(y) */
#define SSS_METRIC_FLOOR_NR (7000)
int Nid1_start = 0;
int Nid1_end = N_ID_1_NUMBER;

View File

@@ -10,6 +10,7 @@
#include "executables/nr-softmodem-common.h"
#include <forms.h>
#include <openair1/PHY/TOOLS/calibration_scope.h>
#include "openair1/PHY/TOOLS/tools_defs.h""
#define TPUT_WINDOW_LENGTH 100
#define ScaleZone 4
@@ -22,11 +23,8 @@ typedef struct {
int16_t i;
} scopeSample_t;
#define SquaredNorm(VaR) ((VaR).r * (VaR).r + (VaR).i * (VaR).i)
typedef struct {
void ** samplesRx;
openair0_device_t *rfdevice;
} calibData_t;
struct OAI_phy_scope_s;
typedef struct OAIgraph {
FL_OBJECT *graph;
FL_OBJECT *text;
@@ -42,16 +40,20 @@ typedef struct OAIgraph {
double *waterFallAvg;
bool initDone;
int iteration;
void (*funct) (struct OAIgraph *graph, calibData_t *);
void (*funct)(struct OAIgraph *graph,struct OAI_phy_scope_s * );
} OAIgraph_t;
/* Forms and Objects */
typedef struct {
calibData_t * context;
typedef struct OAI_phy_scope_s{
threads_t *context;
FL_FORM *phy_scope;
OAIgraph_t graph[20];
FL_OBJECT *button_0;
scopeSample_t *timeDomain;
scopeSample_t *timeDomainTx;
scopeSample_t *freqDomain;
scopeSample_t *freqDomainTx;
} OAI_phy_scope_t;
typedef struct {
@@ -104,35 +106,38 @@ static void commonGraph(OAIgraph_t *graph, int type, FL_Coord x, FL_Coord y, FL_
graph->iteration=0;
}
static OAIgraph_t calibrationCommonGraph( void (*funct) (OAIgraph_t *graph, calibData_t *context),
int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label, FL_COLOR pointColor)
static OAIgraph_t calibrationCommonGraph(void (*funct)(OAIgraph_t *graph,OAI_phy_scope_t * scope),
int type,
FL_Coord x,
FL_Coord y,
FL_Coord w,
FL_Coord h,
const char *label,
FL_COLOR pointColor)
{
OAIgraph_t graph = {0};
OAIgraph_t graph = {};
commonGraph(&graph, type, x, y, w, h, label, pointColor);
graph.funct=funct;
return graph;
}
static void setRange(OAIgraph_t *graph, float minX, float maxX, float minY, float maxY) {
if ( maxX > graph->maxX || minX < graph->minX ||
abs(maxX-graph->maxX)>abs(graph->maxX)/2 ||
abs(maxX-graph->maxX)>abs(graph->maxX)/2 ) {
if (maxX > graph->maxX || minX < graph->minX || fabs(maxX - graph->maxX) > fabs(graph->maxX) / 2
|| fabs(maxX - graph->maxX) > fabs(graph->maxX) / 2) {
graph->maxX/=2;
graph->minX/=2;
graph->maxX=max(graph->maxX,maxX);
graph->minX=min(graph->minX,minX);
fl_set_xyplot_xbounds(graph->graph, graph->minX*1.2, graph->maxX*1.2);
graph->maxX = fmax(graph->maxX, maxX);
graph->minX = fmin(graph->minX, minX);
fl_set_xyplot_xbounds(graph->graph, (int32_t)graph->minX * 1.2, (int32_t)graph->maxX * 1.2);
}
if ( maxY > graph->maxY || minY < graph->minY ||
abs(maxY-graph->maxY)>abs(graph->maxY)/2 ||
abs(maxY-graph->maxY)>abs(graph->maxY)/2 ) {
if (((maxY > graph->maxY) && (fabs(maxY - graph->maxY) > (fabs(graph->maxY) / 2)))
|| ((minY < graph->minY) && (fabs(minY - graph->minY) > (fabs(graph->minY) / 2)))) {
graph->maxY/=2;
graph->minY/=2;
graph->maxY=max(graph->maxY,maxY);
graph->minY=min(graph->minY,minY);
fl_set_xyplot_ybounds(graph->graph, graph->minY*1.2, graph->maxY*1.2);
graph->maxY = fmax(graph->maxY, maxY);
graph->minY = fmin(graph->minY, minY);
fl_set_xyplot_ybounds(graph->graph, (int32_t)graph->minY * 1.2, (int32_t)graph->maxY * 1.2);
}
}
@@ -260,21 +265,48 @@ static void genericPowerPerAntena(OAIgraph_t *graph, const int nb_ant, const sc
}
}
static void gNBWaterFall (OAIgraph_t *graph, calibData_t *context) {
static void gNBWaterFall(OAIgraph_t *graph, OAI_phy_scope_t *scope)
{
//use 1st antenna
genericWaterFall(graph, (scopeSample_t *)context->samplesRx[0],
0, 0,
"X axis:one frame in time");
genericWaterFall(graph, scope->timeDomain, DFT, 10, "X axis:one frame in time");
}
static void spectrum(OAIgraph_t *graph, OAI_phy_scope_t *scope)
{
int len = scope->context->dft_sz;
for (int ri = 0; ri < 2; ri++) {
float *values;
float *time;
oai_xygraph_getbuff(graph, &time, &values, len, ri);
for (int i = 0; i < len; i++) {
values[i] = ri ? scope->freqDomain[(i + len / 2) % len].i : scope->freqDomain[(i + len / 2) % len].r;
}
oai_xygraph(graph, time, values, len, ri, 1);
}
}
static void zoomIn(OAIgraph_t *graph, OAI_phy_scope_t *scope)
{
static time_t t = 0;
time_t n = time(NULL);
if (n == t)
return;
t = n;
int len = scope->context->dft_sz;
int detailLen = min(len, 600);
int beg=max(0, rand()%len - detailLen )/2*2;
for (int ri = 0; ri < 2; ri++) {
float *values;
float *time;
oai_xygraph_getbuff(graph, &time, &values, detailLen, ri);
for (int i = 0; i < detailLen; i++)
values[i] = ri ? scope->timeDomain[beg+i].i : scope->timeDomain[beg+i].r;
oai_xygraph(graph, time, values, detailLen, ri, 1);
}
static void gNBfreqWaterFall (OAIgraph_t *graph, calibData_t *context) {
//use 1st antenna
genericWaterFall(graph, (scopeSample_t *)context->samplesRx[0],
0, 0,
"X axis:one frame in time");
}
__attribute__((unused))
static void timeResponse (OAIgraph_t *graph, calibData_t *context)
static void timeResponse(OAIgraph_t *graph, threads_t *context)
{
#if 0
const int len=2*phy_vars_gnb->frame_parms.ofdm_symbol_size;
@@ -290,36 +322,41 @@ static void timeResponse (OAIgraph_t *graph, calibData_t *context)
values[i] = SquaredNorm(data[i]);
}
oai_xygraph(graph,time,values, len, ue, 10);
oai_xygraph(graph,time,values, len/2, ue, 10);
}
}
#endif
}
static void puschIQ (OAIgraph_t *graph, calibData_t *context) {
#if 0
NR_DL_FRAME_PARMS *frame_parms=&phy_vars_gnb->frame_parms;
int sz=frame_parms->N_RB_UL*12*frame_parms->symbols_per_slot;
for (int ue=0; ue<nb_UEs; ue++) {
scopeSample_t *pusch_comp = (scopeSample_t *) phy_vars_gnb->pusch_vars[ue]->rxdataF_comp[0];
static void signalIQ(OAIgraph_t *graph,OAI_phy_scope_t *scope)
{
int len = scope->context->dft_sz;
float *I, *Q;
oai_xygraph_getbuff(graph, &I, &Q, sz, ue);
oai_xygraph_getbuff(graph, &I, &Q, len, 0);
if (pusch_comp) {
for (int k=0; k<sz; k++ ) {
I[k] = pusch_comp[k].r;
Q[k] = pusch_comp[k].i;
for (int k = 0; k < len; k++) {
I[k] = scope->freqDomain[k].r;
Q[k] = scope->freqDomain[k].i;
}
oai_xygraph(graph,I,Q,sz,ue,10);
oai_xygraph(graph, I, Q, DFT, 0, 10);
}
}
#endif
static void signalIQtx(OAIgraph_t *graph,OAI_phy_scope_t *scope)
{
int len = scope->context->dft_sz;
float *I, *Q;
oai_xygraph_getbuff(graph, &I, &Q, len, 0);
for (int k = 0; k < len; k++) {
I[k] = scope->freqDomainTx[k].r;
Q[k] = scope->freqDomainTx[k].i;
}
oai_xygraph(graph, I, Q, DFT, 0, 10);
}
static OAI_phy_scope_t *createScopeCalibration(calibData_t * context) {
static OAI_phy_scope_t *createScopeCalibration(threads_t *context)
{
FL_OBJECT *obj;
OAI_phy_scope_t *fdui = calloc_or_fail((sizeof *fdui), 1);
fdui->context=context;
@@ -329,40 +366,58 @@ static OAI_phy_scope_t *createScopeCalibration(calibData_t * context) {
obj = fl_add_box( FL_BORDER_BOX, 0, 0, 800, 800, "" );
fl_set_object_color( obj, FL_BLACK, FL_WHITE );
int curY=0,x,y,w,h;
OAIgraph_t *graph = fdui->graph;
// Received signal
fdui->graph[0] = calibrationCommonGraph( gNBWaterFall, WATERFALL, 0, curY, 400, 100,
"Received Signal (Time-Domain, one frame)", FL_RED );
*graph++ = calibrationCommonGraph(zoomIn, FL_NORMAL_XYPLOT, 0, curY, 400, 100, "Received Signal in time zoom", FL_RED);
// Time-domain channel response
//fdui->graph[1] = calibrationCommonGraph( timeResponse, FL_NORMAL_XYPLOT, 410, curY, 400, 100, "SRS Frequency Response (samples, abs)", FL_RED );
// fdui->graph[1] = calibrationCommonGraph( timeResponse, FL_NORMAL_XYPLOT, 410, curY, 400, 100, "SRS Frequency Response (samples,
// abs)", FL_RED );
// frequency spectrum
*graph++ = calibrationCommonGraph(spectrum, FL_NORMAL_XYPLOT, 410, curY, 400, 100, "DFT output", FL_YELLOW);
fl_get_object_bbox(fdui->graph[0].graph,&x, &y,&w, &h);
curY+=h;
// Frequency-domain channel response
fdui->graph[1] = calibrationCommonGraph( gNBfreqWaterFall, WATERFALL, 0, curY, 800, 100,
"Channel Frequency domain (RE, one frame)", FL_RED );
fl_get_object_bbox(fdui->graph[1].graph,&x, &y,&w, &h);
curY += h + 20;
// Frequency-domain channel response
*graph++ = calibrationCommonGraph(gNBWaterFall, WATERFALL, 0, curY, 800, 100, "received signal in time", FL_RED);
fl_get_object_bbox(fdui->graph[2].graph, &x, &y, &w, &h);
curY+=h+20;
*graph++ = calibrationCommonGraph(signalIQtx, FL_POINTS_XYPLOT, 0, curY, 300, 300, "Tx generated I/Q of frequency domain", FL_YELLOW);
fl_get_object_bbox(fdui->graph[2].graph,&x, &y,&w, &h);
// LLR of PUSCH
//fdui->graph[3] = calibrationCommonGraph( puschLLR, FL_POINTS_XYPLOT, 0, curY, 500, 200, "PUSCH Log-Likelihood Ratios (LLR, mag)", FL_YELLOW );
// I/Q PUSCH comp
fdui->graph[2] = calibrationCommonGraph( puschIQ, FL_POINTS_XYPLOT, 500, curY, 300, 200,
"PUSCH I/Q of MF Output", FL_YELLOW );
*graph++ = calibrationCommonGraph(signalIQ, FL_POINTS_XYPLOT, 500, curY, 300, 300, "I/Q of frequency domain", FL_YELLOW);
fl_get_object_bbox(fdui->graph[2].graph,&x, &y,&w, &h);
curY+=h;
//fl_get_object_bbox(fdui->graph[6].graph,&x, &y,&w, &h);
curY += h;
fdui->graph[3].graph=NULL;
fl_end_form( );
fdui->phy_scope->fdui = fdui;
fl_show_form (fdui->phy_scope, FL_PLACE_HOTSPOT, FL_FULLBORDER, "LTE UL SCOPE gNB");
fl_show_form(fdui->phy_scope, FL_PLACE_HOTSPOT, FL_FULLBORDER, "calibration SCOPE");
return fdui;
}
void calibrationScope(OAI_phy_scope_t *form) {
int i=0;
int len = form->context->dft_sz;
if (!form->freqDomain)
form->freqDomain = malloc16(len * sizeof(*form->freqDomain));
if (!form->timeDomain)
form->timeDomain = malloc16(len * sizeof(*form->timeDomain));
if (!form->freqDomainTx)
form->freqDomainTx = malloc16(len * sizeof(*form->freqDomain));
if (!form->timeDomainTx)
form->timeDomainTx = malloc16(len * sizeof(*form->timeDomainTx));
pthread_mutex_lock(&form->context->rxMutex);
memcpy(form->timeDomain, form->context->samplesRx[0], len * sizeof(*form->timeDomain));
memcpy(form->timeDomainTx, form->context->samplesTx[0], len * sizeof(*form->timeDomainTx));
pthread_mutex_unlock(&form->context->rxMutex);
dft(get_dft(len), (int16_t *)form->timeDomain, (int16_t *)form->freqDomain, 1);
dft(get_dft(len), (int16_t *)form->timeDomainTx, (int16_t *)form->freqDomainTx, 1);
int i = 0;
while (form->graph[i].graph) {
form->graph[i].funct(form->graph+i, form->context);
form->graph[i].funct(form->graph + i, form);
i++;
}
@@ -370,13 +425,12 @@ void calibrationScope(OAI_phy_scope_t *form) {
}
static void *scopeThread(void *arg) {
calibData_t * context = (calibData_t *)arg;
threads_t *context = (threads_t *)arg;
size_t stksize=0;
pthread_attr_t atr;
pthread_attr_init(&atr);
pthread_attr_getstacksize(&atr, &stksize);
pthread_attr_setstacksize(&atr, 32 * 1024 * 1024);
sleep(3); // no clean interthread barriers
int fl_argc=1;
char *name="Calibration-scope";
fl_initialize (&fl_argc, &name, NULL, 0, 0);
@@ -384,17 +438,14 @@ static void *scopeThread(void *arg) {
while (!oai_exit) {
calibrationScope(form);
usleep(99*1000);
usleep(50 * 1000);
}
return NULL;
}
void CalibrationInitScope(void **samplesRx, openair0_device_t *rfdevice)
void CalibrationInitScope(threads_t *p)
{
pthread_t forms_thread;
calibData_t *tmp = (calibData_t *)malloc_or_fail(sizeof(*tmp));
tmp->samplesRx=samplesRx;
tmp->rfdevice=rfdevice;
threadCreate(&forms_thread, scopeThread, (void*) tmp, "scope", -1, OAI_PRIORITY_RT_LOW);
threadCreate(&forms_thread, scopeThread, (void *)p, "scope", 7, OAI_PRIORITY_RT_LOW);
}

View File

@@ -1,5 +1,16 @@
#ifndef CALIB_SCOPE_H
#define CALIB_SCOPE_H
static const int DFT = 8 * 1024;
void CalibrationInitScope(void **samplesRx, openair0_device_t *rfdevice);
typedef struct {
openair0_device_t *rfdevice;
int antennas;
int dft_sz;
c16_t **samplesRx;
c16_t **samplesTx;
pthread_mutex_t rxMutex;
pthread_mutex_t txMutex;
} threads_t;
void CalibrationInitScope(threads_t *p);
#endif

View File

@@ -9,7 +9,7 @@
#include <openair1/PHY/TOOLS/calibration_scope.h>
#include "nfapi/oai_integration/vendor_ext.h"
#include "common/config/config_userapi.h"
#include <arpa/inet.h>
int oai_exit=false;
unsigned int mmapped_dma=0;
@@ -35,6 +35,109 @@ uint32_t ulsch_slot_modval;
int read_recplayconfig(recplay_conf_t **recplay_conf, recplay_state_t **recplay_state) {return 0;}
void nfapi_setmode(nfapi_mode_t nfapi_mode) {}
void set_taus_seed(unsigned int seed_init){};
// configmodule_interface_t *uniqCfg = NULL;
const int tx_ahead = DFT * 50;
openair0_timestamp_t rx_timestamp = 0;
openair0_timestamp_t tx_timestamp = 0;
void *write_thread(void *arg)
{
threads_t params = *(threads_t *)arg;
c16_t **samplesTx = params.samplesTx;
uint64_t ts = 0;
for (int i = 0; i < params.dft_sz; i++) {
// Better to select a frequency having an integer division with the sampling rate to avoid having DFT leakage later on
// .r = cos and .i = sin -> having a positive spectrum
// For negative spectrum -> .r = sin and .i = cos
samplesTx[0][i].r = 16000 * cos((ts * M_PI * 2 * 30720) / 122880);
samplesTx[0][i].i = 16000 * sin((ts * M_PI * 2 * 30720) / 122880); // samplesTx[0][i].r;
// Hamming Window - to allow some pseudo-continuity between batches as this is not a continuously generated signal as in real
// life
samplesTx[0][i].r = (samplesTx[0][i].r) * (0.54 - 0.46 * cos(2 * M_PI * 30720 / 122880));
samplesTx[0][i].i = (samplesTx[0][i].i) * (0.54 - 0.46 * cos(2 * M_PI * 30720 / 122880));
ts++;
}
double avg = 0;
for (int i = 0; i < params.dft_sz; i++) {
avg += sqrt(squaredMod(samplesTx[0][i]));
}
printf("avg: %f \n", avg / params.dft_sz);
uint64_t count = 0;
struct timespec last_second;
clock_gettime(CLOCK_REALTIME, &last_second);
openair0_timestamp_t last_tx_timestamp = 0, new_tx = 0;
while (!oai_exit) {
/*
do {
pthread_mutex_lock(&params.txMutex);
printf("write got lock\n");
new_tx = tx_timestamp & ~31;
pthread_mutex_unlock(&params.txMutex);
if (new_tx == last_tx_timestamp)
usleep(5);
} while (last_tx_timestamp == new_tx);
*/
last_tx_timestamp = new_tx & ~31;
params.rfdevice->trx_write_func(params.rfdevice, new_tx + tx_ahead, (void **)samplesTx, params.dft_sz, params.antennas, 0);
count++;
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
if (now.tv_sec != last_second.tv_sec) {
printf("write thread wrote %lu times in one second\n", count);
last_second = now;
count = 0;
}
}
return NULL;
}
void *read_thread(void *arg)
{
threads_t params = *(threads_t *)arg;
c16_t **samplesRx = params.samplesRx;
uint64_t count = 0;
struct timespec last_second;
clock_gettime(CLOCK_REALTIME, &last_second);
while (!oai_exit) {
pthread_mutex_lock(&params.rxMutex);
int ret = params.rfdevice->trx_read_func(params.rfdevice, &rx_timestamp, (void **)samplesRx, params.dft_sz, params.antennas);
pthread_mutex_unlock(&params.rxMutex);
if (ret != params.dft_sz)
printf("read of :%d\n", ret);
count++;
pthread_mutex_lock(&params.txMutex);
tx_timestamp = rx_timestamp;
pthread_mutex_unlock(&params.txMutex);
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
if (now.tv_sec != last_second.tv_sec) {
printf("read thread got %lu blocks in one second, samples per block: %d, nb samples: %lu\n", count, ret, count*ret);
count=0;
last_second.tv_sec++;
#if 0
FILE *fd = fopen("trace.iq", "w+");
if (!fd)
abort();
/* We should advance +1 only if header was detected in previous steps
* Which will make the read working even without timestamped rxdata
*/
c16_t *s = samplesRx[0]; /* Exclude the header from the samples */
for (int i = 0; i < ret; i++) {
/* We need to throw the entie 256-bits word if we detect the 64-bits header.
* This may happens when receiving a big packet size in chuncks.
*/
fprintf(fd, "%d %d %d\n", i, s[i].r, s[i].i);
}
#endif
}
}
return NULL;
}
int main(int argc, char **argv) {
/// static configuration for NR at the moment
if ((uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY)) == NULL) {
@@ -52,16 +155,6 @@ int main(int argc, char **argv) {
CONFIG_CLEARRTFLAG(CONFIG_NOEXITONHELP);
lock_memory_to_ram();
int sampling_rate=30.72e6;
int DFT=2048;
int TxAdvanceInDFTSize=12;
int antennas=1;
uint64_t freq=3619.200e6;
int rxGain=90;
int txGain=90;
int filterBand=40e6;
char * usrp_addrs="type=b200";
int h=open("/dev/cpu_dma_latency", 0666);
int lat=2; // micro second
assert(sizeof(lat)==write(h,&lat,sizeof(lat)));
@@ -114,178 +207,35 @@ int main(int argc, char **argv) {
.is_init = 0,
/*!brief Can be used by driver to hold internal structure*/
.priv = NULL,
/* Functions API, which are called by the application*/
/*! \brief Called to start the transceiver. Return 0 if OK, < 0 if error
@param device pointer to the device structure specific to the RF hardware target
*/
.trx_start_func=NULL,
/*! \brief Called to configure the device
@param device pointer to the device structure specific to the RF hardware target
*/
.trx_config_func=NULL,
/*! \brief Called to send a request message between RAU-RRU on control port
@param device pointer to the device structure specific to the RF hardware target
@param msg pointer to the message structure passed between RAU-RRU
@param msg_len length of the message
*/
.trx_ctlsend_func=NULL,
/*! \brief Called to receive a reply message between RAU-RRU on control port
@param device pointer to the device structure specific to the RF hardware target
@param msg pointer to the message structure passed between RAU-RRU
@param msg_len length of the message
*/
.trx_ctlrecv_func=NULL,
/*! \brief Called to send samples to the RF target
@param device pointer to the device structure specific to the RF hardware target
@param timestamp The timestamp at whicch the first sample MUST be sent
@param buff Buffer which holds the samples (2 dimensional)
@param nsamps number of samples to be sent
@param number of antennas
@param flags flags must be set to TRUE if timestamp parameter needs to be applied
*/
.trx_write_func=NULL,
/*! \brief Called to send samples to the RF target
@param device pointer to the device structure specific to the RF hardware target
@param timestamp The timestamp at whicch the first sample MUST be sent
@param buff Buffer which holds the samples (1 dimensional)
@param nsamps number of samples to be sent
@param antenna_id index of the antenna if the device has multiple anteannas
@param flags flags must be set to TRUE if timestamp parameter needs to be applied
*/
.trx_write_func2=NULL,
/*! \brief Receive samples from hardware.
* Read \ref nsamps samples from each channel to buffers. buff[0] is the array for
* the first channel. *ptimestamp is the time at which the first sample
* was received.
* \param device the hardware to use
* \param[out] ptimestamp the time at which the first sample was received.
* \param[out] buff An array of pointers to buffers for received samples. The buffers must be large enough to hold the number of samples \ref nsamps.
* \param nsamps Number of samples. One sample is 2 byte I + 2 byte Q => 4 byte.
* \param num_antennas number of antennas from which to receive samples
* \returns the number of sample read
*/
.trx_read_func=NULL,
/*! \brief Receive samples from hardware, this version provides a single antenna at a time and returns.
* Read \ref nsamps samples from each channel to buffers. buff[0] is the array for
* the first channel. *ptimestamp is the time at which the first sample
* was received.
* \param device the hardware to use
* \param[out] ptimestamp the time at which the first sample was received.
* \param[out] buff A pointers to a buffer for received samples. The buffer must be large enough to hold the number of samples \ref nsamps.
* \param nsamps Number of samples. One sample is 2 byte I + 2 byte Q => 4 byte.
* \param antenna_id Index of antenna from which samples were received
* \returns the number of sample read
*/
.trx_read_func2=NULL,
/*! \brief print the device statistics
* \param device the hardware to use
* \returns 0 on success
*/
/*! \brief print the device statistics
* \param device the hardware to use
* \returns 0 on success
*/
.trx_get_stats_func=NULL,
/*! \brief Reset device statistics
* \param device the hardware to use
* \returns 0 in success
*/
.trx_reset_stats_func=NULL,
/*! \brief Terminate operation of the transceiver -- free all associated resources
* \param device the hardware to use
*/
.trx_end_func=NULL,
/*! \brief Stop operation of the transceiver
*/
.trx_stop_func=NULL,
/* Functions API related to UE*/
/*! \brief Set RX feaquencies
* \param device the hardware to use
* \param openair0_cfg RF frontend parameters set by application
* \returns 0 in success
*/
.trx_set_freq_func=NULL,
/*! \brief Set gains
* \param device the hardware to use
* \param openair0_cfg RF frontend parameters set by application
* \returns 0 in success
*/
.trx_set_gains_func=NULL,
/*! \brief RRU Configuration callback
* \param idx RU index
* \param arg pointer to capabilities or configuration
*/
.configure_rru=NULL,
/*! \brief Pointer to generic RRU private information
*/
.thirdparty_priv=NULL,
.thirdparty_init=NULL,
/*! \brief Callback for Third-party RRU Cleanup routine
\param device the hardware configuration to use
*/
.thirdparty_cleanup=NULL,
/*! \brief Callback for Third-party start streaming routine
\param device the hardware configuration to use
*/
.thirdparty_startstreaming=NULL,
/*! \brief RRU Configuration callback
* \param idx RU index
* \param arg pointer to capabilities or configuration
*/
.trx_write_init=NULL,
/* \brief Get internal parameter
* \param id parameter to get
* \return a pointer to the parameter
*/
.get_internal_parameter=NULL,
};
openair0_device_load(&rfdevice, &openair0_cfg);
void ** samplesRx = (void **)malloc16(antennas* sizeof(c16_t *) );
void ** samplesTx = (void **)malloc16(antennas* sizeof(c16_t *) );
int fd=open(getenv("rftestInputFile"),O_RDONLY);
AssertFatal(fd>=0,"%s",strerror(errno));
printf("generate a sinus wave at middle RB");
load_dftslib();
c16_t **samplesRx = malloc16(antennas * sizeof(c16_t *));
for (int i = 0; i < antennas; i++) {
samplesRx[i] = (int32_t *)malloc16_clear( DFT*sizeof(c16_t) );
samplesTx[i] = (int32_t *)malloc16_clear( DFT*sizeof(c16_t) );
samplesRx[i] = malloc16_clear(DFT * sizeof(c16_t));
}
c16_t **samplesTx = malloc16(antennas * sizeof(c16_t *));
for (int i = 0; i < antennas; i++) {
samplesTx[i] = malloc16_clear(DFT * sizeof(c16_t));
}
CalibrationInitScope(samplesRx, &rfdevice);
openair0_timestamp_t timestamp=0;
/* scopedata shall be filled from a software FIFO and not directly from the samples */
threads_t params = (threads_t){&rfdevice, antennas, DFT, samplesRx, samplesTx};
pthread_mutex_init(&params.rxMutex, NULL);
pthread_mutex_init(&params.txMutex, NULL);
CalibrationInitScope(&params);
rfdevice.trx_start_func(&rfdevice);
while(!oai_exit) {
for (int i=0; i<antennas; i++) {
ssize_t len = read(fd, samplesTx[i], DFT*sizeof(c16_t));
if (len < 0) {
fprintf(stderr, "error during read(): errno %d, %s\n", errno, strerror(errno));
exit(1);
}
}
rfdevice.trx_read_func(&rfdevice, &timestamp, samplesRx, DFT, antennas);
rfdevice.trx_write_func(&rfdevice, timestamp + TxAdvanceInDFTSize * DFT, samplesTx, DFT, antennas, 0);
}
pthread_t w_thread;
// threadCreate(&w_thread, write_thread, &params, "write_thr", -1, OAI_PRIORITY_RT);
pthread_t r_thread;
threadCreate(&r_thread, read_thread, &params, "read_thr", 2, OAI_PRIORITY_RT);
(void)pthread_join(w_thread, NULL);
(void)pthread_join(r_thread, NULL);
return 0;
}

View File

@@ -397,6 +397,7 @@ static void nr_rrc_configure_default_SI(NR_UE_RRC_SI_INFO *SI_info,
NR_SchedulingInfo_t *schedulingInfo = si_SchedulingInfo->schedulingInfoList.list.array[i];
for (int j = 0; j < schedulingInfo->sib_MappingInfo.list.count; j++) {
NR_SIB_TypeInfo_t *sib_Type = schedulingInfo->sib_MappingInfo.list.array[j];
LOG_W(PHY,"SIB1 tells us we have SIB%ld\n", sib_Type->type+2);
SI_info->default_otherSI_map[i] |= 1 << sib_Type->type;
}
}

View File

@@ -47,6 +47,11 @@ if(OAI_VRTSIM)
add_subdirectory(vrtsim)
endif()
add_boolean_option(OAI_OC OFF "Activate OAI's USRP driver" OFF)
if(OAI_OC)
add_subdirectory(oc)
endif()
add_boolean_option(OAI_RF_EMULATOR ON "Activate OAI's RF emulator" OFF)
if(OAI_RF_EMULATOR)
add_subdirectory(emulator)

7
radio/oc/CMakeLists.txt Normal file
View File

@@ -0,0 +1,7 @@
add_library(oai_ocdevif MODULE oc_lib.cpp)
target_link_libraries(oai_ocdevif PRIVATE UTIL)
set_target_properties(oai_ocdevif PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
add_custom_command(TARGET oai_ocdevif POST_BUILD
COMMAND ${CMAKE_COMMAND} -E create_symlink liboai_ocdevif.so liboai_device.so
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})

42
radio/oc/README.md Normal file
View File

@@ -0,0 +1,42 @@
[[_TOC_]]
# Open Cells SDR device documentation
## General
OAI works with open cells SDR, compatible with USRP config files
Example files can be found in the `ci-scripts/conf_files/` directory with a
`usrp` in the name, for instance
[`gnb.sa.band78.106prb.usrpn310.ddsuu-2x2.conf`](../../ci-scripts/conf_files/gnb.sa.band78.106prb.usrpn310.ddsuu-2x2.conf).
## Configuration
to use OC SDR driver, add --device.name oai_ocdevif on the command line, or the equivalent value in the configuration file (any OAI executable)
You can specify to use external or interal clock or time source either by
adding the parameters in the `sdr_addrs` field or by using the fields
`clock_src` or `time_src`
Valid choices for clock and time source are `internal`, `external`, and `gpsdo`.
```bash
device = {
name="oai_ocdevif";
}
RUs = (
{
local_rf = "yes"
nb_tx = 2
nb_rx = 2
att_tx = 0
att_rx = 0;
bands = [78];
max_pdschReferenceSignalPower = -27;
max_rxgain = 75;
eNB_instances = [0];
}
);
```

649
radio/oc/oc_lib.cpp Normal file
View File

@@ -0,0 +1,649 @@
/*
* Licensed by open cells project
*/
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <iostream>
#include <complex>
#include <fstream>
#include <cmath>
#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"
#define DEVICE_WRITE_DEFAULT "/dev/xdma0_h2c_0"
#define DEVICE_READ_DEFAULT "/dev/xdma0_c2h_0"
#define OC_BUFFER 8192 * 16 // in bytes
#define SAMPLE_BUF (OC_BUFFER / sizeof(c16_t)) // in samples
#define NB_BLOCKS_PER_READ 16
#define READ_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 = 0xA5A5A5A5A5A5A5A5;
static const uint64_t magic_rx = 0xA5A50be3A5A5A5A5LL;
static const uint32_t magic_footer1 = 0xce11;
static const uint32_t magic_footer2 = 0x5A;
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)) header_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 {
header_t h;
uint32_t b[READ_BLOCK_NB_SAMPLES];
footer_t f;
} __attribute__((packed)) packet_t;
static inline void dumpHD(std::string ctx, header_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 tx_ts;
c16_t **tx_block;
size_t tx_block_sz;
bool first_tx;
bool rxMagicFound;
uint seqNum;
uint lastPpsOffset;
int nb_blocks_per_read;
int remain_samples;
packet_t *current_rx_packet;
} 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)
{
// threads_t params = *(threads_t *)arg;
return NULL;
}
void *read_thread(void *arg)
{
// threads_t params = *(threads_t *)arg;
return NULL;
}
#if 0
//TEST 4G 20MHz
int nsamps2 = (nsamps*4) / 8 ;
simde__m256i buff_tx[nsamps2];
simde__m256i *out=buff_tx;
int *in=(int *)buff[0];
// bring RX data into 12 LSBs for softmodem RX
for (uint j = 0; j < nsamps/2; j++) {
simde__m256i tmp=simde_mm256_set_epi32(in[1],in[1],in[1],in[1], in[0],in[0],in[0],in[0]);
in+=2;
*out++ = simde_mm256_slli_epi16(tmp, 6);
}
#endif
static int32_t signalEnergy(int32_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 = (c16_t *)input;
uint16_t lefover_count = length - ((length >> 2) << 2);
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 void write_block(oc_state_t *s)
{
// uint64_t st = rdtsc_oai();
uint8_t buf[SAMPLE_BUF * sizeof(c16_t) + sizeof(header_t)];
*(header_t *)buf = (header_t){
.control = magic,
/*
.sdrStatus = 0,
.timestamp = s->tx_ts,
.trailer = SAMPLE_BUF,
*/
};
memcpy(buf + sizeof(header_t), s->tx_block[0], SAMPLE_BUF * sizeof(c16_t));
size_t wrote = write(s->fd_write, buf, sizeof(buf));
// uint64_t end = rdtsc_oai();
// if (end-st > 100*5000)
// LOG_E(HW,"one write to xdma took %ld µs, ts:%lu\n", (end-st)/5000, s->tx_ts);
/*
static uint64_t old;
if (old-st > 5000*500)
LOG_E(HW,"we come back to writer after: %ld µs\n", (old-st)/5000);
old=st;
*/
wrote = (wrote - sizeof(header_t)) / sizeof(c16_t);
if (wrote != SAMPLE_BUF)
LOG_E(HW, "write to SDR failed, request: %lu, wrote %ld\n", SAMPLE_BUF, wrote / sizeof(c16_t));
if (wrote < 0)
LOG_E(HW, "write to %s failed, errno %d:%s\n", s->filename_write, errno, strerror(errno));
s->tx_ts += wrote;
s->tx_block_sz = 0;
s->tx_count++;
LOG_D(HW, "wrote at ts: %lu, energy: %u\n", s->tx_ts, signalEnergy((int32_t *)s->tx_block[0], SAMPLE_BUF));
}
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;
c16_t *in = (c16_t *)buff[0];
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_D(HW, "gap of %ld\n", gap);
while (gap) {
int tmp = std::min(gap, (int64_t)SAMPLE_BUF - (int64_t)s->tx_block_sz);
memset(s->tx_block[0] + s->tx_block_sz, 0, tmp * sizeof(*in));
gap -= tmp;
s->tx_block_sz += tmp;
if (s->tx_block_sz == SAMPLE_BUF)
write_block(s);
}
int wr_sz = nsamps;
while (wr_sz) {
int tmp = std::min((long unsigned int)wr_sz, SAMPLE_BUF - s->tx_block_sz);
simde__m256i *sig = (simde__m256i *)(s->tx_block[0] + s->tx_block_sz);
if ((intptr_t)sig % 32)
abort();
for (int j = 0; j < tmp; j += 8)
*sig++ = simde_mm256_slli_epi16(simde_mm256_loadu_si256((simde__m256i *)(in + j)), 4);
// memcpy(s->tx_block[0] + s->tx_block_sz, in, tmp * sizeof(*in));
wr_sz -= tmp;
s->tx_block_sz += tmp;
if (s->tx_block_sz == SAMPLE_BUF)
write_block(s);
}
s->tx_ts = timestamp + nsamps;
return nsamps;
}
static void initial_block_align (oc_state_t *s) {
printf("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)) {
printf("Error reading %ld bytes: %ld\n",sizeof(b), ret);
usleep(10000);
return;
}
int i;
header_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)) {
printf("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 = (header_t *)(b + i);
dumpHD("first header:", *rx);
break;
}
if (i == (READ_BLOCK_NB_SAMPLES + PKT_HEADER_NB_SAMPLES)) {
printf("%%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_count = 1;
//}
}
static bool get_blocks(oc_state_t *s) {
static struct timespec last_second={}, origin={};
static struct timespec now={};
static uint64_t tot_samples= 0;
int readSz= sizeof(*s->current_rx_packet)* s->nb_blocks_per_read;
packet_t* p= s->current_rx_packet;
ssize_t ret = read(s->fd_read, p, readSz);
if (ret != readSz || p[0].h.control != magic_rx) {
printf((char *)"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) {
printf("avg rate:%f\n", (float)tot_samples/(now.tv_sec*1000000 - origin.tv_sec*1000000 + now.tv_nsec/1000.0 - origin.tv_nsec/1000.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)
printf("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 -> 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].f.control1 != magic_footer1 || p[i].f.control2 != magic_footer2)
printf("footer error\n");
if (p[i].f.timerOverflow)
printf("timerOverflow\n");
if (p[i].f.atomicPacket) {
printf("Not atomic\n");
}
static int last_filler = 0;
if (last_filler != p[i].f.filler)
printf("filler changed to %x\n", p[i].f.filler);
last_filler = p[i].f.filler;
if (s->seqNum % 65536 != p[i].h.packetSeqNum) {
printf("expected 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->remain_samples= sizeof(s->current_rx_packet->b) * s->nb_blocks_per_read / sizeof(*s->current_rx_packet->b);
//printf("got %d samples\n", s->remain_samples);
return true;
}
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;
if (s->rx_count == -1)
initial_block_align(s);
if (s->rx_count == -1)
return 0;
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) {
packet_t* p=s->current_rx_packet;
int nb_samples_per_packet=sizeof(p->b)/sizeof(*p->b);
int nb_samples=nb_samples_per_packet*s->nb_blocks_per_read;
int consumed_samples=nb_samples-s->remain_samples;
int bloc=consumed_samples/nb_samples_per_packet;
int nb_consumed_samples_in_block= consumed_samples%nb_samples_per_packet;
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,p[bloc].b+nb_consumed_samples_in_block,toCopy*sizeof(*p->b));
s->remain_samples-=toCopy;
remain_to_get -=toCopy;
*ptimestamp = s->rx_timestamp;
}
if( remain_to_get > 0 ) {
static uint64_t a;
uint64_t n=rdtsc_oai();
if (n-a > 4200*100)
printf("blocked for %ld\n", (n-a)/4200);
a=n;
if (!get_blocks(s)) {
printf("getblocks returned bad\n");
return 0;
}
uint64_t b=rdtsc_oai();
if ( b-a > 4200*400)
printf("one call to xdma: %ld µs\n", (b-a)/4200);
a=b;
}
}
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;
printf("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;
int nb_tx = device->openair0_cfg->tx_num_channels;
s->tx_block = (c16_t **)malloc(nb_tx * sizeof(*s->tx_block));
for (int i = 0; i < nb_tx; i++)
s->tx_block[i] = (c16_t *)malloc16(OC_BUFFER);
s->current_rx_packet=(packet_t*)malloc16(s->nb_blocks_per_read*sizeof(* s->current_rx_packet));
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);
}
/*
threads_t params = (threads_t){&rfdevice, antennas, DFT};
pthread_t w_thread;
threadCreate(&w_thread, write_thread, &params, "write_thr", -1, OAI_PRIORITY_RT);
pthread_t r_thread;
threadCreate(&r_thread, read_thread, &params, "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;
int nb_tx = device->openair0_cfg->tx_num_channels;
if (s && nb_tx > 0) {
for (int i = 0; i < nb_tx; i++)
free(s->tx_block[i]);
free(s->tx_block);
}
}
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;
}
}