/*
 * 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 "nr_phy_scope.h"
#include "executables/nr-softmodem-common.h"
#include <forms.h>

#define TPUT_WINDOW_LENGTH 100
#define localBuff(NaMe,SiZe) float NaMe[SiZe]; memset(NaMe,0,sizeof(NaMe));
int otg_enabled;

FL_COLOR rx_antenna_colors[4] = {FL_RED,FL_BLUE,FL_GREEN,FL_YELLOW};

float tput_time_enb[NUMBER_OF_UE_MAX][TPUT_WINDOW_LENGTH] = {{0}};
float tput_enb[NUMBER_OF_UE_MAX][TPUT_WINDOW_LENGTH] = {{0}};
float tput_time_ue[NUMBER_OF_UE_MAX][TPUT_WINDOW_LENGTH] = {{0}};
float tput_ue[NUMBER_OF_UE_MAX][TPUT_WINDOW_LENGTH] = {{0}};
float tput_ue_max[NUMBER_OF_UE_MAX] = {0};

typedef struct {
  int16_t r;
  int16_t i;
} scopeSample_t;
#define SquaredNorm(VaR) ((VaR).r*(VaR).r+(VaR).i*(VaR).i)

typedef struct OAIgraph {
  FL_OBJECT *graph;
  void (*gNBfunct) (FL_OBJECT *graph, PHY_VARS_gNB *phy_vars_gnb, RU_t *phy_vars_ru, int UE_id);
  void (*nrUEfunct) (FL_OBJECT *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id);
} OAIgraph_t;

/* Forms and Objects */
typedef struct {
  FL_FORM    *phy_scope;
  OAIgraph_t graph[20];
  FL_OBJECT *button_0;
} FD_phy_scope_t;

typedef struct {
  FL_FORM    *stats_form;
  void       *vdata;
  char       *cdata;
  long        ldata;
  FL_OBJECT *stats_text;
  FL_OBJECT *stats_button;
} FD_stats_form;

static void drawsymbol(FL_OBJECT *obj, int id,
                       FL_POINT *p, int n, int w, int h) {
  fl_points( p, n, FL_YELLOW);
}

static void ia_receiver_on_off( FL_OBJECT *button, long arg) {
  if (fl_get_button(button)) {
    fl_set_object_label(button, "IA Receiver ON");
    //    PHY_vars_UE_g[0][0]->use_ia_receiver = 1;
    fl_set_object_color(button, FL_GREEN, FL_GREEN);
  } else {
    fl_set_object_label(button, "IA Receiver OFF");
    //    PHY_vars_UE_g[0][0]->use_ia_receiver = 0;
    fl_set_object_color(button, FL_RED, FL_RED);
  }
}

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);
  }
}

static FL_OBJECT *commonGraph( int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label, FL_COLOR pointColor) {
  FL_OBJECT *graph;
  graph=fl_add_xyplot(type, x, y, w, h, label);
  fl_set_object_boxtype(graph, FL_EMBOSSED_BOX );
  fl_set_object_lcolor(graph, FL_WHITE ); // Label color
  fl_set_xyplot_symbol(graph, 0, drawsymbol);
  fl_set_object_color(graph, FL_BLACK, pointColor);
  return graph;
}

static OAIgraph_t gNBcommonGraph( void (*funct) (FL_OBJECT *graph, PHY_VARS_gNB *phy_vars_gnb, RU_t *phy_vars_ru, 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;
  graph.graph=commonGraph(type, x, y, w, h, label, pointColor);
  graph.gNBfunct=funct;
  graph.nrUEfunct=NULL;
  return graph;
}

static OAIgraph_t nrUEcommonGraph( void (*funct) (FL_OBJECT *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;
  graph.graph=commonGraph(type, x, y, w, h, label, pointColor);
  graph.gNBfunct=NULL;
  graph.nrUEfunct=funct;
  return graph;
}

void phy_scope_gNB(FD_phy_scope_t *form,
                   PHY_VARS_gNB *phy_vars_gnb,
                   RU_t *phy_vars_ru,
                   int UE_id) {
  static FD_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) {
    form->graph[i].gNBfunct(form->graph[i].graph, phy_vars_gnb, phy_vars_ru, UE_id);
    i++;
  }

  fl_check_forms();
}

static void timeSignal (FL_OBJECT *graph, PHY_VARS_gNB *phy_vars_gnb, RU_t *phy_vars_ru, int UE_id) {
  // 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;
  uint8_t nb_antennas_rx = frame_parms->nb_antennas_rx;
  scopeSample_t **rxsig_t = (scopeSample_t **)phy_vars_ru->common.rxdata;
  float rxsig_t_dB[frame_parms->samples_per_frame];
  float time[frame_parms->samples_per_frame];

  for (int arx=0; arx<nb_antennas_rx; arx++) {
    if (rxsig_t[arx] != NULL) {
      for (int i=0; i<frame_parms->samples_per_frame; i++) {
        rxsig_t_dB[i] = 10*log10(1.0+SquaredNorm(rxsig_t[arx][i]));
      }

      if (arx==0)
        fl_set_xyplot_data(graph,time,rxsig_t_dB, frame_parms->samples_per_frame,"","","");
      else
        fl_add_xyplot_overlay(graph,arx,time,rxsig_t_dB,frame_parms->samples_per_frame,rx_antenna_colors[arx]);
    }
  }
}

static void timeResponse (FL_OBJECT *graph, PHY_VARS_gNB *phy_vars_gnb, RU_t *phy_vars_ru, int UE_id) {
  if (!phy_vars_gnb->pusch_vars[UE_id]->ul_ch_estimates_time)
    return;

  NR_DL_FRAME_PARMS *frame_parms=&phy_vars_gnb->frame_parms;
  uint8_t nb_antennas_rx = frame_parms->nb_antennas_rx;
  scopeSample_t **chest_t = (scopeSample_t **) phy_vars_gnb->pusch_vars[UE_id]->ul_ch_estimates_time;
  int ymax = 0;
  float time2[2*frame_parms->ofdm_symbol_size];
  float chest_t_abs[2*frame_parms->ofdm_symbol_size];

  for (int arx=0; arx<nb_antennas_rx; arx++) {
    if (chest_t[arx] !=NULL) {
      for (int i=0; i<(2*frame_parms->ofdm_symbol_size); i++) {
        time2[i] = (float)i;
        chest_t_abs[i] = 10*log10(1.0 + SquaredNorm(chest_t[0][i]));

        if (chest_t_abs[i] > ymax)
          ymax = chest_t_abs[i];
      }

      if (arx==0)
        fl_set_xyplot_data(graph,time2,chest_t_abs,(2*frame_parms->ofdm_symbol_size),"","","");
      else {
        fl_add_xyplot_overlay(graph,arx,time2,chest_t_abs,(frame_parms->ofdm_symbol_size>>3),rx_antenna_colors[arx]);
        fl_set_xyplot_overlay_type(graph,arx,FL_DASHED_XYPLOT);
      }
    }
  }

  // Avoid flickering effect
  //        fl_get_xyplot_ybounds(form->chest_t,&ymin,&ymax);
  fl_set_xyplot_ybounds(graph,0,ymax);
}

static void frequencyResponse (FL_OBJECT *graph, PHY_VARS_gNB *phy_vars_gnb, RU_t *phy_vars_ru, int UE_id) {
  // Channel Frequency Response
  if (!phy_vars_ru->common.rxdataF)
    return;

  NR_DL_FRAME_PARMS *frame_parms=&phy_vars_gnb->frame_parms;
  //uint8_t nb_antennas_rx = frame_parms->nb_antennas_rx;
  scopeSample_t **rxsig_f = (scopeSample_t **) phy_vars_ru->common.rxdataF;
  float rxsig_f_dB[frame_parms->samples_per_slot_wCP];
  float time[frame_parms->samples_per_slot_wCP];

  if (rxsig_f[0] != NULL) {
    for (int i=0; i<frame_parms->samples_per_slot_wCP; i++) {
      rxsig_f_dB[i] = 10*log10(1.0+ SquaredNorm(rxsig_f[0][i]));
      time[i] = (float) i;
    }

    fl_set_xyplot_data(graph,time,rxsig_f_dB,frame_parms->samples_per_slot_wCP,"","","");
  }

  /*
  for (int arx=1; arx<nb_antennas_rx; arx++) {
    if (chest_f[(atx<<1)+arx] != NULL) {
      for (int k=0; k<nsymb_ce; k++) {
  time[k] = (float)ind;
  chest_f_abs[k] = (short)10*log10(1.0+SquaredNorm(chest_f[(atx<<1)+arx][k]));
  ind++;
      }
      fl_add_xyplot_overlay(form->chest_f,1,time,chest_f_abs,nsymb_ce,rx_antenna_colors[arx]);
    }
  }
  */
  /*
    // 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,3);
    fl_set_xyplot_xgrid(form->chest_f,FL_GRID_MAJOR);
    fl_set_xyplot_data(form->chest_f,freq,chest_f_abs,nsymb_ce,"","","");
  */
}

static void puschLLR (FL_OBJECT *graph, PHY_VARS_gNB *phy_vars_gnb, RU_t *phy_vars_ru, int UE_id) {
  // PUSCH LLRs
  if (!phy_vars_gnb->pusch_vars[UE_id]->llr)
    return;

  //NR_DL_FRAME_PARMS *frame_parms=&phy_vars_gnb->frame_parms;
  //int Qm = 2;
  int16_t *pusch_llr = (int16_t *) phy_vars_gnb->pusch_vars[UE_id]->llr;
  int coded_bits_per_codeword =3*8*6144+12; // (8*((3*8*6144)+12)); // frame_parms->N_RB_UL*12*Qm*frame_parms->symbols_per_tti;
  float llr[coded_bits_per_codeword];
  float bit[coded_bits_per_codeword];

  for (int i=0; i<coded_bits_per_codeword; i++) {
    llr[i] = (float) pusch_llr[i];
    bit[i] = (float) i;
  }

  fl_set_xyplot_data(graph,bit,llr,coded_bits_per_codeword,"","","");
}

static void puschIQ (FL_OBJECT *graph, PHY_VARS_gNB *phy_vars_gnb, RU_t *phy_vars_ru, int UE_id) {
  // PUSCH I/Q of MF Output
  if (!phy_vars_gnb->pusch_vars[UE_id]->rxdataF_comp)
    return;

  NR_DL_FRAME_PARMS *frame_parms=&phy_vars_gnb->frame_parms;
  scopeSample_t *pusch_comp = (scopeSample_t *) phy_vars_gnb->pusch_vars[UE_id]->rxdataF_comp[0];
  int sz=frame_parms->N_RB_UL*12*frame_parms->symbols_per_slot;
  float I[sz], Q[sz];

  for (int k=0; k<sz; k++ ) {
    I[k] = pusch_comp[k].r;
    Q[k] = pusch_comp[k].i;
  }

  fl_set_xyplot_data(graph,I,Q,sz,"","","");

  // PUSCH I/Q of MF Output
  if (NULL) {
    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 pucchEnergy (FL_OBJECT *graph, PHY_VARS_gNB *phy_vars_gnb, RU_t *phy_vars_ru, int UE_id) {
}

static void pucchIQ (FL_OBJECT *graph, PHY_VARS_gNB *phy_vars_gnb, RU_t *phy_vars_ru, int UE_id) {
}

static void puschThroughtput (FL_OBJECT *graph, PHY_VARS_gNB *phy_vars_gnb, RU_t *phy_vars_ru, int UE_id) {
  // PUSCH Throughput
  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);
}

static FD_phy_scope_t *create_phy_scope_gnb(int UE_id ) {
  FL_OBJECT *obj;
  FD_phy_scope_t *fdui = fl_malloc( sizeof *fdui );
  // Define form
  fdui->phy_scope = fl_bgn_form( FL_NO_BOX, 800, 800 );
  // This the whole UI box
  obj = fl_add_box( FL_BORDER_BOX, 0, 0, 800, 800, "" );
  fl_set_object_color( obj, FL_BLACK, FL_BLACK );
  // Received signal
  fdui->graph[0] = gNBcommonGraph( timeSignal, FL_IMPULSE_XYPLOT, 20, 20, 370, 100,
                                   "Received Signal (Time-Domain, dB)", FL_RED );
  // Time-domain channel response
  fdui->graph[1] = gNBcommonGraph( timeResponse, FL_NORMAL_XYPLOT, 410, 20, 370, 100,
                                   "SRS Frequency Response (samples, abs)", FL_RED );
  // Frequency-domain channel response
  fdui->graph[2] = gNBcommonGraph( frequencyResponse, FL_IMPULSE_XYPLOT, 20, 140, 760, 100,
                                   "Channel Frequency  Response (RE, dB)", FL_RED );
  // LLR of PUSCH
  fdui->graph[3] = gNBcommonGraph( puschLLR, FL_POINTS_XYPLOT, 20, 260, 500, 200,
                                   "PUSCH Log-Likelihood Ratios (LLR, mag)", FL_YELLOW );
  // I/Q PUSCH comp
  fdui->graph[4] = gNBcommonGraph( puschIQ, FL_POINTS_XYPLOT, 540, 260, 240, 200,
                                   "PUSCH I/Q of MF Output", FL_YELLOW );
  // I/Q PUCCH comp (format 1)
  fdui->graph[5] = gNBcommonGraph( pucchEnergy, FL_POINTS_XYPLOT, 540, 480, 240, 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, 540, 600, 240, 100,
                                   "PUCCH I/Q of MF Output", FL_YELLOW );
  // Throughput on PUSCH
  fdui->graph[7] = gNBcommonGraph( puschThroughtput, FL_NORMAL_XYPLOT, 20, 480, 500, 100,
                                   "PUSCH Throughput [frame]/[kbit/s]", FL_WHITE );
  fdui->graph[8].graph=NULL;

  // Generic eNB Button
  if (0) { // code kept to later add command buttons
    fdui->button_0 = fl_add_button( FL_PUSH_BUTTON, 20, 600, 240, 40, "" );
    fl_set_object_lalign(fdui->button_0, FL_ALIGN_CENTER );
    fl_set_button(fdui->button_0,0);
    otg_enabled = 0;
    fl_set_object_label(fdui->button_0, "DL Traffic OFF");
    fl_set_object_color(fdui->button_0, FL_RED, FL_RED);
    fl_set_object_callback(fdui->button_0, dl_traffic_on_off, 0 );
  }

  fl_end_form( );
  fdui->phy_scope->fdui = fdui;
  char title[100];
  sprintf (title, "LTE UL SCOPE eNB for UE %d",UE_id);
  fl_show_form (fdui->phy_scope, FL_PLACE_HOTSPOT, FL_FULLBORDER, title);
  return fdui;
}

static FD_phy_scope_t             *form_gnb[NUMBER_OF_UE_MAX]= {0};
static unsigned char                   scope_enb_num_ue = 1;

static void *scope_thread_gNB(void *arg) {
  scopeParms_t *p=(scopeParms_t *) arg;
  //# ifdef ENABLE_XFORMS_WRITE_STATS
  //  FILE *gNB_stats = fopen("gNB_stats.txt", "w");
  //#endif
  size_t stksize;
  pthread_attr_t atr;
  pthread_attr_getstacksize(&atr, &stksize);
  pthread_attr_setstacksize(&atr,32*1024*1024 );
  sleep(3); // no clean interthread barriers

  while (!oai_exit) {
    int ue_cnt=0;

    for(int UE_id=0; UE_id<NUMBER_OF_UE_MAX; UE_id++) {
      if ((ue_cnt<scope_enb_num_ue)) {
        //this function needs to be written
        phy_scope_gNB(form_gnb[ue_cnt], p->gNB, p->ru, UE_id);
        ue_cnt++;
      }
    }

    usleep(99*1000);
  }

  //  printf("%s",stats_buffer);
  /*#ifdef ENABLE_XFORMS_WRITE_STATS

    if (eNB_stats) {
    rewind (gNB_stats);
    fwrite (stats_buffer, 1, len, gNB_stats);
    fclose (gNB_stats);
    }

    #endif
    pthread_exit((void *)arg);
    }*/
  return NULL;
}

void gNBinitScope(scopeParms_t *p) {
  //FD_stats_form *form_stats=NULL,*form_stats_l2=NULL;
  fl_initialize (p->argc, p->argv, NULL, 0, 0);

  /*
    form_stats_l2 = create_form_stats_form();
    fl_show_form (form_stats_l2->stats_form, FL_PLACE_HOTSPOT, FL_FULLBORDER, "l2 stats");
    form_stats = create_form_stats_form();
    fl_show_form (form_stats->stats_form, FL_PLACE_HOTSPOT, FL_FULLBORDER, "stats");
  */
  for(int UE_id=0; UE_id<scope_enb_num_ue; UE_id++) {
    form_gnb[UE_id] = create_phy_scope_gnb(UE_id);
  } // UE_id

  static scopeParms_t parms;
  memcpy(&parms,p,sizeof(parms));
  pthread_t forms_thread;
  threadCreate(&forms_thread, scope_thread_gNB, &parms, "scope", -1, OAI_PRIORITY_RT_LOW);
}

static void ueTimeResponse  (FL_OBJECT *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
  // Received signal in time domain of receive antenna 0
  if (! phy_vars_ue->common_vars.rxdata)
    return;

  NR_DL_FRAME_PARMS *frame_parms = &phy_vars_ue->frame_parms;
  uint8_t nb_antennas_rx = frame_parms->nb_antennas_rx;
  int samples_per_frame = frame_parms->samples_per_frame;
  scopeSample_t **rxsig_t = (scopeSample_t **) phy_vars_ue->common_vars.rxdata;
  float rxsig_t_dB[samples_per_frame];
  float  time[samples_per_frame];

  if (rxsig_t[0] != NULL) {
    for (int i=0; i<samples_per_frame; i++) {
      rxsig_t_dB[i] = 10*log10(1.0+SquaredNorm(rxsig_t[0][i]));
      time[i] = (float) i;
    }

    fl_set_xyplot_data(graph,time,rxsig_t_dB,samples_per_frame,"","","");
  }

  for (int arx=1; arx<nb_antennas_rx; arx++) {
    if (rxsig_t[arx] != NULL) {
      for (int i=0; i<FRAME_LENGTH_COMPLEX_SAMPLES; i++) {
        rxsig_t_dB[i] = 10*log10(1.0+SquaredNorm(rxsig_t[arx][i]));
      }

      fl_add_xyplot_overlay(graph,arx,time,rxsig_t_dB,FRAME_LENGTH_COMPLEX_SAMPLES,rx_antenna_colors[arx]);
    }
  }

  if (phy_vars_ue->is_synchronized==0) {
    //float corr[samples_per_frame];
    for (int ind=0; ind<3; ind++) {
      /*
        if (pss_corr_ue[ind]) {
        for (i=0; i<samples_per_frame; i++) {
        corr[i] = (float) pss_corr_ue[ind][i];
        time[i] = (float) i;
        }

        if (ind==0)
        fl_set_xyplot_data(form->chest_t,time,corr,samples_per_frame,"","","");
        else
        fl_add_xyplot_overlay(form->chest_t,ind,time,corr,samples_per_frame,rx_antenna_colors[ind]);

        overlay = 1;
        }
      */
    }

    // if the UE is not synchronized, we can make only the time*power graph
  }
}

static void ueChannelResponse  (FL_OBJECT *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
  // Channel Impulse Response
  if (!phy_vars_ue->pbch_vars[eNB_id]->dl_ch_estimates_time)
    return;

  NR_DL_FRAME_PARMS *frame_parms = &phy_vars_ue->frame_parms;
  uint8_t nb_antennas_rx = frame_parms->nb_antennas_rx;
  scopeSample_t **chest_t = (scopeSample_t **) phy_vars_ue->pbch_vars[eNB_id]->dl_ch_estimates_time;
  int ymax = 0;
  float chest_t_abs[frame_parms->ofdm_symbol_size];
  float time[frame_parms->ofdm_symbol_size>>3];

  if (chest_t[0] !=NULL) {
    for (int i=0; i<(frame_parms->ofdm_symbol_size>>3); i++) {
      chest_t_abs[i] = SquaredNorm(chest_t[0][i]);
      time[i] = (float) i;

      if (chest_t_abs[i] > ymax)
        ymax = chest_t_abs[i];
    }

    fl_set_xyplot_data(graph,time,chest_t_abs,(frame_parms->ofdm_symbol_size>>3),"","","");
  }

  for (int arx=1; arx<nb_antennas_rx; arx++) {
    if (chest_t[arx] !=NULL) {
      for (int i=0; i<(frame_parms->ofdm_symbol_size>>3); i++) {
        chest_t_abs[i] = SquaredNorm(chest_t[arx][i]);

        if (chest_t_abs[i] > ymax)
          ymax = chest_t_abs[i];
      }

      fl_add_xyplot_overlay(graph,arx,time,chest_t_abs,(frame_parms->ofdm_symbol_size>>3),rx_antenna_colors[arx]);
      fl_set_xyplot_overlay_type(graph,arx,FL_DASHED_XYPLOT);
    }
  }

  // Avoid flickering effect
  // fl_get_xyplot_ybounds(form->chest_t,&ymin,&ymax); // Does not always work...
  fl_set_xyplot_ybounds(graph,0,(double) ymax);
}

static void uePbchFrequencyResp  (FL_OBJECT *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 chest_f_abs[frame_parms->ofdm_symbol_size];
  float freq[frame_parms->ofdm_symbol_size];

  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);
  fl_set_xyplot_data(graph,freq,chest_f_abs,frame_parms->ofdm_symbol_size,"","","");
}

static void uePbchLLR  (FL_OBJECT *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
  // PBCH LLRs
  if ( !phy_vars_ue->pbch_vars[eNB_id]->llr)
    return;

  int16_t *pbch_llr = (int16_t *) phy_vars_ue->pbch_vars[eNB_id]->llr;
  float llr_pbch[864], bit_pbch[864];

  for (int i=0; i<864; i++) {
    llr_pbch[i] = (float) pbch_llr[i];
    bit_pbch[i] = (float) i;
  }

  fl_set_xyplot_data(graph,bit_pbch,llr_pbch,864,"","","");
}

static void uePbchIQ  (FL_OBJECT *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
  // PBCH I/Q of MF Output
  if (!phy_vars_ue->pbch_vars[eNB_id]->rxdataF_comp[0])
    return;

  int16_t *pbch_comp = (int16_t *) phy_vars_ue->pbch_vars[eNB_id]->rxdataF_comp[0];
  localBuff(I,180*3);
  localBuff(Q,180*3);
  int first_symbol=1;
  int base=0;

  for (int symbol=first_symbol; symbol<(first_symbol+3); symbol++) {
    int nb_re;

    if (symbol == 2 || symbol == 6)
      nb_re = 72;
    else
      nb_re = 180;

    AssertFatal(base+nb_re<180*3,"");

    for (int i=0; i<nb_re; i++) {
      I[base+i] = pbch_comp[2*symbol*20*12+2*i];
      Q[base+i] = pbch_comp[2*symbol*20*12+2*i+1];
    }

    base+=nb_re;
  }

  fl_set_xyplot_data(graph,I,Q,base,"","","");
}

static void uePcchLLR  (FL_OBJECT *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
  // PDCCH LLRs
  if (!phy_vars_ue->pdcch_vars[0][eNB_id]->llr)
    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_antennas_tx;
  scopeSample_t **chest_f = (scopeSample_t **) phy_vars_ue->pbch_vars[eNB_id]->dl_ch_estimates;
  int ind = 0;
  float chest_f_abs[frame_parms->ofdm_symbol_size];
  float freq[frame_parms->ofdm_symbol_size];

  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);
  fl_set_xyplot_data(graph,freq,chest_f_abs,frame_parms->ofdm_symbol_size,"","","");
}

static void uePcchIQ  (FL_OBJECT *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
  // PDCCH I/Q of MF Output
  if (!phy_vars_ue->pdcch_vars[0][eNB_id]->rxdataF_comp[0])
    return;

  int nb=12*273*4; // 12*frame_parms->N_RB_DL*num_pdcch_symbols
  localBuff(I,nb*RX_NB_TH_MAX);
  localBuff(Q,nb*RX_NB_TH_MAX);
  int base=0;

  for (int thr=0 ; thr < RX_NB_TH_MAX ; thr ++ ) {
    int16_t  *pdcch_comp = (int16_t *) phy_vars_ue->pdcch_vars[thr][eNB_id]->rxdataF_comp[0];

    for (int i=0; i< nb; i++) {
      I[base+i] = pdcch_comp[i*2];
      Q[base+i] = pdcch_comp[i*2+1];
    }

    base+=nb;
  }

  fl_set_xyplot_data(graph,I,Q,base,"","","");
}

static void uePdschLLR  (FL_OBJECT *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
  // PDSCH LLRs
  if (!phy_vars_ue->pdsch_vars[0][eNB_id]->llr[0])
    return;

  int num_re = 4500;
  int Qm = 2;
  int coded_bits_per_codeword = num_re*Qm;
  localBuff(llr,coded_bits_per_codeword*RX_NB_TH_MAX);
  localBuff(bit,coded_bits_per_codeword*RX_NB_TH_MAX);
  int base=0;

  for (int thr=0 ; thr < RX_NB_TH_MAX ; thr ++ ) {
    int16_t *pdsch_llr = (int16_t *) phy_vars_ue->pdsch_vars[thr][eNB_id]->llr[0]; // stream 0

    for (int i=0; i<coded_bits_per_codeword; i++) {
      llr[base+i] = (float) pdsch_llr[i];
      bit[base+i] = (float) base+i;
    }

    base+=coded_bits_per_codeword;
  }

  //fl_set_xyplot_xbounds(form->pdsch_llr,0,coded_bits_per_codeword);
  fl_set_xyplot_data(graph,bit,llr,base,"","","");
}

static void uePdschIQ  (FL_OBJECT *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
  // PDSCH I/Q of MF Output
  if (!phy_vars_ue->pdsch_vars[0][eNB_id]->rxdataF_comp0[0])
    return;

  NR_DL_FRAME_PARMS *frame_parms = &phy_vars_ue->frame_parms;
  int sz=7*2*frame_parms->N_RB_DL*12; // size of the malloced buffer
  localBuff(I,sz*RX_NB_TH_MAX);
  localBuff(Q,sz*RX_NB_TH_MAX);
  int base=0;

  for (int thr=0 ; thr < RX_NB_TH_MAX ; thr ++ ) {
    int16_t *pdsch_comp = (int16_t *) phy_vars_ue->pdsch_vars[thr][eNB_id]->rxdataF_comp0[0];

    for (int s=0; s<sz; s++) {
      I[s+base] += pdsch_comp[2*s];
      Q[s+base] += pdsch_comp[2*s+1];
    }

    base+=sz;
  }

  fl_set_xyplot_data(graph,I,Q,base,"","","");
  /*

  // 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]);
  */
}

static void uePdschThroughput  (FL_OBJECT *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
}

static FD_phy_scope_t *create_phy_scope_nrue( int ID ) {
  FL_OBJECT *obj;
  FD_phy_scope_t *fdui = fl_malloc( sizeof *fdui );
  // Define form
  fdui->phy_scope = fl_bgn_form( FL_NO_BOX, 800, 900 );
  // 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 );
  // Received signal
  fdui->graph[0] = nrUEcommonGraph(ueTimeResponse,
                                   FL_IMPULSE_XYPLOT, 20, 20, 370, 100, "Received Signal (Time-Domain, dB)", FL_RED );
  // Time-domain channel response
  fdui->graph[1] = nrUEcommonGraph(ueChannelResponse,
                                   FL_NORMAL_XYPLOT, 410, 20, 370, 100, "Channel Impulse Response (samples, abs)", FL_RED );
  // Frequency-domain channel response
  fdui->graph[2] = nrUEcommonGraph(uePbchFrequencyResp,
                                   FL_IMPULSE_XYPLOT, 20, 140, 760, 100, "Channel Frequency data (RE, dB)", FL_RED );
  // LLR of PBCH
  fdui->graph[3] = nrUEcommonGraph(uePbchLLR,
                                   FL_POINTS_XYPLOT, 20, 260, 500, 100, "PBCH Log-Likelihood Ratios (LLR, mag)", FL_GREEN );
  fl_set_xyplot_xgrid(fdui->graph[3].graph,FL_GRID_MAJOR);
  // I/Q PBCH comp
  fdui->graph[4] = nrUEcommonGraph(uePbchIQ,
                                   FL_POINTS_XYPLOT, 540, 260, 240, 100, "PBCH I/Q of MF Output", FL_GREEN );
  // LLR of PDCCH
  fdui->graph[5] = nrUEcommonGraph(uePcchLLR,
                                   FL_POINTS_XYPLOT, 20, 380, 500, 100, "PDCCH Log-Likelihood Ratios (LLR, mag)", FL_CYAN );
  // I/Q PDCCH comp
  fdui->graph[6] = nrUEcommonGraph(uePcchIQ,
                                   FL_POINTS_XYPLOT, 540, 380, 240, 100, "PDCCH I/Q of MF Output", FL_CYAN );
  // LLR of PDSCH
  fdui->graph[7] = nrUEcommonGraph(uePdschLLR,
                                   FL_POINTS_XYPLOT, 20, 500, 500, 200, "PDSCH Log-Likelihood Ratios (LLR, mag)", FL_YELLOW );
  // I/Q PDSCH comp
  fdui->graph[8] = nrUEcommonGraph(uePdschIQ,
                                   FL_POINTS_XYPLOT, 540, 500, 240, 200, "PDSCH I/Q of MF Output", FL_YELLOW );
  // Throughput on PDSCH
  fdui->graph[9] = nrUEcommonGraph(uePdschThroughput,
                                   FL_NORMAL_XYPLOT, 20, 720, 500, 100, "PDSCH Throughput [frame]/[kbit/s]", FL_WHITE );
  fdui->graph[10].graph=NULL;
  // Generic UE Button
  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);
  fl_end_form( );
  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;
}

void phy_scope_nrUE(FD_phy_scope_t *form,
                    PHY_VARS_NR_UE *phy_vars_ue,
                    int eNB_id,
                    int UE_id) {
  static FD_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) {
    form->graph[i].nrUEfunct(form->graph[i].graph, phy_vars_ue, eNB_id, UE_id);
    i++;
  }

  fl_check_forms();
}

static FD_phy_scope_t  *form_nrue[NUMBER_OF_UE_MAX];
static pthread_t forms_thread;

static void *nrUEscopeThread(void *arg) {
  PHY_VARS_NR_UE *ue=(PHY_VARS_NR_UE *)arg;
  size_t stksize;
  pthread_attr_t atr;
  pthread_attr_getstacksize(&atr, &stksize);
  pthread_attr_setstacksize(&atr,32*1024*1024 );

  while (!oai_exit) {
    phy_scope_nrUE(form_nrue[0],
                   ue,
                   0,0);
    usleep(99*1000);
  }

  pthread_exit((void *)arg);
}

void nrUEinitScope(PHY_VARS_NR_UE *ue) {
  int fl_argc=1;
  char *name="5G-UE-scope";
  fl_initialize (&fl_argc, &name, NULL, 0, 0);
  form_nrue[0] = create_phy_scope_nrue(0);
  threadCreate(&forms_thread, nrUEscopeThread, ue, "scope", -1, OAI_PRIORITY_RT_LOW);
}

// 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<NUMBER_OF_UE_MAX; 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 = fl_malloc( sizeof *fdui );
  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




