// This is the searcher process. It requests captured data from the main
// thread and launches a new thread for every cell it finds. Each new
// cell thread then requests sample data from the main thread.
void searcher_thread(
    capbuf_sync_t & capbuf_sync,
    global_thread_data_t & global_thread_data,
    tracked_cell_list_t & tracked_cell_list
) {
    if (verbosity>=1) {
        cout << "Searcher process has been launched." << endl;
    }

    global_thread_data.searcher_thread_id=syscall(SYS_gettid);

    if (nice(20)==-1) {
        cerr << "Error: could not reduce searcher process priority" << endl;
        ABORT(-1);
    }

    // Keep track of serial numbers to be used when launching a new
    // tracker thread.
    ivec serial_num(504);
    serial_num=1;

    // Shortcut
    const double & fc_requested=global_thread_data.fc_requested;
    const double & fc_programmed=global_thread_data.fc_programmed;
    const double & fs_programmed=global_thread_data.fs_programmed;
//  double freq_correction;

    const bool sampling_carrier_twist = global_thread_data.sampling_carrier_twist();
    double k_factor = global_thread_data.k_factor();
//  double correction = global_thread_data.correction();

    vec coef(( sizeof( chn_6RB_filter_coef )/sizeof(float) ));
    for (uint16 i=0; i<length(coef); i++) {
        coef(i) = chn_6RB_filter_coef[i];
    }

    // Calculate the threshold vector
    const uint8 thresh1_n_nines=12;
    double rx_cutoff=(6*12*15e3/2+4*15e3)/(FS_LTE/16/2);

    // for PSS correlate
    //cout << "DS_COMB_ARM override!!!" << endl;
#define DS_COMB_ARM 2
    mat xc_incoherent_collapsed_pow;
    imat xc_incoherent_collapsed_frq;
    vector <mat> xc_incoherent_single(3);
    vector <mat> xc_incoherent(3);
    vec sp_incoherent;
    vector <mat> xc(3);
    vec sp;

    // for SSS detection
#define THRESH2_N_SIGMA 3
    vec sss_h1_np_est_meas;
    vec sss_h2_np_est_meas;
    cvec sss_h1_nrm_est_meas;
    cvec sss_h2_nrm_est_meas;
    cvec sss_h1_ext_est_meas;
    cvec sss_h2_ext_est_meas;
    mat log_lik_nrm;
    mat log_lik_ext;

    // for time frequency grid
    // Extract time and frequency grid
    cmat tfg;
    vec tfg_timestamp;
    // Compensate for time and frequency offsets
    cmat tfg_comp;
    vec tfg_comp_timestamp;

    vec period_ppm;
    double xcorr_pss_time;

    Real_Timer tt; // for profiling

    // Get the current frequency offset (because it won't change anymore after main thread launches this thread, so move it outside loop)
    vec f_search_set(1);
    cmat pss_fo_set;// pre-generate frequencies offseted pss time domain sequence
    // because it is already included in global_thread_data.frequency_offset();
    f_search_set(0)=global_thread_data.frequency_offset();
    pss_fo_set_gen(f_search_set, pss_fo_set);

//  freq_correction = fc_programmed*(correction-1)/correction;

//  cout << opencl_platform << " " << opencl_device <<  " " << sampling_carrier_twist << "\n";
    uint16 opencl_platform = global_thread_data.opencl_platform();
    uint16 opencl_device = global_thread_data.opencl_device();
    lte_opencl_t lte_ocl(opencl_platform, opencl_device);

#ifdef USE_OPENCL
    uint16 filter_workitem = global_thread_data.filter_workitem();
    uint16 xcorr_workitem = global_thread_data.xcorr_workitem();
#ifdef FILTER_MCHN_SIMPLE_KERNEL
    lte_ocl.setup_filter_mchn((string)"filter_mchn_simple_kernel.cl", CAPLENGTH, length(f_search_set)*3, pss_fo_set.cols(), xcorr_workitem);
#else
    lte_ocl.setup_filter_mchn((string)"filter_mchn_kernels.cl", CAPLENGTH, length(f_search_set)*3, pss_fo_set.cols(), xcorr_workitem);
#endif
    lte_ocl.setup_filter_my((string)"filter_my_kernels.cl", CAPLENGTH, filter_workitem);
#endif

    // Loop forever.
    while (true) {
        // Used to measure searcher cycle time.
        tt.tic();

        // Request data.
        {
            boost::mutex::scoped_lock lock(capbuf_sync.mutex);
            capbuf_sync.request=true;

            // Wait for data to become ready.
            capbuf_sync.condition.wait(lock);
        }

        // Results are stored in this vector.
        list<Cell> detected_cells;

        // Local reference to the capture buffer.
        cvec &capbuf=capbuf_sync.capbuf;

        capbuf = capbuf - mean(capbuf); // remove DC

#ifdef USE_OPENCL
        lte_ocl.filter_my(capbuf); // be careful! capbuf.zeros() will slow down the xcorr part pretty much!
#else
        filter_my(coef, capbuf);
#endif

        // Correlate
        uint16 n_comb_xc;
        uint16 n_comb_sp;
        if (verbosity>=2) {
            cout << "  Calculating PSS correlations" << endl;
        }

        sampling_ppm_f_search_set_by_pss(lte_ocl, 0, capbuf, pss_fo_set, 1, 0, f_search_set, period_ppm, xc, xcorr_pss_time);

        xcorr_pss(capbuf,f_search_set,DS_COMB_ARM,fc_requested,fc_programmed,fs_programmed,xc,xc_incoherent_collapsed_pow,xc_incoherent_collapsed_frq,xc_incoherent_single,xc_incoherent,sp_incoherent,sp,n_comb_xc,n_comb_sp,sampling_carrier_twist,k_factor);

        // Calculate the threshold vector
        double R_th1=chi2cdf_inv(1-pow(10.0,-thresh1_n_nines),2*n_comb_xc*(2*DS_COMB_ARM+1));
        vec Z_th1=R_th1*sp_incoherent/rx_cutoff/137/n_comb_xc/(2*DS_COMB_ARM+1);

        // Search for the peaks
        if (verbosity>=2) {
            cout << "  Searching for and examining correlation peaks..." << endl;
        }
        peak_search(xc_incoherent_collapsed_pow,xc_incoherent_collapsed_frq,Z_th1,f_search_set,fc_requested,fc_programmed,xc_incoherent_single,DS_COMB_ARM, sampling_carrier_twist, (const double)k_factor, detected_cells);

        // Loop and check each peak
        list<Cell>::iterator iterator=detected_cells.begin();
        int tdd_flag = 1;
        while (iterator!=detected_cells.end()) {
            tdd_flag = !tdd_flag;

            // Detect SSS if possible
#define THRESH2_N_SIGMA 3
            (*iterator)=sss_detect((*iterator),capbuf,THRESH2_N_SIGMA,fc_requested,fc_programmed,fs_programmed,sss_h1_np_est_meas,sss_h2_np_est_meas,sss_h1_nrm_est_meas,sss_h2_nrm_est_meas,sss_h1_ext_est_meas,sss_h2_ext_est_meas,log_lik_nrm,log_lik_ext,sampling_carrier_twist,tdd_flag);
            if ((*iterator).n_id_1!=-1) {
                if (verbosity>=2) {
                    cout << "Detected PSS/SSS correspoding to cell ID: " << (*iterator).n_id_cell() << endl;
                }

                // Check to see if this cell has already been detected previously.
                bool match=false;
                {
                    boost::mutex::scoped_lock lock(tracked_cell_list.mutex);
                    list<tracked_cell_t *>::iterator tci=tracked_cell_list.tracked_cells.begin();
                    match=false;
                    while (tci!=tracked_cell_list.tracked_cells.end()) {
                        if ((*(*tci)).n_id_cell==(*iterator).n_id_cell()) {
                            match=true;
                            break;
                        }
                        ++tci;
                    }
                }
                if (match) {
                    if (verbosity>=2) {
                        cout << "Cell already being tracked..." << endl;
                    }
                    ++iterator;
                    continue;
                }

                // Fine FOE
                (*iterator)=pss_sss_foe((*iterator),capbuf,fc_requested,fc_programmed,fs_programmed,sampling_carrier_twist,tdd_flag);

                // Extract time and frequency grid
                extract_tfg((*iterator),capbuf,fc_requested,fc_programmed,fs_programmed,tfg,tfg_timestamp,sampling_carrier_twist);

                // Create object containing all RS
                RS_DL rs_dl((*iterator).n_id_cell(),6,(*iterator).cp_type);

                // Compensate for time and frequency offsets
                (*iterator)=tfoec((*iterator),tfg,tfg_timestamp,fc_requested,fc_programmed,rs_dl,tfg_comp,tfg_comp_timestamp,sampling_carrier_twist);

                // Finally, attempt to decode the MIB
                (*iterator)=decode_mib((*iterator),tfg_comp,rs_dl);
                //cout << (*iterator) << endl << endl;
                //sleep(100000);
                if ((*iterator).n_rb_dl==-1) {
                    // No MIB could be successfully decoded.
                    iterator=detected_cells.erase(iterator);
                    continue;
                }

                /*
                if (verbosity>=1) {
                  cout << "Detected a new cell!" << endl;
                  cout << "  cell ID: " << (*iterator).n_id_cell() << endl;
                  cout << "  RX power level: " << db10((*iterator).pss_pow) << " dB" << endl;
                  cout << "  residual frequency offset: " << (*iterator).freq_superfine << " Hz" << endl;
                  cout << "  frame start: " << (*iterator).frame_start << endl;
                }
                */

                //      cout << ((*iterator).frame_start) <<  " " << k_factor << " " << capbuf_sync.late << (*iterator).k_factor <<  "\n";
                //      (*iterator).frame_start = 3619.95;
                //      capbuf_sync.late = 0;
                // Launch a cell tracker process!
                //tracked_cell_t * new_cell = new tracked_cell_t((*iterator).n_id_cell(),(*iterator).n_ports,(*iterator).cp_type,(*iterator).frame_start/k_factor+capbuf_sync.late,serial_num((*iterator).n_id_cell()));
                tracked_cell_t * new_cell = new tracked_cell_t(
                    (*iterator).n_id_cell(),
                    (*iterator).n_ports,
                    (*iterator).duplex_mode,
                    (*iterator).cp_type,
                    (*iterator).n_rb_dl,
                    (*iterator).phich_duration,
                    (*iterator).phich_resource,
                    (*iterator).frame_start*(FS_LTE/16)/(fs_programmed*k_factor)+capbuf_sync.late,
                    serial_num((*iterator).n_id_cell())//,
                    //        (*iterator).freq_superfine
                );

                serial_num((*iterator).n_id_cell())++;
                // Cannot launch thread here. If thread was launched here, it would
                // have the same (low) priority as the searcher thread.
                {
                    boost::mutex::scoped_lock lock(tracked_cell_list.mutex);
                    tracked_cell_list.tracked_cells.push_back(new_cell);
                }
                //CALLGRIND_START_INSTRUMENTATION;
#define MAX_DETECTED 1e6
                static uint32 n_found=0;
                n_found++;
                if (n_found==MAX_DETECTED) {
                    cout << "Searcher thread has stopped!" << endl;
                    sleep(1000000);
                }

                ++iterator;

            } else {
                // No SSS detected.
                iterator=detected_cells.erase(iterator);
                continue;
            }
        }

        global_thread_data.searcher_cycle_time(tt.toc());
//    global_thread_data.searcher_cycle_time(xcorr_pss_time);
    }
    // Will never reach here...
}
Exemplo n.º 2
0
void display_thread(
  sampbuf_sync_t & sampbuf_sync,
  global_thread_data_t & global_thread_data,
  tracked_cell_list_t & tracked_cell_list,
  bool & expert_mode
) {
  global_thread_data.display_thread_id=syscall(SYS_gettid);

  // Initialize the curses screen
  initscr();
  start_color();
  use_default_colors();
  if (LINES<LINES_MIN) {
    //endwin();
    cerr << "Error: resize window so it has at least " << LINES_MIN << " rows and " << COLS_MIN << " columns" << endl;
    ABORT(-1);
  }
  if (COLS<COLS_MIN) {
    //endwin();
    cerr << "Error: resize window so it has at least " << LINES_MIN << " rows and " << COLS_MIN << " columns" << endl;
    ABORT(-1);
  }
  // Do not echo input chars to screen.
  noecho();
  // Turn off display of cursor.
  curs_set(0);
  // Enable function keys, arrow keys, etc.
  keypad(stdscr,true);
  //init_color(COLOR_BLACK,0,0,0);
  //init_pair(3,COLOR_GREEN,-1);
  init_pair(RED,COLOR_RED,-1);
  init_pair(GREEN,COLOR_GREEN,-1);
  init_pair(YELLOW,COLOR_YELLOW,-1);
  init_pair(BLUE,COLOR_BLUE,-1);
  init_pair(MAGENTA,COLOR_MAGENTA,-1);
  init_pair(CYAN,COLOR_CYAN,-1);
  init_pair(WHITE,COLOR_WHITE,-1);
  // Reduce delay between pressing 'Esc' and having this program
  // respond.
  // In ms.
  set_escdelay(50);

  // Start, end, and size of cell info display area.
  const uint16 CELL_DISP_START_ROW=2;
  const uint16 CELL_DISP_END_ROW=LINES-6;
  const uint16 CELL_DISP_N_ROWS=CELL_DISP_END_ROW-CELL_DISP_START_ROW+1;

  // Record where a particular cell was most recently printed.
  ivec disp_history(504);
  disp_history=-1;
  vector <row_desc_t> row_desc(CELL_DISP_N_ROWS);
  //bvec row_occupied(CELL_DISP_N_ROWS);

  // Settings that the user can change in realtime
  bool auto_refresh=true;
  double refresh_delay_sec=1;
  bool fifo_status=false;
  bool avg_values=true;
  disp_mode_t disp_mode=STD;
  int8 detail_type=0;
#define N_DETAILS 2
  // Send control chars directly to program.
  //cbreak();
  halfdelay(round_i(refresh_delay_sec*10.0));
  int16 highlight_row=-1;
  while (true) {
    clear();
    //attron(COLOR_PAIR(3));

    // No matter which display mode we are in, always update row_disc
    // and highlight_row.
    bool all_cells_displayed=true;
    for (uint16 t=0;t<CELL_DISP_N_ROWS;t++) {
      row_desc[t].occupied=false;
      row_desc[t].n_id_cell=-1;
      row_desc[t].duplex_mode=-3;
      row_desc[t].port_num=-2;
      //row_occupied(t)=false;
    }
    move(2,0);
    {
      boost::mutex::scoped_lock lock(tracked_cell_list.mutex);
      list <tracked_cell_t *>::iterator it=tracked_cell_list.tracked_cells.begin();
      vector <tracked_cell_t *> pass2_display;
      while (it!=tracked_cell_list.tracked_cells.end()) {
        tracked_cell_t & tracked_cell=(*(*it));

        // Deadlock possible???
        boost::mutex::scoped_lock lock1(tracked_cell.fifo_mutex);
        boost::mutex::scoped_lock lock2(tracked_cell.meas_mutex);

        uint8 n_rows_required=tracked_cell.n_ports+2;

        // If this cell has been displayed before, try to display it
        // in the same location.
        if (disp_history(tracked_cell.n_id_cell)!=-1) {
          uint16 row_desired=disp_history(tracked_cell.n_id_cell);
          if (will_fit(row_desc,row_desired,n_rows_required-1)) {
            if (disp_mode==STD)
              display_cell(tracked_cell,row_desired+CELL_DISP_START_ROW,fifo_status,avg_values,expert_mode);
            set_occupied(tracked_cell,row_desc,row_desired);
          } else {
            pass2_display.push_back(&tracked_cell);
          }
        } else {
          pass2_display.push_back(&tracked_cell);
        }
        ++it;
      }

      // Display cells that cannot be displayed where they have previously
      // been displayed.
      for (uint8 t=0;t<pass2_display.size();t++) {
        tracked_cell_t & tracked_cell=(*pass2_display[t]);

        // Deadlock possible???
        boost::mutex::scoped_lock lock1(tracked_cell.fifo_mutex);
        boost::mutex::scoped_lock lock2(tracked_cell.meas_mutex);

        uint8 n_rows_required=tracked_cell.n_ports+2;
        bool placed=false;
        for (uint16 k=0;k<CELL_DISP_N_ROWS-n_rows_required+1;k++) {
          if (will_fit(row_desc,k,n_rows_required)) {
            if (disp_mode==STD)
              display_cell(tracked_cell,k+CELL_DISP_START_ROW,fifo_status,avg_values,expert_mode);
            set_occupied(tracked_cell,row_desc,k);
            disp_history(tracked_cell.n_id_cell)=k;
            placed=true;
            break;
          }
        }
        if (!placed)
          all_cells_displayed=false;
      }
    }

    // Always highlight a row, if a cell exists.
    if (highlight_row==-1) {
      for (uint16 t=0;t<CELL_DISP_N_ROWS;t++) {
        if (row_desc[t].n_id_cell!=-1) {
          highlight_row=t;
          break;
        }
      }
    }

    if (disp_mode==STD) {
      // Header and footer
      {
        stringstream ss;
        ss << "LTE-Tracker www.evrytania.com; 1.0 to " << MAJOR_VERSION << "." << MINOR_VERSION << "." << PATCH_LEVEL << ": OpenCL/TDD/HACKRF/bladeRF/ext-LNB added by Jiao.([email protected]).";
        move(0,0);
        attron(COLOR_PAIR(CYAN));
        print_center(ss.str());
        attroff(COLOR_PAIR(CYAN));
      }

      move(CELL_DISP_END_ROW+1,0);
      if (!all_cells_displayed) {
        attron(COLOR_PAIR(YELLOW));
        printw("Warning: some tracked cells could not be displayed! (screen has too few rows)\n");
        attroff(COLOR_PAIR(YELLOW));
      } else {
        printw("\n");
      }
      if (global_thread_data.cell_seconds_dropped()||global_thread_data.raw_seconds_dropped()) {
        attron(COLOR_PAIR(RED));
        printw("[dropped cell/raw data: %i/%i s]\n",global_thread_data.cell_seconds_dropped(),global_thread_data.raw_seconds_dropped());
        attroff(COLOR_PAIR(RED));
      } else {
        printw("\n");
      }
      if (expert_mode) {
        if (avg_values) {
          printw("Showing average measurements\n");
        } else {
          printw("Showing instant measurements\n");
        }
      } else {
        printw("\n");
      }
      attron(COLOR_PAIR(MAGENTA));
      printw("[InitFO: %5.0lfHz][TrackFO: %5.2lfHz]",global_thread_data.initial_frequency_offset(), global_thread_data.frequency_offset()-global_thread_data.initial_frequency_offset());
      //attron(A_BOLD);
//      printw("[searcher delay: %.1lf s]",global_thread_data.searcher_cycle_time());
      printw("[SearcherDelay: %4.2fs][rqst %5.2lfMHz][prog %5.2lfMHz][fs %5.2lfMHz][k %5.3lf][twist %d]",global_thread_data.searcher_cycle_time(), global_thread_data.fc_requested/1e6, global_thread_data.fc_programmed/1e6, global_thread_data.fs_programmed/1e6, global_thread_data.k_factor(), global_thread_data.sampling_carrier_twist());
      //attroff(A_BOLD);

      if (fifo_status) {
        boost::mutex::scoped_lock lock(sampbuf_sync.mutex);
        stringstream ss;
        uint8 w=ceil(log10(sampbuf_sync.fifo_peak_size));
        ss << "[inp buf: " << setw(w) << sampbuf_sync.fifo.size() << "/" << sampbuf_sync.fifo_peak_size << "]";
        //attron(A_BOLD);
        printw("%s\n",ss.str().c_str());
        //attroff(A_BOLD);
      } else {
        printw("\n");
      }
      attroff(COLOR_PAIR(MAGENTA));

      /*
      printw("Searcher thread ID: %5i\n",global_thread_data.searcher_thread_id);
      printw("Producer thread ID: %5i\n",global_thread_data.producer_thread_id);
      printw("Main     thread ID: %5i\n",global_thread_data.main_thread_id);
      printw("Display  thread ID: %5i\n\n",global_thread_data.display_thread_id);
      */

      attron(COLOR_PAIR(GREEN));
      //printw("Up/down = select, left/right = details, (h)elp, (q)uit\n");
      print_right("right arrow > TF");
      print_center("(h)elp, (q)uit");
      attroff(COLOR_PAIR(GREEN));
      //attroff(A_BOLD);

      // Highlight one of the rows
      if (highlight_row!=-1) {
        if (row_desc[highlight_row].n_id_cell==-1) {
          highlight_row=-1;
        } else {
          move(CELL_DISP_START_ROW+highlight_row,0);
          chgat(-1,A_REVERSE,0,NULL);
        }
      }

    } else {
      // Zoom into port details

      // Shortcuts
      const int16 & n_id_cell=row_desc[highlight_row].n_id_cell;
      const int8 & duplex_mode=row_desc[highlight_row].duplex_mode;
      const int8 & port_num=row_desc[highlight_row].port_num;

      {
        boost::mutex::scoped_lock lock(tracked_cell_list.mutex);
        list <tracked_cell_t *>::iterator it=tracked_cell_list.tracked_cells.begin();
        vector <tracked_cell_t *> pass2_display;
        bool cell_found=false;
        while (it!=tracked_cell_list.tracked_cells.end()) {
          tracked_cell_t & tracked_cell=(*(*it));
          if ((tracked_cell.n_id_cell==n_id_cell)&&((port_num==-1)||(port_num<tracked_cell.n_ports))) {
            cell_found=true;
            break;
          }
          ++it;
        }
        if (cell_found) {
          tracked_cell_t & tracked_cell=(*(*it));
          boost::mutex::scoped_lock lock2(tracked_cell.meas_mutex);
          if (detail_type==0) {
            // Plot transfer function mag
            vec trace;
            if (port_num==-1) {
              trace=db10(sqr(tracked_cell.sync_ce));
            } else {
              trace=db10(sqr(tracked_cell.ce.get_row(port_num)));
            }
            plot_trace(
              // Trace desc.
              trace,itpp_ext::matlab_range(0.0,71.0),
              // X axis
              0,71,12,NAN,
              // Y axis
              -50,0,10,
              // UL corner
              1,0,
              // LR corner
              LINES-2,0+72+4,
              true
            );
            move(0,0);
            stringstream ss;
            if (duplex_mode==0)
                ss << "FDD Cell " << n_id_cell;
            else
                ss << "TDD Cell " << n_id_cell;
            if (port_num==-1) {
              ss << " Sync channel magnitude\n";
            } else {
              ss << " port " << port_num << " magnitude\n";
            }
            attron(COLOR_PAIR(CYAN));
            print_center(ss.str());
            attroff(COLOR_PAIR(CYAN));
            move(LINES-1,0);
            attron(COLOR_PAIR(GREEN));
            print_center("(h)elp, (q)uit");
            print_right("right arrow > phase resp");
            print_left("top menu < left arrow");
            attroff(COLOR_PAIR(GREEN));
          } else if (detail_type==1) {
            // Plot transfer function phase
            vec trace;
            double mean_ang;
            if (port_num==-1) {
              trace=arg(tracked_cell.sync_ce);
              mean_ang=arg(sum(exp(J*trace(5,66))));
              trace=arg(tracked_cell.sync_ce*exp(J*-mean_ang));
              trace(0)=NAN;
              trace(1)=NAN;
              trace(2)=NAN;
              trace(3)=NAN;
              trace(4)=NAN;
              trace(67)=NAN;
              trace(68)=NAN;
              trace(69)=NAN;
              trace(70)=NAN;
              trace(71)=NAN;
            } else {
              trace=arg(tracked_cell.ce.get_row(port_num));
              mean_ang=arg(sum(exp(J*trace)));
              trace=arg(tracked_cell.ce.get_row(port_num)*exp(J*-mean_ang));
            }
            trace=trace/pi*180;
            plot_trace(
              // Trace desc.
              trace,itpp_ext::matlab_range(0.0,71.0),
              // X axis
              0,71,12,(mean_ang+pi)/(2*pi)*71,
              // Y axis
              -40,40,10,
              // UL corner
              1,0,
              // LR corner
              LINES-2,0+72+4,
              false
            );
            move(0,0);
            stringstream ss;
            if (duplex_mode==0)
                ss << "FDD Cell " << n_id_cell;
            else
                ss << "TDD Cell " << n_id_cell;
            if (port_num==-1) {
              ss << " Sync channel phase\n";
            } else {
              ss << " port " << port_num << " phase\n";
            }
            attron(COLOR_PAIR(CYAN));
            print_center(ss.str());
            attroff(COLOR_PAIR(CYAN));
            move(LINES-1,0);
            attron(COLOR_PAIR(GREEN));
            print_center("(h)elp, (q)uit");
            //print_right("right arrow > phase response");
            print_left("mag resp < left arrow");
            attroff(COLOR_PAIR(GREEN));
          } else if (detail_type==2) {
            // Frequency domain autocorrelation
            const vec trace=abs(tracked_cell.ac_fd);
            plot_trace(
              // Trace desc.
              trace,itpp_ext::matlab_range(0.0,11.0),
              // X axis
              0,11,2,NAN,
              // Y axis
              0,1.2,.5,
              // UL corner
              0,0,
              // LR corner
              LINES-3,0+72+4,
              true
            );
            move(LINES-2,0);
            if (duplex_mode==0)
                printw("FDD Cell ID: %i\n",n_id_cell);
            else
                printw("TDD Cell ID: %i\n",n_id_cell);
            printw("Frequency domain channel autocorrelation function. x-axis spans 1.26MHz\n");
          } else if (detail_type==3) {
            // Time domain autocorrelation
            const vec trace=abs(tracked_cell.ac_td);
            plot_trace(
              // Trace desc.
              trace,itpp_ext::matlab_range(0.0,71.0)*.0005,
              // X axis
              0,71*.0005,.010,NAN,
              // Y axis
              0,3.2,.5,
              // UL corner
              0,0,
              // LR corner
              LINES-3,0+72+4,
              true
            );
            move(LINES-2,0);
            if (duplex_mode == 0)
                printw("FDD Cell ID: %i\n",n_id_cell);
            else
                printw("TDD Cell ID: %i\n",n_id_cell);
            printw("Time domain channel autocorrelation function. x-axis spans 35.5ms\n");
          }
        } else {
          move(1,0);
          printw("Cell is no longer being tracked. Press left arrow to go back!\n");
        }
      }

    }

    refresh();

    // Handle keyboard input.
    // Previous halfdelay() function ensures that this will not block.
    int ch=getch();
    switch (ch) {
      case 'q':
      case 'Q':
        if (global_thread_data.dev_use() == dev_type_t::RTLSDR) {
          #ifdef HAVE_RTLSDR
          if ( global_thread_data.rtlsdr_dev()!=NULL ) {
            rtlsdr_close( global_thread_data.rtlsdr_dev() );
            global_thread_data.rtlsdr_dev(NULL);
          }
          #endif
        } else if (global_thread_data.dev_use() == dev_type_t::HACKRF) {
          #ifdef HAVE_HACKRF
          if (global_thread_data.hackrf_dev()!=NULL) {
            hackrf_close( global_thread_data.hackrf_dev() );
            global_thread_data.hackrf_dev(NULL);
            hackrf_exit();
          }
          #endif
        } else if (global_thread_data.dev_use() == dev_type_t::BLADERF) {
          #ifdef HAVE_BLADERF
          if (global_thread_data.bladerf_dev()!=NULL) {
            bladerf_close( global_thread_data.bladerf_dev() );
            global_thread_data.bladerf_dev(NULL);
          }
          #endif
        }
        ABORT(-1);
        break;
      case 'r':
      case 'R':
        auto_refresh=!auto_refresh;
        if (auto_refresh) {
          halfdelay(1+round_i(refresh_delay_sec*10.0));
        } else {
          cbreak();
        }
        break;
      case '-':
      case '_':
        refresh_delay_sec=MIN(15,refresh_delay_sec*1.5);
        halfdelay(round_i(refresh_delay_sec*10.0));
        break;
      case '+':
      case '=':
        refresh_delay_sec=MAX(0.001,refresh_delay_sec/1.5);
        halfdelay(round_i(refresh_delay_sec*10.0));
        break;
      case 'f':
      case 'F':
        fifo_status=!fifo_status;
        break;
      case 'a':
      case 'A':
        avg_values=!avg_values;
        break;
      case 27:
        // Escape key
        disp_mode=STD;
        break;
      case 'k':
      case 'K':
      case KEY_UP:
        for (int16 t=highlight_row-1;t>=0;t--) {
          if (row_desc[t].n_id_cell!=-1) {
            highlight_row=t;
            break;
          }
        }
        break;
      case 'j':
      case 'J':
      case KEY_DOWN:
        for (uint16 t=highlight_row+1;t<CELL_DISP_N_ROWS;t++) {
          if (row_desc[t].n_id_cell!=-1) {
            highlight_row=t;
            break;
          }
        }
        break;
      case 'l':
      case 'L':
      case KEY_RIGHT:
      case '\n':
        if (disp_mode==STD) {
          disp_mode=DETAIL;
          detail_type=0;
        } else {
          detail_type=MIN(detail_type+1,N_DETAILS-1);
        }
        break;
      case KEY_LEFT:
        if (disp_mode==DETAIL) {
          if (detail_type==0) {
            disp_mode=STD;
          } else {
            detail_type-=1;
          }
        }
        break;
      case 'h':
      case 'H':
        clear();
        move(0,0);
        if (disp_mode==STD) {
          print_center("LTE-Tracker main display help menu\n");
          printw("\n");
          //printw("Display frequency response:\n");
          printw("up/down keys move selection bar\n");
          printw("'Enter' or right-arrow displays the frequency response\n");
          printw("\n");
          printw("P0 : SNR received from port 0\n");
          printw("S  : SNR of the PSS/SSS synchronization channel\n");
          printw("TO : frame timing referenced to the dongle's timescale of 19200 samples\n");
          printw("FO : dongle's current residual frequency offset\n");
          printw("\n");
          printw("'q' exits the program\n");
          printw("+/- increases or decreases screen refresh rate\n");
          printw("'r' turns automatic screen refresh on/off\n");
          printw("'f' displays the current status of the fifo's\n");
          printw("Esc always returns to the top menu\n");
          printw("\n");
          printw("Health is a measure of the amount of time since the last time the MIB\n");
          printw("was successfully decoded for that cell. Cells are dropped when health\n");
          printw("reaches 0.\n");
          printw("\n");
          printw("searcher delay is the amount of time (in seconds) that it takes for the\n");
          printw("searcher to complete a full search cycle\n");
          printw("\n");
          printw("Press any key to exit help screen!\n");
          cbreak();
          getch();
          if (auto_refresh) {
            halfdelay(round_i(refresh_delay_sec*10.0));
          }
        } else {
          print_center("LTE-Tracker mag/phase response help menu\n");
          printw("\n");
          printw("up/down keys change port and cell ID\n");
          printw("left/right keys change to phase response and main menu\n");
          printw("\n");
          printw("'q' exits the program\n");
          printw("+/- increases or decreases screen refresh rate\n");
          printw("'r' turns automatic screen refresh on/off\n");
          printw("Esc always returns to the top menu\n");
          printw("\n");
          printw("For the phase plot, the average phase offset is removed from the trace, but\n");
          printw("the value of this phase offset is indicated by an asterisk on the x axis.\n");
          printw("\n");
          printw("Press any key to exit help screen!\n");
          cbreak();
          getch();
          if (auto_refresh) {
            halfdelay(round_i(refresh_delay_sec*10.0));
          }
        }
        break;
    }
  }
}
// Process that takes samples and distributes them to the appropriate
// process.
void producer_thread(
  sampbuf_sync_t & sampbuf_sync,
  capbuf_sync_t & capbuf_sync,
  global_thread_data_t & global_thread_data,
  tracked_cell_list_t & tracked_cell_list,
  double & fc
) {
  global_thread_data.producer_thread_id=syscall(SYS_gettid);

  // Main loop which distributes data to the appropriate subthread.
  // Local storage for each cell.
  cell_local_t cell_local[504];
  for (uint16 t=0;t<504;t++) {
    cell_local[t].serial_num=0;
  }

  //Real_Timer tt;
  double sample_time=-1;
  bool searcher_capbuf_filling=false;
  uint32 searcher_capbuf_idx=0;
  //unsigned long long int sample_number=0;
  // Elevate privileges of the producer thread.
  //int retval=nice(-10);
  //if (retval==-1) {
  //  cerr << "Error: could not set elevated privileges" << endl;
  //  ABORT(-1);
  //}
  //int policy=SCHED_RR;
  //struct sched_param param;
  //param.sched_priority=50;
  ////pthread_getschedparam(pthread_self(), &policy, &param);
  //if (pthread_setschedparam(pthread_self(),policy,&param)) {
  //  cerr << "Error: could not elevate main thread priority" << endl;
  //  ABORT(-1);
  //}
  //tt.tic();
#define BLOCK_SIZE 10000
  while (true) {
    // Each iteration of this loop processes one block of data.
    const double frequency_offset=global_thread_data.frequency_offset();
    const double k_factor=(global_thread_data.fc_requested-frequency_offset)/global_thread_data.fc_programmed;
    //const double k_factor_inv=1/k_factor;
    const double & fs_programmed=global_thread_data.fs_programmed;

    // Get the next block
    //complex <double> sample;
    cvec samples(BLOCK_SIZE);
    vec samples_timestamp(BLOCK_SIZE);
    uint32 n_samples;
    {
      boost::mutex::scoped_lock lock(sampbuf_sync.mutex);
      while (sampbuf_sync.fifo.size()<2) {
        sampbuf_sync.condition.wait(lock);
      }
      // Dump data if there is too much in the fifo
      while (sampbuf_sync.fifo.size()>2*FS_LTE/16*1.5) {
        for (uint32 t=0;t<(unsigned)round_i(fs_programmed*k_factor);t++) {
          sampbuf_sync.fifo.pop_front();
        }
        global_thread_data.raw_seconds_dropped_inc();
      }
      n_samples=BLOCK_SIZE;
      // complex <double> sample_temp;
      for (uint16 t=0;t<BLOCK_SIZE;t++) {
        if (sampbuf_sync.fifo.size()<2) {
          n_samples=t;
          break;
        }
        // sample_temp.real()=(sampbuf_sync.fifo.front()-127.0)/128.0;
        double sample_real = (sampbuf_sync.fifo.front()-127.0)/128.0;
        sampbuf_sync.fifo.pop_front();
        // sample_temp.imag()=(sampbuf_sync.fifo.front()-127.0)/128.0;
        double sample_imag = (sampbuf_sync.fifo.front()-127.0)/128.0;
        complex <double> sample_temp(sample_real, sample_imag);

        sampbuf_sync.fifo.pop_front();
        samples(t)=sample_temp;
        sample_time+=(FS_LTE/16)/(fs_programmed*k_factor);
        //sample_time=itpp_ext::matlab_mod(sample_time,19200.0);
        if (sample_time>19200.0)
          sample_time-=19200.0;
        samples_timestamp(t)=sample_time;
      }
    }

    // Handle the searcher capture buffer
    for (uint32 t=0;t<n_samples;t++) {
      if ((capbuf_sync.request)&&(abs(WRAP(samples_timestamp(t)-0,-19200.0/2,19200.0/2))<0.5)) {
        //cout << "searcher data cap beginning" << samples_timestamp(t) << endl;
        capbuf_sync.request=false;
        searcher_capbuf_filling=true;
        searcher_capbuf_idx=0;
        capbuf_sync.late=WRAP(samples_timestamp(t)-0,-19200.0/2,19200.0/2);
      }

      // Populate the capture buffer
      if (searcher_capbuf_filling) {
        capbuf_sync.capbuf(searcher_capbuf_idx++)=samples(t);
        if (searcher_capbuf_idx==(unsigned)capbuf_sync.capbuf.size()) {
          // Buffer is full. Signal the searcher thread.
          searcher_capbuf_filling=false;
          boost::mutex::scoped_lock lock(capbuf_sync.mutex);
          capbuf_sync.condition.notify_one();
          //cout << "searcher data cap finished" << endl;
        }
      }
    }

    // Loop for each tracked cell and save data, if necessary. Also delete
    // threads that may have lost lock.
    {
      boost::mutex::scoped_lock lock(tracked_cell_list.mutex);
      list <tracked_cell_t *>::iterator it=tracked_cell_list.tracked_cells.begin();
      while (it!=tracked_cell_list.tracked_cells.end()) {
        tracked_cell_t & tracked_cell=(*(*it));
        // See if this thread has been launched yet. If not, launch it.
        if (!tracked_cell.launched) {
          tracked_cell.thread=boost::thread(tracker_thread,boost::ref(tracked_cell),boost::ref(global_thread_data));
          tracked_cell.launched=true;
        }
        double frame_timing=tracked_cell.frame_timing();

        cell_local_t & cl=cell_local[tracked_cell.n_id_cell];

        // Initialize local storage if necessary
        if (tracked_cell.serial_num!=cl.serial_num) {
          cl.serial_num=tracked_cell.serial_num;
          cl.pdu.slot_num=0;
          cl.pdu.sym_num=0;
          cl.target_cap_start_time=(tracked_cell.cp_type==cp_type_t::NORMAL)?10:32;
          cl.filling=false;
          cl.buffer_offset=0;
          if (cl.serial_num==1)
            cl.pdu.data.set_size(128);
        }

        // Delete the tracker if lock has been lost.
        if (tracked_cell.kill_me) {
          tracked_cell_t * temp=(*it);
          it=tracked_cell_list.tracked_cells.erase(it);
          delete temp;
          continue;
        }

        // Loop for each sample in the buffer.
        for (uint32 t=0;t<n_samples;t++) {
          // See if we should start filling the buffer.
          if (tracked_cell.tracker_thread_ready&&!cl.filling) {
            double tdiff=WRAP(samples_timestamp(t)-(frame_timing+cl.target_cap_start_time),-19200.0/2,19200.0/2);
            if (
              // Ideal start time is 0.5 samples away from current time
              (abs(tdiff)<0.5) ||
              // It's possible for the frame timing to change between iterations
              // of the outer loop and because of this, it's possible that
              // we missed the best start. Start capturing anyways.
              ((tdiff>0)&&(tdiff<3))
            ) {
              // Configure parameters for this capture
              cl.filling=true;
              cl.pdu.late=tdiff;
              cl.buffer_offset=0;
              // Record the frequency offset and frame timing as they were
              // at the beginning of the capture.
              cl.pdu.frequency_offset=frequency_offset;
              cl.pdu.frame_timing=frame_timing;
            }
          }

          // Save this sample if our state indicates we are filling the
          // buffer.
          if (cl.filling) {
            cl.pdu.data(cl.buffer_offset++)=samples(t);
            if (cl.buffer_offset==128) {
              // Buffer is full! Send PDU
              {
                boost::mutex::scoped_lock lock2(tracked_cell.fifo_mutex);
                tracked_cell.fifo.push(cl.pdu);
                tracked_cell.fifo_peak_size=MAX(tracked_cell.fifo.size(),tracked_cell.fifo_peak_size);
                tracked_cell.fifo_condition.notify_one();
              }
              //cout << "fifo size: " << tracked_cell.fifo.size() << endl;
              // Calculate trigger parameters of next capture
              cl.filling=false;
              if (tracked_cell.cp_type==cp_type_t::EXTENDED) {
                cl.target_cap_start_time+=32+128;
              } else {
                cl.target_cap_start_time+=(cl.pdu.sym_num==6)?128+10:128+9;
              }
              cl.target_cap_start_time=mod(cl.target_cap_start_time,19200);
              slot_sym_inc(tracked_cell.n_symb_dl(),cl.pdu.slot_num,cl.pdu.sym_num);
            }
          }
        }
        ++it;
      }
    }
  }
}