UltraScan III
us_export.cpp
Go to the documentation of this file.
1 
3 #include <QApplication>
4 
5 #include "us_export.h"
6 #include "us_settings.h"
7 #include "us_gui_settings.h"
8 #include "us_gui_util.h"
9 #include "us_constants.h"
10 #include "us_passwd.h"
11 #include "us_load_auc.h"
12 #include "us_util.h"
13 #include "us_investigator.h"
14 #include "us_report.h"
15 #include "us_license.h"
16 #include "us_license_t.h"
17 #include "us_sleep.h"
18 #include "us_matrix.h"
19 
20 #define MIN_NTC 25
21 
22 // main program
23 int main( int argc, char* argv[] )
24 {
25  QApplication application( argc, argv );
26 
27  #include "main1.inc"
28 
29  // License is OK. Start up.
30 
32  w.show();
33  return application.exec();
34 }
35 
36 // US_ExportLegacy class constructor
38 {
39  setObjectName( "US_ExportLegacy" );
40 
43 
44  // set up the GUI
45  setPalette( US_GuiSettings::frameColor() );
46  setWindowTitle( tr( "Export Data in Legacy (Beckman) Format" ) );
47 
48  mainLayout = new QHBoxLayout( this );
49  mainLayout->setSpacing ( 2 );
50  mainLayout->setContentsMargins( 2, 2, 2, 2 );
51 
52  leftLayout = new QVBoxLayout();
53  rightLayout = new QVBoxLayout();
54 
55  analysisLayout = new QGridLayout();
56  runInfoLayout = new QGridLayout();
57  buttonLayout = new QHBoxLayout();
58 
59  leftLayout->addLayout( analysisLayout );
60  leftLayout->addLayout( runInfoLayout );
61  leftLayout->addStretch();
62  leftLayout->addLayout( buttonLayout );
63 
64  // Analysis buttons
65  dkdb_cntrls = new US_Disk_DB_Controls( local );
66  pb_load = us_pushbutton( tr( "Load Data" ) );
67  pb_details = us_pushbutton( tr( "Run Details" ) );
68  pb_save = us_pushbutton( tr( "Export Data" ) );
69  pb_view = us_pushbutton( tr( "View Data Report" ) );
70 
71  connect( dkdb_cntrls, SIGNAL( changed( bool ) ),
72  this, SLOT( update_disk_db( bool ) ) );
73  connect( pb_load, SIGNAL( clicked() ),
74  this, SLOT( load() ) );
75  connect( pb_details, SIGNAL( clicked() ),
76  this, SLOT( details() ) );
77  connect( pb_save, SIGNAL( clicked() ),
78  this, SLOT( export_data() ) );
79  connect( pb_view, SIGNAL( clicked() ),
80  this, SLOT( view_report() ) );
81 
82  pb_load ->setEnabled( true );
83  pb_details ->setEnabled( false );
84  pb_save ->setEnabled( false );
85  pb_view ->setEnabled( false );
86 
87  int row = 0;
88  analysisLayout->addLayout( dkdb_cntrls, row++, 0, 1, 2 );
89  analysisLayout->addWidget( pb_load, row, 0, 1, 1 );
90  analysisLayout->addWidget( pb_details, row++, 1, 1, 1 );
91  analysisLayout->addWidget( pb_save, row, 0, 1, 1 );
92  analysisLayout->addWidget( pb_view, row++, 1, 1, 1 );
93 
94  // Run info
95  QLabel* lb_info = us_banner( tr( "Information for this Run" ) );
96  QLabel* lb_triples = us_banner( tr( "Cell / Channel / Wavelength" ) );
97  QLabel* lb_id = us_label ( tr( "Run ID:" ) );
98  QLabel* lb_temp = us_label ( tr( "Avg. Temp.:" ) );
99  QLabel* lb_stat = us_banner( tr( "Export Status" ) );
100 
101  le_id = us_lineedit( "", -1, true );
102  le_temp = us_lineedit( "", -1, true );
103 
105  QFontMetrics fm( font );
106  int fontHeight = fm.lineSpacing();
107 
108  te_desc = us_textedit();
109  te_desc->setMaximumHeight( fontHeight * 3 + 12 ); // Add for border
110  us_setReadOnly( te_desc, true );
111 
112  te_stat = us_textedit();
113  te_stat->setMaximumHeight( fontHeight * 3 + 12 ); // Add for border
114  te_stat->setTextColor( Qt::blue );
115  us_setReadOnly( te_stat, true );
116 
118  lw_triples->setMaximumHeight( fontHeight * 8 + 12 );
119 
120  row = 0;
121  runInfoLayout->addWidget( lb_info , row++, 0, 1, 4 );
122  runInfoLayout->addWidget( lb_id , row, 0, 1, 1 );
123  runInfoLayout->addWidget( le_id , row++, 1, 1, 3 );
124  runInfoLayout->addWidget( lb_temp , row, 0, 1, 1 );
125  runInfoLayout->addWidget( le_temp , row++, 1, 1, 3 );
126  runInfoLayout->addWidget( te_desc , row, 0, 3, 4 ); row += 3;
127  runInfoLayout->addWidget( lb_triples, row++, 0, 1, 4 );
128  runInfoLayout->addWidget( lw_triples, row++, 0, 8, 4 ); row += 8;
129  runInfoLayout->addWidget( lb_stat , row++, 0, 1, 4 );
130  runInfoLayout->addWidget( te_stat , row, 0, 1, 4 ); row += 3;
131 
132  // Plots
134  tr( "Velocity Data" ),
135  tr( "Radius (cm)" ),
136  tr( "Absorbance" ) );
137 
138  data_plot2->setCanvasBackground( Qt::black );
139  data_plot2->setMinimumSize( 560, 240 );
140 
141  // Standard buttons
142  pb_reset = us_pushbutton( tr( "Reset" ) );
143  pb_help = us_pushbutton( tr( "Help" ) );
144  pb_close = us_pushbutton( tr( "Close" ) );
145 
146  buttonLayout->addWidget( pb_reset );
147  buttonLayout->addWidget( pb_help );
148  buttonLayout->addWidget( pb_close );
149 
150  connect( pb_reset, SIGNAL( clicked() ),
151  this, SLOT( reset() ) );
152  connect( pb_close, SIGNAL( clicked() ),
153  this, SLOT( close() ) );
154  connect( pb_help, SIGNAL( clicked() ),
155  this, SLOT( help() ) );
156 
157  rightLayout->addLayout( plotLayout2 );
158  rightLayout->setStretchFactor( plotLayout2, 3 );
159 
160  mainLayout->addLayout( leftLayout );
161  mainLayout->addLayout( rightLayout );
162  mainLayout->setStretchFactor( leftLayout, 3 );
163  mainLayout->setStretchFactor( rightLayout, 5 );
164 
165  dataLoaded = false;
166 
167  adjustSize();
168  setMaximumSize( qApp->desktop()->size() - QSize( 40, 40 ) );
169 }
170 
171 // Load data
173 {
174  QString file;
175  QStringList files;
176  QStringList parts;
177  lw_triples-> disconnect();
178  lw_triples-> clear();
179  rawList. clear();
180  triples. clear();
181 
182  dataLoaded = false;
183  bool isLocal = !dkdb_cntrls->db();
184 
185  US_LoadAUC* dialog =
186  new US_LoadAUC( isLocal, rawList, triples, workingDir );
187 
188  connect( dialog, SIGNAL( changed ( bool ) ),
189  this, SLOT( update_disk_db( bool ) ) );
190 
191  if ( dialog->exec() != QDialog::Accepted ) return;
192 
193  qApp->processEvents();
195  QFontMetrics fm( font );
196  int fontHeight = fm.lineSpacing();
197  int ntriples = triples.size();
198  lw_triples->setMaximumHeight( fontHeight * min( ntriples, 8 ) + 12 );
199 
200  for ( int ii = 0; ii < ntriples; ii++ )
201  lw_triples->addItem( triples.at( ii ) );
202 
203  rdata = &rawList [ 0 ];
205  double avgTemp = rdata->average_temperature();
206  runID = workingDir.section( "/", -1, -1 );
207 
209 
210  // set ID, description, and avg temperature text
211  le_id ->setText( runID );
212  te_desc->setText( rdata->description );
213  le_temp->setText( QString::number( avgTemp, 'f', 1 ) + " " + DEGC );
214  if ( ntriples > 1 )
215  te_stat->setText( tr( "%1 input %2 triples" )
216  .arg( ntriples ).arg( rawDtype ) );
217  else
218  te_stat->setText( tr( "1 input %1 triple" ).arg( rawDtype ) );
219 
220  lw_triples->setCurrentRow( 0 );
221  connect( lw_triples, SIGNAL( currentRowChanged( int ) ),
222  SLOT( new_triple( int ) ) );
223 
224  if ( rawDtype == "RI" )
225  { // Possibly convert Pseudo Absorbance to Intensity
226  QVector< double > RIProfile;
227  QMessageBox msgBox( this );
228  msgBox.setTextFormat ( Qt::RichText );
229 
230  // Get any RI Profile
231  int nrip = getRIProfile( RIProfile );
232 
233  if ( nrip == 0 )
234  { // No RI Profile: export RA
235  msgBox.setWindowTitle ( tr( "RA Export Type" ) );
236  QString mtxt = tr( "The input is <b>RI</b> data,<br>&nbsp;&nbsp;"
237  "but <b>no RI Profile</b> exists!<br><br>"
238  "So, <b>Pseudo Absorbance (RA)</b><br>&nbsp;&nbsp;"
239  "data will be exported." );
240  msgBox.setText ( mtxt );
241  msgBox.addButton ( QMessageBox::Ok );
242  msgBox.exec();
243  rawDtype = "RA";
244  }
245 
246  else
247  { // RI Profile exists: ask user RA/RI preference
248  QString ybtn = tr( "Absorbance" );
249  QString nbtn = tr( "Intensity" );
250  QString mtxt = tr( "For <b>RI</b> data, you may export values as<br>"
251  "<b>Intensity</b> or "
252  "<b>Pseudo Absorbance</b>.<br><br>"
253  "Which export type do you want?" );
254  msgBox.setWindowTitle ( tr( "RI Export Type" ) );
255  msgBox.setText ( mtxt );
256  QPushButton* pb_abs = msgBox.addButton( ybtn, QMessageBox::YesRole );
257  msgBox.addButton ( nbtn, QMessageBox::NoRole );
258  msgBox.setDefaultButton( pb_abs );
259  msgBox.exec();
260 
261  if ( msgBox.clickedButton() == pb_abs )
262  { // RA chosen: no conversion necessary
263 DbgLv(1) << "RI Export: YES : Absorbance";
264  rawDtype = "RA";
265  }
266 
267  else
268  { // RI chosen: convert data to Intensity
269 DbgLv(1) << "RI Export: NO : Intensity";
270  convertToIntensity( RIProfile );
271  }
272  }
273 
274  if ( rawDtype == "RA" )
275  { // Export type changed to RA: modify data headers
276  for ( int kk = 0; kk < rawList.size(); kk++ )
277  {
278  rawList[ kk ].type[ 1 ] = 'A';
279  }
280  }
281  }
282 DbgLv(1) << "Chosen/Forced export rawDtype" << rawDtype;
283 
284  dataLoaded = true;
285 
286  update( 0 );
287 
288  pb_details->setEnabled( true );
289  pb_view ->setEnabled( true );
290  delete dialog;
291 }
292 
293 // Details
295 {
296  QString workDir = dkdb_cntrls->db() ? tr( "(database)" )
297  : workingDir;
298  US_RunDetails2* dialog = new US_RunDetails2( rawList, runID,
299  workDir, triples );
300 
301  dialog->move( this->pos() + QPoint( 100, 100 ) );
302  dialog->exec();
303  qApp->processEvents();
304 
305  delete dialog;
306 }
307 
308 // Update based on selected triples row
309 void US_ExportLegacy::update( int drow )
310 {
311  rdata = &rawList [ drow ];
313  double avgTemp = rdata->average_temperature();
314 
315  le_id ->setText( runID );
316  le_temp->setText( QString::number( avgTemp, 'f', 1 ) + " " + DEGC );
317  te_desc->setText( rdata->description );
318 
319  data_plot();
320 
321  pb_view->setEnabled( true );
322  pb_save->setEnabled( true );
323 }
324 
325 
326 // Data plot
328 {
329  data_plot2->detachItems();
330 
331  if ( !dataLoaded )
332  return;
333 
334  int drow = lw_triples->currentRow();
335  rdata = &rawList [ drow ];
337  QString dataType = tr( "Absorbance" );
338  if ( rawDtype == "RI" ) dataType = tr( "Intensity" );
339  if ( rawDtype == "WI" ) dataType = tr( "Intensity" );
340  if ( rawDtype == "IP" ) dataType = tr( "Interference" );
341  if ( rawDtype == "FI" ) dataType = tr( "Fluourescence" );
342  data_plot2->setTitle(
343  tr( "Velocity Data for " ) + runID );
344  data_plot2->setAxisTitle( QwtPlot::yLeft,
345  dataType + tr( " at " ) + rawWaveln + tr( " nm" ) );
346  data_plot2->setAxisTitle( QwtPlot::xBottom,
347  tr( "Radius (cm)" ) );
348 
349  data_plot2->clear();
350  us_grid( data_plot2 );
351 
353 
354  QVector< double > vecr( valueCount );
355  QVector< double > vecv( valueCount );
356  double* rr = vecr.data();
357  double* vv = vecv.data();
358 
359  QString title;
360  QwtPlotCurve* curve;
361  QPen pen_plot( US_GuiSettings::plotCurve() );
362 
363  // Draw curves
364  for ( int ii = 0; ii < scanCount; ii++ )
365  {
366  for ( int jj = 0; jj < valueCount; jj++ )
367  { // accumulate coordinates of curve within baseline-to-plateau
368  rr[ jj ] = rdata->radius( jj );
369  vv[ jj ] = rdata->value( ii, jj );
370  }
371 
372  // Plot scan curve
373  title = tr( "Curve " ) + QString::number( ii );
374  curve = us_curve( data_plot2, title );
375 
376  curve->setPen( pen_plot );
377 
378  curve->setData( rr, vv, valueCount );
379  }
380 
381  data_plot2->replot();
382 }
383 
384 // Save the report and image data
386 {
387  QString rawDtyp2;
388  rDataStrings( rdata, rawDtyp2, rawCell, rawChann, rawWaveln );
389  QStringList files;
390  QString legadir( US_Settings::importDir() );
391 
392  // Insure that */imports/runid exists
393  mkdir( legadir, runID );
394  QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
395 
396  if ( rawDtype == "RI" || rawDtype == "WI" )
397  { // Export intensity
398  exp_intensity( files );
399  }
400  else if ( rawDtype == "IP" )
401  { // Export interference
402  exp_interference( files );
403  }
404  else
405  { // Export most types of data
406  exp_mosttypes( files );
407  }
408 
409  QApplication::restoreOverrideCursor();
410 
411  // Report the files created
412  int nfiles = files.count();
413 
414  QString umsg = tr( "In directory \"" ) + legadir + "\",\n"
415  + tr( " in subdirectory \"" ) + runID + "\",\n"
416  + tr( " %1 files were written:\n" ).arg( nfiles );
417  umsg += files[ 0 ] + "\n...\n" + files[ nfiles - 1 ] + "\n";
418 
419  QMessageBox::information( this, tr( "Successfully Written" ), umsg );
420 
421  // Give a summary in the status box
422  int ntriples = triples.size();
423  umsg.clear();
424  if ( ntriples > 1 )
425  umsg = tr( "From %1 input %2 triples, legacy output\n" )
426  .arg( ntriples ).arg( rawDtype );
427  else
428  umsg = tr( "From 1 input %1 triple, legacy output\n" ).arg( rawDtype );
429 
430  umsg += tr( "was written to %1 files." ).arg( nfiles );
431 
432  te_stat->setText( umsg );
433 }
434 
435 // Export most types of data (1 channel per file set, 3 columns per point)
436 void US_ExportLegacy::exp_mosttypes( QStringList& files )
437 {
438  // Get data pointers and output directory
439  rdata = &rawList [ 0 ];
440  QString legadir( US_Settings::importDir() );
441  mkdir( legadir, runID );
442  QString odirname = legadir + "/" + runID + "/";
443  QString ofname;
444  QString ofpath;
445  int ntriples = triples.size();
446 
447  // Determine data type
449  QString ddesc = rdata->description + "\n";
450  QString htype = QString( "U" );
451  htype = ( rawDtype == "RA" ) ? "R" : htype;
452  htype = ( rawDtype == "RI" ) ? "I" : htype;
453  htype = ( rawDtype == "IP" ) ? "P" : htype;
454  htype = ( rawDtype == "FI" ) ? "F" : htype;
455  htype = ( rawDtype == "WA" ) ? "W" : htype;
456  htype = ( rawDtype == "WI" ) ? "V" : htype;
457 DbgLv(1) << "rawDtype" << rawDtype << "htype" << htype;
458  // Get first scan header information
459  bool wldata = ( QString( rawDtype ).left( 1 ) == "W" );
460  int hcell = rdata->cell;
461  double htemp = dscan->temperature;
462  int hrpm = qRound( dscan->rpm );
463  int hsecs = qRound( dscan->seconds );
464  double homeg = dscan->omega2t;
465  double hradi = dscan->wavelength;
466  int hwavl = qRound( dscan->wavelength );
467  int hcoun = 3;
468  QString oline;
469  int nscan = rdata->scanCount();
470  int nvalu = rdata->pointCount();
471  QString fext = "." + rawDtype + QString::number( hcell );
472  bool channdir = false;
473 
474  if ( htype == "R" )
475  { // For RA, determine if pseudo absorbance needing channel subdirectories
476  for ( int drow = 0; drow < ntriples; drow++ )
477  {
478  QString chann = triples[ drow ].section( "/", 1, 1 ).simplified();
479 DbgLv(1) << " drow chann" << drow << chann;
480  if ( chann != "A" )
481  {
482  channdir = true;
483  break;
484  }
485  }
486  }
487 
488  for ( int drow = 0; drow < ntriples; drow++ )
489  { // Output a set of files for each input triple
490  rdata = &rawList [ drow ]; // Current data
491  ddesc = rdata->description + "\n";
492  hcell = rdata->cell;
493  fext = "." + rawDtype + QString::number( hcell );
494  QString chann = triples[ drow ].section( "/", 1, 1 ).simplified();
495 
496  if ( channdir )
497  { // For pseudo absorbance, output to channel subdirectory
498  QString odirchan = runID + "_channel" + chann;
499  odirname = legadir + "/" + odirchan + "/";
500  mkdir( legadir, odirchan );
501  }
502 
503  for ( int ii = 0; ii < nscan; ii++ )
504  { // Output a file for each scan
505  ofname = chann + QString().sprintf( "%05i", ( ii + 1 ) ) + fext;
506  ofpath = odirname + ofname; // Full path file name for scan
507  dscan = &rdata->scanData[ ii ]; // Scan pointer
508  htemp = dscan->temperature; // Temperature
509  hrpm = dscan->rpm; // RPM
510  hsecs = qRound( dscan->seconds ); // Seconds as integer
511  homeg = dscan->omega2t; // Omega^2 * T
512  hwavl = qRound( dscan->wavelength ); // Wavelength as integer
513  hradi = dscan->wavelength; // Radius (possibly)
514  // Format most of header line
515  oline = htype
516  + QString().sprintf( "%2i%5.1f%6i %07i%11.4E",
517  hcell, htemp, hrpm, hsecs, homeg ).replace( "E+", "E" );
518  // Complete header line, using radius if Wavelength data
519  oline = oline + ( wldata
520  ? QString().sprintf( "%6.3f %i\n", hradi, hcoun )
521  : QString().sprintf( "%4i %i\n", hwavl, hcoun ) );
522 DbgLv(1) << "OFNAME" << ofname;
523 
524  QFile legfile( ofpath );
525 
526  if ( ! legfile.open( QIODevice::WriteOnly | QIODevice::Text ) )
527  {
528  qDebug() << "*ERROR* Opening file" << ofpath;
529  continue;
530  }
531 
532  QTextStream ts( &legfile );
533  ts << ddesc; // Write description line
534  ts << oline; // Write header line
535 DbgLv(1) << " LINE:" << QString(ddesc).replace("\n","");
536 DbgLv(1) << " LINE:" << QString(oline).replace("\n","");
537 
538  for ( int jj = 0; jj < nvalu; jj++ )
539  { // Output a line for each data point
540  double radi = rdata->radius ( jj );
541  double valu = rdata->value ( ii, jj );
542  double stdd = rdata->std_dev( ii, jj );
543  // Format data line: Radius Value StdDev
544  QString oline = QString().sprintf( "%9.4f %12.5E %13.5E\n",
545  radi, valu, stdd )
546  .replace( "E+", "E+00" ).replace( "E-", "E-00" );
547 if (jj < 3 || jj > (nvalu-4))
548  DbgLv(1) << " LINE:" << QString(oline).replace("\n","");
549 
550  ts << oline; // Write data line
551  } // END: values loop
552 
553  files << ofname; // Save scan file name
554  legfile.close(); // Close file
555  } // END: scan loop
556  }
557 }
558 
559 // Special export of intensity data (2 channels at a time)
560 void US_ExportLegacy::exp_intensity( QStringList& files )
561 {
562  // Get 1st triple data pointer and output directory
563  rdata = &rawList [ 0 ];
564  QString legadir( US_Settings::importDir() );
565  mkdir( legadir, runID );
566  QString odirname = legadir + "/" + runID + "/";
567  QString ofname;
568  QString ofpath;
569  int ntriples = triples.size();
570 
571  // Get data type
573  QString ddesc = rdata->description + "\n";
574  QString htype = QString( "U" );
575  htype = ( rawDtype == "RA" ) ? "R" : htype;
576  htype = ( rawDtype == "RI" ) ? "I" : htype;
577  htype = ( rawDtype == "IP" ) ? "P" : htype;
578  htype = ( rawDtype == "FI" ) ? "F" : htype;
579  htype = ( rawDtype == "WA" ) ? "W" : htype;
580  htype = ( rawDtype == "WI" ) ? "V" : htype;
581 DbgLv(1) << "rawDtype" << rawDtype << "htype" << htype;
582  bool wldata = ( QString( rawDtype ).left( 1 ) == "W" );
583  int hcell = rdata->cell;
584  double htemp = dscan->temperature;
585  int hrpm = qRound( dscan->rpm );
586  int hsecs = qRound( dscan->seconds );
587  double homeg = dscan->omega2t;
588  double hradi = dscan->wavelength;
589  int hwavl = qRound( dscan->wavelength );
590  int hcoun = 3;
591  int nscan = rdata->scanCount();
592  int nvalu = rdata->pointCount();
593  QString fext = "." + rawDtype + QString::number( hcell );
594  QString tripa;
595  QString oline;
596  bool twofer;
597  bool bfirst;
598  US_DataIO::RawData* rdat2 = rdata;
599 
600  for ( int drow = 0; drow < ntriples; drow++ )
601  { // Output a set of files for each input triple
602  rdata = &rawList [ drow ];
603  ddesc = rdata->description + "\n";
604  hcell = rdata->cell;
605  fext = "." + rawDtype + QString::number( hcell );
606  tripa = triples[ drow ];
607  twofer = tripa.contains( "A" );
608  bfirst = false;
609  if ( twofer )
610  { // We have channel A, test if we have a B next
611  int erow = drow + 1; // Next triple index
612  QString tripa = triples[ drow ]; // First triple in pair
613  if ( erow < ntriples &&
614  triples[ erow ].contains( "B" ) &&
615  QString( triples[ erow ] ).replace( "B", "A" ) == tripa )
616  { // We have a B that goes with this A: set up to do 2-at-a-time
617  rdat2 = &rawList[ erow ];
618  drow = erow;
619  }
620 
621  else // No next triple or not B, so only do A on this pass
622  twofer = false;
623  }
624  else // We are at a B, so set to output "Radius 0.0 Value-B"
625  bfirst = true;
626 
627  for ( int ii = 0; ii < nscan; ii++ )
628  { // Output a file for each scan
629  ofname = QString().sprintf( "%05i", ( ii + 1 ) ) + fext;
630  ofpath = odirname + ofname; // Full path output file
631  dscan = &rdata->scanData[ ii ]; // Current scan pointer
632  htemp = dscan->temperature; // Temperature
633  hrpm = dscan->rpm; // RPM
634  hsecs = qRound( dscan->seconds ); // Seconds as integer
635  homeg = dscan->omega2t; // Omega^2*T
636  hwavl = qRound( dscan->wavelength ); // Wavelength as integer
637  hradi = dscan->wavelength; // Radius (possibly)
638  // Format most of header line
639  oline = htype
640  + QString().sprintf( "%2i%5.1f%6i %07i%11.4E",
641  hcell, htemp, hrpm, hsecs, homeg ).replace( "E+", "E" );
642  // Complete header line, using radius if Wavelength data
643  oline = oline + ( wldata
644  ? QString().sprintf( "%6.3f %i\n", hradi, hcoun )
645  : QString().sprintf( "%4i %i\n", hwavl, hcoun ) );
646 DbgLv(1) << "OFNAME" << ofname;
647 
648  QFile legfile( ofpath );
649 
650  if ( ! legfile.open( QIODevice::WriteOnly | QIODevice::Text ) )
651  {
652  qDebug() << "*ERROR* Opening file" << ofpath;
653  continue;
654  }
655 
656  QTextStream ts( &legfile );
657  ts << ddesc; // Description line
658  ts << oline; // Header line
659 DbgLv(1) << " LINE:" << QString(ddesc).replace("\n","");
660 DbgLv(1) << " LINE:" << QString(oline).replace("\n","");
661 
662  for ( int jj = 0; jj < nvalu; jj++ )
663  { // Output a line for each data point
664  double radi = rdata->radius( jj );
665  double valu = rdata->value ( ii, jj );
666  double stdd = twofer ? rdat2->value( ii, jj ) : 0.0;
667  stdd = bfirst ? valu : stdd;
668  valu = bfirst ? 0.0 : valu;
669  // Format line mostly as Radius,Value-A,Value-B
670  QString oline = QString().sprintf( "%9.4f %12.5E %13.5E\n",
671  radi, valu, stdd )
672  .replace( "E+", "E+00" ).replace( "E-", "E-00" );
673 if (jj < 3 || jj > (nvalu-4))
674  DbgLv(1) << " LINE:" << QString(oline).replace("\n","");
675 
676  ts << oline; // Data line
677  } // END: values loop
678 
679  files << ofname; // Save just-output file name
680  legfile.close(); // Close the file
681  } // END: scan loop
682  }
683 }
684 
685 // Special export of interference data (2 columns per point)
686 void US_ExportLegacy::exp_interference( QStringList& files )
687 {
688  // Get 1st triple data pointers and output directory
689  rdata = &rawList [ 0 ];
690  QString legadir( US_Settings::importDir() );
691  mkdir( legadir, runID );
692  QString odirname = legadir + "/" + runID + "/";
693  QString ofname;
694  QString ofpath;
695  int ntriples = triples.size();
696 
697  // Get data type
699  QString ddesc = rdata->description + "\n";
700  QString htype = QString( "U" );
701  htype = ( rawDtype == "RA" ) ? "R" : htype;
702  htype = ( rawDtype == "RI" ) ? "I" : htype;
703  htype = ( rawDtype == "IP" ) ? "P" : htype;
704  htype = ( rawDtype == "FI" ) ? "F" : htype;
705  htype = ( rawDtype == "WA" ) ? "W" : htype;
706  htype = ( rawDtype == "WI" ) ? "V" : htype;
707 DbgLv(1) << "rawDtype" << rawDtype << "htype" << htype;
708  bool wldata = ( QString( rawDtype ).left( 1 ) == "W" );
709  int hcell = rdata->cell;
710  double htemp = dscan->temperature;
711  int hrpm = qRound( dscan->rpm );
712  int hsecs = qRound( dscan->seconds );
713  double homeg = dscan->omega2t;
714  double hradi = dscan->wavelength;
715  int hwavl = qRound( dscan->wavelength );
716  int hcoun = 3;
717  QString oline;
718  int nscan = rdata->scanCount();
719  int nvalu = rdata->pointCount();
720  QString fext = "." + rawDtype + QString::number( hcell );
721 
722  for ( int drow = 0; drow < ntriples; drow++ )
723  { // Output a set of files for each input triple
724  rdata = &rawList [ drow ];
725  ddesc = rdata->description + "\n";
726  hcell = rdata->cell;
727  fext = "." + rawDtype + QString::number( hcell );
728 
729  for ( int ii = 0; ii < nscan; ii++ )
730  { // Output a file for each scan
731  ofname = QString().sprintf( "%05i", ( ii + 1 ) ) + fext;
732  ofpath = odirname + ofname; // Full path output file
733  dscan = &rdata->scanData[ ii ]; // Current scan pointer
734  htemp = dscan->temperature; // Temperature
735  hrpm = dscan->rpm; // RPM
736  hsecs = qRound( dscan->seconds ); // Seconds as integer
737  homeg = dscan->omega2t; // Omega^2*T
738  hwavl = qRound( dscan->wavelength ); // Wavelength as integer
739  hradi = dscan->wavelength; // Radius (possibly)
740  // Format most of header line
741  oline = htype
742  + QString().sprintf( "%2i%5.1f%6i %07i%11.4E",
743  hcell, htemp, hrpm, hsecs, homeg ).replace( "E+", "E" );
744  // Complete header line, using radius if Wavelength data
745  oline = oline + ( wldata
746  ? QString().sprintf( "%6.3f %i\n", hradi, hcoun )
747  : QString().sprintf( "%4i %i\n", hwavl, hcoun ) );
748 DbgLv(1) << "OFNAME" << ofname;
749 
750  QFile legfile( ofpath );
751 
752  if ( ! legfile.open( QIODevice::WriteOnly | QIODevice::Text ) )
753  {
754  qDebug() << "*ERROR* Opening file" << ofpath;
755  continue;
756  }
757 
758  QTextStream ts( &legfile );
759  ts << ddesc; // Write description line
760  ts << oline; // Write header line
761 DbgLv(1) << " LINE:" << QString(ddesc).replace("\n","");
762 DbgLv(1) << " LINE:" << QString(oline).replace("\n","");
763 
764  for ( int jj = 0; jj < nvalu; jj++ )
765  { // Output a line for each data point
766  double radi = rdata->radius( jj );
767  double valu = rdata->value ( ii, jj );
768  // Format the data line: Radius Value-A
769  QString oline = QString().sprintf( "%9.4f %12.5E\n", radi, valu );
770 if (jj < 3 || jj > (nvalu-4))
771  DbgLv(1) << " LINE:" << QString(oline).replace("\n","");
772 
773  ts << oline; // Write data line
774  } // END: values loop
775 
776  files << ofname;
777  legfile.close();
778  } // END: scan loop
779  }
780 }
781 
782 // View the report text
784 {
785  QString mtext;
786  QTextStream ts( &mtext );
787 
788  // Generate the report file
789  write_report( ts );
790 
791  // Display the report dialog
792  US_Editor* editd = new US_Editor( US_Editor::DEFAULT, true, "", this );
793  editd->setWindowTitle( tr( "Report: FE Match Model Simulation" ) );
794  editd->move( this->pos() + QPoint( 100, 100 ) );
795  editd->resize( 740, 700 );
796  editd->e->setFont( QFont( US_GuiSettings::fontFamily(),
798  editd->e->setHtml( mtext );
799  editd->show();
800 }
801 
802 // Write the report HTML text stream
803 void US_ExportLegacy::write_report( QTextStream& ts )
804 {
805  ts << html_header( "US_ExportLegacy", "Legacy Export", rdata );
806  ts << data_details();
807  ts << " </body>\n</html>\n";
808 }
809 
810 // String to accomplish line indentation
811 QString US_ExportLegacy::indent( int spaces ) const
812 {
813  return ( QString( " " ).leftJustified( spaces, ' ' ) );
814 }
815 
816 // Compose data details text
818 {
819  QString dataType = tr( "Unknown" );
820  if ( rawDtype == "RA" ) dataType = tr( "Radial Absorbance" );
821  if ( rawDtype == "RI" ) dataType = tr( "Radial Intensity" );
822  if ( rawDtype == "WA" ) dataType = tr( "Wavelength Absorbance" );
823  if ( rawDtype == "WI" ) dataType = tr( "Wavelength Intensity" );
824  if ( rawDtype == "IP" ) dataType = tr( "Interference" );
825  if ( rawDtype == "FI" ) dataType = tr( "Fluourescence Intensity" );
826  dataType = dataType + " (" + rawDtype + ")";
827  QString expDir = US_Settings::importDir() + "/" + runID;
828 
829  QString s =
830  "\n" + indent( 4 ) + tr( "<h3>Detailed Run Information:</h3>\n" )
831  + indent( 4 ) + "<table>\n"
832  + table_row( tr( "Cell Description:" ), rdata->description )
833  + table_row( tr( "Data Directory:" ), workingDir )
834  + table_row( tr( "Export Directory:" ), expDir )
835  + table_row( tr( "Data Type:" ), dataType )
836  + table_row( tr( "Rotor Speed:" ),
837  QString::number( (int)rdata->scanData[ 0 ].rpm ) + " rpm" );
838 
839  // Temperature data
840  double sum = 0.0;
841  double maxTemp = -1.0e99;
842  double minTemp = 1.0e99;
843 
844  for ( int i = 0; i < rdata->scanCount(); i++ )
845  {
846  double t = rdata->scanData[ i ].temperature;
847  sum += t;
848  maxTemp = max( maxTemp, t );
849  minTemp = min( minTemp, t );
850  }
851 
852  QString average = QString::number( sum / rdata->scanCount(), 'f', 1 );
853 
854  s += table_row( tr( "Average Temperature:" ), average + " " + MLDEGC );
855 
856  if ( maxTemp - minTemp <= US_Settings::tempTolerance() )
857  s += table_row( tr( "Temperature Variation:" ), tr( "Within tolerance" ) );
858  else
859  s += table_row( tr( "Temperature Variation:" ),
860  tr( "(!) OUTSIDE TOLERANCE (!)" ) );
861 
862  // Time data
863  double tcorrec = time_correction();
864  int minutes = (int)tcorrec / 60;
865  int seconds = (int)tcorrec % 60;
866 
867  QString m = ( minutes == 1 ) ? tr( " minute " ) : tr( " minutes " );
868  QString sec = ( seconds == 1 ) ? tr( " second" ) : tr( " seconds" );
869  s += table_row( tr( "Time Correction:" ),
870  QString::number( minutes ) + m +
871  QString::number( seconds ) + sec );
872 
873  double duration = rawList.last().scanData.last().seconds;
874 
875  int hours = (int) duration / 3600;
876  minutes = (int) duration / 60 - hours * 60;
877  seconds = (int) duration % 60;
878 
879  QString h;
880  h = ( hours == 1 ) ? tr( " hour " ) : tr( " hours " );
881  m = ( minutes == 1 ) ? tr( " minute " ) : tr( " minutes " );
882  sec = ( seconds == 1 ) ? tr( " second " ) : tr( " seconds " );
883 
884  s += table_row( tr( "Run Duration:" ),
885  QString::number( hours ) + h +
886  QString::number( minutes ) + m +
887  QString::number( seconds ) + sec );
888 
889  s += table_row( tr( "Wavelength:" ), rawWaveln + " nm" );
890 
891  return s;
892 }
893 
894 // Create a subdirectory if need be
895 bool US_ExportLegacy::mkdir( QString& baseDir, QString& subdir )
896 {
897  // Make sure */ultrascan/data/legacy exists
898  QDir().mkpath( baseDir );
899 
900  QDir folder( baseDir );
901 
902  // Report subdirectory already exists
903  if ( folder.exists( subdir ) ) return true;
904 
905  // Create the subdirectory and report if successful
906  if ( folder.mkdir( subdir ) ) return true;
907 
908  // Otherwise, report a problem
909  QMessageBox::warning( this,
910  tr( "File error" ),
911  tr( "Could not create the directory:\n" ) + baseDir + "/" + subdir );
912 
913  return false;
914 }
915 
916 // Slot to handle selection of a new triple
918 {
919  update( trow );
920 
921  data_plot();
922 }
923 
924 // Update the disk/DB choice element
926 {
927  isDB ? dkdb_cntrls->set_db() : dkdb_cntrls->set_disk();
928 }
929 
930 // Reset data set
932 {
933  if ( ! dataLoaded ) return;
934 
935  lw_triples-> disconnect();
936  lw_triples-> clear();
937  rawList. clear();
938  triples. clear();
939 
940  dataLoaded = false;
941 
942  data_plot2->detachItems();
943  data_plot2->clear();
944  data_plot2->replot();
945 
946  pb_details ->setEnabled( false );
947  pb_view ->setEnabled( false );
948  pb_save ->setEnabled( false );
949  le_id ->setText( "" );
950  le_temp ->setText( "" );
951  te_desc ->setText( "" );
952  te_stat ->setText( "" );
953 }
954 
955 // Table row HTML with 2 columns
956 QString US_ExportLegacy::table_row( const QString& s1, const QString& s2 ) const
957 {
958  return( indent( 6 ) + "<tr><td>" + s1 + "</td><td>" + s2 + "</td></tr>\n" );
959 }
960 
961 // Compose a report HTML header
962 QString US_ExportLegacy::html_header( QString title, QString head1,
963  US_DataIO::RawData* rdata )
964 {
966 
967  QString s = QString( "<?xml version=\"1.0\"?>\n" );
968  s += "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n";
969  s += " \"http://www.w3.org/TR/xhtml1/DTD"
970  "/xhtml1-strict.dtd\">\n";
971  s += "<html xmlns=\"http://www.w3.org/1999/xhtml\""
972  " xml:lang=\"en\" lang=\"en\">\n";
973  s += " <head>\n";
974  s += " <title> " + title + " </title>\n";
975  s += " <meta http-equiv=\"Content-Type\" content="
976  "\"text/html; charset=iso-8859-1\"/>\n";
977  s += " <style type=\"text/css\" >\n";
978  s += " td { padding-right: 1em; }\n";
979  s += " body { background-color: white; }\n";
980  s += " </style>\n";
981  s += " </head>\n <body>\n";
982  s += " <h1>" + head1 + "</h1>\n";
983  s += indent( 4 ) + tr( "<h2>Data Report for Run \"" ) + runID;
984  s += "\",<br/>\n" + indent( 4 ) + "&nbsp;" + tr( " Cell " ) + rawCell;
985  s += tr( ", Channel " ) + rawChann;
986  s += tr( ", Wavelength " ) + rawWaveln + "<br/></h2>\n";
987 
988  return s;
989 }
990 
991 // Compose some strings from RawData that exist for EditedData
993  QString& Dtype, QString& Cell, QString& Chann, QString& Waveln )
994 {
995  Dtype = QString( QChar( rdata->type[ 0 ] ) )
996  + QString( QChar( rdata->type[ 1 ] ) );
997  Cell = QString::number( rdata->cell );
998  Chann = QString( QChar( rdata->channel ) );
999  Waveln = QString::number( rdata->scanData[ 0 ].wavelength );
1000 }
1001 
1002 // Compute time correction
1004 {
1005  int size = rawList[ 0 ].scanCount();
1006 
1007  for ( int ii = 1; ii < rawList.size(); ii++ )
1008  size += rawList[ ii ].scanCount();
1009 
1010  int count = 0;
1011 
1012  QVector< double > vecx( size );
1013  QVector< double > vecy( size );
1014  double* x = vecx.data();
1015  double* y = vecy.data();
1016 
1017  double c[ 2 ]; // Looking for a linear fit
1018 
1019  for ( int ii = 0; ii < rawList.size(); ii++ )
1020  {
1021  US_DataIO::RawData* d = &rawList[ ii ];
1022 
1023  for ( int jj = 0; jj < d->scanCount(); jj++ )
1024  {
1025  if ( d->scanData[ jj ].omega2t > 9.99999e10 ) break;
1026 
1027  x[ count ] = d->scanData[ jj ].omega2t;
1028  y[ count ] = d->scanData[ jj ].seconds;
1029  count++;
1030  }
1031  }
1032 
1033  US_Matrix::lsfit( c, x, y, count, 2 );
1034 
1035  return c[ 0 ]; // Return the time value corresponding to zero omega2t
1036 }
1037 
1038 // Get RI Profile if it exists
1039 int US_ExportLegacy::getRIProfile( QVector< double >& RIProfile )
1040 {
1041  QString ripxml;
1042  bool isDB = dkdb_cntrls->db();
1043  runID = workingDir.section( "/", -1, -1 );
1044 
1045  if ( isDB )
1046  { // Data from DB: get any RIProfile from experiment record
1047  US_Passwd pw;
1048  US_DB2 db( pw.getPasswd() );
1049  QStringList query;
1050  query << "get_experiment_info_by_runID"
1051  << runID
1052  << QString::number( US_Settings::us_inv_ID() );
1053  db.query( query );
1054  if ( db.lastErrno() == US_DB2::NOROWS )
1055  return 0;
1056  db.next();
1057  ripxml = db.value( 16 ).toString();
1058  }
1059 
1060  else
1061  { // Data local: read in any RIProfile from an XML file
1062  QString filename = workingDir + "/" + runID + ".RIProfile.xml";
1063 
1064  QFile fi( filename );
1065 
1066  if ( !fi.open( QIODevice::ReadOnly | QIODevice::Text ) )
1067  return 0;
1068 
1069  ripxml = fi.readAll();
1070  fi.close();
1071  }
1072 
1073  if ( ripxml.isEmpty() )
1074  return 0;
1075 
1076  parseRIProfile( ripxml, RIProfile );
1077 
1078  return RIProfile.size();
1079 }
1080 
1081 // Convert pseudo absorbance to intensity if RIProfile exists
1082 void US_ExportLegacy::convertToIntensity( QVector< double >& RIProfile )
1083 {
1084  int ntriples = rawList.size();
1085  int lrip = RIProfile.size() - 1;
1086  if ( lrip < 0 )
1087  return;
1088 DbgLv(1) << "CnvPA: ntrip lrip" << ntriples << lrip;
1089 
1090  for ( int kk = 0; kk < ntriples; kk++ )
1091  { // Loop to convert each data set to Intensity
1092  rdata = &rawList[ kk ];
1093  scanCount = rdata->scanCount();
1095 DbgLv(1) << "CnvPA: kk scns vals" << kk << scanCount << valueCount;
1096 
1097  for ( int ii = 0; ii < scanCount; ii++ )
1098  { // Convert each scan using a RIProfile term
1099  US_DataIO::Scan* rscan = &rdata->scanData[ ii ];
1100  double rip = RIProfile[ qMin( ii, lrip ) ];
1101 DbgLv(1) << "CnvPA: ii rip" << ii << rip;
1102 DbgLv(1) << "CnvPA: aval0" << rscan->rvalues[0]
1103  << "pow" << pow(10.0,rscan->rvalues[0]);
1104 
1105  for ( int jj = 0; jj < valueCount; jj++ )
1106  {
1107  double aval = rscan->rvalues[ jj ];
1108  rscan->rvalues[ jj ] = rip / pow( 10.0, aval );
1109  }
1110 DbgLv(1) << "CnvPA: ival0" << rscan->rvalues[0];
1111  }
1112  }
1113 }
1114 
1115 // Parse RI Profile values from XML
1116 void US_ExportLegacy::parseRIProfile( QString& ripxml,
1117  QVector< double >& RIProfile )
1118 {
1119  RIProfile.clear();
1120  QXmlStreamReader xml( ripxml );
1121  QXmlStreamAttributes atts;
1122 
1123  while ( ! xml.atEnd() )
1124  {
1125  xml.readNext();
1126 
1127  if ( xml.isStartElement() && xml.name() == "RI" )
1128  {
1129  atts = xml.attributes();
1130  RIProfile << atts.value( "value" ).toString().toDouble();
1131  }
1132  }
1133 int nprof=RIProfile.size();
1134 DbgLv(1) << "ParsRIProf: nprof" << RIProfile.size();
1135 if(nprof>0) DbgLv(1) << "ParsRIProf: RIP0" << RIProfile[0];
1136 if(nprof>1) DbgLv(1) << "ParsRIProf: RIPn" << RIProfile[nprof-1];
1137 }
1138