// 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... }
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, ¶m); //if (pthread_setschedparam(pthread_self(),policy,¶m)) { // 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; } } } }