UltraScan III
us_mwlr_viewer.cpp
Go to the documentation of this file.
1 #include <QApplication>
2 
3 #include "us_mwlr_viewer.h"
4 #include "us_mwl_run.h"
5 #include "us_mwl_pltctrl.h"
6 #include "us_license_t.h"
7 #include "us_license.h"
8 #include "us_util.h"
9 #include "us_settings.h"
10 #include "us_gui_settings.h"
11 #include "us_plot.h"
12 #include "us_math2.h"
13 #include "us_db2.h"
14 #include "us_passwd.h"
15 #include "us_investigator.h"
16 #include "us_constants.h"
17 #include "us_report.h"
18 #include "us_gui_util.h"
19 #include "us_util.h"
20 #include "us_editor.h"
21 #include "us_images.h"
22 
23 #ifdef WIN32
24 #include <float.h>
25 #define isnan _isnan
26 #endif
27 
28 #ifndef DbgLv
29 #define DbgLv(a) if(dbg_level>=a)qDebug()
30 #endif
31 
32 int main( int argc, char* argv[] )
33 {
34  QApplication application( argc, argv );
35 
36  #include "main1.inc"
37 
38  // License is OK. Start up.
39 
40  US_MwlRawViewer ww;
41  ww.show();
42  return application.exec();
43 }
44 
46 {
47  const QChar chlamb( 955 );
48 
49  setWindowTitle( tr( "Multi-Wavelength Raw Data Viewer" ) );
50  setPalette( US_GuiSettings::frameColor() );
51 
52  QGridLayout* settings = new QGridLayout;
53 
54  navgrec = 10;
55  is_wrecs = true;
57  p3d_ctld = NULL;
58  p3d_pltw = NULL;
60  QFontMetrics fmet( sfont );
61  int fwid = fmet.maxWidth();
62  int lwid = fwid * 4;
63  int swid = lwid + fwid;
64 
65  // Load controls
66  QLabel* lb_run = us_banner( tr( "Load the Run" ) );
67  pb_loadMwl = us_pushbutton( tr( "Load Raw MWL Data" ) );
68  pb_loadAUC = us_pushbutton( tr( "Load US3 MWL Data" ) );
69  pb_reset = us_pushbutton( tr( "Reset Data" ) );
70  pb_details = us_pushbutton( tr( "Data Details" ), false );
71 
72  QLabel* lb_dir = us_label( tr( "Directory" ), -1 );
73  le_dir = us_lineedit( "", -1, true );
74 
75  QLabel* lb_runID = us_label( tr( "Run ID:" ), -1 );
76  le_runID = us_lineedit( "", -1, true );
77 
78  QLabel* lb_cellchn = us_label( tr( "Cell / Channel" ), -1 );
80 
81  int rhgt = le_runID->height();
82  QLabel* lb_recavg = us_label( tr( "Record Average:" ), -1 );
83  ct_recavg = us_counter( 1, 1, 100, 1 );
84  ct_recavg->setStep( 1.0 );
85  ct_recavg->setFont( sfont );
86  ct_recavg->setMinimumWidth( lwid );
87  ct_recavg->resize( rhgt, swid );
88  ct_recavg->setValue( navgrec );
89 
90  // Plot Range controls
91  QLabel* lb_prcntls = us_banner( tr( "Plot Range Controls" ) );
92  QLabel* lb_rstart = us_label( tr( "Radius Start:" ), -1 );
94  QLabel* lb_rend = us_label( tr( "Radius End:" ), -1 );
95  cb_rend = us_comboBox();
96  QLabel* lb_lstart = us_label( tr( "%1 Start:" ).arg( chlamb ), -1 );
98  QLabel* lb_lend = us_label( tr( "%1 End:" ).arg( chlamb ), -1 );
99  cb_lend = us_comboBox();
100  lb_pltrec = us_label( tr( "Plot (W nm)" ) );
102 
103  pb_prev = us_pushbutton( tr( "Previous" ) );
104  pb_next = us_pushbutton( tr( "Next" ) );
107  us_checkbox( tr( "X-axis Wavelength" ), ck_xwavlen, false );
108 
109  // Scan controls
110  QLabel* lb_scanctl = us_banner( tr( "Scan Control" ) );
111  QLabel* lb_from = us_label( tr( "From:" ) );
112  QLabel* lb_to = us_label( tr( "To:" ) );
113  ct_from = us_counter( 2, 0, 500, 1 );
114  ct_to = us_counter( 2, 0, 500, 1 );
115  pb_exclude = us_pushbutton( tr( "Exclude Scan Range" ) );
116  pb_include = us_pushbutton( tr( "Include All Scans" ) );
117  ct_from ->setFont( sfont );
118  ct_from ->setMinimumWidth( lwid );
119  ct_from ->resize( rhgt, swid );
120  ct_to ->setFont( sfont );
121  ct_to ->setMinimumWidth( lwid );
122  ct_to ->resize( rhgt, swid );
123  ct_from ->setValue( 0 );
124  ct_to ->setValue( 0 );
125  ct_from ->setStep ( 1 );
126  ct_to ->setStep ( 1 );
127 
128  // Advanced Plotting controls
129  QLabel* lb_advplot = us_banner( tr( "Advanced Plotting Control" ) );
130  pb_plot2d = us_pushbutton( tr( "Refresh 2D Plot" ) );
131  pb_movie2d = us_pushbutton( tr( "Show 2D Movie" ) );
132  pb_plot3d = us_pushbutton( tr( "Plot 3D" ) );
133  pb_movie3d = us_pushbutton( tr( "Show 3D Movie" ) );
134  pb_svplot = us_pushbutton( tr( "Save Plot(s)" ) );
135  pb_svmovie = us_pushbutton( tr( "Save Movie(s)" ) );
136  us_checkbox( tr( "Hold 3D Movie Colors Constant" ), ck_hcolorc, false );
137 
138  // Status and standard pushbuttons
139  QLabel* lb_status = us_banner( tr( "Status" ) );
140  le_status = us_lineedit( tr( "(no data loaded)" ), -1, true );
141  QPalette stpal;
142  stpal.setColor( QPalette::Text, Qt::white );
143  stpal.setColor( QPalette::Base, Qt::blue );
144  le_status->setPalette( stpal );
145 
146  QPushButton* pb_help = us_pushbutton( tr( "Help" ) );
147  QPushButton* pb_close = us_pushbutton( tr( "Close" ) );
148 
149  // Signals and Slots
150  connect( pb_loadMwl, SIGNAL( clicked() ),
151  this, SLOT ( load_mwl_raw() ) );
152  connect( pb_loadAUC, SIGNAL( clicked() ),
153  this, SLOT ( load_auc_mwl() ) );
154  connect( pb_reset, SIGNAL( clicked() ),
155  this, SLOT ( resetAll() ) );
156  connect( pb_details, SIGNAL( clicked() ),
157  this, SLOT ( runDetails() ) );
158  connect( cb_cellchn, SIGNAL( currentIndexChanged( int ) ),
159  this, SLOT ( changeCellCh( ) ) );
160  connect( cb_rstart, SIGNAL( currentIndexChanged( int ) ),
161  this, SLOT ( changeRadius( ) ) );
162  connect( cb_rend, SIGNAL( currentIndexChanged( int ) ),
163  this, SLOT ( changeRadius( ) ) );
164  connect( cb_lstart, SIGNAL( currentIndexChanged( int ) ),
165  this, SLOT ( changeLambda( ) ) );
166  connect( cb_lend, SIGNAL( currentIndexChanged( int ) ),
167  this, SLOT ( changeLambda( ) ) );
168  connect( ct_recavg, SIGNAL( valueChanged( double ) ),
169  this, SLOT ( changeAverage() ) );
170  connect( cb_pltrec, SIGNAL( currentIndexChanged( int ) ),
171  this, SLOT ( changeRecord( ) ) );
172  connect( ck_xwavlen, SIGNAL( toggled ( bool ) ),
173  this, SLOT ( changeRectype( bool ) ) );
174  connect( pb_prev, SIGNAL( clicked() ),
175  this, SLOT ( prevPlot() ) );
176  connect( pb_next, SIGNAL( clicked() ),
177  this, SLOT ( nextPlot() ) );
178  connect( ct_from, SIGNAL( valueChanged( double ) ),
179  this, SLOT ( exclude_from( double ) ) );
180  connect( ct_to, SIGNAL( valueChanged( double ) ),
181  this, SLOT ( exclude_to ( double ) ) );
182  connect( pb_exclude, SIGNAL( clicked() ),
183  this, SLOT ( exclude_scans() ) );
184  connect( pb_include, SIGNAL( clicked() ),
185  this, SLOT ( include_scans() ) );
186  connect( pb_plot2d, SIGNAL( clicked() ),
187  this, SLOT ( changeCellCh() ) );
188  connect( pb_movie2d, SIGNAL( clicked() ),
189  this, SLOT ( show_2d_movie() ) );
190  connect( pb_plot3d, SIGNAL( clicked() ),
191  this, SLOT ( plot_3d() ) );
192  connect( pb_movie3d, SIGNAL( clicked() ),
193  this, SLOT ( show_3d_movie() ) );
194  connect( pb_svplot, SIGNAL( clicked() ),
195  this, SLOT ( save_plot() ) );
196  connect( pb_svmovie, SIGNAL( clicked() ),
197  this, SLOT ( save_movie() ) );
198  connect( pb_help, SIGNAL( clicked() ),
199  this, SLOT ( help() ) );
200  connect( pb_close, SIGNAL( clicked() ),
201  this, SLOT ( close() ) );
202 
203  // Do the left-side layout
204  int row = 0;
205  settings->addWidget( lb_run, row++, 0, 1, 8 );
206  settings->addWidget( lb_dir, row++, 0, 1, 8 );
207  settings->addWidget( le_dir, row++, 0, 1, 8 );
208  settings->addWidget( lb_runID, row, 0, 1, 2 );
209  settings->addWidget( le_runID, row++, 2, 1, 6 );
210  settings->addWidget( pb_loadMwl, row, 0, 1, 4 );
211  settings->addWidget( pb_loadAUC, row++, 4, 1, 4 );
212  settings->addWidget( pb_reset, row, 0, 1, 4 );
213  settings->addWidget( pb_details, row++, 4, 1, 4 );
214  settings->addWidget( lb_cellchn, row, 0, 1, 4 );
215  settings->addWidget( cb_cellchn, row++, 4, 1, 4 );
216  settings->addWidget( lb_prcntls, row++, 0, 1, 8 );
217  settings->addWidget( lb_rstart, row, 0, 1, 2 );
218  settings->addWidget( cb_rstart, row, 2, 1, 2 );
219  settings->addWidget( lb_rend, row, 4, 1, 2 );
220  settings->addWidget( cb_rend, row++, 6, 1, 2 );
221  settings->addWidget( lb_lstart, row, 0, 1, 2 );
222  settings->addWidget( cb_lstart, row, 2, 1, 2 );
223  settings->addWidget( lb_lend, row, 4, 1, 2 );
224  settings->addWidget( cb_lend, row++, 6, 1, 2 );
225  settings->addWidget( lb_recavg, row, 0, 1, 2 );
226  settings->addWidget( ct_recavg, row, 2, 1, 2 );
227  settings->addWidget( ck_xwavlen, row++, 4, 1, 4 );
228  settings->addWidget( lb_pltrec, row, 0, 1, 2 );
229  settings->addWidget( cb_pltrec, row, 2, 1, 2 );
230  settings->addWidget( pb_prev, row, 4, 1, 2 );
231  settings->addWidget( pb_next, row++, 6, 1, 2 );
232  settings->addWidget( lb_advplot, row++, 0, 1, 8 );
233  settings->addWidget( pb_plot2d, row, 0, 1, 4 );
234  settings->addWidget( pb_movie2d, row++, 4, 1, 4 );
235  settings->addWidget( pb_plot3d, row, 0, 1, 4 );
236  settings->addWidget( pb_movie3d, row++, 4, 1, 4 );
237  settings->addWidget( pb_svplot, row, 0, 1, 4 );
238  settings->addWidget( pb_svmovie, row++, 4, 1, 4 );
239  settings->addWidget( ck_hcolorc, row++, 0, 1, 8 );
240  settings->addWidget( lb_scanctl, row++, 0, 1, 8 );
241  settings->addWidget( lb_from, row, 0, 1, 1 );
242  settings->addWidget( ct_from, row, 1, 1, 3 );
243  settings->addWidget( lb_to, row, 4, 1, 1 );
244  settings->addWidget( ct_to, row++, 5, 1, 3 );
245  settings->addWidget( pb_exclude, row, 0, 1, 4 );
246  settings->addWidget( pb_include, row++, 4, 1, 4 );
247  settings->addWidget( lb_status, row++, 0, 1, 8 );
248  settings->addWidget( le_status, row++, 0, 1, 8 );
249  settings->addWidget( pb_help, row, 0, 1, 4 );
250  settings->addWidget( pb_close, row++, 4, 1, 4 );
251 
252  // Plot layout for the right side of window
253  QBoxLayout* plot = new US_Plot( data_plot,
254  tr( "Intensity Data" ),
255  tr( "Radius (in cm)" ),
256  tr( "Intensity" ) );
257 
258  data_plot->setMinimumSize( 600, 400 );
259 
260  data_plot->enableAxis( QwtPlot::xBottom, true );
261  data_plot->enableAxis( QwtPlot::yLeft , true );
262 
263  data_plot->setAxisScale( QwtPlot::xBottom, 5.8, 7.2 );
264  data_plot->setAxisScale( QwtPlot::yLeft , 0.0, 1.5 );
265 
266  picker = new US_PlotPicker( data_plot );
267  picker->setRubberBand ( QwtPicker::VLineRubberBand );
268  picker->setMousePattern ( QwtEventPattern::MouseSelect1,
269  Qt::LeftButton, Qt::ControlModifier );
270 
271  // Now let's assemble the page
272 
273  QVBoxLayout* left = new QVBoxLayout;
274 
275  left->addLayout( settings );
276 
277  QVBoxLayout* right = new QVBoxLayout;
278 
279  right->addLayout( plot );
280 
281  QHBoxLayout* main = new QHBoxLayout( this );
282  main->setSpacing ( 2 );
283  main->setContentsMargins ( 2, 2, 2, 2 );
284 
285  main->addLayout( left );
286  main->addLayout( right );
287 
288  main->setStretch( 0, 2 );
289  main->setStretch( 1, 4 );
290 
291  reset();
292  adjustSize();
293 }
294 
296 {
297  cb_cellchn->disconnect();
298  cb_cellchn->clear();
299  le_dir ->setText( "" );
300  le_runID ->setText( "" );
301 
302  pb_loadMwl->setEnabled( true );
303  pb_loadAUC->setEnabled( true );
304  pb_details->setEnabled( false );
305  cb_cellchn->setEnabled( false );
306  cb_rstart ->setEnabled( false );
307  cb_rend ->setEnabled( false );
308  cb_lstart ->setEnabled( false );
309  cb_lend ->setEnabled( false );
310  ct_recavg ->setEnabled( false );
311  ck_xwavlen->setEnabled( false );
312  cb_pltrec ->setEnabled( false );
313  pb_prev ->setEnabled( false );
314  pb_next ->setEnabled( false );
315  ct_from ->setEnabled( false );
316  ct_to ->setEnabled( false );
317  pb_exclude->setEnabled( false );
318  pb_include->setEnabled( false );
319  pb_reset ->setEnabled( false );
320  ct_from ->setEnabled( false );
321  ct_to ->setEnabled( false );
322  pb_exclude->setEnabled( false );
323  pb_include->setEnabled( false );
324  pb_plot2d ->setEnabled( false );
325  pb_movie2d->setEnabled( false );
326  pb_plot3d ->setEnabled( false );
327  pb_movie3d->setEnabled( false );
328  pb_svplot ->setEnabled( false );
329  pb_svmovie->setEnabled( false );
330  ck_hcolorc->setEnabled( false );
331 
332  // Clear any data structures
333  lambdas .clear();
334  radii .clear();
335  allData .clear();
336  excludes .clear();
337  curr_adata.clear();
338  curr_cdata.clear();
339  prev_cdata.clear();
340  curr_recxs.clear();
341  prev_recxs.clear();
342 
343  data_plot->detachItems();
344  picker ->disconnect();
345  data_plot->setAxisScale( QwtPlot::xBottom, 5.8, 7.2 );
346  data_plot->setAxisScale( QwtPlot::yLeft , 0.0, 1.5 );
347  grid = us_grid( data_plot );
348  data_plot->replot();
349  connect( cb_cellchn, SIGNAL( currentIndexChanged( int ) ),
350  this, SLOT ( changeCellCh( ) ) );
351 
352  last_xmin = -1.0;
353  last_xmax = -1.0;
354  last_ymin = -1.0;
355  last_ymax = -1.0;
356 
357  mwl_data.clear();
358  le_status->setText( tr( "(no data loaded)" ) );
359 }
360 
362 {
363  if ( allData.size() > 0 )
364  {
365  int status = QMessageBox::information( this,
366  tr( "New Data Warning" ),
367  tr( "This will erase all data currently on the screen, and "
368  "reset the program to its starting condition. No hard-drive "
369  "data or database information will be affected. Proceed? " ),
370  tr( "&OK" ), tr( "&Cancel" ),
371  0, 0, 1 );
372  if ( status != 0 ) return;
373  }
374 
375  reset();
376 
377  runID = "";
378  data_plot->setTitle( tr( "Intensity Data" ) );
379 }
380 
381 
382 // Enable the common dialog controls based on the presence of data
384 {
385  if ( allData.size() == 0 )
386  { // If no data yet, just reset
387  reset();
388  return;
389  }
390 
391  // Enable and disable controls now
392  pb_loadMwl->setEnabled( false );
393  pb_loadAUC->setEnabled( false );
394  pb_reset ->setEnabled( true );
395  pb_details->setEnabled( true );
396  cb_cellchn->setEnabled( true );
397  cb_rstart ->setEnabled( true );
398  cb_rend ->setEnabled( true );
399  cb_lstart ->setEnabled( true );
400  cb_lend ->setEnabled( true );
401  ct_recavg ->setEnabled( true );
402  ck_xwavlen->setEnabled( true );
403  cb_pltrec ->setEnabled( true );
404  pb_prev ->setEnabled( true );
405  pb_next ->setEnabled( true );
406  pb_plot2d ->setEnabled( true );
407  pb_movie2d->setEnabled( true );
408  ct_from ->setEnabled( true );
409  ct_to ->setEnabled( true );
410  pb_exclude->setEnabled( true );
411  pb_plot3d ->setEnabled( true );
412  pb_svplot ->setEnabled( true );
413  pb_svmovie->setEnabled( true );
414  ck_hcolorc->setEnabled( true );
415 
416  ncellch = cellchans.size();
417  nlambda = lambdas .size();
418  nscan = allData[ 0 ].scanCount();
419  npoint = allData[ 0 ].pointCount();
420  ntpoint = nscan * npoint;
421  QStringList slrads;
422  QStringList sllmbs;
423 
424  for ( int jj = 0; jj < npoint; jj++ )
425  slrads << QString().sprintf( "%.3f", radii[ jj ] );
426 
427  for ( int jj = 0; jj < nlambda; jj++ )
428  sllmbs << QString::number( lambdas[ jj ] );
429 
430  connect_ranges( false );
431  cb_cellchn->clear();
432  cb_rstart ->clear();
433  cb_rend ->clear();
434  cb_lstart ->clear();
435  cb_lend ->clear();
436  cb_pltrec ->clear();
437 
438  cb_cellchn->addItems( cellchans );
439  cb_rstart ->addItems( slrads );
440  cb_rend ->addItems( slrads );
441  cb_lstart ->addItems( sllmbs );
442  cb_lend ->addItems( sllmbs );
443  cb_pltrec ->addItems( sllmbs );
444 
445  cb_cellchn->setCurrentIndex( 0 );
446  cb_rstart ->setCurrentIndex( 0 );
447  cb_rend ->setCurrentIndex( npoint - 1 );
448  cb_lstart ->setCurrentIndex( 0 );
449  cb_lend ->setCurrentIndex( nlambda - 1 );
450  connect_ranges( true );
451 
452  have_rngs = false;
453  compute_ranges( );
454 
455  ct_from ->setMaxValue( nscan );
456  ct_to ->setMaxValue( nscan );
457  cb_cellchn->setCurrentIndex( 0 );
458  cb_pltrec ->setCurrentIndex( nlambda / 2 );
459  qApp->processEvents();
460 
461  changeCellCh(); // Force a plot initialize
462 }
463 
464 // Load MWL raw (.mwrs) data
466 {
467  // Ask for data directory
468  QString dir = "";
469  US_MwlRun lddiag( dir, true );
470  if ( lddiag.exec() == QDialog::Rejected )
471  return;
472 
473  // Restore area beneath dialog
474  qApp->processEvents();
475  if ( dir.isEmpty() ) return;
476  dir.replace( "\\", "/" ); // WIN32 issue
477 
478  // Get mwl file names
479  QDir dirdir( dir, "*", QDir::Name, QDir::Files | QDir::Readable );
480  dirdir.makeAbsolute();
481  if ( dir.right( 1 ) != "/" ) dir += "/"; // Ensure trailing /
482 
483  // See if we need to fix the runID
484  QStringList components = dir.split( "/", QString::SkipEmptyParts );
485  QString runType = "RI";
486  QString new_runID = components.last();
487  QRegExp rx( "[^A-Za-z0-9_-]" );
488 
489  int pos = 0;
490  bool runID_changed = false;
491  while ( ( pos = rx.indexIn( new_runID ) ) != -1 )
492  {
493  new_runID.replace( pos, 1, "_" ); // Replace 1 char at pos
494  runID_changed = true;
495  }
496 
497  // Let the user know if the runID name has changed
498  if ( runID_changed )
499  {
500  QMessageBox::warning( this,
501  tr( "RunID Name Changed" ),
502  tr( "The runID name has been changed. It may consist only "
503  "of alphanumeric \ncharacters, the underscore, and the "
504  "hyphen. New runID: " ) + new_runID );
505  }
506 
507  // Set the runID and directory
508  runID = new_runID;
509  le_runID->setText( runID );
510  le_dir ->setText( dir );
511  currentDir = QString( dir );
512 
513  // Read the data
514  QApplication::setOverrideCursor( QCursor( Qt::WaitCursor) );
515  le_status->setText( tr( "Reading Raw MWL data ..." ) );
516 
518 
519  // Build the AUC equivalent
521 
522  QApplication::restoreOverrideCursor();
523  QApplication::restoreOverrideCursor();
524  mwl_fnames = dirdir.entryList( QStringList( "*.mwrs" ),
525  QDir::Files, QDir::Name );
526  mwl_fnames.sort();
527  ntriple = mwl_fnames.size();
528  runID = new_runID;
530  radii.clear();
531  radii << allData[ 0 ].xvalues;
532  nscan = allData[ 0 ].scanCount();
533  npoint = allData[ 0 ].pointCount();
534 
535 DbgLv(1) << "RD: mwr ntriple" << ntriple;
536 DbgLv(1) << "RD: ncellch" << ncellch;
537 DbgLv(1) << "RD: nscan" << nscan << "npoint" << npoint;
538 DbgLv(1) << "RD: rvS rvE" << radii[0] << radii[npoint-1];
539  cb_cellchn->disconnect();
540  cb_cellchn->clear();
541  cb_cellchn->addItems( cellchans );
542  connect( cb_cellchn, SIGNAL( currentIndexChanged( int ) ),
543  this, SLOT ( changeCellCh( ) ) );
544 
546  int wvlo = lambdas[ 0 ];
547  int wvhi = lambdas[ nlambda - 1 ];
548 DbgLv(1) << "RD: nwl wvlo wvhi" << nlambda << wvlo << wvhi;
549  ntriple = nlambda * ncellch; // Number triples
550  ntpoint = npoint * nscan; // Number radius points per triple
551 DbgLv(1) << "RD: allData size" << allData.size();
552 
553  // Ok to enable some buttons now
554  enableControls();
555 }
556 
557 // Load US3 AUC multi-wavelength data
559 {
560  int status = 0;
561  resetAll();
562 
563  QString dir = "";
564  US_MwlRun lddiag( dir, false );
565  if ( lddiag.exec() == QDialog::Rejected )
566  return;
567 
568  // Restore area beneath dialog
569  qApp->processEvents();
570 
571  if ( dir.isEmpty() ) return;
572 
573  dir.replace( "\\", "/" ); // WIN32 issue
574  if ( dir.right( 1 ) != "/" ) dir += "/"; // Ensure trailing '/'
575 
576  // Check the runID
577  QStringList components = dir.split( "/", QString::SkipEmptyParts );
578  QString new_runID = components.last();
579 
580  QRegExp rx( "^[A-Za-z0-9_-]{1,80}$" );
581  if ( rx.indexIn( new_runID ) < 0 )
582  {
583  QMessageBox::warning( this,
584  tr( "Bad runID Name" ),
585  tr( "The runID name may consist only of alphanumeric\n"
586  "characters, the underscore, and the hyphen." ) );
587  return;
588  }
589 
590  // Set the runID and directory
591  runID = new_runID;
592  le_runID ->setText( runID );
593  le_dir ->setText( dir );
594  currentDir = QString( dir );
595 
596  // Error reporting
597  if ( status == US_DB2::NO_PROJECT )
598  {
599  QMessageBox::information( this,
600  tr( "Attention" ),
601  tr( "The project was not found.\n"
602  "Please select an existing project and try again.\n" ) );
603  }
604 
605  else if ( status != US_DB2::OK )
606  {
607  QMessageBox::information( this,
608  tr( "Disk Read Problem" ),
609  tr( "Could not read data from the disk.\n"
610  "Disk status: " ) + QString::number( status ) );
611  }
612 
613  // Load the AUC data
614  le_status->setText( tr( "Reading AUC Mwl data ..." ) );
615  QApplication::setOverrideCursor( QCursor( Qt::WaitCursor) );
616  QDir dirdir( dir, "*", QDir::Name, QDir::Files | QDir::Readable );
617  dirdir.makeAbsolute();
618  if ( dir.right( 1 ) != "/" ) dir += "/"; // Ensure trailing /
619  mwl_fnames = dirdir.entryList( QStringList( "*.auc" ),
620  QDir::Files, QDir::Name );
621  mwl_fnames.sort();
622  ntriple = mwl_fnames.size();
623  runID = new_runID;
624  cellchans.clear();
625  lambdas .clear();
626 DbgLv(1) << "RD: mwr ntriple" << ntriple;
627 
628  for ( int ii = 0; ii < ntriple; ii++ )
629  {
630  QString mwrfname = mwl_fnames.at( ii );
631  QString mwrfpath = currentDir + "/" + mwrfname;
632  US_DataIO::RawData rdata;
633 
634  US_DataIO::readRawData( mwrfpath, rdata );
635 
636  allData << rdata;
637 
638  QString celchn = QString::number( rdata.cell ) + " / " +
639  QString( rdata.channel );
640  int lambda = qRound( rdata.scanData[ 0 ].wavelength );
641 
642  if ( ! cellchans.contains( celchn ) )
643  cellchans << celchn;
644 
645  if ( ! lambdas.contains( lambda ) )
646  lambdas << lambda;
647 
648  le_status->setText( tr( "Data in for triple %1 of %2" )
649  .arg( ii + 1 ).arg( ntriple ) );
650  qApp->processEvents();
651  }
652 
653  radii.clear();
654  radii << allData[ 0 ].xvalues;
655 
656  ncellch = cellchans .size();
657  nlambda = lambdas .size();
658  nscan = allData[ 0 ].scanCount();
659  npoint = allData[ 0 ].pointCount();
660  ntpoint = nscan * npoint;
661 DbgLv(1) << "RD: mwr ncellch nlambda nscan npoint"
662  << ncellch << nlambda << nscan << npoint;
663 DbgLv(1) << "RD: rvS rvE" << radii[0] << radii[npoint-1];
664  le_status->setText( tr( "All %1 raw AUCs have been loaded." )
665  .arg( ntriple ) );
666  QApplication::restoreOverrideCursor();
667  QApplication::restoreOverrideCursor();
668  qApp->processEvents();
669 
670  ct_from->setMaxValue( nscan );
671  ct_to ->setMaxValue( nscan );
672 
673  // Ok to enable some buttons now
674  enableControls();
675 }
676 
677 // Display detailed information about the data
679 {
680  // Initialize statistics for data
681  double ofdmax = -1e99;
682  double o90max = -1e99;
683  double ow1max = -1e99;
684  double ow2max = -1e99;
685  double ow3max = -1e99;
686  double afdmax = -1e99;
687  double a90max = -1e99;
688  double aw1max = -1e99;
689  double aw2max = -1e99;
690  double aw3max = -1e99;
691  double ofdmin = 1e99;
692  double o90min = 1e99;
693  double ow1min = 1e99;
694  double ow2min = 1e99;
695  double ow3min = 1e99;
696  double afdmin = 1e99;
697  double a90min = 1e99;
698  double aw1min = 1e99;
699  double aw2min = 1e99;
700  double aw3min = 1e99;
701  double ofdavg = 0.0;
702  double o90avg = 0.0;
703  double ow1avg = 0.0;
704  double ow2avg = 0.0;
705  double ow3avg = 0.0;
706  double afdavg = 0.0;
707  double a90avg = 0.0;
708  double aw1avg = 0.0;
709  double aw2avg = 0.0;
710  double aw3avg = 0.0;
711  int ofdknt = 0;
712  int o90knt = 0;
713  int ow1knt = 0;
714  int ow2knt = 0;
715  int ow3knt = 0;
716  int afdknt = 0;
717  int a90knt = 0;
718  int aw1knt = 0;
719  int aw2knt = 0;
720  int aw3knt = 0;
721  bool wv1 = false;
722  bool wv2 = false;
723  bool wv3 = false;
724  int awvlo = 0;
725  int awvmd = nlambda / 2;
726  int awvhi = nlambda - 1;
727  int owvlo = 0;
728  int owvhi = nlambda - 1;
729  double wvvmid = lambdas[ nlambda / 2 ];
730  int owvmd = lambdas.indexOf( wvvmid );
731  int rplo = ( npoint * 5 ) / 100;
732  int rphi = npoint - rplo;
733  double rdata = 0.0;
734  int wvx = 0;
735  int rpx = 0;
736 DbgLv(1) << "DDet: nlambda ntriple ntpoint" << nlambda << ntriple << ntpoint;
737  le_status->setText( tr( "Computing data statistics..." ) );
738  qApp->processEvents();
739  QApplication::setOverrideCursor( QCursor( Qt::WaitCursor) );
740 
741  // Accumulate statistics for original data
742  for ( int ii = 0; ii < ntriple; ii++ )
743  {
744  wv1 = ( wvx == owvlo );
745  wv2 = ( wvx == owvmd );
746  wv3 = ( wvx == owvhi );
747  rpx = 0;
748  int scx = 0;
749 
750  for ( int jj = 0; jj < ntpoint; jj++ )
751  {
752  rdata = allData[ ii ].reading( scx, rpx );
753  ofdmin = qMin( ofdmin, rdata );
754  ofdmax = qMax( ofdmax, rdata );
755  ofdavg += rdata;
756  ofdknt++;
757  if ( rpx > rplo && rpx < rphi )
758  {
759  o90min = qMin( o90min, rdata );
760  o90max = qMax( o90max, rdata );
761  o90avg += rdata;
762  o90knt++;
763  if ( wv1 )
764  {
765  ow1min = qMin( ow1min, rdata );
766  ow1max = qMax( ow1max, rdata );
767  ow1avg += rdata;
768  ow1knt++;
769  }
770  if ( wv2 )
771  {
772  ow2min = qMin( ow2min, rdata );
773  ow2max = qMax( ow2max, rdata );
774  ow2avg += rdata;
775  ow2knt++;
776  }
777  if ( wv3 )
778  {
779  ow3min = qMin( ow3min, rdata );
780  ow3max = qMax( ow3max, rdata );
781  ow3avg += rdata;
782  ow3knt++;
783  }
784  }
785  rpx++;
786  if ( rpx >= npoint )
787  rpx = 0;
788  }
789  wvx++;
790  if ( wvx >= nlambda )
791  wvx = 0;
792  }
793 DbgLv(1) << "DDet: ow3min ow3avg" << ow3min << ow3avg;
794 
795  // Accumulate statistics for averaged data
796  double avsc = 1.0;
797  int nha1 = navgrec / 2;
798  int nha2 = navgrec - nha1;
799  wvx = 0;
800  rpx = 0;
801 
802  for ( int ii = 0; ii < ntriple; ii++ )
803  {
804  wv1 = ( wvx == awvlo );
805  wv2 = ( wvx == awvmd );
806  wv3 = ( wvx == awvhi );
807  rpx = 0;
808  int scx = 0;
809  int wvxs = wvx - nha1;
810  int wvxe = wvx + nha2;
811  int chof = ( ii / nlambda ) * nlambda;
812  wvxs = qMax( wvxs, 0 );
813  wvxe = qMin( wvxe, nlambda );
814  wvxe = qMax( wvxe, ( wvxs + 1 ) );
815  wvxs += chof;
816  wvxe += chof;
817  avsc = 1.0 / (double)( wvxe - wvxs );
818 
819  for ( int jj = 0; jj < ntpoint; jj++ )
820  {
821  rdata = 0.0;
822  for ( int kk = wvxs; kk < wvxe; kk++ )
823  rdata += allData[ kk ].reading( scx, rpx );
824  rdata *= avsc;
825  afdmin = qMin( afdmin, rdata );
826  afdmax = qMax( afdmax, rdata );
827  afdavg += rdata;
828  afdknt++;
829  if ( rpx > rplo && rpx < rphi )
830  {
831  a90min = qMin( a90min, rdata );
832  a90max = qMax( a90max, rdata );
833  a90avg += rdata;
834  a90knt++;
835  if ( wv1 )
836  {
837  aw1min = qMin( aw1min, rdata );
838  aw1max = qMax( aw1max, rdata );
839  aw1avg += rdata;
840  aw1knt++;
841  }
842  if ( wv2 )
843  {
844  aw2min = qMin( aw2min, rdata );
845  aw2max = qMax( aw2max, rdata );
846  aw2avg += rdata;
847  aw2knt++;
848  }
849  if ( wv3 )
850  {
851  aw3min = qMin( aw3min, rdata );
852  aw3max = qMax( aw3max, rdata );
853  aw3avg += rdata;
854  aw3knt++;
855  }
856  }
857  rpx++;
858  if ( rpx >= npoint )
859  rpx = 0;
860  }
861  wvx++;
862  if ( wvx >= nlambda )
863  wvx = 0;
864  }
865 DbgLv(1) << "DDet: aw3min aw3avg" << aw3min << aw3avg;
866  le_status->setText( tr( "Data statistics have been computed." ) );
867  qApp->processEvents();
868  QApplication::restoreOverrideCursor();
869  QApplication::restoreOverrideCursor();
870 
871  // Now build the report text string
872  ofdavg /= (double)ofdknt;
873  o90avg /= (double)o90knt;
874  ow1avg /= (double)ow1knt;
875  ow2avg /= (double)ow2knt;
876  ow3avg /= (double)ow3knt;
877  afdavg /= (double)afdknt;
878  a90avg /= (double)a90knt;
879  aw1avg /= (double)aw1knt;
880  aw2avg /= (double)aw2knt;
881  aw3avg /= (double)aw3knt;
882  int lx = ntriple - 1;
883 DbgLv(1) << "DDet: aw3knt aw3avg" << aw3knt << aw3avg << "lx" << lx;
884  double s1tem = allData[ 0 ].scanData[ 0 ].temperature;
885  double s1rot = allData[ 0 ].scanData[ 0 ].rpm;
886  double s1omg = allData[ 0 ].scanData[ 0 ].omega2t;
887  double s1etm = allData[ 0 ].scanData[ 0 ].seconds;
888  double s2tem = allData[ lx ].scanData[ 0 ].temperature;
889  double s2rot = allData[ lx ].scanData[ 0 ].rpm;
890  double s2omg = allData[ lx ].scanData[ 0 ].omega2t;
891  double s2etm = allData[ lx ].scanData[ 0 ].seconds;
892  double owvmin = lambdas[ 0 ];
893  double owvmax = lambdas[ nlambda - 1 ];
894 DbgLv(1) << "DDet: owvmin owvmax" << owvmin << owvmax;
895  double owvain = ( owvmax - owvmin ) / (double)( nlambda - 1 );
896  double awvain = ( owvmax - owvmin ) / (double)( nlambda - 1 );
897  double wavv1 = owvmin;
898  double wavv2 = lambdas[ owvmd ];
899  double wavv3 = owvmax;
900 DbgLv(1) << "DDet: owvmd wavv2" << owvmd << wavv2;
901  QString ffname = mwl_fnames[ 0 ];
902 DbgLv(1) << "DDet: ffname" << ffname;
903  lx = mwl_fnames.count() - 1;
904  QString lfname = mwl_fnames[ lx ];
905 DbgLv(1) << "DDet: lfname" << lfname;
906  QString msg = tr( "Multi-Wavelength Statistics for RunID %1,\n" )
907  .arg( runID );
908  msg += tr( " from Directory %1\n\n" ).arg( currentDir );
909  msg += tr( "General Data Set Values and Counts.\n" );
910  msg += tr( " First File Name: %1\n" ).arg( ffname );
911  msg += tr( " Last File Name: %1\n" ).arg( lfname );
912  msg += tr( " Scans: %1\n" ).arg( nscan );
913  msg += tr( " Radius Data Points: %1\n" ).arg( npoint );
914  msg += tr( "Values for 1st Cell/Channel Scan 1.\n" );
915  msg += tr( " Temperature %1\n" ).arg( s1tem );
916  msg += tr( " Omega^2T %1\n" ).arg( s1omg );
917  msg += tr( " Elapsed Time %1\n" ).arg( s1etm );
918  msg += tr( " RotorSpeed %1\n" ).arg( s1rot );
919  msg += tr( "Values for Last Cell/Channel Scan %1.\n" ).arg( nscan );
920  msg += tr( " Temperature %1\n" ).arg( s2tem );
921  msg += tr( " Omega^2T %1\n" ).arg( s2omg );
922  msg += tr( " Elapsed Time %1\n" ).arg( s2etm );
923  msg += tr( " RotorSpeed %1\n" ).arg( s2rot );
924  msg += tr( "Original Data Wavelengths.\n" );
925  msg += tr( " Count of Wavelengths: %1\n" ).arg( nlambda );
926  msg += tr( " Minimum: %1\n" ).arg( owvmin );
927  msg += tr( " Maximum: %1\n" ).arg( owvmax );
928  msg += tr( " Average Increment: %1\n" ).arg( owvain );
929  msg += tr( "%1-point Averaged Data Wavelengths.\n" ).arg( navgrec );
930  msg += tr( " Count of Wavelengths: %1\n" ).arg( klambda );
931  msg += tr( " Minimum: %1\n" ).arg( owvmin );
932  msg += tr( " Maximum: %1\n" ).arg( owvmax );
933  msg += tr( " Average Increment: %1\n" ).arg( awvain );
934  msg += tr( "\nOriginal Full Intensity Data.\n" );
935  msg += tr( " Minimum: %1\n" ).arg( ofdmin );
936  msg += tr( " Maximum: %1\n" ).arg( ofdmax );
937  msg += tr( " Average: %1\n" ).arg( ofdavg );
938  msg += tr( "%1-point Averaged Full Intensity Data.\n" ).arg( navgrec );
939  msg += tr( " Minimum: %1\n" ).arg( afdmin );
940  msg += tr( " Maximum: %1\n" ).arg( afdmax );
941  msg += tr( " Average: %1\n" ).arg( afdavg );
942  msg += tr( "Original Middle 90% Intensity Data.\n" );
943  msg += tr( " Minimum: %1\n" ).arg( o90min );
944  msg += tr( " Maximum: %1\n" ).arg( o90max );
945  msg += tr( " Average: %1\n" ).arg( o90avg );
946  msg += tr( "%1-point Averaged Middle 90% Intensity Data.\n" ).arg( navgrec );
947  msg += tr( " Minimum: %1\n" ).arg( a90min );
948  msg += tr( " Maximum: %1\n" ).arg( a90max );
949  msg += tr( " Average: %1\n" ).arg( a90avg );
950  msg += tr( "%1 nm Original Middle 90% Intensity Data.\n" )
951  .arg( wavv1 );
952  msg += tr( " Minimum: %1\n" ).arg( ow1min );
953  msg += tr( " Maximum: %1\n" ).arg( ow1max );
954  msg += tr( " Average: %1\n" ).arg( ow1avg );
955  msg += tr( "%1 nm %2-point Averaged Middle 90% Intensity Data.\n" )
956  .arg( wavv1 ).arg( navgrec );
957  msg += tr( " Minimum: %1\n" ).arg( aw1min );
958  msg += tr( " Maximum: %1\n" ).arg( aw1max );
959  msg += tr( " Average: %1\n" ).arg( aw1avg );
960  msg += tr( "%1 nm Original Middle 90% Intensity Data.\n" )
961  .arg( wavv2 );
962  msg += tr( " Minimum: %1\n" ).arg( ow2min );
963  msg += tr( " Maximum: %1\n" ).arg( ow2max );
964  msg += tr( " Average: %1\n" ).arg( ow2avg );
965  msg += tr( "%1 nm %2-point Averaged Middle 90% Intensity Data.\n" )
966  .arg( wavv2 ).arg( navgrec );
967  msg += tr( " Minimum: %1\n" ).arg( aw2min );
968  msg += tr( " Maximum: %1\n" ).arg( aw2max );
969  msg += tr( " Average: %1\n" ).arg( aw2avg );
970  msg += tr( "%1 nm Original Middle 90% Intensity Data.\n" )
971  .arg( wavv3 );
972  msg += tr( " Minimum: %1\n" ).arg( ow3min );
973  msg += tr( " Maximum: %1\n" ).arg( ow3max );
974  msg += tr( " Average: %1\n" ).arg( ow3avg );
975  msg += tr( "%1 nm %2-point Averaged Middle 90% Intensity Data.\n" )
976  .arg( wavv3 ).arg( navgrec );
977  msg += tr( " Minimum: %1\n" ).arg( aw3min );
978  msg += tr( " Maximum: %1\n" ).arg( aw3max );
979  msg += tr( " Average: %1\n" ).arg( aw3avg );
980 
981  // Open the dialog and display the report text
982  US_Editor* editd = new US_Editor( US_Editor::DEFAULT, true );
983  editd->setWindowTitle( tr( "Multi-Wavelength Data Statistics" ) );
984  editd->move( pos() + QPoint( 200, 200 ) );
985  editd->resize( 600, 500 );
986  editd->e->setFont( QFont( US_Widgets::fixedFont().family(),
988  editd->e->setText( msg );
989  editd->show();
990 }
991 
993 {
994  // Match the description to find the correct triple in memory
995  QString cellch = cb_cellchn->currentText();
996 DbgLv(1) << "chgCC: cellch" << cellch << "last_xmin" << last_xmin;
997  if ( allData.size() < 1 )
998  return;
999 
1000  if ( last_xmin < 0.0 )
1001  { // If first time plotting any data, detect actual data bounds
1002  have_rngs = false;
1003  compute_ranges();
1004 
1005  int trxs = trpxs + lmbxs;
1006  int trxe = trpxs + lmbxe;
1007 
1008  if ( is_wrecs )
1009  { // For wavelength records, X bounds are radius bounds
1010  last_xmin = allData[ trxs ].xvalues[ radxs ];
1011  last_xmax = last_xmin;
1012  US_DataIO::RawData* edata = &allData[ trxs ];
1013 
1014  for ( int rdx = radxs; rdx < radxe; rdx++ )
1015  {
1016  last_xmin = qMin( last_xmin, edata->xvalues[ rdx ] );
1017  last_xmax = qMax( last_xmax, edata->xvalues[ rdx ] );
1018  }
1019  }
1020 
1021  else
1022  { // For radius records, X bounds are lambda bounds
1023  last_xmin = lambdas[ lmbxs ];
1024  last_xmax = lambdas[ lmbxe ];
1025  }
1026 
1027  last_ymin = allData[ trxs ].reading( 0, 0 );
1028  last_ymax = last_ymin;
1029 
1030  for ( int trx = trxs; trx < trxe; trx++ )
1031  { // Accumulate Y bounds, the amplitude bounds
1032  US_DataIO::RawData* edata = &allData[ trx ];
1033 
1034  for ( int scx = 0; scx < nscan; scx++ )
1035  {
1036  for ( int rdx = radxs; rdx < radxe; rdx++ )
1037  {
1038  last_ymin = qMin( last_ymin, edata->reading( scx, rdx ) );
1039  last_ymax = qMax( last_ymax, edata->reading( scx, rdx ) );
1040  }
1041  }
1042  }
1043 DbgLv(1) << "chgCC: trxs trxe" << trxs << trxe;
1044  }
1045 
1046  else
1047  { // After first time, detect what has been already set
1048  QwtScaleDiv* sdx = data_plot->axisScaleDiv( QwtPlot::xBottom );
1049  QwtScaleDiv* sdy = data_plot->axisScaleDiv( QwtPlot::yLeft );
1050  last_xmin = sdx->lowerBound();
1051  last_xmax = sdx->upperBound();
1052  last_ymin = sdy->lowerBound();
1053  last_ymax = sdy->upperBound();
1054  }
1055 DbgLv(1) << "chgCC: xmin xmax ymin ymax"
1056  << last_xmin << last_xmax << last_ymin << last_ymax;
1057 
1058  curr_cdata.clear();
1059  prev_cdata.clear();
1060  curr_recxs.clear();
1061  prev_recxs.clear();
1062 
1063  have_rngs = false;
1064  changeRecord();
1065 }
1066 
1067 // Plot the current data record
1069 {
1070  if ( allData.size() == 0 )
1071  return;
1072 
1073  plot_titles(); // Set the titles
1074 
1075  plot_all(); // Plot the data
1076 }
1077 
1078 // Compose plot titles for the current record
1080 {
1081  QString cellch = cb_cellchn ->currentText();
1082  QString cell = cellch.section( "/", 0, 0 ).simplified();
1083  QString chan = cellch.section( "/", 1, 1 ).simplified();
1084  QString prec = cb_pltrec->currentText();
1085 
1086  // Plot Title and legends
1087  QString title = "Radial Intensity Data\nRun ID: " + runID +
1088  "\n Cell: " + cell + " Channel: " + chan;
1089  QString xLegend;
1090  QString yLegend = "Radial Intensity at " + prec;
1091 
1092  if ( is_wrecs )
1093  {
1094  title += QString( " Wavelength: " ) + prec;
1095  xLegend = QString( "Radius (in cm)" );
1096  yLegend += QString( " nm" );
1097  }
1098 
1099  else
1100  {
1101  title += QString( " Radius: " ) + prec;
1102  xLegend = QString( "Wavelength (in nm)" );
1103  yLegend += QString( " cm" );
1104  }
1105 
1106  data_plot->setTitle( title );
1107  data_plot->setAxisTitle( QwtPlot::yLeft, yLegend );
1108  data_plot->setAxisTitle( QwtPlot::xBottom, xLegend );
1109 
1110 }
1111 
1112 // Draw scan curves for the current plot record
1114 {
1115  data_plot->detachItems();
1116  grid = us_grid( data_plot );
1117 
1118  // Make sure ranges are set up, then build an averaged data vector
1119  compute_ranges();
1120 
1121  build_avg_data();
1122 
1123 DbgLv(1) << "PltA: kpoint" << kpoint << "datsize" << curr_adata.size();
1124  // Build the X,Y vectors
1125  QVector< double > rvec( kpoint );
1126  QVector< double > vvec( kpoint );
1127  double* rr = rvec.data();
1128  double* vv = vvec.data();
1129 
1130  int scan_from = (int)ct_from->value();
1131  int scan_to = (int)ct_to ->value();
1132  int scan_nbr = 0;
1133  QPen pen_red ( Qt::red );
1134  QPen pen_plot( US_GuiSettings::plotCurve() );
1135  int rdx = 0;
1136 
1137  for ( int ptx = 0; ptx < kpoint; ptx++ ) // One-time build of X vector
1138  rr[ ptx ] = pltxvals[ ptx ];
1139 
1140  for ( int scnx = 0; scnx < nscan; scnx++ ) // Build Y vector for each scan
1141  {
1142  if ( excludes.contains( scnx ) ) continue;
1143 
1144  for ( int ptx = 0; ptx < kpoint; ptx++ )
1145  {
1146  vv[ ptx ] = curr_adata[ rdx++ ];
1147  }
1148 
1149  scan_nbr++;
1150  QString title = tr( "Raw Data at scan " )
1151  + QString::number( scan_nbr );
1152  QwtPlotCurve* curv = us_curve( data_plot, title );
1153 
1154  if ( scan_nbr > scan_to || scan_nbr < scan_from )
1155  curv->setPen( pen_plot ); // Normal pen
1156  else
1157  curv->setPen( pen_red ); // Scan-focus pen
1158 
1159  curv->setData( rr, vv, kpoint ); // Build a scan curve
1160 //DbgLv(1) << "PltA: scx" << scx << "rr0 vv0 rrn vvn"
1161 // << rr[0] << rr[kpoint-1] << vv[0] << vv[kpoint-1];
1162  }
1163 
1164 DbgLv(1) << "PltA: last_xmin" << last_xmin;
1165  if ( last_xmin < 0.0 )
1166  { // If first time, use auto scale to set plot ranges
1167  data_plot->setAxisAutoScale( QwtPlot::yLeft );
1168  data_plot->setAxisAutoScale( QwtPlot::xBottom );
1169  }
1170 
1171  else
1172  { // After first time, use the same plot ranges as set before
1173  data_plot->setAxisScale( QwtPlot::xBottom, last_xmin, last_xmax );
1174  data_plot->setAxisScale( QwtPlot::yLeft , last_ymin, last_ymax );
1175  }
1176 
1177  // Draw the plot
1178  data_plot->replot();
1179 
1180  // Pick up the actual bounds plotted (including any Config changes)
1181  QwtScaleDiv* sdx = data_plot->axisScaleDiv( QwtPlot::xBottom );
1182  QwtScaleDiv* sdy = data_plot->axisScaleDiv( QwtPlot::yLeft );
1183  last_xmin = sdx->lowerBound();
1184  last_xmax = sdx->upperBound();
1185  last_ymin = sdy->lowerBound();
1186  last_ymax = sdy->upperBound();
1187 DbgLv(1) << "PltA: xlo xhi" << last_xmin << last_xmax
1188  << "ylo yhi" << last_ymin << last_ymax;
1189 }
1190 
1191 // Slot to handle a change in start or end radius
1193 {
1194 DbgLv(1) << "chgRadius";
1195  have_rngs = false;
1196  compute_ranges(); // Recompute ranges
1197 
1198  if ( !is_wrecs )
1199  { // For radius records, we need to reset the plot record list
1200  cb_pltrec->disconnect();
1201  cb_pltrec->clear();
1202 
1203  for ( int rdx = radxs; rdx < radxe; rdx++ )
1204  { // Build the list of radii that are within range
1205  QString citem = QString().sprintf( "%.3f", radii[ rdx ] );
1206 
1207  cb_pltrec->addItem( citem );
1208  }
1209 
1210  connect( cb_pltrec, SIGNAL( currentIndexChanged( int ) ),
1211  this, SLOT ( changeRecord( ) ) );
1212 
1213  recx = ( radxe - radxs ) / 2;
1214  cb_pltrec->setCurrentIndex( recx );
1215  }
1216 
1217  build_avg_data(); // Rebuild initial averaged data
1218 }
1219 
1220 // Slot to handle a change in start or end lambda
1222 {
1223 DbgLv(1) << "chgLambda";
1224  have_rngs = false;
1225  compute_ranges(); // Recompute ranges
1226 
1227  if ( is_wrecs )
1228  { // For wavelength records, we need to reset the plot record list
1229  cb_pltrec->disconnect();
1230  cb_pltrec->clear();
1231 
1232  for ( int wvx = lmbxs; wvx < lmbxe; wvx++ )
1233  { // Build the list of wavelengths that are within range
1234  QString citem = QString::number( lambdas[ wvx ] );
1235 
1236  cb_pltrec->addItem( citem );
1237  }
1238 
1239  connect( cb_pltrec, SIGNAL( currentIndexChanged( int ) ),
1240  this, SLOT ( changeRecord( ) ) );
1241 
1242  recx = ( lmbxe - lmbxs ) / 2;
1243  cb_pltrec->setCurrentIndex( recx );
1244  }
1245 
1246  build_avg_data(); // Rebuild initial averaged data
1247 }
1248 
1249 // Slot to handle a change in the plot record
1251 {
1252  recx = cb_pltrec->currentIndex();
1253 DbgLv(1) << "chgRec: recx" << recx;
1254  bool plt_one = ! le_status->text().contains( tr( "saving" ) );
1255 
1256  // Insure we have averaged data for this record
1257  build_avg_data();
1258 
1259  // Plot what we have
1260  plot_current();
1261 
1262  // Update status text (if not part of movie save) and set prev/next arrows
1263  if ( plt_one )
1264  le_status->setText( lb_pltrec->text() + ": " + cb_pltrec->currentText() );
1265  pb_prev ->setEnabled( ( recx > 0 ) );
1266  pb_next ->setEnabled( ( recx < ( krecs -1 ) ) );
1267 }
1268 
1269 // Slot to handle a change in the plot record type
1271 {
1272 DbgLv(1) << "chgRtype: wlnrec" << wlnrec;
1273  have_rngs = false;
1274  is_wrecs = ! wlnrec;
1275 
1276  compute_ranges(); // Recompute ranges for new type
1277  prev_recxs.clear(); // Reset averaging components
1278  prev_cdata.clear();
1279  changeCellCh(); // Force a plot initialize
1280 
1281  if ( is_wrecs )
1282  { // Change record button title and reset for lambda record
1283  lb_pltrec->setText( tr( "Plot (W nm)" ) );
1284  changeLambda(); // Change to lambda records
1285  last_xmin = rad_start; // Change X limits
1286  last_xmax = rad_end;
1287  }
1288  else
1289  { // Change record button title and reset for radius record
1290  lb_pltrec->setText( tr( "Plot (R cm)" ) );
1291  changeRadius(); // Change to radius records
1292  last_xmin = (double)lmb_start; // Change X limits
1293  last_xmax = (double)lmb_end;
1294  }
1295 
1296  // Since X has changed, reset the plot X scale
1297  data_plot->setAxisScale( QwtPlot::xBottom, last_xmin, last_xmax );
1298 
1299  plot_current(); // Plot initial record of this type
1300 }
1301 
1302 // Slot to handle a click to go to the previous record
1304 {
1305  int pltrx = cb_pltrec->currentIndex() - 1;
1306 
1307  if ( pltrx < 1 )
1308  {
1309  pltrx = 0;
1310  pb_prev->setEnabled( false );
1311  }
1312 
1313  QwtScaleDiv* sdx = data_plot->axisScaleDiv( QwtPlot::xBottom );
1314  QwtScaleDiv* sdy = data_plot->axisScaleDiv( QwtPlot::yLeft );
1315  last_xmin = sdx->lowerBound();
1316  last_xmax = sdx->upperBound();
1317  last_ymin = sdy->lowerBound();
1318  last_ymax = sdy->upperBound();
1319 
1320  cb_pltrec->setCurrentIndex( pltrx );
1321 }
1322 
1323 // Slot to handle a click to go to the next record
1325 {
1326  int pltrx = cb_pltrec->currentIndex() + 1;
1327  int nitems = cb_pltrec->count();
1328 
1329  if ( ( pltrx + 2 ) > nitems )
1330  {
1331  pltrx = nitems - 1;
1332  pb_next->setEnabled( false );
1333  }
1334 
1335  QwtScaleDiv* sdx = data_plot->axisScaleDiv( QwtPlot::xBottom );
1336  QwtScaleDiv* sdy = data_plot->axisScaleDiv( QwtPlot::yLeft );
1337  last_xmin = sdx->lowerBound();
1338  last_xmax = sdx->upperBound();
1339  last_ymin = sdy->lowerBound();
1340  last_ymax = sdy->upperBound();
1341 
1342  cb_pltrec->setCurrentIndex( pltrx );
1343 }
1344 
1345 // Slot to handle a change in the number of averaging points
1347 {
1348 DbgLv(1) << "chgAvg:";
1349  prev_recxs.clear();
1350  prev_cdata.clear();
1351  changeRecord();
1352 }
1353 
1354 // Build the component data vectors for averaging.
1355 // The vector of data record readings vectors spans a window of records
1356 // around the current record. Its size is usually the number of averaging
1357 // points, but is less at either end of the records range.
1359 {
1360  compute_ranges(); // Insure we have current plot ranges
1361 
1362  QVector< double > fillv;
1363  curr_recxs.clear();
1364  curr_cdata.clear();
1365  navgrec = ct_recavg->value(); // Usual number of averaging records
1366  int kavgc = navgrec; // Actual count
1367  int kavgh = kavgc / 2; // Half count
1368  int arxs = recx - kavgh; // Record start index
1369  int arxe = recx + kavgh; // Record end index
1370  arxs = qMax( arxs, 0 ); // Start at least 0
1371  arxe = qMin( arxe, krecs - 1 ) + 1; // End at most range count
1372  arxe = qMax( arxe, ( arxs + 1 ) );
1373  kavgc = arxe - arxs; // Actual averaging count
1374  curr_cdata.fill( fillv, kavgc ); // Initialize component datas
1375 DbgLv(1) << "BldC: kavgc" << kavgc << "arxs arxe" << arxs << arxe;
1376  int jrx = 0;
1377 
1378  for ( int krx = arxs; krx < arxe; krx++, jrx++ )
1379  { // Build or move component records
1380  int prx = prev_recxs.indexOf( krx );
1381 DbgLv(1) << "BldC: krx" << krx << "prx" << prx;
1382 
1383  if ( prx < 0 )
1384  { // Previous components missing this one, so build a new one
1385  build_rec_data( krx, curr_cdata[ jrx ] );
1386  }
1387  else
1388  { // Previous components had this one, so just copy to new location
1389  curr_cdata[ jrx ] = prev_cdata[ prx ];
1390  }
1391 
1392  curr_recxs << krx; // Keep components indexes
1393  }
1394 
1395  prev_recxs = curr_recxs; // Preserve indexes for next time
1396  prev_cdata = curr_cdata; // Preserve data for next time
1397 
1398 }
1399 
1400 // Build the averaged data vector for the current plot record
1401 // Detect the actual number of component records, sum at each data point,
1402 // then obtain the averaged point.
1404 {
1405  build_cmp_data(); // Build components for averaging
1406 
1407  int kavgc = curr_recxs.size(); // Actual count of components
1408  double avgscl = 1.0 / (double)kavgc; // Averaging scale factor
1409 DbgLv(1) << "BldA: kavgc" << kavgc << "ktpoint" << ktpoint;
1410 DbgLv(1) << "BldA: cd size" << curr_cdata.size();
1411 DbgLv(1) << "BldA: cd0 size" << curr_cdata[0].size();
1412 int nn=curr_cdata.size()-1;
1413 DbgLv(1) << "BldA: cdn size" << curr_cdata[nn].size();
1414 
1415  curr_adata.clear(); // Initial averaged data
1416 
1417  for ( int ptx = 0; ptx < ktpoint; ptx++ )
1418  { // Compute the average at each data point
1419  double dsum = 0.0;
1420 
1421  for ( int rcx = 0; rcx < kavgc; rcx++ )
1422  { // Build a data point sum across all components
1423  dsum += curr_cdata[ rcx ][ ptx ];
1424 if(ptx==(ktpoint/2))
1425 DbgLv(1) << "BldA: ptx rcx" << ptx << rcx << "cdat dsum"
1426  << curr_cdata[rcx][ptx] << dsum;
1427  }
1428 
1429  curr_adata << ( dsum * avgscl ); // Store the average data point
1430  }
1431 DbgLv(1) << "BldA: curr_adata size" << curr_adata.size();
1432 }
1433 
1434 // Build a record data vector.
1435 // This routine is called when necessary to build an averaging component
1436 // at a specified record. The output flat vector of doubles has successive
1437 // data points across the current X range, for non-excluded scans.
1438 void US_MwlRawViewer::build_rec_data( const int recx,
1439  QVector< double >& rdata )
1440 {
1441  compute_ranges(); // Compute record and data ranges
1442 
1443  rdata.clear(); // Initialize the data vector
1444  kscan = 0; // Count of actual produced scans
1445 
1446 DbgLv(1) << "BldR: recx" << recx;
1447  if ( is_wrecs )
1448  { // Build data values for wavelength record with x-axis radius
1449  krecs = klambda; // Count of wavelength records
1450  kpoint = kradii; // Count of radius points per scan
1451  int trpx = trpxs + lmbxs + recx; // Triple index of record
1452 DbgLv(1) << "BldR: trx rxs rxe nscan" << trpx << radxs << radxe << nscan;
1453 
1454  for ( int scnx = 0; scnx < nscan; scnx++ )
1455  { // Examine all scans, but only store for included ones
1456  if ( excludes.contains( scnx ) ) continue;
1457 
1458  for ( int radx = radxs; radx < radxe; radx++ )
1459  { // Save a data point for each radius (input=triple is constant)
1460  rdata << allData[ trpx ].reading( scnx, radx );
1461  }
1462 
1463  kscan++; // Bump count of produced scans
1464  }
1465 DbgLv(1) << "BldR: kscan" << kscan << "rd size" << rdata.size();
1466  }
1467 
1468  else
1469  { // Build data values for radius record with x-axis wavelength
1470  krecs = kradii; // Count of radius records
1471  kpoint = klambda; // Count of wavelength points per scan
1472  int wavxs = trpxs + lmbxs; // Start wavelength index in triples
1473  int wavxe = trpxs + lmbxe; // End wavelength index in triples
1474 
1475  for ( int scnx = 0; scnx < nscan; scnx++ )
1476  { // Examine all scans, but only store for included ones
1477  if ( excludes.contains( scnx ) ) continue;
1478 
1479  for ( int wavx = wavxs; wavx < wavxe; wavx++ )
1480  { // Save a data point for each wavelength (radius=record is constant)
1481  rdata << allData[ wavx ].reading( scnx, recx );
1482  }
1483 
1484  kscan++; // Bump count of produced scans
1485  }
1486  }
1487 
1488  ktpoint = kscan * kpoint; // Count of total data points
1489 }
1490 
1491 // Compute the plot range indexes implied by current settings
1493 {
1494  if ( have_rngs ) // If we just did this computation, return now
1495  return;
1496 
1497  ccx = cb_cellchn->currentIndex(); // Cell/Channel index
1498  rad_start = cb_rstart ->currentText().toDouble(); // Radius start
1499  rad_end = cb_rend ->currentText().toDouble(); // Radius end
1500  lmb_start = cb_lstart ->currentText().toInt(); // Lambda start
1501  lmb_end = cb_lend ->currentText().toInt(); // Lambda end
1502  recx = cb_pltrec ->currentIndex(); // Plot record index
1503  lmbxs = lambdas.indexOf( lmb_start ); // Lambda start index
1504  lmbxe = lambdas.indexOf( lmb_end ) + 1; // Lambda end index
1505  radxs = dvec_index( radii, rad_start ); // Radius start index
1506  radxe = dvec_index( radii, rad_end ) + 1; // Radius end index
1507 DbgLv(1) << "cmpR: rS rE rxS rxE" << rad_start << rad_end << radxs << radxe;
1508 DbgLv(1) << "cmpR: rvS rvE" << radii[radxs] << radii[radxe-1];
1509  klambda = lmbxe - lmbxs; // Count of plot lambdas
1510  kradii = radxe - radxs; // Count of plot radii
1511  kscan = nscan - excludes.size(); // Count included scans
1512  trpxs = ccx * nlambda; // Start triple index
1513  pltxvals.clear();
1514  if ( is_wrecs )
1515  {
1516  krecs = klambda; // Count of plot recs
1517  kpoint = kradii; // Count of plot points
1518  for ( int jj = radxs; jj < radxe; jj++ )
1519  pltxvals << radii[ jj ]; // Plot X values
1520 DbgLv(1) << "cmpR: pxS pxE" << pltxvals[0] << pltxvals[kpoint-1];
1521  }
1522  else
1523  {
1524  krecs = kradii; // Count of plot recs
1525  kpoint = klambda; // Count of plot points
1526  for ( int jj = lmbxs; jj < lmbxe; jj++ )
1527  pltxvals << (double)lambdas[ jj ]; // Plot X values
1528  }
1529  ktpoint = kscan * kpoint; // Total plot data points
1530  have_rngs = true; // Mark ranges computed
1531 DbgLv(1) << "cmpR: is_wrecs" << is_wrecs << "kpoint" << kpoint;
1532 }
1533 
1534 // Connect or Disconnect plot-range related controls
1536 {
1537  if ( conn )
1538  { // Connect the range-related controls
1539  connect( cb_cellchn, SIGNAL( currentIndexChanged( int ) ),
1540  this, SLOT ( changeCellCh( ) ) );
1541  connect( cb_rstart, SIGNAL( currentIndexChanged( int ) ),
1542  this, SLOT ( changeRadius( ) ) );
1543  connect( cb_rend, SIGNAL( currentIndexChanged( int ) ),
1544  this, SLOT ( changeRadius( ) ) );
1545  connect( cb_lstart, SIGNAL( currentIndexChanged( int ) ),
1546  this, SLOT ( changeLambda( ) ) );
1547  connect( cb_lend, SIGNAL( currentIndexChanged( int ) ),
1548  this, SLOT ( changeLambda( ) ) );
1549  connect( cb_pltrec, SIGNAL( currentIndexChanged( int ) ),
1550  this, SLOT ( changeRecord( ) ) );
1551  }
1552 
1553  else
1554  { // Disconnect the range-related controls
1555  cb_cellchn->disconnect();
1556  cb_rstart ->disconnect();
1557  cb_rend ->disconnect();
1558  cb_lstart ->disconnect();
1559  cb_lend ->disconnect();
1560  cb_pltrec ->disconnect();
1561  }
1562 }
1563 
1564 // Slot to handle a change in scan exclude "from" value
1566 {
1567  int scan_from = (int)sfr;
1568  int scan_to = (int)ct_to ->value();
1569 
1570  if ( scan_to < scan_from )
1571  {
1572  ct_to ->disconnect();
1573  ct_to ->setValue( scan_from );
1574 
1575  connect( ct_to, SIGNAL( valueChanged( double ) ),
1576  this, SLOT ( exclude_to ( double ) ) );
1577  }
1578 
1579  plot_current();
1580 }
1581 
1582 // Slot to handle a change in scan exclude "to" value
1584 {
1585  int scan_to = (int)sto;
1586  int scan_from = (int)ct_from->value();
1587 
1588  if ( scan_from > scan_to )
1589  {
1590  ct_from->disconnect();
1591  ct_from->setValue( scan_to );
1592 
1593  connect( ct_from, SIGNAL( valueChanged( double ) ),
1594  this, SLOT ( exclude_from( double ) ) );
1595  }
1596 
1597  plot_current();
1598 }
1599 
1600 // Slot to handle click of Exclude Scan Range
1602 {
1603  int scan_from = (int)ct_from->value();
1604  int scan_to = (int)ct_to ->value();
1605  int scan_knt = 1;
1606 
1607  for ( int scnx = 0; scnx < nscan; scnx++ )
1608  {
1609  if ( excludes.contains( scnx ) ) continue;
1610 
1611  if ( scan_knt >= scan_from && scan_knt <= scan_to )
1612  excludes << scnx;
1613 
1614  scan_knt++;
1615  }
1616 
1617  qSort( excludes );
1618  curr_cdata.clear();
1619  prev_cdata.clear();
1620  curr_recxs.clear();
1621  prev_recxs.clear();
1622  kscan = nscan - excludes.count();
1623 DbgLv(1) << "Excl: kscan" << kscan;
1624  ct_from ->disconnect();
1625  ct_to ->disconnect();
1626  ct_from ->setMaxValue( kscan );
1627  ct_to ->setMaxValue( kscan );
1628  connect( ct_from, SIGNAL( valueChanged( double ) ),
1629  this, SLOT ( exclude_from( double ) ) );
1630  connect( ct_to, SIGNAL( valueChanged( double ) ),
1631  this, SLOT ( exclude_to ( double ) ) );
1632  ct_to ->setValue( 0 );
1633  pb_include->setEnabled( true );
1634 }
1635 
1636 // Slot to handle click of Include All (restore of all scans)
1638 {
1639  excludes.clear();
1640 
1641  kscan = nscan;
1642  ktpoint = kscan * kpoint;
1643 DbgLv(1) << "Incl: nscan" << nscan << "kscn ecnt" << kscan << excludes.count();
1644  curr_cdata.clear(); // Reset averaging data
1645  prev_cdata.clear();
1646  curr_recxs.clear();
1647  prev_recxs.clear();
1648  ct_to ->setValue( 0 );
1649  changeRecord(); // Force replot
1650 
1651  ct_from ->disconnect();
1652  ct_to ->disconnect();
1653  ct_from ->setMaxValue( kscan );
1654  ct_to ->setMaxValue( kscan );
1655  connect( ct_from, SIGNAL( valueChanged( double ) ),
1656  this, SLOT ( exclude_from( double ) ) );
1657  connect( ct_to, SIGNAL( valueChanged( double ) ),
1658  this, SLOT ( exclude_to ( double ) ) );
1659  ct_to ->setValue( 0 );
1660  pb_include->setEnabled( false );
1661 }
1662 
1663 // Slot to show a 2-D movie
1665 {
1666 DbgLv(1) << "Show 2D Movie";
1667  // Loop to plot each record in the current cell
1668  int krecs = cb_pltrec->count();
1669  int svrec = recx; // Save currently plotted record
1670  changeCellCh(); // Force save of scales
1671 
1672  for ( int prx = 0; prx < krecs; prx++ )
1673  {
1674  cb_pltrec->setCurrentIndex( prx ); // Plot each record in the range
1675  qApp->processEvents();
1676  }
1677 
1678  cb_pltrec->setCurrentIndex( svrec ); // Restore previous plot record
1679  qApp->processEvents();
1680 }
1681 
1682 // Slot to open a dialog for 3-D plotting
1684 {
1685 DbgLv(1) << "Plt3D";
1687 
1688 DbgLv(1) << "Plt3D: open MPC";
1689  if ( p3d_ctld == NULL )
1690  {
1691  p3d_pltw = NULL;
1692  p3d_ctld = new US_MwlPlotControl( this, &xyzdat );
1693  p3d_ctld->show();
1694  // Position near the upper right corner of the desktop
1695  int cx = qApp->desktop()->width() - p3d_ctld->width() - 40;
1696  int cy = 40;
1697  p3d_ctld->move( cx, cy );
1698  connect( p3d_ctld, SIGNAL( has_closed() ),
1699  this, SLOT ( p3dctrl_closed() ) );
1700  }
1701 
1702  else
1703  {
1704  p3d_ctld->setFocus();
1705  p3d_ctld->do_3dplot();
1706 
1707  p3d_pltw = p3d_ctld->widget_3dplot();
1708 
1709  if ( p3d_pltw != NULL )
1710  {
1711  p3d_pltw->reloadData( &xyzdat );
1712 
1713  int scan_nbr = live_scan( );
1714  QString ptitle = tr( "MWL 3-D Plot, Scan " )
1715  + QString::number( scan_nbr );
1716 
1717  p3d_pltw->setPlotTitle( ptitle );
1718  p3d_pltw->replot();
1719  }
1720  }
1721 
1722  pb_movie3d->setEnabled( true );
1723 }
1724 
1725 // Slot to show a 3-D movie
1727 {
1728  bool hold_color = ck_hcolorc->isChecked();
1729 
1730 //DbgLv(1) << "sh3M: Show 3D Movie";
1731  if ( p3d_ctld == NULL )
1732  { // This should not happen, but disallow Show 3D Movie if no plot opened
1733  return;
1734  }
1735 
1736  p3d_pltw = p3d_ctld->widget_3dplot(); // Main 3D plot window
1737 
1738  if ( p3d_pltw == NULL )
1739  { // If the dialog is opened
1740  QMessageBox::warning( this,
1741  tr( "3-D Plot Window Not Opened" ),
1742  tr( "You cannot start a 3-D movie until you have first"
1743  " opened the small plot control dialog by clicking"
1744  " on the \"Plot 3D\" button in this program's main"
1745  " window. From that control dialog you must click"
1746  " on the \"3D Plot\" button to open the 3-D plotting"
1747  " window.\n\nYou should insure that scale and"
1748  " orientation are correct for the current plot, as"
1749  " these will be in force for each movie frame."
1750  " You may then again click on \"Show 3D Movie\"." ) );
1751  return;
1752  }
1753 
1754  // Determine the range of scans that will be in the movie
1755  int fscnx = -1;
1756  int lscnx = -1;
1757  int krscan = 0;
1758 
1759  live_scan( &fscnx, &lscnx, &krscan );
1760 DbgLv(1) << "sh3M: fscnx lscnx krscan" << fscnx << lscnx << krscan;
1761 
1762  QString statmsg = ( nscan == krscan )
1763  ? tr( "Of %1 scans, showing: Scan " ).arg( nscan )
1764  : tr( "Of %1 in-range, included scans (%2 total),"
1765  " showing: Scan " ).arg( krscan ).arg( nscan );
1766  QString ptbase = tr( "MWL 3-D Plot, Scan SSS" );
1767  QString ptitle = tr( "MWL 3-D Plot, Scan 1" );
1768  QString str_scan;
1769 
1770  // Loop to show movie frames for each included scan that is in range
1771  for ( int scnx = fscnx; scnx <= lscnx; scnx++ )
1772  {
1773  if ( excludes.contains( scnx ) ) continue;
1774 
1775  build_xyz_data( xyzdat, scnx ); // Build data for this scan
1776 
1777  p3d_pltw->reloadData( &xyzdat ); // Load the data in the plot window
1778 // p3d_ctld->do_3dplot(); // Do the plot
1779  str_scan = QString::number( ( scnx + 1 ) );
1780  ptitle = QString( ptbase ).replace( "SSS", str_scan );
1781  p3d_pltw->setPlotTitle( ptitle ); // Reset the title for scan_nbr
1782  p3d_pltw->replot( hold_color ); // Do the plot
1783 DbgLv(1) << "sh3M: scnx ptitle" << scnx << ptitle;
1784 
1785  le_status->setText( statmsg + str_scan );
1786  qApp->processEvents();
1787  }
1788 }
1789 
1790 // Slot to save the current plot
1792 {
1793 DbgLv(1) << "Save Plot";
1794  QString savedir = US_Settings::reportDir() + "/" + runID;
1795  QDir().mkpath( savedir );
1796  savedir = savedir.replace( "\\", "/" ) + "/";
1797  QString fname2d = runID + ( ck_xwavlen->isChecked()
1798  ? ".radRec_RRRRR_2D.png"
1799  : ".lmbRec_RRRRR_2D.png" );
1800  QString fname3d = runID + ".Scan_SSSS_3D.png";
1801  p3d_pltw = ( p3d_ctld == NULL ) ? NULL : p3d_ctld->widget_3dplot();
1802  int nfiles = ( p3d_pltw != NULL ) ? 2 : 1;
1803 
1804  if ( nfiles == 2 )
1805  { // If there is a 3D window, first save a PNG of that window
1806  int scan_fr = (int)ct_from->value();
1807  int scan_to = (int)ct_to ->value();
1808  scan_fr = ( scan_to < 1 ) ? 1 : scan_fr;
1809  int scan_knt = 0;
1810  int scan_nbr = 1;
1811 
1812  for ( int scnx = 0; scnx < nscan; scnx++ )
1813  {
1814  if ( excludes.contains( scnx ) ) continue;
1815  scan_knt++; // Non-excluded count
1816  if ( scan_knt < scan_fr ) continue;
1817  scan_nbr = scnx + 1;
1818  break;
1819  }
1820 
1821  p3d_pltw->replot(); // Do the plot
1822  QString s_scan = QString().sprintf( "%04d", scan_nbr );
1823  fname3d = fname3d.replace( "SSSS", s_scan );
1824  QString fpath3d = savedir + fname3d;
1825 
1826  p3d_pltw->save_plot( fpath3d, QString( "png" ) );
1827  }
1828 
1829  // Always save a PNG of the 2-D plot
1830  QString ccr = cb_cellchn->currentText().remove( " / " );
1831  QString rec_str = ccr + cb_pltrec->currentText().remove( "." );
1832  fname2d = fname2d.replace( "RRRRR", rec_str );
1833  QString fpath2d = savedir + fname2d;
1834 
1835  US_GuiUtil::save_png( fpath2d, data_plot );
1836 
1837  // Report the file(s) saved
1838  QString mtitle = ( nfiles == 1 )
1839  ? tr( "Plot File Saved" )
1840  : tr( "Plot Files Saved" );
1841  QString msg = tr( "In the directory\n %1,\n\n" ).arg( savedir );
1842  if ( nfiles == 1 )
1843  msg += tr( "File\n %1 was saved." ).arg( fname2d );
1844  else
1845  msg += tr( "Files\n %1 ; and\n %2\nwere saved." )
1846  .arg( fname3d ).arg( fname2d );
1847 
1848  QMessageBox::information( this, mtitle, msg );
1849 }
1850 
1851 // Slot to save the current cell's movie(s)
1853 {
1854  bool save_2d = true;
1855  bool save_3d = ( p3d_pltw != NULL );
1856 
1857 DbgLv(1) << "Save Movie";
1858  if ( save_3d )
1859  { // If there is a 3D window, ask the user which movies to run
1860  QMessageBox msgBox( this );
1861  msgBox.setWindowTitle( tr( "Multiple Possible Movie Saves" ) );
1862  msgBox.setTextFormat( Qt::RichText );
1863  msgBox.setText(
1864  tr( "A 3-D plot window is opened, so you may save both a "
1865  "3-D movie and a 2-D one; or you may choose to save "
1866  "only one.<br/><br/>Save both?<ul>"
1867  "<li><b>Yes</b> to save both movies;</li>"
1868  "<li><b>No</b> to save just the 3-D movie;</li>"
1869  "<li><b>Cancel</b> to cancel any save in order "
1870  "to edit/close the 3-D window.</li></ul>" ) );
1871  msgBox.addButton( QMessageBox::Yes );
1872  msgBox.addButton( QMessageBox::No );
1873  msgBox.addButton( QMessageBox::Cancel );
1874  msgBox.setDefaultButton( QMessageBox::Yes );
1875  int stat = msgBox.exec();
1876 
1877  save_3d = ( stat != QMessageBox::Cancel );
1878  save_2d = ( stat == QMessageBox::Yes );
1879  }
1880 
1881  if ( save_3d )
1882  save_3d_movie();
1883 
1884  if ( save_2d )
1885  save_2d_movie();
1886 }
1887 
1888 // Slot to save the current cell's 2-D movie
1890 {
1891 DbgLv(1) << "Save 2D Movie";
1892  // Loop to plot each record in the cell and save an image to file
1893  int krecs = cb_pltrec->count();
1894  int svrec = recx; // Save currently plotted record
1895  QStringList fnames;
1896  QString savedir = US_Settings::reportDir() + "/" + runID;
1897  QDir().mkpath( savedir );
1898  savedir = savedir.replace( "\\", "/" ) + "/";
1899  QString bfname = runID + ( ck_xwavlen->isChecked()
1900  ? ".radRec_RRRRR_2D_frame_XXXXX.png"
1901  : ".lmbRec_RRRRR_2D_frame_XXXXX.png" );
1902  QString ccr = cb_cellchn->currentText().remove( " / " );
1903  QString bstat = tr( "Of %1 records, saving record " ).arg( krecs );
1904  le_status->setText( bstat );
1905 
1906  for ( int prx = 0; prx < krecs; prx++ )
1907  {
1908  cb_pltrec->setCurrentIndex( prx ); // Plot each record in the range
1909  qApp->processEvents();
1910 
1911  QString rec_str = ccr + cb_pltrec->currentText().remove( "." );
1912  QString frm_str = QString().sprintf( "%05d", ( prx + 1 ) );
1913  QString fname = QString( bfname ).replace( "RRRRR", rec_str )
1914  .replace( "XXXXX", frm_str );
1915  QString fpath = savedir + fname;
1916 
1917  le_status->setText( bstat + rec_str + ", frame " + frm_str );
1918 
1919  US_GuiUtil::save_png( fpath, data_plot );
1920  fnames << fname;
1921  }
1922 
1923  cb_pltrec->setCurrentIndex( svrec ); // Restore previous plot record
1924  qApp->processEvents();
1925 
1926  QMessageBox::information( this, tr( "Frame Files Saved" ),
1927  tr( "In the directory\n %1,\n\n%2 2-D movie frame files"
1928  " were saved:\n %3\n ...\n %4 ." )
1929  .arg( savedir ).arg( krecs ).arg( fnames[ 0 ] )
1930  .arg( fnames[ krecs - 1 ] ) );
1931 }
1932 
1933 // Slot to save the current cell's 3-D movie
1935 {
1936 DbgLv(1) << "Save 3-D Movie";
1937  bool hold_color = ck_hcolorc->isChecked();
1938 
1939  if ( p3d_ctld == NULL )
1940  { // This should not happen, but disallow Save 3D Movie if no plot opened
1941  return;
1942  }
1943 
1944  p3d_pltw = p3d_ctld->widget_3dplot(); // Main 3D plot window
1945 
1946  if ( p3d_pltw == NULL )
1947  { // If the plot window is not opened, explain what to do
1948  QMessageBox::warning( this,
1949  tr( "3-D Plot Window Not Opened" ),
1950  tr( "You cannot save a 3-D movie until you have first"
1951  " opened a 3-D plotting window by clicking on the"
1952  " \"3D Plot\" button in the small plot control"
1953  " dialog.\n\nYou should insure that scale and"
1954  " orientation are correct for the current plot, as"
1955  " these will be in force for each movie frame."
1956  " You may then again click on \"Save Movie(s)\"." ) );
1957  return;
1958  }
1959 
1960  // Determine the range of scans that will be in the movie
1961  int fscnx;
1962  int lscnx;
1963  int krscan;
1964 
1965  live_scan( &fscnx, &lscnx, &krscan );
1966 
1967  // Get the save directory and base file name for saved image files
1968  QStringList ffnames;
1969  QString imgtype( "png" );
1970  QString ptbase = tr( "MWL 3-D Plot, Scan SSS" );
1971  QString statmsg = tr( "Of %1 included in-range scans,"
1972  " saving: Scan " ).arg( krscan );
1973  QString savedir = US_Settings::reportDir() + "/" + runID;
1974  QDir().mkpath( savedir );
1975  savedir = savedir.replace( "\\", "/" ) + "/";
1976  QString bfname = runID + ".Scan_SSSS_3D_frame_XXXX.png";
1977  QApplication::setOverrideCursor( QCursor( Qt::WaitCursor) );
1978  int kframe = 0;
1979 
1980  // Loop to save movie frames for each included scan that is in range
1981  for ( int scnx = fscnx; scnx <= lscnx; scnx++ )
1982  {
1983  if ( excludes.contains( scnx ) ) continue;
1984 
1985  build_xyz_data( xyzdat, scnx ); // Build data for this scan
1986 
1987  p3d_pltw->reloadData( &xyzdat ); // Load the data in the plot window
1988 
1989  kframe++;
1990  int scan_nbr = scnx + 1;
1991  QString t_scan = QString::number( scan_nbr );
1992  QString ptitle = QString( ptbase ).replace( "SSS", t_scan );
1993  p3d_pltw->setPlotTitle( ptitle ); // Reset the title for scan_nbr
1994  p3d_pltw->replot( hold_color ); // Do the plot
1995 
1996  le_status->setText( statmsg + t_scan );
1997  qApp->processEvents();
1998 
1999  // Create a frame file for just-completed plot and save its name
2000  QString s_scan = QString().sprintf( "%04d", scan_nbr );
2001  QString s_frame = QString().sprintf( "%04d", kframe );
2002  QString fname = QString( bfname ).replace( "SSSS", s_scan )
2003  .replace( "XXXX", s_frame );
2004  QString fpath = savedir + fname;
2005 
2006  p3d_pltw->save_plot( fpath, imgtype );
2007 
2008  ffnames << fname;
2009  qApp->processEvents();
2010  }
2011 
2012  QApplication::restoreOverrideCursor();
2013  QApplication::restoreOverrideCursor();
2014 
2015  // Report the frame files saved
2016  QMessageBox::information( this,
2017  tr( "Frame Files Saved" ),
2018  tr( "In the directory\n %1,\n\n%2 3-D movie frame files"
2019  " were saved:\n %3\n ...\n %4 ." )
2020  .arg( savedir ).arg( kframe ).arg( ffnames[ 0 ] )
2021  .arg( ffnames[ kframe - 1 ] ) );
2022 }
2023 
2024 // Utility to find an index in a QVector<double> to a value epsilon match
2025 int US_MwlRawViewer::dvec_index( QVector< double >& dvec, const double dval )
2026 {
2027  const double eps = 1.e-4;
2028 
2029  int indx = dvec.indexOf( dval ); // Try to find an exact match
2030 
2031  if ( indx < 0 )
2032  { // If no exact match was found, look for a match within epsilon
2033 
2034  for ( int jj = 0; jj < dvec.size(); jj++ )
2035  { // Search doubles vector
2036  double ddif = qAbs( dvec[ jj ] - dval );
2037 
2038  if ( ddif < eps )
2039  { // If vector value matches within epsilon, break and return
2040  indx = jj;
2041  break;
2042  }
2043  }
2044  }
2045 
2046  return indx;
2047 }
2048 
2049 // Build XYZ data vector for 3D plot
2050 int US_MwlRawViewer::build_xyz_data( QVector< QVector3D >& xyzd, int scan )
2051 {
2052  // Insure we have ranges set
2053  compute_ranges();
2054 
2055  // Initialize counters
2056  int k3dtot = 0;
2057  int scnx = scan;
2058  int scan_fr = (int)ct_from->value();
2059  int scan_to = (int)ct_to ->value();
2060  scan_fr = ( scan_to < 1 ) ? 1 : scan_fr;
2061  scan_to = ( scan_to < 1 ) ? kscan : scan_to;
2062  int scan_knt = 1;
2063  if ( excludes.count() > 0 ) qSort( excludes );
2064 
2065  // Get index of scan for which to build
2066  if ( scan < 0 )
2067  { // If no scan given, compute index of first included scan in given range
2068  for ( scnx = 0; scnx < nscan; scnx++ )
2069  {
2070  if ( excludes.contains( scnx ) ) continue;
2071 
2072  if ( scan_knt >= scan_fr )
2073  break;
2074 
2075  scan_knt++;
2076  }
2077  }
2078 
2079  else
2080  { // If scan given, insure it is included and in given range
2081  for ( scnx = 0; scnx < nscan; scnx++ )
2082  {
2083  if ( excludes.contains( scnx ) ) continue;
2084 
2085  if ( scan_knt >= scan_fr && scan_knt <= scan_to && scnx == scan )
2086  break;
2087 
2088  scan_knt++;
2089  }
2090  }
2091 
2092  if ( scnx >= nscan )
2093  {
2094  qDebug() << "BldXYZ: *ERROR* scan invalid:" << scan << nscan;
2095  return k3dtot;
2096  }
2097 
2098  QVector< int > lmb3d;
2099  QVector< double > rad3d;
2100 
2101  // Build the list of lambda to plot
2102  for ( int lmbx = lmbxs; lmbx < lmbxe; lmbx++ )
2103  lmb3d << lambdas[ lmbx ];
2104 
2105  // Build the list of radii to plot
2106  for ( int radx = radxs; radx < radxe; radx++ )
2107  rad3d << radii[ radx ];
2108 
2109  k3dlamb = lmb3d.count(); // Lambda plot count
2110  k3drads = rad3d.count(); // Radius plot count
2111 DbgLv(1) << "Bxyz: lambda count" << k3dlamb << "range"
2112  << lmb3d[0] << lmb3d[k3dlamb-1];
2113 DbgLv(1) << "Bxyz: radius count" << k3drads << "range"
2114  << rad3d[0] << rad3d[k3drads-1];
2115  int nhavg = navgrec / 2; // Half avg. points
2116  int wvx = lmbxs; // True lambda index
2117 DbgLv(1) << "Bxyz: navg" << navgrec << "trpxs wvx" << trpxs << wvx;
2118  xyzd.clear();
2119 
2120  // Now build the data points
2121  for ( int klx = 0; klx < k3dlamb; klx++, wvx++ )
2122  { // Outer loop is lambdas
2123  int lambda = lmb3d[ klx ]; // Lambda value
2124  int wvxs = qMax( ( wvx - nhavg ), 0 ); // Start for avg.
2125  int wvxe = qMin( ( wvxs + navgrec ), nlambda ); // End for avg.
2126  int kavgc = wvxe - wvxs; // True avg. count
2127  int trxs = trpxs + wvxs; // Triple start
2128  int trxe = trpxs + wvxe; // Triple end
2129  int rdx = radxs; // True radius index
2130  double yval = (double)lambda; // Y is lambda
2131 
2132  for ( int krx = 0; krx < k3drads; krx++, rdx++ )
2133  {
2134  double xval = rad3d[ krx ]; // X is radius
2135  double zval = 0.0; // Initial Z sum
2136 
2137  for ( int trx = trxs; trx < trxe; trx++ ) // WvLn range Z sum
2138  zval += allData[ trx ].reading( scnx, rdx );
2139 
2140  zval /= (double)kavgc; // Averaged Z value
2141 
2142  xyzd << QVector3D( xval, yval, zval ); // Store X,Y,Z point
2143 DbgLv(2) << "Bxyz: k" << xyzd.count() << "x y z" << xval << yval << zval;
2144  }
2145  }
2146 
2147  k3dtot = xyzd.count();
2148 int i=k3drads-1;
2149 int j=i+1;
2150 int k=j+1;
2151 int h=i/2;
2152 int m=k3dtot-2;
2153 int n=m+1;
2154 DbgLv(1) << "Bxyz: k3dtot" << k3dtot << "k3l k3r" << k3dlamb << k3drads;
2155 DbgLv(1) << "Bxyz: xyz0" << xyzd[0] << 0;
2156 DbgLv(1) << "Bxyz: xyz1" << xyzd[1] << 1;
2157 DbgLv(1) << "Bxyz: xyzh" << xyzd[h] << h;
2158 DbgLv(1) << "Bxyz: xyzi" << xyzd[i] << i;
2159 DbgLv(1) << "Bxyz: xyzj" << xyzd[j] << j;
2160 DbgLv(1) << "Bxyz: xyzj" << xyzd[k] << k;
2161 DbgLv(1) << "Bxyz: xyzm" << xyzd[m] << m;
2162 DbgLv(1) << "Bxyz: xyzn" << xyzd[n] << n;
2163 
2164  return k3dtot;
2165 }
2166 
2167 // Slot to handle the close of the 3D plot control dialog
2169 {
2170  p3d_ctld = NULL;
2171  p3d_pltw = NULL;
2172 
2173  pb_movie3d->setEnabled( false );
2174 }
2175 
2176 // Utility to return first live scan number; plus optionally indexes and count
2177 int US_MwlRawViewer::live_scan( int* fsP, int* lsP, int* ksP )
2178 {
2179  int fscnx = -1;
2180  int lscnx = -1;
2181  int krscan = 0;
2182  int scan_fr = (int)ct_from->value();
2183  int scan_to = (int)ct_to ->value();
2184  scan_fr = ( scan_to < 1 ) ? 1 : scan_fr;
2185  scan_to = ( scan_to < 1 ) ? nscan : scan_to;
2186  int scan_knt = 0;
2187 
2188  for ( int scnx = 0; scnx < nscan; scnx++ )
2189  {
2190  if ( excludes.contains( scnx ) ) continue;
2191 
2192  scan_knt++; // Non-excluded count
2193 
2194  if ( scan_knt < scan_fr || scan_knt > scan_to ) continue;
2195 
2196  krscan++; // In-range count
2197 
2198  if ( fscnx < 0 )
2199  fscnx = scnx; // First in-range
2200  lscnx = scnx; // Last in-range
2201  }
2202 
2203  if ( fsP != NULL ) *fsP = fscnx;
2204  if ( lsP != NULL ) *lsP = lscnx;
2205  if ( ksP != NULL ) *ksP = krscan;
2206 
2207  return ( fscnx + 1 );
2208 }
2209