Files
openairinterface5g/openair1/PHY/TOOLS/nr_phy_scope.c
T

1205 lines
42 KiB
C

/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Author and copyright: Laurent Thomas, open-cells.com
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/* Form definition file generated by fdesign */
#include <stdlib.h>
#include "executables/softmodem-common.h"
#include "executables/nr-softmodem-common.h"
#ifdef WEBSRVSCOPE
#include <ulfius.h>
#include "common/utils/websrv/websrv.h"
#include "common/utils/websrv/websrv_noforms.h"
/* STATICFORXSCOPE will be used to make function being called from websrv code non static when we compile
* with WEBSRVSCOPE mocro defined, but static when we compile for Xforms scope
*/
#define STATICFORXSCOPE
#define fl_add_canvas websrv_fl_add_canvas
#define fl_add_xyplot websrv_fl_add_xyplot
#define fl_get_xyplot_data_pointer websrv_fl_get_xyplot_data_pointer
#else
#include <forms.h>
#define STATICFORXSCOPE static
static const int scope_enb_num_ue = 1;
#endif
#include "nr_phy_scope.h"
#include "phy_scope.h"
const FL_COLOR rx_antenna_colors[4] = {FL_RED,FL_BLUE,FL_GREEN,FL_YELLOW};
const FL_COLOR water_colors[4] = {FL_BLUE,FL_GREEN,FL_YELLOW,FL_RED};
static void drawsymbol(FL_OBJECT *obj, int id,
FL_POINT *p, int n, int w, int h) {
fl_points( p, n, FL_YELLOW);
}
// button callback example
#if 0
static void dl_traffic_on_off( FL_OBJECT *button, long arg) {
if (fl_get_button(button)) {
fl_set_object_label(button, "DL Traffic ON");
otg_enabled = 1;
fl_set_object_color(button, FL_GREEN, FL_GREEN);
} else {
fl_set_object_label(button, "DL Traffic OFF");
otg_enabled = 0;
fl_set_object_color(button, FL_RED, FL_RED);
}
}
#endif
#define WATERFALL 10000
#ifdef WEBSRVSCOPE
/* copy data from softmodem buffer to message body */
int websrv_cpiqbuff_tomsg(OAIgraph_t *graph, scopeSample_t *c, int n, int id, int base)
{
int I = 0;
websrv_scope_params_t *WP = websrv_scope_getparams();
int newn = n;
websrv_scopedata_msg_t *msg;
websrv_nf_getdata(graph->graph, id, &msg);
if (n > MAX_NIQ_WEBSOCKMSG) {
LOG_E(UTIL, "Buffer id %i too small for %i iqs...\n", id, n);
return 0;
}
/* copy and sort the chart data on x to improve frontend perf */
int16_t *data_xy = msg->data_xy + base;
int16_t max_x = INT16_MIN;
for (int i = 0; i < n; i++) {
if (c[i].r < WP->xmin || c[i].i < WP->ymin || c[i].r > WP->xmax || c[i].i > WP->ymax) {
newn--;
continue;
}
if (max_x <= c[i].r) {
data_xy[I] = max_x = c[i].r;
data_xy[I + 1] = c[i].i;
I = I + 2;
} else {
for (int j = I + (base * 2); j >= 0; j = j - 2) {
if (msg->data_xy[j] <= c[i].r || j == 0) {
for (int k = I + (base * 2); k > j; k = k - 2) {
msg->data_xy[k + 2] = msg->data_xy[k];
msg->data_xy[k + 3] = msg->data_xy[k + 1];
}
msg->data_xy[j + 2] = c[i].r;
msg->data_xy[j + 3] = c[i].i;
I = I + 2;
break;
}
}
}
}
return newn;
}
int websrv_cpllrbuff_tomsg(OAIgraph_t *graph, int16_t *llrs, int n, int id, int iteration, int max_iteration)
{
websrv_scopedata_msg_t *msg;
websrv_nf_getdata(graph->graph, id, &msg);
websrv_scope_params_t *WP = websrv_scope_getparams();
if (n > MAX_LLR_WEBSOCKMSG) {
LOG_E(UTIL, "Buffer id %i too small for %i iqs...\n", id, n);
return 0;
}
/* save number of points (xmax) at beginning of buffer, as we try to minimize data sent */
/* all iterations (UE pdschh case) have same n (number of data points) */
if (iteration == 0) {
int32_t *iptr = (int32_t *)(msg->data_xy);
*iptr = n;
}
/* for each point we save llr and the corresponding offset if llr's below the configured threshold have been skipped */
/* offset is saved in 8bits so llr always transmitted when offset reach the 8 bits max. offset relative to x of previously transmitted point */
int16_t *dptr = msg->data_xy;
int newn = 2; // 2 first int16 used for saving maxx
int xoffset = 1;
int xres = (n / 1000 > CHAR_MAX) ? CHAR_MAX : (n / 1000);
int16_t latestllr = 0;
int imin = WP->llrxmin;
int imax = WP->llrxmax;
if (imax > n)
imax = n;
if (imin > imax)
imin = imax;
/* to keep data sorted, we insert points of each iteration at their x position, */
for (int i = imin; i < imax; i++) {
if (((llrs[i] - latestllr) >= WP->llr_ythresh) || ((latestllr - llrs[i]) >= WP->llr_ythresh) || (xoffset >= xres)) {
dptr[newn + iteration] = llrs[i];
latestllr = llrs[i];
dptr[newn + iteration + 1] = (int16_t)xoffset;
xoffset = 1;
newn = newn + max_iteration + 2;
} else {
xoffset++;
}
}
/* discard points which cannot be distinguished on graph: x and llr have almost identical values
for ( int i=4; i<(newn-1); i=i+2) {
for (int j=i+2 ; j<(i+(xres*2)) && j<(newn-1); j=j+2) {
if ( ((dptr[j] - dptr[i]) < yres) || ((dptr[i] - dptr[j]) < yres) ) {
newn=newn-2;
for (int k = j; k< newn; k++) {
((int8_t *)(msg->data_xy))[k] = (int8_t)msg->data_xy[k+2];
((int8_t *)(msg->data_xy))[k+1] = (int8_t)msg->data_xy[k+3];
}
}
}
}
*/
return newn;
}
void websrv_get_WF_buffers(OAIgraph_t *graph, websrv_scopedata_msg_t *msgp[])
{
for (int i = 0; i < sizeof(water_colors) / sizeof(FL_COLOR); i++) {
websrv_scopedata_msg_t *msg;
websrv_nf_getdata(graph->graph, i, &msg);
msg->header.msgtype = SCOPEMSG_TYPE_DATA;
msg->header.chartid = graph->chartid;
msg->header.datasetid = i;
msg->header.update = (i == (sizeof(water_colors) / sizeof(FL_COLOR) - 1)) ? 1 : 0;
msg->data_xy[0] = 0;
msgp[i] = msg;
}
}
void websrv_setpoint(int x, int y, websrv_scopedata_msg_t *msg)
{
msg->data_xy[0]++;
msg->data_xy[msg->data_xy[0]] = (int16_t)x;
msg->data_xy[0]++;
msg->data_xy[msg->data_xy[0]] = (int16_t)y;
}
#endif
static void commonGraph(OAIgraph_t *graph, int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label, FL_COLOR pointColor)
{
memset(graph, 0, sizeof(*graph));
if (type==WATERFALL) {
graph->waterFallh=h-15;
graph->waterFallAvg=malloc(sizeof(*graph->waterFallAvg) * graph->waterFallh);
for (int i=0; i< graph->waterFallh; i++)
graph->waterFallAvg[i]=0;
graph->graph=fl_add_canvas(FL_NORMAL_CANVAS, x, y, w, graph->waterFallh, label);
graph->text=fl_add_text(FL_NORMAL_TEXT, x, y+graph->waterFallh, w, 15, label);
fl_set_object_lcolor(graph->text,FL_WHITE);
fl_set_object_color(graph->text, FL_BLACK, FL_BLACK);
fl_set_object_lalign(graph->text, FL_ALIGN_CENTER );
} else {
graph->graph=fl_add_xyplot(type, x, y, w, h, label);
fl_set_object_lcolor(graph->graph, FL_WHITE ); // Label color
fl_set_object_color(graph->graph, FL_BLACK, pointColor);
for (int i=0; i< FL_MAX_XYPLOTOVERLAY; i++)
fl_set_xyplot_symbol(graph->graph, i, drawsymbol);
}
graph->x=x;
graph->y=y;
graph->w=w;
graph->h=h;
graph->maxX=0;
graph->maxY=0;
graph->minX=0;
graph->minY=0;
graph->initDone=false;
graph->iteration=0;
#ifdef WEBSRVSCOPE
graph->enabled = false;
#else
graph->enabled = true;
#endif
}
static OAIgraph_t gNBcommonGraph( void (*funct) (OAIgraph_t *graph, scopeData_t *p, int UE_id),
int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label, FL_COLOR pointColor) {
OAIgraph_t graph;
commonGraph(&graph, type, x, y, w, h, label, pointColor);
graph.gNBfunct=funct;
graph.nrUEfunct=NULL;
return graph;
}
static OAIgraph_t nrUEcommonGraph( void (*funct) (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id),
int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label, FL_COLOR pointColor) {
OAIgraph_t graph;
commonGraph(&graph, type, x, y, w, h, label, pointColor);
graph.gNBfunct=NULL;
graph.nrUEfunct=funct;
return graph;
}
#ifndef WEBSRVSCOPE
static void setRange(OAIgraph_t *graph, float minX, float maxX, float minY, float maxY) {
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);
}
if (maxY > graph->maxY || minY < graph->minY || fabs(maxY - graph->maxY) > fabs(graph->maxY) / 2
|| fabs(maxY - graph->maxY) > fabs(graph->maxY) / 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);
}
}
static void oai_xygraph_getbuff(OAIgraph_t *graph, float **x, float **y, int len, int layer) {
float *old_x;
float *old_y;
int old_len=-1;
if (graph->iteration >1)
fl_get_xyplot_data_pointer(graph->graph, layer, &old_x, &old_y, &old_len);
if (old_len != len) {
LOG_W(HW, "allocating graph of %d points\n", len);
float values[len];
float time[len];
// make time in case we will use it
for (int i=0; i<len; i++)
time[i] = values[i] = i;
if (layer==0)
fl_set_xyplot_data(graph->graph,time,values,len,"","","");
else
fl_add_xyplot_overlay(graph->graph,layer,time,values,len,rx_antenna_colors[layer]);
fl_get_xyplot_data_pointer(graph->graph, layer, &old_x, &old_y, &old_len);
AssertFatal(old_len == len, "graph len %i old_len %i\n", len, old_len);
}
*x=old_x;
*y=old_y;
}
#endif
static void oai_xygraph(OAIgraph_t *graph, float *x, float *y, int len, int layer, bool NoAutoScale) {
#ifdef WEBSRVSCOPE
websrv_scopedata_msg_t *msg = NULL;
websrv_nf_getdata(graph->graph, layer, &msg);
msg->header.msgtype = SCOPEMSG_TYPE_DATA;
msg->header.chartid = graph->chartid;
msg->header.datasetid = graph->datasetid;
msg->header.msgseg = 0;
msg->header.update = 1;
websrv_scope_senddata(len, (msg->header.chartid == SCOPEMSG_DATAID_LLR) ? 2 : 4, msg);
#else
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);
}
#endif
graph->iteration++;
}
static void genericWaterFall (OAIgraph_t *graph, scopeSample_t *values, const int datasize, const int divisions, const char *label) {
if ( values == NULL )
return;
#ifdef WEBSRVSCOPE
websrv_scopedata_msg_t *msgp[sizeof(water_colors) / sizeof(FL_COLOR)];
websrv_get_WF_buffers(graph, msgp);
#endif
fl_winset(FL_ObjWin(graph->graph));
const int samplesPerPixel=datasize/graph->w;
int displayPart=graph->waterFallh-ScaleZone;
int row=graph->iteration%displayPart;
double avg=0;
for (int i=0; i < displayPart; i++)
avg+=graph->waterFallAvg[i];
avg/=displayPart;
graph->waterFallAvg[row]=0;
for (int pix=0; pix<graph->w; pix++) {
scopeSample_t *end=values+(pix+1)*samplesPerPixel;
end-=2;
AssertFatal(end <= values+datasize,"diff : %tu", end-values+datasize);
double val=0;
for (scopeSample_t *s=values+(pix)*samplesPerPixel;
s <end;
s++)
val += SquaredNorm(*s);
val/=samplesPerPixel;
graph->waterFallAvg[row]+=val/graph->w;
int col=0;
if (val > avg*2 )
col=1;
if (val > avg*10 )
col=2;
if (val > avg*100 )
col=3;
#ifdef WEBSRVSCOPE
websrv_setpoint(pix, graph->iteration % displayPart, msgp[col]);
#else
fl_point(pix, graph->iteration%displayPart, water_colors[col]);
#endif
}
#ifdef WEBSRVSCOPE
for (int i = 0; i < sizeof(water_colors) / sizeof(FL_COLOR); i++) {
msgp[i]->header.msgseg = graph->iteration % displayPart;
websrv_scope_senddata(msgp[i]->data_xy[0], 2, msgp[i]);
}
#else
if (graph->initDone==false) {
for ( int i=0; i < graph->waterFallh; i++ )
for ( int j = 0 ; j < graph->w ; j++ )
fl_point(j, i, FL_BLACK);
for ( int i=1; i<divisions; i++)
for (int j= displayPart; j<graph->waterFallh; j++)
fl_point(i*(graph->w/divisions),j, FL_WHITE);
graph->initDone=true;
}
fl_set_object_label_f(graph->text, "%s, avg I/Q pow: %4.1f", label, 0/*sqrt(avg)*/);
#endif
graph->iteration++;
}
static void genericPowerPerAntena(OAIgraph_t *graph, const int nb_ant, const scopeSample_t **data, const int len) {
#ifdef WEBSRVSCOPE
websrv_scopedata_msg_t *msg = NULL;
websrv_nf_getdata(graph->graph, 0, &msg);
float *values = (float *)msg->data_xy;
#else
float *values, *time;
oai_xygraph_getbuff(graph, &time, &values, len, 0);
#endif
for (int ant=0; ant<nb_ant; ant++) {
if (data[ant] != NULL) {
for (int i=0; i<len; i++) {
values[i] = SquaredNorm(data[ant][i]);
}
#ifdef WEBSRVSCOPE
msg->header.msgtype = SCOPEMSG_TYPE_DATA;
msg->header.chartid = graph->chartid;
msg->header.datasetid = graph->datasetid;
msg->header.msgseg = 0;
msg->header.update = (ant == (nb_ant - 1)) ? 1 : 0;
websrv_scope_senddata(len, 4, msg);
#else
oai_xygraph(graph,time,values, len, ant, 10);
#endif
}
}
}
static void gNBWaterFall (OAIgraph_t *graph, scopeData_t *p, int nb_UEs) {
NR_DL_FRAME_PARMS *frame_parms=&p->gNB->frame_parms;
//use 1st antenna
genericWaterFall(graph, (scopeSample_t *)p->ru->common.rxdata[0],
frame_parms->samples_per_frame, frame_parms->slots_per_frame,
"X axis:one frame in time");
}
/* replaced by waterfall
static void timeSignal (OAIgraph_t *graph, PHY_VARS_gNB *phy_vars_gnb, RU_t *phy_vars_ru, const int nb_UEs) {
// Received signal in time domain of receive antenna 0
if (!phy_vars_ru->common.rxdata)
return;
NR_DL_FRAME_PARMS *frame_parms=&phy_vars_gnb->frame_parms;
genericLogPowerPerAntena(graph, frame_parms->nb_antennas_rx,
(const scopeSample_t **)phy_vars_ru->common.rxdata,
frame_parms->samples_per_frame);
}
*/
static void timeResponse (OAIgraph_t *graph, scopeData_t *p, int nb_UEs) {
scopeGraphData_t *val = p->liveData[gNBulDelay];
if (!val || !val->dataSize)
return;
#ifdef WEBSRVSCOPE
websrv_scopedata_msg_t *msg = NULL;
websrv_nf_getdata(graph->graph, 0, &msg);
float *values = (float *)msg->data_xy;
#else
float *values, *time;
oai_xygraph_getbuff(graph, &time, &values, val->lineSz, 0);
#endif
// We display UEs randomly, with one buffer
c16_t *samples = (c16_t *)(val + 1);
for (int i = 0; i < val->lineSz; i++) {
values[i] = SquaredNorm(samples[i]);
}
#ifdef WEBSRVSCOPE
msg->header.msgtype = SCOPEMSG_TYPE_DATA;
msg->header.chartid = graph->chartid;
msg->header.datasetid = graph->datasetid;
msg->header.msgseg = 0;
msg->header.update = 1;
websrv_scope_senddata(val->lineSz, 4, msg);
#else
oai_xygraph(graph, time, values, val->lineSz, 0, 10);
#endif
}
static void gNBfreqWaterFall (OAIgraph_t *graph, scopeData_t *p, int nb_UEs) {
NR_DL_FRAME_PARMS *frame_parms=&p->gNB->frame_parms;
//use 1st antenna
genericWaterFall(graph,
(scopeSample_t *)p->liveData[gNBRxdataF],
frame_parms->samples_per_frame_wCP,
frame_parms->slots_per_frame,
"X axis: Frequency domain, one subframe");
}
/*
static void frequencyResponse (OAIgraph_t *graph, PHY_VARS_gNB *phy_vars_gnb, RU_t *phy_vars_ru, int nb_UEs) {
NR_DL_FRAME_PARMS *frame_parms=&phy_vars_gnb->frame_parms;
genericLogPowerPerAntena(graph, frame_parms->nb_antennas_rx,
(const scopeSample_t **)phy_vars_ru->common.rxdataF,
frame_parms->samples_per_slot_wCP);
}
*/
static void puschLLR (OAIgraph_t *graph, scopeData_t *p, int nb_UEs) {
#ifdef WEBSRVSCOPE
int uestart = nb_UEs - 1; // web scope shows one UE signal, that can be selected from GUI
#else
int uestart = 0; // xforms scope designed to display nb_UEs signals
#endif
NR_DL_FRAME_PARMS *frame_parms=&p->gNB->frame_parms;
int num_re = frame_parms->N_RB_UL*12*frame_parms->symbols_per_slot;
int Qm = 2;
int coded_bits_per_codeword = num_re*Qm;
for (int ue = uestart; ue < nb_UEs; ue++) {
if (p->gNB->pusch_vars &&
p->gNB->pusch_vars[ue].llr ) {
int16_t *pusch_llr = (int16_t *)p->gNB->pusch_vars[ue].llr;
float *llr=NULL, *bit=NULL;
int nx = coded_bits_per_codeword;
#ifdef WEBSRVSCOPE
nx = websrv_cpllrbuff_tomsg(graph, pusch_llr, coded_bits_per_codeword, ue, 0, 0);
#else
oai_xygraph_getbuff(graph, &bit, &llr, coded_bits_per_codeword, ue);
for (int i=0; i<coded_bits_per_codeword; i++) {
llr[i] = (float) pusch_llr[i];
}
#endif
oai_xygraph(graph, bit, llr, nx, ue, 10);
}
}
}
static void puschIQ (OAIgraph_t *graph, scopeData_t *p, int nb_UEs) {
NR_DL_FRAME_PARMS *frame_parms=&p->gNB->frame_parms;
int sz=frame_parms->N_RB_UL*12*frame_parms->symbols_per_slot;
int newsz = sz;
#ifdef WEBSRVSCOPE
int uestart = nb_UEs - 1; // web scope shows one UE signal, that can be selected from GUI
#else
int uestart = 0; // xforms scope designed to display nb_UEs signals
#endif
for (int ue = uestart; ue < nb_UEs; ue++) {
if (p->gNB->pusch_vars &&
p->gNB->pusch_vars[ue].rxdataF_comp &&
p->gNB->pusch_vars[ue].rxdataF_comp[0] ) {
scopeSample_t *pusch_comp = (scopeSample_t *)p->gNB->pusch_vars[ue].rxdataF_comp[0];
float *I=NULL, *Q=NULL;
#ifdef WEBSRVSCOPE
newsz = websrv_cpiqbuff_tomsg(graph, pusch_comp, sz, 0, 0);
#else
oai_xygraph_getbuff(graph, &I, &Q, sz, ue);
for (int k=0; k<sz; k++ ) {
I[k] = pusch_comp[k].r;
Q[k] = pusch_comp[k].i;
}
#endif
oai_xygraph(graph, I, Q, newsz, ue, 10);
}
}
}
static void pucchEnergy (OAIgraph_t *graph, scopeData_t *p, int nb_UEs) {
// PUSCH I/Q of MF Output
/*
int32_t *pucch1ab_comp = (int32_t *) NULL; //phy_vars_gnb->pucch1ab_stats[UE_id];
int32_t *pucch1_comp = (int32_t *) NULL; //phy_vars_gnb->pucch1_stats[UE_id];
float I_pucch[10240],Q_pucch[10240],A_pucch[10240],B_pucch[10240],C_pucch[10240];
for (int ind=0; ind<10240; ind++) {
I_pucch[ind] = (float)pucch1ab_comp[2*(ind)];
Q_pucch[ind] = (float)pucch1ab_comp[2*(ind)+1];
A_pucch[ind] = pucch1_comp?(10*log10(pucch1_comp[ind])):0;
B_pucch[ind] = ind;
int32_t *pucch1_thres = (int32_t *) NULL; // phy_vars_gnb->pucch1_stats_thres[UE_id];
C_pucch[ind] = pucch1_thres?(float)pucch1_thres[ind]:0;
}
fl_set_xyplot_data(graph,I_pucch,Q_pucch,10240,"","","");
fl_set_xyplot_data(graph,B_pucch,A_pucch,1024,"","","");
fl_add_xyplot_overlay(graph,1,B_pucch,C_pucch,1024,FL_RED);
fl_set_xyplot_ybounds(graph,-5000,5000);
fl_set_xyplot_xbounds(graph,-5000,5000);
fl_set_xyplot_ybounds(graph,0,80);
}
*/
}
static void pucchIQ (OAIgraph_t *graph, scopeData_t *p, int nb_UEs) {
}
static void puschThroughtput (OAIgraph_t *graph, scopeData_t *p, int nb_UEs) {
// PUSCH Throughput
/*
float tput_time_enb[MAX_MOBILES_PER_GNB][TPUT_WINDOW_LENGTH] = {{0}};
float tput_enb[MAX_MOBILES_PER_GNB][TPUT_WINDOW_LENGTH] = {{0}};
memmove( tput_time_enb[UE_id], &tput_time_enb[UE_id][1], (TPUT_WINDOW_LENGTH-1)*sizeof(float) );
memmove( tput_enb[UE_id], &tput_enb[UE_id][1], (TPUT_WINDOW_LENGTH-1)*sizeof(float) );
tput_time_enb[UE_id][TPUT_WINDOW_LENGTH-1] = (float) 0;
// tput_enb[UE_id][TPUT_WINDOW_LENGTH-1] = ((float) total_dlsch_bitrate)/1000.0;
fl_set_xyplot_data(graph,tput_time_enb[UE_id],tput_enb[UE_id],TPUT_WINDOW_LENGTH,"","","");
// fl_get_xyplot_ybounds(form->pusch_tput,&ymin,&ymax);
// fl_set_xyplot_ybounds(form->pusch_tput,0,ymax);
*/
}
STATICFORXSCOPE OAI_phy_scope_t *create_phy_scope_gnb(void)
{
FL_OBJECT *obj;
OAI_phy_scope_t *fdui = calloc_or_fail((sizeof *fdui), 1);
// Define form
fdui->phy_scope = fl_bgn_form( FL_NO_BOX, 800, 800 );
fl_set_form_dblbuffer(fdui->phy_scope, 1);
// This the whole UI box
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;
// Received signal
fdui->graph[0] = gNBcommonGraph( gNBWaterFall, WATERFALL, 0, curY, 400, 100,
"Received Signal (Time-Domain, one frame)", FL_RED );
fdui->graph[0].chartid = SCOPEMSG_DATAID_WF; // tells websrv frontend to use WF chart for displaying
fdui->graph[0].datasetid = 0; // not used for WF
// Time-domain channel response
fdui->graph[1] = gNBcommonGraph( timeResponse, FL_NORMAL_XYPLOT, 410, curY, 400, 100,
"SRS Frequency Response (samples, abs)", FL_RED );
fl_get_object_bbox(fdui->graph[1].graph,&x, &y,&w, &h);
curY+=h;
fdui->graph[1].chartid = SCOPEMSG_DATAID_TRESP;
// Frequency-domain channel response
fdui->graph[2] = gNBcommonGraph( gNBfreqWaterFall, WATERFALL, 0, curY, 800, 100,
"Channel Frequency domain (RE, one frame)", FL_RED );
fl_get_object_bbox(fdui->graph[2].graph,&x, &y,&w, &h);
curY+=h+20;
fdui->graph[2].chartid = SCOPEMSG_DATAID_WF; // tells websrv frontend to use WF chart for displaying
fdui->graph[2].datasetid = 0; // not used for WF
// LLR of PUSCH
fdui->graph[3] = gNBcommonGraph( puschLLR, FL_POINTS_XYPLOT, 0, curY, 500, 200,
"PUSCH Log-Likelihood Ratios (LLR, mag)", FL_YELLOW );
fdui->graph[3].chartid = SCOPEMSG_DATAID_LLR; // tells websrv frontend to use LLR chart for displaying
fdui->graph[3].datasetid = 0; // tells websrv frontend to use dataset index 0 in LLR chart
// I/Q PUSCH comp
fdui->graph[4] = gNBcommonGraph( puschIQ, FL_POINTS_XYPLOT, 500, curY, 300, 200,
"PUSCH I/Q of MF Output", FL_YELLOW );
fl_get_object_bbox(fdui->graph[3].graph,&x, &y,&w, &h);
curY+=h;
fdui->graph[4].chartid = SCOPEMSG_DATAID_IQ; // tells websrv frontend to use constellation chart for displaying
fdui->graph[4].datasetid = 0; // tells websrv frontend to use dataset 0 of constellation chart
// I/Q PUCCH comp (format 1)
fdui->graph[5] = gNBcommonGraph( pucchEnergy, FL_POINTS_XYPLOT, 0, curY, 300, 100,
"PUCCH1 Energy (SR)", FL_YELLOW );
// fl_set_xyplot_xgrid( fdui->pusch_llr,FL_GRID_MAJOR);
// I/Q PUCCH comp (fromat 1a/b)
fdui->graph[6] = gNBcommonGraph( pucchIQ, FL_POINTS_XYPLOT, 500, curY, 300, 100,
"PUCCH I/Q of MF Output", FL_YELLOW );
fl_get_object_bbox(fdui->graph[6].graph,&x, &y,&w, &h);
curY+=h;
fdui->graph[6].chartid = SCOPEMSG_DATAID_IQ; // tells websrv frontend to use constellation chart for displaying
fdui->graph[6].datasetid = 1; // tells websrv frontend to use dataset 1 of constellation chart
// Throughput on PUSCH
fdui->graph[7] = gNBcommonGraph( puschThroughtput, FL_NORMAL_XYPLOT, 0, curY, 500, 100,
"PUSCH Throughput [frame]/[kbit/s]", FL_WHITE );
fdui->graph[8].graph=NULL;
fl_end_form( );
if (fdui->phy_scope)
fdui->phy_scope->fdui = fdui;
fl_show_form (fdui->phy_scope, FL_PLACE_HOTSPOT, FL_FULLBORDER, "NR UL SCOPE gNB");
return fdui;
}
STATICFORXSCOPE void phy_scope_gNB(OAI_phy_scope_t *form, scopeData_t *p, int UE_id)
{
static OAI_phy_scope_t *rememberForm=NULL;
if (form==NULL)
form=rememberForm;
else
rememberForm=form;
if (form==NULL)
return;
int i=0;
while (form->graph[i].graph) {
if (form->graph[i].enabled)
form->graph[i].gNBfunct(form->graph + i, p, UE_id);
i++;
}
//fl_check_forms();
}
#ifndef WEBSRVSCOPE
static void *scope_thread_gNB(void *arg) {
scopeData_t *p=(scopeData_t *) arg;
size_t stksize=0;
pthread_attr_t atr;
pthread_attr_init(&atr);
pthread_attr_getstacksize(&atr, &stksize);
pthread_attr_setstacksize(&atr,32*1024*1024 );
sleep(3); // no clean interthread barriers
int fl_argc=1;
char *name="5G-gNB-scope";
fl_initialize (&fl_argc, &name, NULL, 0, 0);
int nb_ue=min(MAX_MOBILES_PER_GNB, scope_enb_num_ue);
OAI_phy_scope_t *form_gnb = create_phy_scope_gnb();
while (!oai_exit) {
fl_freeze_form(form_gnb->phy_scope);
phy_scope_gNB(form_gnb, p, nb_ue);
fl_unfreeze_form(form_gnb->phy_scope);
fl_redraw_form(form_gnb->phy_scope);
usleep(99*1000);
}
return NULL;
}
#endif
static void scopeUpdaterGnb(enum PlotTypeGnbIf plotType, int numElt)
{
switch (plotType) {
case puschLLRe:
/* update PUSCH LLR plot */
break;
case puschIQe:
/* update PUSCH IQ plot */
break;
}
}
STATICFORXSCOPE void gNBinitScope(scopeParms_t *p)
{
AssertFatal(p->gNB->scopeData = calloc_or_fail(sizeof(scopeData_t), 1), "");
scopeData_t *scope=(scopeData_t *) p->gNB->scopeData;
scope->argc=p->argc;
scope->argv=p->argv;
scope->ru=p->ru;
scope->gNB=p->gNB;
scope->scopeUpdater = scopeUpdaterGnb;
scope->copyData = copyData;
#ifndef WEBSRVSCOPE
pthread_t forms_thread;
threadCreate(&forms_thread, scope_thread_gNB, p->gNB->scopeData, "scope", -1, OAI_PRIORITY_RT_LOW);
copyDataMutexInit(scope);
#endif
}
static void ueWaterFall (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
// Received signal in time domain of receive antenna 0
genericWaterFall(graph,
(scopeSample_t *) phy_vars_ue->common_vars.rxdata[0],
phy_vars_ue->frame_parms.samples_per_frame,
phy_vars_ue->frame_parms.slots_per_frame,
"X axis: one frame time");
}
/* replaced by waterfall
static void ueTimeResponse (OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
// Received signal in time domain of receive antenna 0
genericLogPowerPerAntena(graph, phy_vars_ue->frame_parms.nb_antennas_rx,
(const scopeSample_t **) phy_vars_ue->common_vars.rxdata,
phy_vars_ue->frame_parms.samples_per_frame);
}
*/
static void ueChannelResponse (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
enum scopeDataType typ = (phy_vars_ue->sl_mode) ? psbchDlChEstimateTime : pbchDlChEstimateTime;
// Channel Impulse Response
if (!data[typ])
return;
const scopeSample_t *tmp = (scopeSample_t *)(data[typ] + 1);
genericPowerPerAntena(graph, data[typ]->colSz, &tmp, data[typ]->lineSz);
}
static void ueFreqWaterFall (scopeGraphData_t **data, OAIgraph_t *graph,PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id ) {
//use 1st antenna
if (!data[commonRxdataF])
return;
const int sz = data[commonRxdataF]->lineSz;
scopeSample_t *rxdataF = (scopeSample_t *)(data[commonRxdataF]+1);
genericWaterFall(graph,
rxdataF,
sz,
1,
"X axis: one slot frequency" );
}
/*
static void uePbchFrequencyResp (OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
// Channel Frequency Response (includes 5 complex sample for filter)
if (!phy_vars_ue->pbch_vars[eNB_id]->dl_ch_estimates)
return;
NR_DL_FRAME_PARMS *frame_parms = &phy_vars_ue->frame_parms;
uint8_t nb_antennas_rx = frame_parms->nb_antennas_rx;
uint8_t nb_antennas_tx = frame_parms->nb_antenna_ports_gNB;
scopeSample_t **chest_f = (scopeSample_t **) phy_vars_ue->pbch_vars[eNB_id]->dl_ch_estimates;
int ind = 0;
float *freq, *chest_f_abs;
oai_xygraph_getbuff(graph, &freq, &chest_f_abs, frame_parms->ofdm_symbol_size, 0);
for (int atx=0; atx<nb_antennas_tx; atx++) {
for (int arx=0; arx<nb_antennas_rx; arx++) {
if (chest_f[(atx<<1)+arx] != NULL) {
for (int k=0; k<frame_parms->ofdm_symbol_size; k++) {
freq[ind] = (float)ind;
chest_f_abs[ind] = (short)10*log10(1.0+SquaredNorm(chest_f[(atx<<1)+arx][6144+k]));
ind++;
}
}
}
}
// tx antenna 0
//fl_set_xyplot_xbounds(form->chest_f,0,nb_antennas_rx*nb_antennas_tx*nsymb_ce);
//fl_set_xyplot_xtics(form->chest_f,nb_antennas_rx*nb_antennas_tx*frame_parms->symbols_per_tti,2);
// fl_set_xyplot_xtics(form->chest_f,nb_antennas_rx*nb_antennas_tx*2,2);
//fl_set_xyplot_xgrid(form->chest_f,FL_GRID_MAJOR);
oai_xygraph(graph,freq,chest_f_abs,frame_parms->ofdm_symbol_size,0,10);
}
*/
static void uePbchLLR (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
enum scopeDataType typ = (phy_vars_ue->sl_mode) ? psbchLlr : pbchLlr;
// PBCH LLRs
if (!data[typ])
return;
const int sz = data[typ]->lineSz;
// const int antennas=data[typ]->colSz;
// We take the first antenna only for now
int16_t *llrs = (int16_t *)(data[typ] + 1);
float *llr_pbch=NULL, *bit_pbch=NULL;
int nx = sz;
#ifdef WEBSRVSCOPE
nx = websrv_cpllrbuff_tomsg(graph, llrs, sz, UE_id, 0, 0);
#else
oai_xygraph_getbuff(graph, &bit_pbch, &llr_pbch, sz, 0);
for (int i=0; i<sz; i++) {
llr_pbch[i] = llrs[i];
}
#endif
oai_xygraph(graph, bit_pbch, llr_pbch, nx, 0, 10);
}
static void uePbchIQ (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
enum scopeDataType typ = (phy_vars_ue->sl_mode) ? psbchRxdataF_comp : pbchRxdataF_comp;
// PBCH I/Q of MF Output
if (!data[typ])
return;
scopeSample_t *pbch_comp = (scopeSample_t *)(data[typ] + 1);
const int sz = data[typ]->lineSz;
int newsz = sz;
float *I=NULL, *Q=NULL;
#ifdef WEBSRVSCOPE
newsz = websrv_cpiqbuff_tomsg(graph, pbch_comp, sz, 0, 0);
#else
oai_xygraph_getbuff(graph, &I, &Q, sz, 0);
for (int i=0; i<sz; i++) {
I[i]=pbch_comp[i].r;
Q[i]=pbch_comp[i].i;
}
#endif
oai_xygraph(graph, I, Q, newsz, 0, true);
}
static void uePcchLLR (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
// PDCCH LLRs
if (!data[pdcchLlr])
return;
//int num_re = 4*273*12; // 12*frame_parms->N_RB_DL*num_pdcch_symbols
//int Qm = 2;
const int sz=data[pdcchLlr]->lineSz;
float *llr=NULL, *bit=NULL;
int nx = sz;
int16_t *pdcch_llr = (int16_t *)(data[pdcchLlr]+1);
#ifdef WEBSRVSCOPE
nx = websrv_cpllrbuff_tomsg(graph, pdcch_llr, sz, UE_id, 0, 0);
#else
oai_xygraph_getbuff(graph, &bit, &llr, sz, 0);
for (int i=0; i<sz; i++) {
llr[i] = (float) pdcch_llr[i];
}
#endif
oai_xygraph(graph, bit, llr, nx, 0, 10);
}
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
if (!data[pdcchRxdataF_comp])
return;
const int sz=data[pdcchRxdataF_comp]->lineSz;
int newsz = sz;
//const int antennas=data[pdcchRxdataF_comp]->colSz;
// We take the first antenna only for now
float *I=NULL, *Q=NULL;
scopeSample_t *pdcch_comp = (scopeSample_t *) (data[pdcchRxdataF_comp]+1);
#ifdef WEBSRVSCOPE
newsz = websrv_cpiqbuff_tomsg(graph, pdcch_comp, sz, 0, 0);
#else
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;
}
#endif
oai_xygraph(graph, I, Q, newsz, 0, 10);
}
static void uePdschLLR (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
// PDSCH LLRs
if (!data[pdschLlr])
return;
const int sz = data[pdschLlr]->lineSz;
float *llr=NULL, *bit=NULL;
int nx = sz;
int16_t *pdsch_llr = (int16_t *)(data[pdschLlr]+1);
#ifdef WEBSRVSCOPE
nx = websrv_cpllrbuff_tomsg(graph, pdsch_llr, sz, UE_id, 0, 0);
#else
oai_xygraph_getbuff(graph, &bit, &llr, sz, 0);
for (int i=0; i<sz; i++) {
llr[i] = (float) pdsch_llr[i];
}
#endif
//fl_set_xyplot_xbounds(form->pdsch_llr,0,coded_bits_per_codeword);
oai_xygraph(graph, bit, llr, nx, 0, 10);
}
static void uePdschIQ (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
// PDSCH I/Q of MF Output
if (!data[pdschRxdataF_comp])
return;
const int sz=data[pdschRxdataF_comp]->lineSz;
int nz = sz;
float *I=NULL, *Q=NULL;
scopeSample_t *pdsch_comp = (scopeSample_t *) (data[pdschRxdataF_comp]+1);
#ifdef WEBSRVSCOPE
nz += websrv_cpiqbuff_tomsg(graph, pdsch_comp, sz, 0, 0);
#else
oai_xygraph_getbuff(graph, &I, &Q, sz, 0);
for (int s=0; s<sz; s++) {
I[s] = pdsch_comp[s].r;
Q[s] = pdsch_comp[s].i;
}
#endif
oai_xygraph(graph, I, Q, nz, 0, 10);
}
static void uePdschThroughput (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
/*
float tput_time_ue[MAX_MOBILES_PER_GNB][TPUT_WINDOW_LENGTH] = {{0}};
float tput_ue[MAX_MOBILES_PER_GNB][TPUT_WINDOW_LENGTH] = {{0}};
float tput_ue_max[MAX_MOBILES_PER_GNB] = {0};
// PDSCH Throughput
memmove( tput_time_ue[UE_id], &tput_time_ue[UE_id][1], (TPUT_WINDOW_LENGTH-1)*sizeof(float) );
memmove( tput_ue[UE_id], &tput_ue[UE_id][1], (TPUT_WINDOW_LENGTH-1)*sizeof(float) );
tput_time_ue[UE_id][TPUT_WINDOW_LENGTH-1] = (float) frame;
tput_ue[UE_id][TPUT_WINDOW_LENGTH-1] = ((float) total_dlsch_bitrate)/1000.0;
if (tput_ue[UE_id][TPUT_WINDOW_LENGTH-1] > tput_ue_max[UE_id]) {
tput_ue_max[UE_id] = tput_ue[UE_id][TPUT_WINDOW_LENGTH-1];
}
fl_set_xyplot_data(form->pdsch_tput,tput_time_ue[UE_id],tput_ue[UE_id],TPUT_WINDOW_LENGTH,"","","");
fl_set_xyplot_ybounds(form->pdsch_tput,0,tput_ue_max[UE_id]);
*/
}
STATICFORXSCOPE OAI_phy_scope_t *create_phy_scope_nrue(int ID)
{
FL_OBJECT *obj;
OAI_phy_scope_t *fdui = calloc_or_fail((sizeof *fdui), 1);
// Define form
fdui->phy_scope = fl_bgn_form( FL_NO_BOX, 800, 900 );
fl_set_form_dblbuffer(fdui->phy_scope, 1);
// This the whole UI box
obj = fl_add_box( FL_BORDER_BOX, 0, 0, 800, 900, "" );
fl_set_object_color( obj, FL_BLACK, FL_BLACK );
int curY=0,x,y,w,h;
// Received signal
fdui->graph[0] = nrUEcommonGraph(ueWaterFall,
WATERFALL, 0, curY, 400, 100, "Received Signal (Time-Domain, one frame)", FL_RED );
fdui->graph[0].chartid = SCOPEMSG_DATAID_WF; // tells websrv frontend to use WF chart for displaying
fdui->graph[0].datasetid = 0; // not used for WF
// Time-domain channel response
fdui->graph[1] = nrUEcommonGraph(ueChannelResponse,
FL_NORMAL_XYPLOT, 400, curY, 400, 100, "Channel Impulse Response (samples, abs)", FL_RED );
fdui->graph[1].chartid = SCOPEMSG_DATAID_TRESP;
fl_get_object_bbox(fdui->graph[1].graph,&x, &y,&w, &h);
curY+=h;
// Frequency-domain channel response
fdui->graph[2] = nrUEcommonGraph(ueFreqWaterFall,
WATERFALL, 0, curY, 800, 100, "Channel Frequency (RE, one slot)", FL_RED );
fl_get_object_bbox(fdui->graph[2].graph,&x, &y,&w, &h);
curY+=h+20;
fdui->graph[2].chartid = SCOPEMSG_DATAID_WF; // tells websrv frontend to use WF chart for displaying
fdui->graph[2].datasetid = 0; // not used for WF
// LLR of PBCH
fdui->graph[3] = nrUEcommonGraph(uePbchLLR,
FL_POINTS_XYPLOT, 0, curY, 500, 100, "PBCH Log-Likelihood Ratios (LLR, mag)", FL_GREEN );
fl_set_xyplot_xgrid(fdui->graph[3].graph,FL_GRID_MAJOR);
fdui->graph[3].chartid = SCOPEMSG_DATAID_LLR; // tells websrv frontend to use LLR chart for displaying
fdui->graph[3].datasetid = 0; // tells websrv frontend to use dataset index 0 in LLR chart
// I/Q PBCH comp
fdui->graph[4] = nrUEcommonGraph(uePbchIQ,
FL_POINTS_XYPLOT, 500, curY, 300, 100, "PBCH I/Q of MF Output", FL_GREEN );
fl_get_object_bbox(fdui->graph[3].graph,&x, &y,&w, &h);
curY += h;
fdui->graph[4].chartid = SCOPEMSG_DATAID_IQ; // tells websrv frontend to use constellation chart for displaying
fdui->graph[4].datasetid = 0; // tells websrv frontend which dataset to use in the window
// LLR of PDCCH
fdui->graph[5] = nrUEcommonGraph(uePcchLLR,
FL_POINTS_XYPLOT, 0, curY, 500, 100, "PDCCH Log-Likelihood Ratios (LLR, mag)", FL_CYAN );
fl_set_xyplot_xgrid(fdui->graph[5].graph,FL_GRID_MAJOR);
fdui->graph[5].chartid = SCOPEMSG_DATAID_LLR; // tells websrv frontend to use LLR chart for displaying
fdui->graph[5].datasetid = 1; // tells websrv frontend to use dataset index 1 in LLR chart
// I/Q PDCCH comp
fdui->graph[6] = nrUEcommonGraph(uePcchIQ,
FL_POINTS_XYPLOT, 500, curY, 300, 100, "PDCCH I/Q of MF Output", FL_CYAN );
fl_get_object_bbox(fdui->graph[5].graph,&x, &y,&w, &h);
curY+=h;
fdui->graph[6].chartid = SCOPEMSG_DATAID_IQ; // tells websrv frontend to use constellation chart for displaying
fdui->graph[6].datasetid = 1; // tells websrv frontend which dataset to use in the window
// LLR of PDSCH
fdui->graph[7] = nrUEcommonGraph(uePdschLLR,
FL_POINTS_XYPLOT, 0, curY, 500, 200, "PDSCH Log-Likelihood Ratios (LLR, mag)", FL_YELLOW );
fl_set_xyplot_xgrid(fdui->graph[7].graph,FL_GRID_MAJOR);
fdui->graph[7].chartid = SCOPEMSG_DATAID_LLR; // tells websrv frontend to use LLR chart for displaying
fdui->graph[7].datasetid = 2; // tells websrv frontend to use dataset index 2 in LLR chart
// I/Q PDSCH comp
fdui->graph[8] = nrUEcommonGraph(uePdschIQ,
FL_POINTS_XYPLOT, 500, curY, 300, 200, "PDSCH I/Q of MF Output", FL_YELLOW );
fl_get_object_bbox(fdui->graph[8].graph,&x, &y,&w, &h);
curY+=h;
fdui->graph[8].chartid = SCOPEMSG_DATAID_IQ; // tells websrv frontend to use constellation chart for displaying
fdui->graph[8].datasetid = 2; // tells websrv frontend which dataset to use in the window
// Throughput on PDSCH
fdui->graph[9] = nrUEcommonGraph(uePdschThroughput,
FL_NORMAL_XYPLOT, 0, curY, 500, 100, "PDSCH Throughput [frame]/[kbit/s]", FL_WHITE );
fdui->graph[10].graph=NULL;
// Generic UE Button
#if 0
fdui->button_0 = fl_add_button( FL_PUSH_BUTTON, 540, 720, 240, 40, "" );
fl_set_object_lalign(fdui->button_0, FL_ALIGN_CENTER );
//openair_daq_vars.use_ia_receiver = 0;
fl_set_button(fdui->button_0,0);
fl_set_object_label(fdui->button_0, "IA Receiver OFF");
fl_set_object_color(fdui->button_0, FL_RED, FL_RED);
fl_set_object_callback(fdui->button_0, ia_receiver_on_off, 0 );
fl_hide_object(fdui->button_0);
#endif
fl_end_form( );
if (fdui->phy_scope)
fdui->phy_scope->fdui = fdui;
char buf[100];
sprintf(buf,"NR DL SCOPE UE %d", ID);
fl_show_form (fdui->phy_scope, FL_PLACE_HOTSPOT, FL_FULLBORDER, buf);
return fdui;
}
STATICFORXSCOPE void phy_scope_nrUE(scopeGraphData_t **UEliveData, OAI_phy_scope_t *form, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id)
{
static OAI_phy_scope_t *remeberForm=NULL;
if (form==NULL)
form=remeberForm;
else
remeberForm=form;
if (form==NULL)
return;
int i=0;
while (form->graph[i].graph) {
if (form->graph[i].enabled)
form->graph[i].nrUEfunct(UEliveData, form->graph + i, phy_vars_ue, eNB_id, UE_id);
i++;
}
//fl_check_forms();
}
#ifndef WEBSRVSCOPE
static void *nrUEscopeThread(void *arg) {
PHY_VARS_NR_UE *ue=(PHY_VARS_NR_UE *)arg;
size_t stksize;
pthread_attr_t atr= {0};
pthread_attr_getstacksize(&atr, &stksize);
pthread_attr_setstacksize(&atr,32*1024*1024 );
int fl_argc=1;
char *name="5G-UE-scope";
fl_initialize (&fl_argc, &name, NULL, 0, 0);
OAI_phy_scope_t *form_nrue=create_phy_scope_nrue(0);
while (!oai_exit) {
fl_freeze_form(form_nrue->phy_scope);
phy_scope_nrUE((( scopeData_t *)ue->scopeData)->liveData,
form_nrue,
ue,
0,0);
fl_unfreeze_form(form_nrue->phy_scope);
fl_redraw_form(form_nrue->phy_scope);
usleep(99*1000);
}
pthread_exit((void *)arg);
}
#endif
STATICFORXSCOPE void nrUEinitScope(PHY_VARS_NR_UE *ue)
{
AssertFatal(ue->scopeData = calloc_or_fail(sizeof(scopeData_t), 1), "");
scopeData_t *scope=(scopeData_t *) ue->scopeData;
scope->copyData = copyData;
#ifndef WEBSRVSCOPE
pthread_t forms_thread;
threadCreate(&forms_thread, nrUEscopeThread, ue, "scope", -1, OAI_PRIORITY_RT_LOW);
copyDataMutexInit(scope);
#endif
}
void nrscope_autoinit(void *dataptr) {
AssertFatal((IS_SOFTMODEM_GNB || IS_SOFTMODEM_5GUE), "Scope cannot find NRUE or GNB context");
if (IS_SOFTMODEM_GNB)
gNBinitScope(dataptr);
else
nrUEinitScope(dataptr);
}
// Kept to put back the functionality soon
#if 0
//FD_stats_form *form_stats=NULL,*form_stats_l2=NULL;
//char title[255];
//static pthread_t forms_thread; //xforms
static void reset_stats_gNB(FL_OBJECT *button,
long arg) {
int i,k;
//PHY_VARS_gNB *phy_vars_gNB = RC.gNB[0][0];
for (i=0; i<MAX_MOBILES_PER_GNB; i++) {
for (k=0; k<8; k++) { //harq_processes
/* for (j=0; j<phy_vars_gNB->dlsch[i][0]->Mlimit; j++) {
phy_vars_gNB->UE_stats[i].dlsch_NAK[k][j]=0;
phy_vars_gNB->UE_stats[i].dlsch_ACK[k][j]=0;
phy_vars_gNB->UE_stats[i].dlsch_trials[k][j]=0;
}
phy_vars_gNB->UE_stats[i].dlsch_l2_errors[k]=0;
phy_vars_gNB->UE_stats[i].ulsch_errors[k]=0;
phy_vars_gNB->UE_stats[i].ulsch_consecutive_errors=0;
phy_vars_gNB->UE_stats[i].dlsch_sliding_cnt=0;
phy_vars_gNB->UE_stats[i].dlsch_NAK_round0=0;
phy_vars_gNB->UE_stats[i].dlsch_mcs_offset=0;*/
}
}
}
static FD_stats_form *create_form_stats_form(int ID) {
FL_OBJECT *obj;
FD_stats_form *fdui = calloc_or_fail(( sizeof *fdui ),1);
fdui->vdata = fdui->cdata = NULL;
fdui->ldata = 0;
fdui->stats_form = fl_bgn_form( FL_NO_BOX, 1115, 900 );
obj = fl_add_box( FL_UP_BOX, 0, 0, 1115, 900, "" );
//fdui->stats_text = obj = fl_add_text( FL_NORMAL_TEXT, 60, 50, 1000, 810, "test" );
//fl_set_object_lsize( obj, FL_TINY_SIZE );
fdui->stats_text = obj = fl_add_browser( FL_NORMAL_BROWSER, 60, 50, 1000, 810, "test" );
fl_set_browser_fontsize(obj,FL_TINY_SIZE);
fdui->stats_button = obj = fl_add_button( FL_PUSH_BUTTON, 60, 10, 130, 30, "Reset Stats" );
fl_set_object_lalign( obj, FL_ALIGN_CENTER );
fl_set_object_color( obj, FL_GREEN, FL_GREEN);
fl_end_form( );
fdui->stats_form->fdui = fdui;
return fdui;
}
#endif