mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 12:40:28 +00:00
Compare commits
26 Commits
nfapi-Dela
...
test-oc1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
519d5b661a | ||
|
|
21e407d2c9 | ||
|
|
d87d388546 | ||
|
|
597a569c91 | ||
|
|
00f3d711aa | ||
|
|
b251c36518 | ||
|
|
407f19f3b3 | ||
|
|
85a6af5dd5 | ||
|
|
5a3a2ce5b3 | ||
|
|
b454c08103 | ||
|
|
30b3c4f2d8 | ||
|
|
e3a2e557c7 | ||
|
|
0acf8f56e3 | ||
|
|
ae5b22caad | ||
|
|
34593f34ec | ||
|
|
d159864513 | ||
|
|
183aafb42a | ||
|
|
1b567772c4 | ||
|
|
8499ea9165 | ||
|
|
8fb2a7aaa3 | ||
|
|
bfec6910f2 | ||
|
|
fea79b4163 | ||
|
|
de1e868eff | ||
|
|
a358bdb601 | ||
|
|
a13f34d3d6 | ||
|
|
215941da12 |
@@ -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"
|
||||
;;
|
||||
|
||||
@@ -350,4 +350,7 @@ void lock_memory_to_ram(void)
|
||||
int rc = mlockall(MCL_CURRENT | MCL_FUTURE);
|
||||
if (rc != 0)
|
||||
LOG_W(UTIL, "mlockall() failed: %d, %s\n", errno, strerror(errno));
|
||||
int h=open("/dev/cpu_dma_latency", 0666);
|
||||
int lat=2; // micro second
|
||||
assert(sizeof(lat)==write(h,&lat,sizeof(lat)));
|
||||
}
|
||||
|
||||
@@ -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 = pullNotifiedFIFO(&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;
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "common/utils/system.h"
|
||||
#include "common/utils/utils.h"
|
||||
#include "common/utils/LOG/log.h"
|
||||
#include <common/utils/LOG/log.h>
|
||||
#include <common/utils/system.h>
|
||||
#include <openair1/PHY/TOOLS/tools_defs.h>
|
||||
#include <openair1/PHY/impl_defs_top.h>
|
||||
#include "executables/softmodem-common.h"
|
||||
#include "executables/nr-softmodem-common.h"
|
||||
#include <forms.h>
|
||||
@@ -21,12 +18,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 +36,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 +102,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,19 +167,16 @@ static void oai_xygraph_getbuff(OAIgraph_t *graph, float **x, float **y, int len
|
||||
*y=old_y;
|
||||
}
|
||||
|
||||
static void oai_xygraph(OAIgraph_t *graph, float *x, float *y, int len, int layer, bool NoAutoScale) {
|
||||
static void oai_xygraph(OAIgraph_t *graph, float *x, float *y, int len, int layer, int NoAutoScale) {
|
||||
fl_redraw_object(graph->graph);
|
||||
|
||||
if ( NoAutoScale && graph->iteration%NoAutoScale == 0) {
|
||||
float maxX=0, maxY=0, minX=0, minY=0;
|
||||
|
||||
for (int k=0; k<len; k++) {
|
||||
maxX=max(maxX,x[k]);
|
||||
minX=min(minX,x[k]);
|
||||
maxY=max(maxY,y[k]);
|
||||
minY=min(minY,y[k]);
|
||||
}
|
||||
|
||||
setRange(graph, minX-5, maxX+5, minY-5, maxY+5);
|
||||
}
|
||||
|
||||
@@ -254,29 +252,62 @@ static void genericPowerPerAntena(OAIgraph_t *graph, const int nb_ant, const sc
|
||||
for (int i=0; i<len; i++) {
|
||||
values[i] = SquaredNorm(data[ant][i]);
|
||||
}
|
||||
|
||||
oai_xygraph(graph,time,values, len, ant, 10);
|
||||
oai_xygraph(graph,time,values, len, ant, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, scope->context->dft_sz, 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, 100);
|
||||
}
|
||||
}
|
||||
|
||||
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 = 750; //min(len, 1024);
|
||||
//int beg=5500; 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);
|
||||
memset(values,0,sizeof(*values)*detailLen);
|
||||
float *v=values;
|
||||
//for (int j=2048*3-95; j < len-detailLen; j+=2048) {
|
||||
int j=0;
|
||||
for (int i = 0; i < detailLen; i++)
|
||||
*v++ = ri ? scope->timeDomain[j+i].i : scope->timeDomain[j+i].r;
|
||||
//}
|
||||
oai_xygraph(graph,time, values, detailLen, ri, 10);
|
||||
}
|
||||
}
|
||||
|
||||
__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,93 +321,109 @@ 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;
|
||||
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, len, 0);
|
||||
|
||||
for (int ue=0; ue<nb_UEs; ue++) {
|
||||
scopeSample_t *pusch_comp = (scopeSample_t *) phy_vars_gnb->pusch_vars[ue]->rxdataF_comp[0];
|
||||
float *I, *Q;
|
||||
oai_xygraph_getbuff(graph, &I, &Q, sz, ue);
|
||||
|
||||
if (pusch_comp) {
|
||||
for (int k=0; k<sz; k++ ) {
|
||||
I[k] = pusch_comp[k].r;
|
||||
Q[k] = pusch_comp[k].i;
|
||||
}
|
||||
|
||||
oai_xygraph(graph,I,Q,sz,ue,10);
|
||||
}
|
||||
for (int k = 0; k < len; k++) {
|
||||
I[k] = scope->freqDomain[k].r;
|
||||
Q[k] = scope->freqDomain[k].i;
|
||||
}
|
||||
#endif
|
||||
|
||||
oai_xygraph(graph, I, Q, scope->context->dft_sz, 0, 100);
|
||||
}
|
||||
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, scope->context->dft_sz, 0, 100);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
// Define form
|
||||
fdui->phy_scope = fl_bgn_form( FL_NO_BOX, 800, 800 );
|
||||
fdui->phy_scope = fl_bgn_form( FL_NO_BOX, 1200, 800 );
|
||||
// This the whole UI box
|
||||
obj = fl_add_box( FL_BORDER_BOX, 0, 0, 800, 800, "" );
|
||||
obj = fl_add_box( FL_BORDER_BOX, 0, 0, 1200, 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 );
|
||||
|
||||
// Time-domain channel response
|
||||
//fdui->graph[1] = calibrationCommonGraph( timeResponse, FL_NORMAL_XYPLOT, 410, curY, 400, 100, "SRS Frequency Response (samples, abs)", FL_RED );
|
||||
*graph++ = calibrationCommonGraph(zoomIn, FL_NORMAL_XYPLOT, 0, curY, 1200, 100, "Received Signal in time zoom", FL_RED);
|
||||
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 );
|
||||
curY += h + 20;
|
||||
// frequency spectrum
|
||||
*graph++ = calibrationCommonGraph(spectrum, FL_NORMAL_XYPLOT, 0, curY, 1200, 100, "DFT output", FL_YELLOW);
|
||||
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, 1200, 100, "received signal in time", FL_RED);
|
||||
fl_get_object_bbox(fdui->graph[2].graph, &x, &y, &w, &h);
|
||||
curY+=h+20;
|
||||
// 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 );
|
||||
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;
|
||||
*graph++ = calibrationCommonGraph(signalIQ, FL_POINTS_XYPLOT, 0, curY, 300, 300, "I/Q of frequency domain", FL_YELLOW);
|
||||
*graph++ = calibrationCommonGraph(signalIQtx, FL_POINTS_XYPLOT, 500, curY, 300, 300, "Tx generated I/Q of frequency domain", FL_YELLOW);
|
||||
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);
|
||||
__attribute__((aligned(32))) c16_t signal[len];
|
||||
for (int i = 0; i < len; i++)
|
||||
signal[i] = (c16_t){form->timeDomain[i].r, form->timeDomain[i].i};
|
||||
dft(get_dft(len), (int16_t *)signal, (int16_t *)form->freqDomain, 1);
|
||||
for (int i = 0; i < len; i++)
|
||||
signal[i] = (c16_t){form->timeDomainTx[i].r, form->timeDomainTx[i].i};
|
||||
dft(get_dft(len), (int16_t *)signal, (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++;
|
||||
}
|
||||
|
||||
//fl_check_forms();
|
||||
}
|
||||
|
||||
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 +431,17 @@ 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);
|
||||
// calibData_t *tmp = malloc_or_fail(sizeof(*tmp));
|
||||
// tmp->samplesRx=samplesRx;
|
||||
// tmp->rfdevice=rfdevice;
|
||||
threadCreate(&forms_thread, scopeThread, (void *)p, "scope", 7, OAI_PRIORITY_RT_LOW);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
#ifndef CALIB_SCOPE_H
|
||||
#define CALIB_SCOPE_H
|
||||
|
||||
void CalibrationInitScope(void **samplesRx, openair0_device_t *rfdevice);
|
||||
enum e_pattern_scheme { e_SINUS, e_CHIRP, e_QPSK, e_QAM_16, e_QAM_64, e_QAM_256, e_RAMP, e_SIGNATURE, e_MAX_PATT_SCHEME };
|
||||
|
||||
typedef struct {
|
||||
uint tx;
|
||||
uint rx;
|
||||
uint freq;
|
||||
enum e_pattern_scheme tx_pattern;
|
||||
uint amplitude;
|
||||
uint sinus_freq;
|
||||
uint dft;
|
||||
char * file;
|
||||
char * dump_iq;
|
||||
} config_t;
|
||||
|
||||
typedef struct {
|
||||
config_t *c;
|
||||
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
|
||||
|
||||
@@ -2,14 +2,18 @@
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#define __USE_GNU
|
||||
#include <stdint.h>
|
||||
#include "openair1/PHY/defs_common.h"
|
||||
#include <sys/stat.h>
|
||||
#include <openair1/PHY/impl_defs_top.h>
|
||||
#include <radio/COMMON/common_lib.h>
|
||||
#include <executables/softmodem-common.h>
|
||||
#include <openair1/PHY/TOOLS/calibration_scope.h>
|
||||
#include "nfapi/oai_integration/vendor_ext.h"
|
||||
#include "common/config/config_userapi.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <pthread.h>
|
||||
|
||||
int oai_exit=false;
|
||||
unsigned int mmapped_dma=0;
|
||||
@@ -24,6 +28,7 @@ uint32_t target_dl_mcs = 9;
|
||||
uint64_t dlsch_slot_bitmap = (1<<1);
|
||||
uint64_t ulsch_slot_bitmap = (1<<8);
|
||||
uint32_t target_ul_bw = 50;
|
||||
|
||||
uint32_t target_dl_bw = 50;
|
||||
uint32_t target_dl_Nl;
|
||||
uint32_t target_ul_Nl;
|
||||
@@ -35,8 +40,479 @@ 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;
|
||||
openair0_timestamp_t rx_timestamp = 0;
|
||||
openair0_timestamp_t tx_timestamp = 0;
|
||||
openair0_timestamp_t last_hole = 0;
|
||||
const int hole_size=10;
|
||||
pthread_cond_t tx_trig;
|
||||
|
||||
static uint32_t rng_state = 2463534242u; // non-zero seed
|
||||
static int chip = 16;
|
||||
|
||||
static inline uint32_t xorshift32(void)
|
||||
{
|
||||
uint32_t x = rng_state;
|
||||
x ^= x << 13;
|
||||
x ^= x >> 17;
|
||||
x ^= x << 5;
|
||||
rng_state = x;
|
||||
return x;
|
||||
}
|
||||
|
||||
/* ------------------ Bit pool ------------------ */
|
||||
static uint32_t bit_pool = 0;
|
||||
static int bits_left = 0;
|
||||
|
||||
/* Ensure at least n bits available */
|
||||
static inline void refill_bits(int n)
|
||||
{
|
||||
if (bits_left < n) {
|
||||
bit_pool = xorshift32();
|
||||
bits_left = 32;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------ Uniform generators ------------------ */
|
||||
|
||||
/* 2-bit uniform [0..3] */
|
||||
static inline uint8_t rand_u2(void)
|
||||
{
|
||||
refill_bits(2);
|
||||
uint8_t val = bit_pool & 0x3;
|
||||
bit_pool >>= 2;
|
||||
bits_left -= 2;
|
||||
return val;
|
||||
}
|
||||
|
||||
/* 4-bit uniform [0..15] */
|
||||
static inline uint8_t rand_u4(void)
|
||||
{
|
||||
refill_bits(4);
|
||||
uint8_t val = bit_pool & 0xF;
|
||||
bit_pool >>= 4;
|
||||
bits_left -= 4;
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline uint8_t rand_u6(void)
|
||||
{
|
||||
refill_bits(6);
|
||||
uint8_t val = bit_pool & 0x3F; // 6 bits
|
||||
bit_pool >>= 6;
|
||||
bits_left -= 6;
|
||||
return val;
|
||||
}
|
||||
|
||||
/* 8-bit uniform [0..255] */
|
||||
static inline uint8_t rand_u8(void)
|
||||
{
|
||||
refill_bits(8);
|
||||
uint8_t val = bit_pool & 0xFF;
|
||||
bit_pool >>= 8;
|
||||
bits_left -= 8;
|
||||
return val;
|
||||
}
|
||||
|
||||
static c16_t * read_file (char * filename, int *sz) {
|
||||
int fd=open(filename,O_RDONLY);
|
||||
if (fd < 0)
|
||||
abort();
|
||||
struct stat st;
|
||||
stat(filename, &st);
|
||||
char * f=mmap(NULL,st.st_size ,PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
char* p=f;
|
||||
int count=0;
|
||||
char tmp[64];
|
||||
char* endfile=f+st.st_size;
|
||||
do {
|
||||
while ((*p < '0' || *p > '9') && *p != '-' && p<endfile)
|
||||
p++;
|
||||
if (p != endfile) {
|
||||
count ++;
|
||||
// begining of number
|
||||
char * end=p;
|
||||
while (((*end >= '0' && *end <= '9') || *end == '-') && end< endfile)
|
||||
end++;
|
||||
p=end;
|
||||
}
|
||||
} while (p && p<endfile);
|
||||
int16_t *vect=malloc(count * sizeof(*vect));
|
||||
int16_t* ptr=vect;
|
||||
p=f;
|
||||
do {
|
||||
while ((*p < '0' || *p > '9') && *p != '-' && p<endfile)
|
||||
p++;
|
||||
if (p != endfile) {
|
||||
// begining of number
|
||||
char * end=p;
|
||||
while (((*end >= '0' && *end <= '9') || *end == '-') && end< endfile)
|
||||
end++;
|
||||
memcpy(tmp,p,end-p);
|
||||
tmp[end-p]=0;
|
||||
*ptr++=atoi(tmp);
|
||||
p=end;
|
||||
}
|
||||
} while (p && p<endfile);
|
||||
*sz=count/2;
|
||||
return (c16_t*)vect;
|
||||
}
|
||||
|
||||
void *write_thread(void *arg)
|
||||
{
|
||||
threads_t *params = (threads_t *)arg;
|
||||
c16_t **samplesTx = params->samplesTx;
|
||||
uint64_t ts = 0;
|
||||
const float WAVE_AMP = params->c->amplitude;
|
||||
const float sin_freq = params->c->sinus_freq;
|
||||
c16_t * file_input=NULL;
|
||||
int num_samples=0;
|
||||
if ( params->c->file)
|
||||
file_input=read_file(params->c->file, &num_samples);
|
||||
else {
|
||||
switch (params->c->tx_pattern) {
|
||||
case e_CHIRP: {
|
||||
double Fs = 122880.0;
|
||||
double f0 = -30 * 1000.0; // start freq
|
||||
double f1 = 30 * 1000.0; // end freq
|
||||
double T = params->dft_sz / Fs;
|
||||
double k = (f1 - f0) / T; // Hz/s sweep rate
|
||||
|
||||
for (int i = 0; i < params->dft_sz; i++) {
|
||||
double t = ts / Fs;
|
||||
double phase = 2 * M_PI * (f0 * t + 0.5 * k * t * t);
|
||||
samplesTx[0][i].r = WAVE_AMP * cos(phase);
|
||||
samplesTx[0][i].i = WAVE_AMP * sin(phase);
|
||||
ts++;
|
||||
}
|
||||
} break;
|
||||
case e_QPSK: {
|
||||
__attribute__((aligned(32))) c16_t freq_signal[params->dft_sz];
|
||||
int val = 0;
|
||||
const float required_BW = 6000.0e3;
|
||||
const float dft_binsize = ((float)122.88e6 / (float)params->dft_sz);
|
||||
const float sqrt2 = 0.70711;
|
||||
int amp = WAVE_AMP * sqrt2 * sqrt2;
|
||||
int center = dft_binsize / 2;
|
||||
int bin_masking = (int)(((float)122.88e6 - required_BW) / (float)dft_binsize);
|
||||
for (int carrier = 0; carrier < params->dft_sz; carrier++) {
|
||||
if (carrier >= bin_masking && carrier <= (center + bin_masking)) {
|
||||
int i = rand_u2(); // rand() % 4;
|
||||
val ^= 1 << i;
|
||||
freq_signal[carrier] = (c16_t){(1 - 2 * (val & 1)) * amp, (1 - 2 * ((val >> 1) & 1)) * amp};
|
||||
} else {
|
||||
freq_signal[carrier].r = 0;
|
||||
freq_signal[carrier].i = 0;
|
||||
}
|
||||
ts++;
|
||||
}
|
||||
dft(get_dft(params->dft_sz), (int16_t *)freq_signal, (int16_t *)samplesTx[0], 1);
|
||||
} break;
|
||||
case e_QAM_16: {
|
||||
__attribute__((aligned(32))) c16_t freq_signal[params->dft_sz];
|
||||
const float required_BW = 15000.0e3;
|
||||
const float dft_binsize = ((float)122.88e6 / (float)params->dft_sz);
|
||||
const float sqrt2 = 0.70711;
|
||||
const float sqrt10 = 0.31623;
|
||||
int center = dft_binsize / 2;
|
||||
int amp = WAVE_AMP * sqrt10 * sqrt2;
|
||||
int bin_masking = (int)(((float)122.88e6 - required_BW) / (float)dft_binsize);
|
||||
for (int carrier = 0; carrier < params->dft_sz; carrier++) {
|
||||
if (carrier >= bin_masking && carrier <= (center + bin_masking)) {
|
||||
int i = rand_u4(); // rand() % 16;
|
||||
freq_signal[carrier].r = (1 - 2 * (i & 1)) * (2 - (1 - 2 * ((i >> 2) & 1))) * amp;
|
||||
freq_signal[carrier].i = (1 - 2 * ((i >> 1) & 1)) * (2 - (1 - 2 * ((i >> 3) & 1))) * amp;
|
||||
} else {
|
||||
freq_signal[carrier].r = 0;
|
||||
freq_signal[carrier].i = 0;
|
||||
}
|
||||
ts++;
|
||||
}
|
||||
dft(get_dft(params->dft_sz), (int16_t *)freq_signal, (int16_t *)samplesTx[0], 1);
|
||||
} break;
|
||||
case e_QAM_64: {
|
||||
__attribute__((aligned(32))) c16_t freq_signal[params->dft_sz];
|
||||
const float required_BW = 12000.0e3;
|
||||
const float dft_binsize = ((float)122.88e6 / (float)params->dft_sz);
|
||||
const float sqrt2 = 0.70711;
|
||||
const float sqrt42 = 0.154303;
|
||||
int center = dft_binsize / 2;
|
||||
int amp = WAVE_AMP * sqrt42 * sqrt2;
|
||||
int bin_masking = (int)(((float)122.88e6 - required_BW) / (float)dft_binsize);
|
||||
for (int carrier = 0; carrier < params->dft_sz; carrier++) {
|
||||
if (carrier >= bin_masking && carrier <= (center + bin_masking)) {
|
||||
int i = rand_u6(); // rand() % 64;
|
||||
freq_signal[carrier] =
|
||||
(c16_t){((1 - 2 * (i & 1)) * (4 - (1 - 2 * ((i >> 2) & 1)) * (2 - (1 - 2 * ((i >> 4) & 1))))) * amp,
|
||||
((1 - 2 * ((i >> 1) & 1)) * (4 - (1 - 2 * ((i >> 3) & 1)) * (2 - (1 - 2 * ((i >> 5) & 1))))) * amp};
|
||||
} else {
|
||||
freq_signal[carrier].r = 0;
|
||||
freq_signal[carrier].i = 0;
|
||||
}
|
||||
ts++;
|
||||
}
|
||||
dft(get_dft(params->dft_sz), (int16_t *)freq_signal, (int16_t *)samplesTx[0], 1);
|
||||
} break;
|
||||
case e_QAM_256: {
|
||||
__attribute__((aligned(32))) c16_t freq_signal[params->dft_sz] = {};
|
||||
const float required_BW = 6000.0e3;
|
||||
const float dft_binsize = ((float)122.88e6 / (float)params->dft_sz);
|
||||
int center = dft_binsize / 2;
|
||||
float sqrt42 = 0.15430;
|
||||
const float sqrt2 = 0.70711;
|
||||
int amp = WAVE_AMP * sqrt42 * sqrt2;
|
||||
int bin_masking = (int)(((float)122.88e6 - required_BW) / (float)dft_binsize);
|
||||
for (int carrier = 0; carrier < params->dft_sz; carrier++) {
|
||||
if (carrier >= bin_masking && carrier <= (center + bin_masking)) {
|
||||
int i = rand_u8(); // rand() % 256;
|
||||
freq_signal[carrier] = (c16_t){
|
||||
((1 - 2 * (i & 1)) * (8 - (1 - 2 * ((i >> 2) & 1)) * (4 - (1 - 2 * ((i >> 4) & 1)) * (2 - (1 - 2 * ((i >> 6) & 1))))))
|
||||
* amp,
|
||||
(1 - 2 * ((i >> 1) & 1))
|
||||
* (8 - (1 - 2 * ((i >> 3) & 1)) * (4 - (1 - 2 * ((i >> 5) & 1)) * (2 - (1 - 2 * ((i >> 7) & 1))))) * amp};
|
||||
} else {
|
||||
freq_signal[carrier].r = 0;
|
||||
freq_signal[carrier].i = 0;
|
||||
}
|
||||
ts++;
|
||||
}
|
||||
dft(get_dft(params->dft_sz), (int16_t *)freq_signal, (int16_t *)samplesTx[0], 1);
|
||||
} break;
|
||||
case e_SINUS:
|
||||
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 = WAVE_AMP * cos((ts * M_PI * 2 * sin_freq) / 122880000);
|
||||
samplesTx[0][i].i = WAVE_AMP * sin((ts * M_PI * 2 * sin_freq) / 122880000); // 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 * i / (params->dft_sz-1)));
|
||||
// samplesTx[0][i].i = (samplesTx[0][i].i) * (0.54 - 0.46 * cos(2 * M_PI * i / (params->dft_sz-1)));
|
||||
// samplesTx[0][i].r = (samplesTx[0][i].r) * (0.54 - 0.46 * cos(2 * M_PI * i / (params->dft_sz-1)));
|
||||
ts++;
|
||||
}
|
||||
break;
|
||||
case e_RAMP:
|
||||
num_samples = 2048;
|
||||
const int16_t RAMP_STEP_SIZE = 1;
|
||||
file_input=malloc(num_samples * sizeof(*file_input));
|
||||
for (int i = 0; i < num_samples; i++) {
|
||||
file_input[i] = (c16_t){i * RAMP_STEP_SIZE, 2047 - i * RAMP_STEP_SIZE};
|
||||
// printf("%d, %d\n", file_input[i].r, file_input[i].i);
|
||||
}
|
||||
break;
|
||||
case e_SIGNATURE:
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
if (params->c->dump_iq) {
|
||||
FILE* h=fopen(params->c->dump_iq,"w");
|
||||
for (int i=0; i<params->dft_sz; i++)
|
||||
fprintf(h, "%04hX%04hX\n", samplesTx[0][i].r,samplesTx[0][i].i);
|
||||
fclose(h);
|
||||
}
|
||||
}
|
||||
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;
|
||||
// this is tx ahead in main application, the driver has it's tx ahead that should be smaller to prevent starvation
|
||||
const int tx_ahead = params->dft_sz * 20;
|
||||
char *hole_flag = getenv("HOLE");
|
||||
uint64_t num_samples_file=0;
|
||||
uint64_t tx_cnt = 0;
|
||||
while (!oai_exit) {
|
||||
openair0_timestamp_t new_tx;
|
||||
if (getenv("FAKE_RX") && tx_cnt > atoi(getenv("FAKE_RX"))) {
|
||||
new_tx = last_tx_timestamp + params->dft_sz;
|
||||
} else {
|
||||
do {
|
||||
AssertFatal(!pthread_mutex_lock(¶ms->txMutex), "");
|
||||
AssertFatal(!pthread_cond_wait(&tx_trig, ¶ms->txMutex), "");
|
||||
new_tx = tx_timestamp;
|
||||
AssertFatal(!pthread_mutex_unlock(¶ms->txMutex), "");
|
||||
} while (last_tx_timestamp == new_tx);
|
||||
}
|
||||
tx_cnt++;
|
||||
if (last_tx_timestamp + params->dft_sz != new_tx)
|
||||
LOG_D(HW, "not continuous %ld\n", new_tx - (last_tx_timestamp + params->dft_sz));
|
||||
if (abs(last_tx_timestamp - new_tx) > 1228800) {
|
||||
LOG_W(HW, "large tx gap %ld\n", new_tx - (last_tx_timestamp + params->dft_sz));
|
||||
last_tx_timestamp = new_tx - params->dft_sz;
|
||||
}
|
||||
do {
|
||||
last_tx_timestamp += params->dft_sz;
|
||||
if (params->c->tx_pattern == e_SIGNATURE) {
|
||||
uint64_t signature=last_tx_timestamp+ tx_ahead;
|
||||
int nb_bits=sizeof(signature)*8;
|
||||
memset(samplesTx[0],0,params->dft_sz*sizeof(c16_t));
|
||||
for (int i=0; i<params->dft_sz/chip; i++) {
|
||||
c16_t val={};
|
||||
if (signature & (1ULL << (i%nb_bits))) {
|
||||
if (i&1)
|
||||
val=(c16_t){WAVE_AMP,WAVE_AMP};
|
||||
else
|
||||
val=(c16_t){-WAVE_AMP,-WAVE_AMP};
|
||||
}
|
||||
for (int j=0; j<chip; j++)
|
||||
samplesTx[0][i*chip+j]=val;
|
||||
}
|
||||
}
|
||||
if (num_samples) {
|
||||
for (int i=0; i< params->dft_sz; i++)
|
||||
samplesTx[0][i]=file_input[(num_samples_file++)%num_samples];
|
||||
}
|
||||
c16_t tmp[hole_size];
|
||||
int loc=-1;
|
||||
if (hole_flag && count % 1935 == 0) {
|
||||
loc=((uint)rand())%(params->dft_sz-hole_size);
|
||||
memcpy(tmp, samplesTx[0]+loc,sizeof(tmp));
|
||||
memset(samplesTx[0]+loc, 0,sizeof(tmp));
|
||||
AssertFatal(!pthread_mutex_lock(¶ms->txMutex), "");
|
||||
last_hole=last_tx_timestamp + loc + tx_ahead;
|
||||
LOG_W(HW,"Set hole for: %lu\n", last_hole);
|
||||
AssertFatal(!pthread_mutex_unlock(¶ms->txMutex), "");
|
||||
}
|
||||
params->rfdevice
|
||||
->trx_write_func(params->rfdevice, last_tx_timestamp + tx_ahead, (void **)samplesTx, params->dft_sz, params->antennas, 0);
|
||||
if(loc >= 0)
|
||||
memcpy(samplesTx[0]+loc, tmp, sizeof(tmp));
|
||||
count++;
|
||||
} while (last_tx_timestamp < new_tx);
|
||||
last_tx_timestamp = new_tx;
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
if (now.tv_sec != last_second.tv_sec) {
|
||||
LOG_D(HW, "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;
|
||||
int warmup=0;
|
||||
struct timespec last_second;
|
||||
clock_gettime(CLOCK_REALTIME, &last_second);
|
||||
uint64_t old_sign=0;
|
||||
int nb_aligned = 0;
|
||||
while (!oai_exit) {
|
||||
uint64_t old = rx_timestamp;
|
||||
__attribute__((aligned(32))) c16_t rx[ params->dft_sz ];
|
||||
c16_t *rxptr=rx;
|
||||
int ret =
|
||||
params->rfdevice->trx_read_func(params->rfdevice, &rx_timestamp, (void **)&rxptr, params->dft_sz, params->antennas);
|
||||
if (old + params->dft_sz != rx_timestamp)
|
||||
LOG_E(HW, "not continuous rx %ld\n", rx_timestamp - (old + params->dft_sz));
|
||||
if (ret != params->dft_sz)
|
||||
printf("read of :%d\n", ret);
|
||||
count++;
|
||||
AssertFatal(!pthread_mutex_lock(¶ms->rxMutex), "");
|
||||
memcpy(samplesRx[0],rx, sizeof(rx));
|
||||
// LOG_E(HW,"signal: %lu\n", tx_timestamp);
|
||||
/*
|
||||
for (int i = 0; i < params->dft_sz; i++)
|
||||
params->samplesRx[0][i] = (c16_t){params->samplesRx[0][i].r >>2, params->samplesRx[0][i].i >>2};
|
||||
*/
|
||||
double min=UINT64_MAX, tot_pow=0, tot_samples=0;
|
||||
int min_pos=0;
|
||||
if (getenv("HOLE")) {
|
||||
for (int i = 0; i < params->dft_sz-hole_size; i++) {
|
||||
double local=0;
|
||||
for (int j=i; j<i+hole_size; j++) {
|
||||
local+=params->samplesRx[0][j].r*params->samplesRx[0][j].r+params->samplesRx[0][j].i*params->samplesRx[0][j].i;
|
||||
}
|
||||
tot_samples+=hole_size;
|
||||
tot_pow+=local;
|
||||
if (local < min ) {
|
||||
min=local;
|
||||
min_pos=i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (params->c->tx_pattern == e_SIGNATURE) {
|
||||
int sz=sizeof(rx_timestamp)*8;
|
||||
float sign[sz]={};
|
||||
for (int i=0; i<params->dft_sz/chip; i++){
|
||||
int bit=i%sz;
|
||||
c16_t *tmp=rx+i*chip;
|
||||
for (int j=0; j<chip; j++)
|
||||
sign[bit]+=tmp[j].r*tmp[j].r+tmp[j].i*tmp[j].i;
|
||||
}
|
||||
float max=0;
|
||||
for (int i = 0; i < sz; i++)
|
||||
if (sign[i] >max)
|
||||
max=sign[i];
|
||||
uint64_t encoded_ts=0;
|
||||
for (int i = 0; i < sz; i++)
|
||||
if (sign[i] > max / 2)
|
||||
encoded_ts |= 1ULL << i;
|
||||
if (rx_timestamp == encoded_ts)
|
||||
nb_aligned++;
|
||||
else {
|
||||
printf("after %d aligned packets, %lx, %lx diff with previous signature %ld, diff with header : %ld\n",
|
||||
nb_aligned,
|
||||
rx_timestamp,
|
||||
encoded_ts,
|
||||
(int64_t)encoded_ts - old_sign,
|
||||
rx_timestamp - encoded_ts);
|
||||
nb_aligned = 0;
|
||||
}
|
||||
old_sign=encoded_ts;
|
||||
}
|
||||
AssertFatal(!pthread_mutex_lock(¶ms->txMutex), "");
|
||||
tx_timestamp = rx_timestamp;
|
||||
if (min < tot_pow/(2*tot_samples)) {
|
||||
LOG_I(HW, "found hole %lu, programmed for %lu, received %ld later\n", min_pos+rx_timestamp, last_hole,min_pos+rx_timestamp - last_hole );
|
||||
}
|
||||
warmup++;
|
||||
if (warmup > 1024)
|
||||
AssertFatal(!pthread_cond_signal(&tx_trig), "");
|
||||
AssertFatal(!pthread_mutex_unlock(¶ms->txMutex), "");
|
||||
AssertFatal(!pthread_mutex_unlock(¶ms->rxMutex), "");
|
||||
// dft(get_dft(len), (int16_t *)form->timeDomain, (int16_t *)form->freqDomain, 1);
|
||||
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,245 +520,123 @@ 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 ;
|
||||
|
||||
CONFIG_SETRTFLAG(CONFIG_NOEXITONHELP);
|
||||
get_common_options(uniqCfg);
|
||||
|
||||
config_t c = {1, 1, 3750000, 1, 2047, 10000, 8192, NULL};
|
||||
paramdef_t cmdline_params[] = {
|
||||
{"tx", "enable tx", 0, .uptr = &c.tx, .defintval = 1, TYPE_UINT, 0},
|
||||
{"rx", "enable tx", 0, .uptr = &c.rx, .defintval = 1, TYPE_UINT, 0},
|
||||
{"freq", "center frequency in kHz", 0, .uptr = &c.freq, .defintval = 1, TYPE_UINT, 0},
|
||||
{"tx_pattern",
|
||||
"generate signal for a sine (0), chirp (1), qpsk (2), qam-16 (3), qam-64 (4), qam-256 (5)",
|
||||
0,
|
||||
.uptr = &c.tx_pattern,
|
||||
.defintval = 2,
|
||||
TYPE_UINT,
|
||||
0},
|
||||
{"amplitude", "signal amplitude (int16)", 0, .uptr = &c.amplitude, .defintval = 2047, TYPE_UINT, 0},
|
||||
{"sinus_freq", "if chirp is false, sinut frequency in KHz", .uptr = &c.sinus_freq, .defintval = 10000, TYPE_UINT, 0},
|
||||
{"dft", "dft size for signal frequency/time convertion", .uptr = &c.dft, .defintval = 8192, TYPE_UINT, 0},
|
||||
{"file", "input I/Q samples in ascii, sequence I then Q\n", PARAMFLAG_MALLOCINCONFIG, .strptr = &c.file, .defstrval = NULL, TYPE_STRING, 0},
|
||||
{"dump_iq", "dump the tx iq file at begining", PARAMFLAG_MALLOCINCONFIG, .strptr = &c.dump_iq, .defstrval = NULL, TYPE_STRING, 0},
|
||||
};
|
||||
config_process_cmdline(uniqCfg, cmdline_params, sizeofArray(cmdline_params), NULL);
|
||||
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)));
|
||||
|
||||
openair0_config_t openair0_cfg= {
|
||||
//! the sample rate for both transmit and receive.
|
||||
.sample_rate=sampling_rate,
|
||||
//! samples per packet on the fronthaul interface
|
||||
.samples_per_packet=1024,
|
||||
//! number of RX channels (=RX antennas)
|
||||
.rx_num_channels=antennas,
|
||||
//! number of TX channels (=TX antennas)
|
||||
.tx_num_channels=antennas,
|
||||
//! \brief Center frequency in Hz for RX.
|
||||
//! index: [0..rx_num_channels[
|
||||
.rx_freq={freq,freq,freq,freq},
|
||||
//! \brief Center frequency in Hz for TX.
|
||||
//! index: [0..rx_num_channels[ !!! see lte-ue.c:427 FIXME iterates over rx_num_channels
|
||||
.tx_freq={freq,freq,freq,freq},
|
||||
//! \brief Gain for RX in dB.
|
||||
//! index: [0..rx_num_channels]
|
||||
.rx_gain={rxGain,rxGain,rxGain,rxGain},
|
||||
//! gain for TX in dB
|
||||
.tx_gain={txGain,txGain,txGain,txGain},
|
||||
//! RX bandwidth in Hz
|
||||
.rx_bw=filterBand,
|
||||
//! TX bandwidth in Hz
|
||||
.tx_bw=filterBand,
|
||||
//! clock source
|
||||
.clock_source=external,//internal gpsdo external
|
||||
//! timing_source
|
||||
.time_source=internal, //internal gpsdo external
|
||||
//! Manual SDR IP address
|
||||
.sdr_addrs=usrp_addrs,
|
||||
int sampling_rate = 30.72e6 * 6; // for X300 test, OC will overload it
|
||||
|
||||
int antennas = 1;
|
||||
uint64_t freq = c.freq * 1000;
|
||||
int rxGain = 90;
|
||||
int txGain = 0;
|
||||
int filterBand = 40e6;
|
||||
|
||||
openair0_config_t openair0_cfg = {
|
||||
.duplex_mode = duplex_mode_TDD,
|
||||
.sample_rate = sampling_rate,
|
||||
.num_rb_dl=-1, // flag to say we are rftest, don't scale IQ samples for OAI
|
||||
.tx_sample_advance = 0,
|
||||
.rx_num_channels = antennas,
|
||||
.tx_num_channels = antennas,
|
||||
.rx_freq = {freq, freq, freq, freq},
|
||||
.tx_freq = {freq, freq, freq, freq},
|
||||
.rx_gain_calib_table = NULL,
|
||||
.rx_gain = {rxGain, rxGain, rxGain, rxGain},
|
||||
.tx_gain = {txGain, txGain, txGain, txGain},
|
||||
.rx_bw = filterBand,
|
||||
.tx_bw = filterBand,
|
||||
.clock_source = external, // internal gpsdo external
|
||||
.time_source = internal, // internal gpsdo external
|
||||
.sdr_addrs = "addr=192.168.30.2",
|
||||
.autocal = {0},
|
||||
//! rf devices work with x bits iqs when oai have its own iq format
|
||||
//! the two following parameters are used to convert iqs
|
||||
.configFilename = "",
|
||||
.recplay_mode = 0,
|
||||
.recplay_conf = NULL,
|
||||
};
|
||||
//-----------------------
|
||||
openair0_device_t rfdevice= {
|
||||
/*!brief Type of this device */
|
||||
.type=NONE_DEV,
|
||||
/*!brief Transport protocol type that the device supports (in case I/Q samples need to be transported) */
|
||||
.transp_type=NONE_TP,
|
||||
/*!brief Type of the device's host (RAU/RRU) */
|
||||
.host_type=MIN_HOST_TYPE,
|
||||
/* !brief RF frontend parameters set by application */
|
||||
.openair0_cfg=NULL, //set by device_init
|
||||
/* !brief ETH params set by application */
|
||||
.eth_params=NULL,
|
||||
//! record player data, definition in record_player.h
|
||||
.recplay_state=NULL,
|
||||
/* !brief Indicates if device already initialized */
|
||||
.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_t rfdevice = {
|
||||
/*!brief Type of this device */
|
||||
.type = NONE_DEV,
|
||||
/*!brief Transport protocol type that the device supports (in case I/Q samples need to be transported) */
|
||||
.transp_type = NONE_TP,
|
||||
/*!brief Type of the device's host (RAU/RRU) */
|
||||
.host_type = MIN_HOST_TYPE,
|
||||
/* !brief RF frontend parameters set by application */
|
||||
.openair0_cfg = NULL, // set by device_init
|
||||
/* !brief ETH params set by application */
|
||||
.eth_params = NULL,
|
||||
//! record player data, definition in record_player.h
|
||||
.recplay_state = NULL,
|
||||
/* !brief Indicates if device already initialized */
|
||||
.is_init = 0,
|
||||
/*!brief Can be used by driver to hold internal structure*/
|
||||
.priv = NULL,
|
||||
};
|
||||
|
||||
openair0_device_load(&rfdevice,&openair0_cfg);
|
||||
|
||||
void ** samplesRx = (void **)malloc16(antennas* sizeof(c16_t *) );
|
||||
void ** samplesTx = (void **)malloc16(antennas* sizeof(c16_t *) );
|
||||
openair0_device_load(&rfdevice, &openair0_cfg);
|
||||
|
||||
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) );
|
||||
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] = malloc16_clear(c.dft * sizeof(c16_t));
|
||||
}
|
||||
c16_t **samplesTx = malloc16(antennas * sizeof(c16_t *));
|
||||
for (int i = 0; i < antennas; i++) {
|
||||
samplesTx[i] = malloc16_clear(c.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){&c, &rfdevice, antennas, c.dft, samplesRx, samplesTx};
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
|
||||
AssertFatal(!pthread_mutex_init(¶ms.txMutex, &attr), "");
|
||||
AssertFatal(!pthread_mutex_init(¶ms.txMutex, &attr), "");
|
||||
AssertFatal(!pthread_cond_init(&tx_trig, 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;
|
||||
if (c.tx)
|
||||
threadCreate(&w_thread, write_thread, ¶ms, "write_thr", 3, OAI_PRIORITY_RT);
|
||||
pthread_t r_thread;
|
||||
if (c.rx)
|
||||
threadCreate(&r_thread, read_thread, ¶ms, "read_thr", 2, OAI_PRIORITY_RT);
|
||||
if (c.tx)
|
||||
(void)pthread_join(w_thread, NULL);
|
||||
if (c.rx)
|
||||
(void)pthread_join(r_thread, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -284,7 +284,8 @@ static void oai_xygraph_getbuff(OAIgraph_t *graph, float **x, float **y, int len
|
||||
}
|
||||
#endif
|
||||
|
||||
static void oai_xygraph(OAIgraph_t *graph, float *x, float *y, int len, int layer, bool NoAutoScale) {
|
||||
static void oai_xygraph(OAIgraph_t *graph, float *x, float *y, int len, int layer, int NoAutoScale)
|
||||
{
|
||||
#ifdef WEBSRVSCOPE
|
||||
websrv_scopedata_msg_t *msg = NULL;
|
||||
|
||||
@@ -834,7 +835,7 @@ static void uePbchLLR (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_
|
||||
llr_pbch[i] = llrs[i];
|
||||
}
|
||||
#endif
|
||||
oai_xygraph(graph, bit_pbch, llr_pbch, nx, 0, 10);
|
||||
oai_xygraph(graph, bit_pbch, llr_pbch, nx, 0, 100);
|
||||
}
|
||||
|
||||
static void uePbchIQ (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
|
||||
@@ -859,7 +860,7 @@ static void uePbchIQ (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_U
|
||||
Q[i]=pbch_comp[i].i;
|
||||
}
|
||||
#endif
|
||||
oai_xygraph(graph, I, Q, newsz, 0, true);
|
||||
oai_xygraph(graph, I, Q, newsz, 0, 100);
|
||||
}
|
||||
|
||||
static void uePcchLLR (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
|
||||
@@ -883,7 +884,7 @@ static void uePcchLLR (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_
|
||||
llr[i] = (float) pdcch_llr[i];
|
||||
}
|
||||
#endif
|
||||
oai_xygraph(graph, bit, llr, nx, 0, 10);
|
||||
oai_xygraph(graph, bit, llr, nx, 0, 100);
|
||||
}
|
||||
static void uePcchIQ (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
|
||||
// PDCCH I/Q of MF Output
|
||||
@@ -902,8 +903,11 @@ static void uePcchIQ (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_U
|
||||
oai_xygraph_getbuff(graph, &I, &Q, sz, 0);
|
||||
|
||||
for (int i=0; i<sz; i++) {
|
||||
I[i] = pdcch_comp[i].r;
|
||||
Q[i] = pdcch_comp[i].i;
|
||||
if (pdcch_comp[i].r < 100 && pdcch_comp[i].i < 100) {
|
||||
I[i] = pdcch_comp[i].r;
|
||||
Q[i] = pdcch_comp[i].i;
|
||||
} else
|
||||
I[i] = Q[i] = 0;
|
||||
}
|
||||
#endif
|
||||
oai_xygraph(graph, I, Q, newsz, 0, 10);
|
||||
|
||||
@@ -1883,9 +1883,15 @@ static void nr_rrc_ue_decode_NR_BCCH_BCH_Message(NR_UE_RRC_INST_t *rrc,
|
||||
LOG_E(NR_RRC, "NR_BCCH_BCH decode error\n");
|
||||
return;
|
||||
}
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1))
|
||||
xer_fprint(stdout, &asn_DEF_NR_BCCH_BCH_Message, (void *)bcch_message);
|
||||
|
||||
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
|
||||
static time_t t = 0;
|
||||
time_t t2 = time(0);
|
||||
if (t != t2) {
|
||||
xer_fprint(stdout, &asn_DEF_NR_BCCH_BCH_Message, (void *)bcch_message);
|
||||
t = t2;
|
||||
}
|
||||
}
|
||||
// Actions following cell selection while T311 is running
|
||||
NR_UE_Timers_Constants_t *timers = &rrc->timers_and_constants;
|
||||
if (nr_timer_is_active(&timers->T311)) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
#define MAX_GAP 100ULL
|
||||
const char *const devtype_names[MAX_RF_DEV_TYPE] =
|
||||
{"", "USRP B200", "USRP X300", "USRP N300", "USRP X400", "BLADERF", "LMSSDR", "IRIS", "No HW", "UEDv2", "RFSIMULATOR"};
|
||||
{"", "USRP B200", "USRP X300", "USRP N300", "USRP X400", "BLADERF", "LMSSDR", "IRIS", "No HW", "UEDv2", "RFSIMULATOR", "OC1"};
|
||||
|
||||
const char *get_devname(int devtype) {
|
||||
if (devtype < MAX_RF_DEV_TYPE && devtype !=MIN_RF_DEV_TYPE )
|
||||
|
||||
@@ -76,6 +76,7 @@ typedef enum {
|
||||
/*!\brief device is UEDv2 */
|
||||
UEDv2_DEV,
|
||||
RFSIMULATOR,
|
||||
OC1_DEV,
|
||||
MAX_RF_DEV_TYPE
|
||||
} dev_type_t;
|
||||
/* list of names of devices, needs to match dev_type_t */
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
add_compile_options(-Wunused-parameter)
|
||||
#find_package(Boost REQUIRED)
|
||||
find_package(UHD REQUIRED UHD)
|
||||
#find_package(UHD REQUIRED UHD)
|
||||
|
||||
add_library(oai_usrpdevif MODULE usrp_lib.cpp)
|
||||
#target_include_directories(oai_usrpdevif PRIVATE Boost::boost)
|
||||
target_include_directories(oai_usrpdevif PRIVATE Boost::boost)
|
||||
target_link_libraries(oai_usrpdevif PRIVATE ${UHD_LIBRARIES})
|
||||
target_include_directories(oai_usrpdevif PRIVATE ${UHD_INCLUDE_DIRS})
|
||||
target_link_libraries(oai_usrpdevif PRIVATE log_headers)
|
||||
|
||||
@@ -1190,7 +1190,7 @@ extern "C" {
|
||||
case 184320000:
|
||||
// from usrp_time_offset
|
||||
// openair0_cfg[0].samples_per_packet = 2048;
|
||||
openair0_cfg[0].tx_sample_advance = 15; // to be checked
|
||||
openair0_cfg[0].tx_sample_advance = 132; // to be checked
|
||||
openair0_cfg[0].tx_bw = 100e6;
|
||||
openair0_cfg[0].rx_bw = 100e6;
|
||||
break;
|
||||
|
||||
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];
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
911
radio/oc/oc_lib.cpp
Normal file
911
radio/oc/oc_lib.cpp
Normal file
@@ -0,0 +1,911 @@
|
||||
/*
|
||||
* Licensed by open cells project
|
||||
*/
|
||||
#include <iostream>
|
||||
#include <complex>
|
||||
#include <fstream>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
#include <queue>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#ifdef OAI_INTEGRATION
|
||||
#include "common_lib.h"
|
||||
#include "assertions.h"
|
||||
#else
|
||||
// #define LOG_E(m, a...) printf(a)
|
||||
#include "common_lib.h"
|
||||
#endif
|
||||
#include "system.h"
|
||||
#include <sys/resource.h>
|
||||
#include "common/platform_types.h"
|
||||
#include "openair1/PHY/sse_intrin.h"
|
||||
#include "common/utils/LOG/log.h"
|
||||
#include "common/utils/time_meas.h"
|
||||
|
||||
// Thread-safe queue
|
||||
template <typename T>
|
||||
class TSQueue {
|
||||
private:
|
||||
// Underlying queue
|
||||
|
||||
// mutex for thread synchronization
|
||||
std::mutex m_mutex;
|
||||
|
||||
// Condition variable for signaling
|
||||
std::condition_variable m_cond;
|
||||
|
||||
public:
|
||||
std::queue<T> m_queue;
|
||||
// Pushes an element to the queue
|
||||
void push(T item)
|
||||
{
|
||||
// Acquire lock
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
|
||||
// Add item
|
||||
m_queue.push(item);
|
||||
|
||||
// Notify one thread that
|
||||
// is waiting
|
||||
m_cond.notify_one();
|
||||
}
|
||||
|
||||
// Pops an element off the queue
|
||||
T pop()
|
||||
{
|
||||
// acquire lock
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
|
||||
// wait until queue is not empty
|
||||
m_cond.wait(lock, [this]() { return !m_queue.empty(); });
|
||||
|
||||
// retrieve item
|
||||
T item = m_queue.front();
|
||||
m_queue.pop();
|
||||
|
||||
// return item
|
||||
return item;
|
||||
}
|
||||
};
|
||||
|
||||
#define DEVICE_WRITE_DEFAULT "/dev/xdma0_h2c_0"
|
||||
#define DEVICE_READ_DEFAULT "/dev/xdma0_c2h_0"
|
||||
|
||||
static const uint64_t magic_tx = 0xA5A50be3A5A5A5A5LL;
|
||||
static const uint64_t magic_rx = 0xA5A50be3A5A5A5A5LL;
|
||||
static const uint32_t magic_footer1 = 0xce11;
|
||||
static const uint32_t magic_footer2 = 0x5A;
|
||||
|
||||
#define READ_BLOCK_NB_SAMPLES 2048
|
||||
#define NB_BLOCKS_PER_READ 4
|
||||
|
||||
#define WRITE_BLOCK_NB_SAMPLES 2048 * 4
|
||||
#define NB_BLOCKS_PER_WRITE 2
|
||||
static const uint64_t tx_ahead_max = 32 * 2048;
|
||||
|
||||
typedef struct {
|
||||
uint64_t control;
|
||||
uint32_t packetSeqNum: 16;
|
||||
uint32_t packetSz: 16;
|
||||
uint32_t seqId: 2;
|
||||
uint32_t filler: 6;
|
||||
uint32_t markers: 8;
|
||||
uint32_t filler2: 16;
|
||||
uint32_t txGain: 24;
|
||||
uint32_t filler3: 8;
|
||||
uint32_t ppsOffset;
|
||||
uint64_t timestamp;
|
||||
} __attribute__((packed)) headerTx_t;
|
||||
|
||||
typedef struct {
|
||||
uint64_t control;
|
||||
uint16_t packetSeqNum;
|
||||
uint16_t packetSz;
|
||||
uint8_t RXen : 1;
|
||||
uint8_t TXen: 1;
|
||||
uint8_t ADCsync : 1;
|
||||
uint8_t DACsync : 1;
|
||||
uint8_t TXhfull: 1;
|
||||
uint8_t TXlate : 1;
|
||||
uint8_t TXseqerr : 1;
|
||||
uint8_t filler : 1;
|
||||
uint8_t InbandReadAddr;
|
||||
uint16_t InbandReadValue;
|
||||
uint32_t ppsOffset: 30;
|
||||
uint32_t ppsAlive: 1;
|
||||
uint32_t gpsLock: 1;
|
||||
uint64_t timestamp;
|
||||
} __attribute__((packed)) headerRx_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t control1;
|
||||
uint8_t control2;
|
||||
uint8_t atomicPacket: 1;
|
||||
uint8_t timerOverflow: 1;
|
||||
uint8_t filler: 6;
|
||||
} __attribute__((packed)) footer_t;
|
||||
|
||||
typedef struct {
|
||||
headerRx_t h;
|
||||
c16_t b[READ_BLOCK_NB_SAMPLES];
|
||||
footer_t f;
|
||||
} __attribute__((packed)) rx_packet_t;
|
||||
|
||||
typedef struct {
|
||||
headerTx_t h;
|
||||
c16_t b[WRITE_BLOCK_NB_SAMPLES];
|
||||
// footer_t f;
|
||||
} __attribute__((packed)) tx_packet_t;
|
||||
|
||||
static inline void dumpHD(std::string ctx, headerRx_t h)
|
||||
{
|
||||
printf("header dump, %s\n", ctx.c_str());
|
||||
uint8_t *z = (uint8_t *)&h;
|
||||
for (uint i = 0; i < sizeof(headerRx_t); 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, TXhfull %u, DACsync %u, ADCsync: %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.TXhfull,
|
||||
h.DACsync,
|
||||
h.ADCsync,
|
||||
h.TXen,
|
||||
h.RXen,
|
||||
h.gpsLock,
|
||||
h.ppsAlive,
|
||||
h.ppsOffset,
|
||||
h.timestamp);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int fd_write;
|
||||
bool first_tx;
|
||||
bool continuous_tx;
|
||||
int64_t tx_count;
|
||||
openair0_timestamp_t tx_ts;
|
||||
uint txSeq;
|
||||
tx_packet_t *tx_block;
|
||||
uint8_t *tx_block_pos;
|
||||
uint tx_block_num;
|
||||
TSQueue<tx_packet_t *> *ready_tx;
|
||||
} tx_thr_t;
|
||||
|
||||
typedef struct {
|
||||
int fd_read;
|
||||
int nb_blocks_per_read;
|
||||
openair0_timestamp_t rx_timestamp;
|
||||
openair0_timestamp_t rx_ts_interface;
|
||||
int64_t rx_count;
|
||||
uint lastPpsOffset;
|
||||
int remain_samples;
|
||||
uint64_t gap;
|
||||
uint timerOverflow;
|
||||
uint seqNum;
|
||||
uint txLate;
|
||||
uint txErr;
|
||||
uint atomicPacket;
|
||||
TSQueue<rx_packet_t *> *read_queue;
|
||||
rx_packet_t *rx_live;
|
||||
} rx_thr_t;
|
||||
|
||||
typedef struct {
|
||||
char filename_write[FILENAME_MAX];
|
||||
char filename_read[FILENAME_MAX];
|
||||
int wait_for_first_pps;
|
||||
rx_thr_t rx;
|
||||
tx_thr_t tx;
|
||||
std::atomic<bool> txHfull;
|
||||
TSQueue<uint64_t> *last_rx;
|
||||
} 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;
|
||||
}
|
||||
|
||||
// INSTEAD OF TIMESTAMP AHEAD you need to READ the TXhfull flag from the RX packet header
|
||||
// IF TXhfull FLAG = 0 then you can write 4 packets of 8192 samples ELSE You cannot
|
||||
void *write_thread(void *arg)
|
||||
{
|
||||
oc_state_t *s = (oc_state_t *)arg;
|
||||
FILE* fd=fopen("/tmp/headers", "w");
|
||||
fprintf(fd,"time before call xdma, nano sec in xdma write, packet seq num, timestamp\n");
|
||||
char * log_headers=getenv("LOGHEADERS");
|
||||
// tx_packet_t ref;
|
||||
// int seq=0;
|
||||
uint64_t ts = 0;
|
||||
tx_thr_t *tx = &s->tx;
|
||||
bool do_rx = getenv("FAKE_RX") == NULL;
|
||||
do {
|
||||
tx_packet_t *p = tx->ready_tx->pop();
|
||||
if (do_rx && s->txHfull) {
|
||||
do {
|
||||
struct timespec b, e;
|
||||
clock_gettime(CLOCK_REALTIME, &b);
|
||||
uint64_t last_rx;
|
||||
do {
|
||||
last_rx = s->last_rx->pop();
|
||||
} while (last_rx + tx_ahead_max < p->h.timestamp);
|
||||
clock_gettime(CLOCK_REALTIME, &e);
|
||||
LOG_D(HW,
|
||||
"tx buffer was half full, blocked for %ld ns, rx ts: %lu, tx t: %lu\n",
|
||||
(e.tv_sec - b.tv_sec) * 1000 * 1000 * 1000 + e.tv_nsec - b.tv_nsec,
|
||||
last_rx,
|
||||
p->h.timestamp);
|
||||
} while (s->txHfull);
|
||||
} else
|
||||
LOG_D(HW, "no need to wait tx ts: %lu\n", p->h.timestamp);
|
||||
/*
|
||||
// this is test code to repeat same packet forever with continuous tested timestamp
|
||||
if (!seq)
|
||||
memcpy(&ref,p, sizeof(ref));
|
||||
for (int i=0; i<NB_BLOCKS_PER_WRITE; i++) {
|
||||
memcpy(&p[i].b, ref.b, sizeof(ref.b));
|
||||
if (ts!=p[i].h.timestamp)
|
||||
printf("ERROROROROROO\n");
|
||||
ts=p[i].h.timestamp+WRITE_BLOCK_NB_SAMPLES;
|
||||
}*/
|
||||
struct timespec b, e;
|
||||
clock_gettime(CLOCK_REALTIME,&b);
|
||||
uint8_t *j = (uint8_t *)p;
|
||||
for (int i = 0; i < NB_BLOCKS_PER_WRITE; i++) {
|
||||
tx_packet_t *cur = (tx_packet_t *)j;
|
||||
if (ts != cur->h.timestamp && tx->continuous_tx)
|
||||
LOG_E(HW, "tx is not contiguous\n");
|
||||
ts = cur->h.timestamp + cur->h.packetSz;
|
||||
/*
|
||||
int sz=sizeof(cur->h.timestamp)*8;
|
||||
float sign[sz]={};
|
||||
c16_t* rx=cur->b;
|
||||
for (int i=0; i<cur->h.packetSz/8; i++){
|
||||
int bit=i%sz;
|
||||
for (int j=0; j<8; j++)
|
||||
sign[bit]+=rx[i*8+j].r*rx[i*8+j].r+rx[i*8+j].i*rx[i*8+j].i;
|
||||
}
|
||||
float total=0;
|
||||
for (int i = 0; i < sz; i++)
|
||||
total+=sign[i];
|
||||
total/=sz;
|
||||
uint64_t encoded_ts=0;
|
||||
for (int i = 0; i < sz; i++)
|
||||
if (sign[i] > total)
|
||||
encoded_ts|=1ULL<<i;
|
||||
printf("driver diff encoded versus header %ld\n", cur->h.timestamp - (int64_t)encoded_ts);
|
||||
*/
|
||||
j += sizeof(headerTx_t) + cur->h.packetSz * sizeof(*cur->b);
|
||||
}
|
||||
uint sz_bytes = j - (uint8_t *)p;
|
||||
size_t wrote = write(tx->fd_write, p, sz_bytes);
|
||||
clock_gettime(CLOCK_REALTIME,&e);
|
||||
if (wrote != sz_bytes)
|
||||
LOG_E(HW, "write to SDR failed, request: %u, wrote %ld\n", sz_bytes, wrote);
|
||||
if (wrote < 0)
|
||||
LOG_E(HW, "write to %s failed, errno %d:%s\n", s->filename_write, errno, strerror(errno));
|
||||
|
||||
LOG_D(HW, "wrote: for ts %lu, total size: %u\n", p->h.timestamp, sz_bytes);
|
||||
if (log_headers) {
|
||||
char str[60];
|
||||
memset(str,' ', sizeof(str));
|
||||
snprintf(str,sizeof(str), "%lu.%lu, %lu, %u, %lu\n", b.tv_sec, b.tv_nsec,(e.tv_sec-b.tv_sec)*1000*1000*1000+e.tv_nsec-b.tv_nsec,p->h.packetSeqNum,p->h.timestamp);
|
||||
fwrite(str, sizeof(str),1,fd);
|
||||
}
|
||||
free(p);
|
||||
} while (true);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct energy_s {
|
||||
float mean;
|
||||
float papr_dB;
|
||||
float peak;
|
||||
};
|
||||
|
||||
struct energy_s compute_papr_db(const c16_t *x, size_t N)
|
||||
{
|
||||
float p_max = 0.0f;
|
||||
float p_sum = 0.0f;
|
||||
|
||||
for (size_t n = 0; n < N; n++) {
|
||||
float p = (float)x[n].r * x[n].r + (float)x[n].i * x[n].i;
|
||||
p_sum += p;
|
||||
if (p > p_max)
|
||||
p_max = p;
|
||||
}
|
||||
float p_avg = p_sum / N;
|
||||
float papr = p_max / p_avg;
|
||||
return (struct energy_s){p_avg, 10.0f * log10f(papr), sqrt(p_max)};
|
||||
}
|
||||
|
||||
static int32_t signalEnergy(c16_t *input, uint32_t length)
|
||||
{
|
||||
// init
|
||||
simde__m128 mm0 = simde_mm_setzero_ps();
|
||||
|
||||
// Acc
|
||||
for (uint32_t i = 0; i < (length >> 2); i++) {
|
||||
simde__m128i in = simde_mm_loadu_si128((simde__m128i *)input);
|
||||
mm0 = simde_mm_add_ps(mm0, simde_mm_cvtepi32_ps(simde_mm_madd_epi16(in, in)));
|
||||
input += 4;
|
||||
}
|
||||
|
||||
// leftover
|
||||
float leftover_sum = 0;
|
||||
c16_t *leftover_input = input + (length & ~3);
|
||||
uint16_t lefover_count = length & 3;
|
||||
for (int32_t i = 0; i < lefover_count; i++) {
|
||||
leftover_sum += leftover_input[i].r * leftover_input[i].r + leftover_input[i].i * leftover_input[i].i;
|
||||
}
|
||||
|
||||
// Ave
|
||||
float sums[4];
|
||||
simde_mm_store_ps(sums, mm0);
|
||||
return (uint32_t)((sums[0] + sums[1] + sums[2] + sums[3] + leftover_sum) / (float)length);
|
||||
}
|
||||
|
||||
#define BURST_NUM_OF_PACKETS (1u)
|
||||
// DC-filter: 0 will be done in FPGA after seeing 128-consecutive samples having the same value
|
||||
static inline int write_block(tx_thr_t *tx, c16_t *samples, uint sz, bool no_scaling)
|
||||
{
|
||||
// TO BE REWRITTEN BY LAURENT THOMAS
|
||||
static uint stream_seqId = 0x01;
|
||||
if (BURST_NUM_OF_PACKETS == 1) {
|
||||
stream_seqId = 0x01; // Start of Burst all the time
|
||||
} else if (((tx->tx_count) % BURST_NUM_OF_PACKETS) == 0) {
|
||||
stream_seqId = 0x01; // Start of Burst
|
||||
} else {
|
||||
stream_seqId = 0x02; // Middle of Burst
|
||||
}
|
||||
|
||||
if (!tx->tx_block) {
|
||||
tx->tx_block = (tx_packet_t *)malloc16(NB_BLOCKS_PER_WRITE * sizeof(tx_packet_t));
|
||||
tx->tx_block_pos = (uint8_t *)tx->tx_block;
|
||||
}
|
||||
// LOG_I(HW, "add tx packet for %u samples, ts %lu\n", sz,tx->tx_ts);
|
||||
tx_packet_t *ant0 = (tx_packet_t *)tx->tx_block_pos;
|
||||
ant0->h = (headerTx_t){.control = magic_tx,
|
||||
.packetSeqNum = tx->txSeq++,
|
||||
.packetSz = sz,
|
||||
.seqId = stream_seqId,
|
||||
.filler = 0x02,
|
||||
.markers = 0xb1,
|
||||
.filler2 = 0xabcd,
|
||||
.txGain = 0x112233,
|
||||
.filler3 = 0xf0,
|
||||
.ppsOffset = 0x28272625,
|
||||
.timestamp = (uint64_t)tx->tx_ts};
|
||||
if (no_scaling)
|
||||
memcpy(ant0->b, samples, sz * sizeof(c16_t));
|
||||
else
|
||||
for (uint i = 0; i < sz; i++)
|
||||
ant0->b[i] = (c16_t){(int16_t)(samples[i].r<<4), (int16_t)(samples[i].i<<4)};
|
||||
tx->tx_ts += sz;
|
||||
tx->tx_block_pos += sizeof(headerTx_t) + sz * sizeof(*ant0->b);
|
||||
tx->tx_block_num++;
|
||||
tx->tx_count++;
|
||||
if (tx->tx_block_num == NB_BLOCKS_PER_WRITE) {
|
||||
tx->ready_tx->push(tx->tx_block);
|
||||
tx->tx_block_num = 0;
|
||||
tx->tx_block_pos = NULL;
|
||||
tx->tx_block = NULL;
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
static int oc_write(openair0_device_t *device, openair0_timestamp_t timestamp, void **buff, int nsamps, int cc, int flags)
|
||||
{
|
||||
tx_thr_t *tx = &((oc_state_t *)device->priv)->tx;
|
||||
|
||||
timestamp -= device->openair0_cfg->command_line_sample_advance + device->openair0_cfg->tx_sample_advance;
|
||||
|
||||
if (tx->first_tx) {
|
||||
tx->tx_ts = timestamp;
|
||||
tx->first_tx = false;
|
||||
}
|
||||
|
||||
int64_t gap = timestamp - tx->tx_ts;
|
||||
if (gap < 0) {
|
||||
LOG_E(HW, "out of sequence\n");
|
||||
gap = 0;
|
||||
}
|
||||
|
||||
if (gap)
|
||||
LOG_I(HW, "gap of %ld\n", gap);
|
||||
else
|
||||
LOG_D(HW, ".\n");
|
||||
|
||||
int wr_sz = nsamps;
|
||||
// LOG_E(HW, "ask to write %d\n", wr_sz);
|
||||
while (wr_sz > 0) {
|
||||
int tmp = std::min(wr_sz, WRITE_BLOCK_NB_SAMPLES);
|
||||
int sz = write_block(tx, ((c16_t *)buff[0]) + nsamps - wr_sz, tmp, device->openair0_cfg->num_rb_dl == -1);
|
||||
if (sz != tmp)
|
||||
LOG_E(HW, "ask to write %d, res is %d\n", tmp, sz);
|
||||
wr_sz -= sz;
|
||||
}
|
||||
|
||||
if (tx->tx_ts != timestamp + nsamps)
|
||||
LOG_E(HW,"tx samples count error\n");
|
||||
tx->tx_ts = timestamp + nsamps;
|
||||
|
||||
#if 0
|
||||
static uint nsamps0 = 0;
|
||||
struct energy_s e = compute_papr_db((c16_t *)*buff, nsamps);
|
||||
if (e.mean > 1) {
|
||||
LOG_I(HW, "sent power: %f, papr %f, max %f after %d samples with no energy\n", e.mean, e.papr_dB, e.peak, nsamps0);
|
||||
nsamps0 = 0;
|
||||
} else {
|
||||
nsamps0 += nsamps;
|
||||
}
|
||||
#endif
|
||||
|
||||
return nsamps;
|
||||
}
|
||||
|
||||
static void initial_block_align(rx_thr_t *rx)
|
||||
{
|
||||
LOG_I(HW, "Synchronizing rx\n");
|
||||
__attribute__((aligned(32))) uint32_t b[sizeof(rx_packet_t)];
|
||||
ssize_t ret = read(rx->fd_read, b, sizeof(b));
|
||||
if (ret != sizeof(b)) {
|
||||
LOG_E(HW, "Error reading %ld bytes: %ld (%s)\n", sizeof(b), ret, strerror(errno));
|
||||
usleep(10000);
|
||||
return;
|
||||
}
|
||||
uint i;
|
||||
headerRx_t *rxh = NULL;
|
||||
for (i = 0; i < sizeof(b) / sizeof(*b) - sizeof(headerRx_t); i++)
|
||||
if (b[i] == (magic_rx & UINT32_MAX) && b[i + 1] == ((magic_rx >> 32) & UINT32_MAX)) {
|
||||
LOG_D(HW, "found first magic at %d \n", i);
|
||||
rxh = (headerRx_t *)(b + i);
|
||||
dumpHD("first header:", *rxh);
|
||||
break;
|
||||
}
|
||||
if (i == (sizeof(b) / sizeof(*b) - sizeof(headerRx_t))) {
|
||||
LOG_E(HW, "%%error magic not found\n");
|
||||
return;
|
||||
}
|
||||
ret = read(rx->fd_read, b, i * sizeof(*b));
|
||||
rx->seqNum = rxh->packetSeqNum + 1;
|
||||
rx->rx_timestamp = (int64_t)rxh->timestamp + rxh->packetSz;
|
||||
rx->rx_ts_interface = rxh->timestamp;
|
||||
rx->rx_count = 1;
|
||||
}
|
||||
|
||||
static bool get_blocks(oc_state_t *s, rx_packet_t *p)
|
||||
{
|
||||
rx_thr_t *rx = &s->rx;
|
||||
static struct timespec last_second={}, origin={};
|
||||
static struct timespec now={};
|
||||
static uint64_t tot_samples= 0;
|
||||
|
||||
int readSz = sizeof(*rx->rx_live) * rx->nb_blocks_per_read;
|
||||
ssize_t ret = read(rx->fd_read, p, readSz);
|
||||
if (ret != readSz || p[0].h.control != magic_rx) {
|
||||
LOG_E(HW, "Error reading header asked for %d bytes, got %ld, magic: %lx\n", readSz, ret, p[0].h.control);
|
||||
dumpHD("lost good header:", p[0].h);
|
||||
rx->rx_count = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
if ( last_second.tv_sec==0) {
|
||||
last_second=now;
|
||||
origin=now;
|
||||
}
|
||||
tot_samples += rx->nb_blocks_per_read * sizeof(p->b) / sizeof(*p->b);
|
||||
if (now.tv_sec != last_second.tv_sec) {
|
||||
LOG_I(HW,
|
||||
"driver avg read rate:%f\n errors during last second: %s %u, %s %u, %s %u, %s %u, present "
|
||||
"tx seq %u\n\n ",
|
||||
(float)tot_samples / (now.tv_sec * 1000000 - origin.tv_sec * 1000000 + now.tv_nsec / 1000.0 - origin.tv_nsec / 1000.0),
|
||||
rx->txLate ? "\x1B[93m"
|
||||
"txLate"
|
||||
"\x1B[0m"
|
||||
: "txLate",
|
||||
rx->txLate,
|
||||
rx->txErr ? "\x1B[93m"
|
||||
"txSeqerr"
|
||||
"\x1B[0m"
|
||||
: "txSeqerr",
|
||||
rx->txErr,
|
||||
rx->timerOverflow ? "\x1B[93m"
|
||||
"timerOverflow"
|
||||
"\x1B[0m"
|
||||
: "timerOverflow",
|
||||
rx->timerOverflow,
|
||||
rx->atomicPacket ? "\x1B[93m"
|
||||
"atomicPacket"
|
||||
"\x1B[0m"
|
||||
: "atomicPacket",
|
||||
rx->atomicPacket,
|
||||
s->tx.txSeq);
|
||||
rx->txLate = rx->txErr = rx->timerOverflow = rx->atomicPacket = 0;
|
||||
last_second.tv_sec++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rx->nb_blocks_per_read; i++) {
|
||||
if (rx->rx_timestamp != (int64_t)p[i].h.timestamp)
|
||||
LOG_W(HW,
|
||||
"expected ts: %lu got %lu, diff %ld, seq num %d, atomicPacket %d, timerOverflow %d\n",
|
||||
rx->rx_timestamp,
|
||||
p[i].h.timestamp,
|
||||
(int64_t)p[i].h.timestamp - rx->rx_timestamp,
|
||||
rx->seqNum,
|
||||
(~p[i].f.atomicPacket & 0x01),
|
||||
p[i].f.timerOverflow);
|
||||
rx->rx_timestamp = p[i].h.timestamp + p[i].h.packetSz;
|
||||
/*
|
||||
if (llabs((int64_t)rx->lastPpsOffset - (int64_t)p[i].h.ppsOffset) > 12)
|
||||
// pps_in is based on a real pulse from the GPS. It may have some jitter and drift during time.
|
||||
// Thus it is normal to have some diff between theoretical (expected) and real (measured) values.
|
||||
// >12 ->l means that we flag errors higher than +/- 0.1ppm.
|
||||
// When the board's FPGA starts, the vcxo is not yet discplined to the GPS, thus we may observe
|
||||
// some errors until the frequency error algorithm converges
|
||||
printf("expected pps offset %u got %u, diff %d, seq num %d\n",
|
||||
rx->lastPpsOffset,
|
||||
p[i].h.ppsOffset,
|
||||
p[i].h.ppsOffset- rx->lastPpsOffset,
|
||||
rx->seqNum);
|
||||
rx->lastPpsOffset = (p[i].h.ppsOffset + READ_BLOCK_NB_SAMPLES )% 122880000 ;
|
||||
if (!p[i].h.ppsAlive)
|
||||
printf("pps not alive\n");
|
||||
*/
|
||||
if (p[i].h.TXlate) {
|
||||
LOG_D(HW, "TXlate\n");
|
||||
rx->txLate++;
|
||||
}
|
||||
if (p[i].h.TXseqerr) {
|
||||
LOG_W(HW, "TXseqerr, current tx seq is %u\n", s->tx.txSeq);
|
||||
rx->txErr++;
|
||||
}
|
||||
if (p[i].f.control1 != magic_footer1 || p[i].f.control2 != magic_footer2)
|
||||
LOG_W(HW, "footer error\n");
|
||||
if (p[i].f.timerOverflow) {
|
||||
LOG_D(HW, "timerOverflow\n");
|
||||
rx->timerOverflow++;
|
||||
}
|
||||
if (p[i].f.atomicPacket) {
|
||||
rx->atomicPacket++;
|
||||
LOG_D(HW, "Not atomic\n");
|
||||
}
|
||||
|
||||
static int last_filler = 0;
|
||||
if (last_filler != p[i].f.filler)
|
||||
LOG_I(HW, "filler changed to %x\n", p[i].f.filler);
|
||||
last_filler = p[i].f.filler;
|
||||
if (rx->seqNum % 65536 != p[i].h.packetSeqNum) {
|
||||
LOG_W(HW,
|
||||
"expected rx packet sequence number %u got %u, diff %d\n",
|
||||
rx->seqNum,
|
||||
p[i].h.packetSeqNum,
|
||||
p[i].h.packetSeqNum - rx->seqNum);
|
||||
rx->seqNum = p[i].h.packetSeqNum;
|
||||
// rx->rx_count = -1;
|
||||
// return false;
|
||||
}
|
||||
rx->seqNum++;
|
||||
}
|
||||
|
||||
s->txHfull = p[rx->nb_blocks_per_read - 1].h.TXhfull;
|
||||
if (!s->txHfull)
|
||||
s->last_rx->push(rx->rx_timestamp);
|
||||
LOG_D(HW, "read: %lu\n", rx->rx_timestamp);
|
||||
return true;
|
||||
}
|
||||
|
||||
void *read_thread(void *arg)
|
||||
{
|
||||
oc_state_t *s = (oc_state_t *)arg;
|
||||
rx_thr_t *rx = &s->rx;
|
||||
while (true) {
|
||||
if (rx->rx_count == -1)
|
||||
initial_block_align(rx);
|
||||
if (rx->rx_count == -1) {
|
||||
usleep(10);
|
||||
continue;
|
||||
}
|
||||
if (rx->read_queue->m_queue.size() > 100) {
|
||||
if (!getenv("FAKE_RX"))
|
||||
LOG_W(HW, "rx consumer is too slow, trashing rx queue\n");
|
||||
while (rx->read_queue->m_queue.size())
|
||||
free(rx->read_queue->pop());
|
||||
}
|
||||
rx_packet_t *tmp = (rx_packet_t *)malloc(sizeof(*rx->rx_live) * rx->nb_blocks_per_read);
|
||||
if (!get_blocks(s, tmp)) {
|
||||
printf("getblocks returned bad\n");
|
||||
free(tmp);
|
||||
} else
|
||||
rx->read_queue->push(tmp);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int oc_read(openair0_device_t *device, openair0_timestamp_t *ptimestamp, void **buff, int nsamps, int cc)
|
||||
{
|
||||
rx_thr_t *rx = &((oc_state_t *)device->priv)->rx;
|
||||
static int64_t reads_cnt = 0;
|
||||
if (getenv("FAKE_RX") && reads_cnt > atoi(getenv("FAKE_RX"))) {
|
||||
*ptimestamp = rx->rx_ts_interface;
|
||||
rx->rx_ts_interface += nsamps;
|
||||
return nsamps;
|
||||
}
|
||||
reads_cnt++;
|
||||
c16_t **output=(c16_t**)buff;
|
||||
int remain_to_get=nsamps;
|
||||
while (remain_to_get > 0) {
|
||||
while (rx->remain_samples > 0 && remain_to_get > 0) {
|
||||
if (rx->gap) {
|
||||
c16_t *out = output[0] + nsamps - remain_to_get;
|
||||
while (rx->gap && remain_to_get) {
|
||||
*out++ = {};
|
||||
remain_to_get--;
|
||||
rx->gap--;
|
||||
rx->rx_ts_interface++;
|
||||
}
|
||||
}
|
||||
rx_packet_t *last_rx = rx->rx_live;
|
||||
int nb_samples_per_packet = sizeof(last_rx[0].b) / sizeof(last_rx[0].b[0]);
|
||||
int nb_samples = nb_samples_per_packet * rx->nb_blocks_per_read;
|
||||
int consumed_samples = nb_samples - rx->remain_samples;
|
||||
int nb_consumed_samples_in_block= consumed_samples%nb_samples_per_packet;
|
||||
int bloc = consumed_samples / nb_samples_per_packet;
|
||||
rx_packet_t *cur_pkt = last_rx + bloc;
|
||||
if (nb_consumed_samples_in_block == 0 && cur_pkt->h.timestamp != (uint64_t)rx->rx_ts_interface) {
|
||||
rx->gap = cur_pkt->h.timestamp - rx->rx_ts_interface;
|
||||
if (rx->gap > 0 && rx->gap < 1228800) {
|
||||
LOG_W(HW, "gap of %ld, we fill \n", rx->gap);
|
||||
continue;
|
||||
} else {
|
||||
LOG_W(HW, "gap of %ld, we break timestamp sequence %lu != %lu\n", rx->gap, rx->rx_ts_interface, cur_pkt->h.timestamp);
|
||||
rx->rx_ts_interface = cur_pkt->h.timestamp;
|
||||
rx->gap = 0;
|
||||
}
|
||||
}
|
||||
int remaing_samples_in_block= nb_samples_per_packet-nb_consumed_samples_in_block;
|
||||
int toCopy=std::min(remaing_samples_in_block,remain_to_get );
|
||||
memcpy(output[0] + nsamps - remain_to_get, cur_pkt->b + nb_consumed_samples_in_block, toCopy * sizeof(cur_pkt->b[0]));
|
||||
rx->remain_samples -= toCopy;
|
||||
remain_to_get -= toCopy;
|
||||
rx->rx_ts_interface += toCopy;
|
||||
}
|
||||
if( remain_to_get > 0 ) {
|
||||
free(rx->rx_live);
|
||||
rx->rx_live = rx->read_queue->pop();
|
||||
rx->remain_samples = sizeof(rx->rx_live->b) * rx->nb_blocks_per_read / sizeof(*rx->rx_live->b);
|
||||
}
|
||||
}
|
||||
*ptimestamp = rx->rx_ts_interface - nsamps;
|
||||
return nsamps;
|
||||
}
|
||||
|
||||
static int oc_set_freq(openair0_device_t *device, openair0_config_t *openair0_cfg)
|
||||
{
|
||||
// oc_state_t *s = (oc_state_t *)device->priv;
|
||||
LOG_I(HW,
|
||||
"Setting TX Freq %f, RX Freq %f, tune_offset: %f\n",
|
||||
openair0_cfg[0].tx_freq[0],
|
||||
openair0_cfg[0].rx_freq[0],
|
||||
openair0_cfg[0].tune_offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oc_set_gains(openair0_device_t *device, openair0_config_t *openair0_cfg)
|
||||
{
|
||||
// oc_state_t *s = (oc_state_t *)device->priv;
|
||||
LOG_I(HW, "Setting RX gain to %f \n", openair0_cfg[0].rx_gain[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oc_stop(openair0_device_t *device)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_rx_gain_offset(openair0_config_t *openair0_cfg, int chain_index, int bw_gain_adjust)
|
||||
{
|
||||
int i = 0;
|
||||
// loop through calibration table to find best adjustment factor for RX frequency
|
||||
double min_diff = 6e9, diff, gain_adj = 0.0;
|
||||
|
||||
if (bw_gain_adjust == 1) {
|
||||
switch ((int)openair0_cfg[0].sample_rate) {
|
||||
case 46080000:
|
||||
break;
|
||||
|
||||
case 30720000:
|
||||
break;
|
||||
|
||||
case 23040000:
|
||||
gain_adj = 1.25;
|
||||
break;
|
||||
|
||||
case 15360000:
|
||||
gain_adj = 3.0;
|
||||
break;
|
||||
|
||||
case 7680000:
|
||||
gain_adj = 6.0;
|
||||
break;
|
||||
|
||||
case 3840000:
|
||||
gain_adj = 9.0;
|
||||
break;
|
||||
|
||||
case 1920000:
|
||||
gain_adj = 12.0;
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_E(HW, "unknown sampling rate %d\n", (int)openair0_cfg[0].sample_rate);
|
||||
// exit(-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (openair0_cfg->rx_gain_calib_table[i].freq > 0) {
|
||||
diff = fabs(openair0_cfg->rx_freq[chain_index] - openair0_cfg->rx_gain_calib_table[i].freq);
|
||||
LOG_I(HW,
|
||||
"cal %d: freq %f, offset %f, diff %f\n",
|
||||
i,
|
||||
openair0_cfg->rx_gain_calib_table[i].freq,
|
||||
openair0_cfg->rx_gain_calib_table[i].offset,
|
||||
diff);
|
||||
|
||||
if (min_diff > diff) {
|
||||
min_diff = diff;
|
||||
openair0_cfg->rx_gain_offset[chain_index] = openair0_cfg->rx_gain_calib_table[i].offset + gain_adj;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
static int oc_get_stats(openair0_device_t *device)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int oc_reset_stats(openair0_device_t *device)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
int oc_write_init(openair0_device_t *device)
|
||||
{
|
||||
LOG_E(HW, "trx_write_init should not be called, and is a design error even for USRP\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int oc_start(openair0_device_t *device)
|
||||
{
|
||||
oc_state_t *s = (oc_state_t *)device->priv;
|
||||
s->rx.rx_count = -1;
|
||||
s->wait_for_first_pps = 1;
|
||||
s->tx.first_tx = true;
|
||||
s->rx.nb_blocks_per_read = NB_BLOCKS_PER_READ;
|
||||
s->rx.rx_live = (rx_packet_t *)malloc16(s->rx.nb_blocks_per_read * sizeof(*s->rx.rx_live));
|
||||
s->tx.ready_tx = new TSQueue<tx_packet_t *>;
|
||||
s->rx.read_queue = new TSQueue<rx_packet_t *>;
|
||||
s->last_rx = new TSQueue<uint64_t>;
|
||||
s->tx.fd_write = open(s->filename_write, O_WRONLY);
|
||||
if (s->tx.fd_write < 0) {
|
||||
LOG_E(HW, "Open %s failed, errno %d:%s\n", s->filename_write, errno, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
s->rx.fd_read = open(s->filename_read, O_RDONLY);
|
||||
if (s->rx.fd_read < 0) {
|
||||
LOG_E(HW, "Open %s failed, errno %d:%s\n", s->filename_read, errno, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
pthread_t w_thread;
|
||||
threadCreate(&w_thread, write_thread, s, (char *)"write_thr", -1, OAI_PRIORITY_RT);
|
||||
pthread_t r_thread;
|
||||
threadCreate(&r_thread, read_thread, s, (char *)"read_thr", -1, OAI_PRIORITY_RT);
|
||||
oc_set_gains(device, device->openair0_cfg);
|
||||
oc_set_freq(device, device->openair0_cfg);
|
||||
sync_to_gps(device);
|
||||
check_ref_locked(s);
|
||||
// Fixme: set sampling rate, lack of API in OAI
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void oc_end(openair0_device_t *device)
|
||||
{
|
||||
if (device == NULL)
|
||||
return;
|
||||
// oc_state_t *s = (oc_state_t *)device->priv;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int device_init(openair0_device_t *device, openair0_config_t *openair0_cfg)
|
||||
{
|
||||
LOG_I(HW, "openair0_cfg->clock_source == '%d' (internal = %d, external = %d)\n", openair0_cfg->clock_source, internal, external);
|
||||
oc_state_t *st;
|
||||
if (device->priv == NULL) {
|
||||
st = (oc_state_t *)calloc(1, sizeof(oc_state_t));
|
||||
device->priv = st;
|
||||
strcpy(st->filename_write, DEVICE_WRITE_DEFAULT);
|
||||
strcpy(st->filename_read, DEVICE_READ_DEFAULT);
|
||||
AssertFatal(st != NULL, "OC device: memory allocation failure\n");
|
||||
st->tx.continuous_tx = openair0_cfg->duplex_mode == duplex_mode_FDD;
|
||||
} 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 = OC1_DEV;
|
||||
|
||||
struct {
|
||||
int sample_rate;
|
||||
int tx_sample_advance;
|
||||
double tx_bw;
|
||||
double rx_bw;
|
||||
} config_table[] = {{245760000, 0, 200e6, 200e6},
|
||||
{184320000, 0, 100e6, 100e6},
|
||||
{122880000, 180, 80e6, 80e6},
|
||||
{92160000, 0, 60e6, 60e6},
|
||||
{61440000, 0, 40e6, 40e6},
|
||||
{46080000, 0, 40e6, 40e6},
|
||||
{30720000, 0, 40e6, 40e6},
|
||||
{23040000, 0, 20e6, 20e6},
|
||||
{15360000, 0, 10e6, 10e6},
|
||||
{7680000, 0, 5e6, 5e6},
|
||||
{1920000, 0, 1.25e6, 1.25e6}};
|
||||
size_t i = 0;
|
||||
openair0_cfg[0].sample_rate = 122880000; // only 122880000 is supported
|
||||
for (; i < sizeofArray(config_table); i++)
|
||||
if (config_table[i].sample_rate == (int)openair0_cfg[0].sample_rate) {
|
||||
device->openair0_cfg->tx_sample_advance = config_table[i].tx_sample_advance;
|
||||
device->openair0_cfg->tx_bw = config_table[i].tx_bw;
|
||||
device->openair0_cfg->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