mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 04:30:28 +00:00
first trial
This commit is contained in:
@@ -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}
|
||||
|
||||
@@ -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"
|
||||
;;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -21,12 +22,9 @@ typedef struct {
|
||||
int16_t r;
|
||||
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;
|
||||
#define SquaredNorm(VaR) ((VaR).r * (VaR).r + (VaR).i * (VaR).i)
|
||||
|
||||
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,23 +265,50 @@ 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 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");
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((unused))
|
||||
static void timeResponse (OAIgraph_t *graph, calibData_t *context)
|
||||
static void timeResponse(OAIgraph_t *graph, threads_t *context)
|
||||
{
|
||||
#if 0
|
||||
#if 0
|
||||
const int len=2*phy_vars_gnb->frame_parms.ofdm_symbol_size;
|
||||
float *values, *time;
|
||||
oai_xygraph_getbuff(graph, &time, &values, len, 0);
|
||||
@@ -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
|
||||
#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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
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;
|
||||
curY += h + 20;
|
||||
|
||||
// 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);
|
||||
*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;
|
||||
curY += h;
|
||||
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
|
||||
pthread_attr_setstacksize(&atr, 32 * 1024 * 1024);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,8 +35,111 @@ 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(¶ms.txMutex);
|
||||
printf("write got lock\n");
|
||||
new_tx = tx_timestamp & ~31;
|
||||
pthread_mutex_unlock(¶ms.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(¶ms.rxMutex);
|
||||
int ret = params.rfdevice->trx_read_func(params.rfdevice, &rx_timestamp, (void **)samplesRx, params.dft_sz, params.antennas);
|
||||
pthread_mutex_unlock(¶ms.rxMutex);
|
||||
if (ret != params.dft_sz)
|
||||
printf("read of :%d\n", ret);
|
||||
count++;
|
||||
pthread_mutex_lock(¶ms.txMutex);
|
||||
tx_timestamp = rx_timestamp;
|
||||
pthread_mutex_unlock(¶ms.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
|
||||
/// static configuration for NR at the moment
|
||||
if ((uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY)) == NULL) {
|
||||
exit_fun("[SOFTMODEM] Error, configuration module init failed\n");
|
||||
}
|
||||
@@ -44,7 +147,7 @@ int main(int argc, char **argv) {
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
logInit();
|
||||
paramdef_t cmdline_params[] = CMDLINE_PARAMS_DESC_GNB ;
|
||||
paramdef_t cmdline_params[] = CMDLINE_PARAMS_DESC_GNB;
|
||||
|
||||
CONFIG_SETRTFLAG(CONFIG_NOEXITONHELP);
|
||||
get_common_options(uniqCfg);
|
||||
@@ -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)));
|
||||
@@ -97,195 +190,52 @@ int main(int argc, char **argv) {
|
||||
.recplay_conf = NULL,
|
||||
};
|
||||
//-----------------------
|
||||
openair0_device_t rfdevice= {
|
||||
openair0_device_t rfdevice = {
|
||||
/*!brief Type of this device */
|
||||
.type=NONE_DEV,
|
||||
.type = NONE_DEV,
|
||||
/*!brief Transport protocol type that the device supports (in case I/Q samples need to be transported) */
|
||||
.transp_type=NONE_TP,
|
||||
.transp_type = NONE_TP,
|
||||
/*!brief Type of the device's host (RAU/RRU) */
|
||||
.host_type=MIN_HOST_TYPE,
|
||||
.host_type = MIN_HOST_TYPE,
|
||||
/* !brief RF frontend parameters set by application */
|
||||
.openair0_cfg=NULL, //set by device_init
|
||||
.openair0_cfg = NULL, // set by device_init
|
||||
/* !brief ETH params set by application */
|
||||
.eth_params=NULL,
|
||||
.eth_params = NULL,
|
||||
//! record player data, definition in record_player.h
|
||||
.recplay_state=NULL,
|
||||
.recplay_state = NULL,
|
||||
/* !brief Indicates if device already initialized */
|
||||
.is_init=0,
|
||||
.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,
|
||||
.priv = NULL,
|
||||
};
|
||||
|
||||
openair0_device_load(&rfdevice,&openair0_cfg);
|
||||
openair0_device_load(&rfdevice, &openair0_cfg);
|
||||
|
||||
void ** samplesRx = (void **)malloc16(antennas* sizeof(c16_t *) );
|
||||
void ** samplesTx = (void **)malloc16(antennas* sizeof(c16_t *) );
|
||||
printf("generate a sinus wave at middle RB");
|
||||
load_dftslib();
|
||||
|
||||
int fd=open(getenv("rftestInputFile"),O_RDONLY);
|
||||
AssertFatal(fd>=0,"%s",strerror(errno));
|
||||
|
||||
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) );
|
||||
c16_t **samplesRx = malloc16(antennas * sizeof(c16_t *));
|
||||
for (int i = 0; i < antennas; i++) {
|
||||
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(¶ms.rxMutex, NULL);
|
||||
pthread_mutex_init(¶ms.txMutex, NULL);
|
||||
CalibrationInitScope(¶ms);
|
||||
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, ×tamp, samplesRx, DFT, antennas);
|
||||
rfdevice.trx_write_func(&rfdevice, timestamp + TxAdvanceInDFTSize * DFT, samplesTx, DFT, antennas, 0);
|
||||
}
|
||||
pthread_t w_thread;
|
||||
// threadCreate(&w_thread, write_thread, ¶ms, "write_thr", -1, OAI_PRIORITY_RT);
|
||||
pthread_t r_thread;
|
||||
threadCreate(&r_thread, read_thread, ¶ms, "read_thr", 2, OAI_PRIORITY_RT);
|
||||
(void)pthread_join(w_thread, NULL);
|
||||
(void)pthread_join(r_thread, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
7
radio/oc/CMakeLists.txt
Normal 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
42
radio/oc/README.md
Normal 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
649
radio/oc/oc_lib.cpp
Normal 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, ¶ms, "write_thr", -1, OAI_PRIORITY_RT);
|
||||
pthread_t r_thread;
|
||||
threadCreate(&r_thread, read_thread, ¶ms, "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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user