1 /***************************************************************************
2 * *
3 * copyright : (C) 2014 Northwestern University *
4 * nchapman@u.northwestern.edu *
5 * g-novak@northwestern.edu *
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 ***************************************************************************/
13
14 #include "fitstable.h"
15
16 #include <QXmlStreamWriter>
17 #include <QFileInfo>
18
19 #define DBG if(0)
20
21 using namespace Kst;
22
23 /* Scalar interface */
24
25 class DataInterfaceFitsTableScalar : public DataSource::DataInterface<DataScalar>{
26 public:
DataInterfaceFitsTableScalar(FitsTableSource & s)27 DataInterfaceFitsTableScalar(FitsTableSource& s) : source(s) {}
28
29 // read one element
30 int read(const QString&, DataScalar::ReadInfo&);
31
32 // named elements
list() const33 QStringList list() const { return source._scalars.keys(); }
isListComplete() const34 bool isListComplete() const { return true; }
35 bool isValid(const QString&) const;
36
37 // T specific
dataInfo(const QString &) const38 const DataScalar::DataInfo dataInfo(const QString&) const { return DataScalar::DataInfo(); }
setDataInfo(const QString &,const DataScalar::DataInfo &)39 void setDataInfo(const QString&, const DataScalar::DataInfo&) {}
40
41 // meta data
metaScalars(const QString &)42 QMap<QString, double> metaScalars(const QString&) { return QMap<QString, double>(); }
metaStrings(const QString &)43 QMap<QString, QString> metaStrings(const QString&) { return QMap<QString, QString>(); }
44
45 private:
46 FitsTableSource& source;
47 };
48
49
read(const QString & scalar,DataScalar::ReadInfo & p)50 int DataInterfaceFitsTableScalar::read(const QString& scalar, DataScalar::ReadInfo& p){
51
52 DBG qDebug() << "Entering DataInterfaceFitsTableScalar::read() with scalar: " << scalar << endl;
53 return source.readScalar(p.value, scalar);
54 }
55
56
isValid(const QString & scalar) const57 bool DataInterfaceFitsTableScalar::isValid(const QString& scalar) const{
58
59 DBG qDebug() << "Entering DataInterfaceFitsTableScalar::isValid() with scalar: " << scalar << endl;
60 return source._scalars.contains( scalar );
61 }
62
63 /* String interface */
64
65 class DataInterfaceFitsTableString : public DataSource::DataInterface<DataString>{
66 public:
DataInterfaceFitsTableString(FitsTableSource & s)67 DataInterfaceFitsTableString(FitsTableSource& s) : source(s) {}
68
69 // read one element
70 int read(const QString&, DataString::ReadInfo&);
71
72 // named elements
list() const73 QStringList list() const { return source._strings.keys(); }
isListComplete() const74 bool isListComplete() const { return true; }
75 bool isValid(const QString&) const;
76
77 // T specific
dataInfo(const QString &) const78 const DataString::DataInfo dataInfo(const QString&) const { return DataString::DataInfo(); }
setDataInfo(const QString &,const DataString::DataInfo &)79 void setDataInfo(const QString&, const DataString::DataInfo&) {}
80
81 // meta data
metaScalars(const QString &)82 QMap<QString, double> metaScalars(const QString&) { return QMap<QString, double>(); }
metaStrings(const QString &)83 QMap<QString, QString> metaStrings(const QString&) { return QMap<QString, QString>(); }
84
85 private:
86 FitsTableSource& source;
87 };
88
89
read(const QString & string,DataString::ReadInfo & p)90 int DataInterfaceFitsTableString::read(const QString& string, DataString::ReadInfo& p){
91
92 DBG qDebug() << "Entering DataInterfaceFitsTableString::read() with string: " << string << endl;
93 if (isValid(string) && p.value) {
94 *p.value = source._strings[string];
95 return 1;
96 }
97 return 0;
98 }
99
100
isValid(const QString & string) const101 bool DataInterfaceFitsTableString::isValid(const QString& string) const{
102
103 DBG qDebug() << "Entering DataInterfaceFitsTableString::isValid() with string: " << string << endl;
104 return source._strings.contains( string );
105 }
106
107 /* Vector interface */
108
109 class DataInterfaceFitsTableVector : public DataSource::DataInterface<DataVector>{
110 public:
DataInterfaceFitsTableVector(FitsTableSource & s)111 DataInterfaceFitsTableVector(FitsTableSource& s) : source(s) {}
112
113 // read one element
114 int read(const QString&, DataVector::ReadInfo&);
115
116 // named elements
list() const117 QStringList list() const { return source._fieldList; }
isListComplete() const118 bool isListComplete() const { return true; }
119 bool isValid(const QString&) const;
120
121 // T specific
122 const DataVector::DataInfo dataInfo(const QString&) const;
setDataInfo(const QString &,const DataVector::DataInfo &)123 void setDataInfo(const QString&, const DataVector::DataInfo&) {}
124
125 // meta data
126 QMap<QString, double> metaScalars(const QString&);
127 QMap<QString, QString> metaStrings(const QString&);
128
129 private:
130 FitsTableSource& source;
131 };
132
133
dataInfo(const QString & field) const134 const DataVector::DataInfo DataInterfaceFitsTableVector::dataInfo(const QString &field) const{
135
136 DBG qDebug() << "Entering DataInterfaceFitsTableVector::dataInfo() with field: " << field << endl;
137 if (!source._fieldList.contains(field))
138 return DataVector::DataInfo();
139
140 return DataVector::DataInfo(source.frameCount(field), source.samplesPerFrame(field));
141 }
142
read(const QString & field,DataVector::ReadInfo & p)143 int DataInterfaceFitsTableVector::read(const QString& field, DataVector::ReadInfo& p){
144
145 DBG qDebug() << "Entering DataInterfaceFitsTableVector::read() with field: " << field << endl;
146 return source.readField(p.data, field, p.startingFrame, p.numberOfFrames);
147 }
148
149
isValid(const QString & field) const150 bool DataInterfaceFitsTableVector::isValid(const QString& field) const{
151
152 DBG qDebug() << "Entering DataInterfaceFitsTableVector::isValid() with field: " << field << endl;
153 return source._fieldList.contains(field);
154 }
155
metaScalars(const QString & field)156 QMap<QString, double> DataInterfaceFitsTableVector::metaScalars(const QString& field){
157 Q_UNUSED(field);
158 QMap<QString, double> fieldScalars;
159
160 DBG qDebug() << "Entering DataInterfaceFitsTableVector::metaScalars() with field: " << field << endl;
161 return fieldScalars;
162 }
163
metaStrings(const QString & field)164 QMap<QString, QString> DataInterfaceFitsTableVector::metaStrings(const QString& field){
165
166 QMap<QString, QString> fieldStrings;
167 DBG qDebug() << "Entering DataInterfaceFitsTableVector::metaStrings() with field: " << field << endl;
168 return fieldStrings;
169 }
170
171 /* Matrix interface */
172
173 class DataInterfaceFitsTableMatrix : public DataSource::DataInterface<DataMatrix>{
174 public:
175
DataInterfaceFitsTableMatrix(FitsTableSource & s)176 DataInterfaceFitsTableMatrix(FitsTableSource& s) : source(s) {}
177
178 // read one element
179 int read(const QString&, DataMatrix::ReadInfo&);
180
181 // named elements
list() const182 QStringList list() const { return source._matrixList; }
isListComplete() const183 bool isListComplete() const { return true; }
184 bool isValid(const QString&) const;
185
186 // T specific
187 const DataMatrix::DataInfo dataInfo (const QString&) const;
setDataInfo(const QString &,const DataMatrix::DataInfo &)188 void setDataInfo(const QString&, const DataMatrix::DataInfo&) {}
189
190 // meta data
metaScalars(const QString &)191 QMap<QString, double> metaScalars(const QString&) { return QMap<QString, double>(); }
metaStrings(const QString &)192 QMap<QString, QString> metaStrings(const QString&) { return QMap<QString, QString>(); }
193
194 private:
195 FitsTableSource& source;
196 };
197
198
dataInfo(const QString & matrix) const199 const DataMatrix::DataInfo DataInterfaceFitsTableMatrix::dataInfo(const QString& matrix) const{
200
201 DBG qDebug() << "Entering DataInterfaceFitsTableMatrix::dataInfo() with matrix: " << matrix << endl;
202 if (!source._matrixList.contains( matrix ) ) {
203 return DataMatrix::DataInfo();
204 }
205
206 DataMatrix::DataInfo info;
207 info.samplesPerFrame = 1;
208 info.xSize = 32;
209 info.ySize = 12;
210
211 return info;
212 }
213
214
read(const QString & field,DataMatrix::ReadInfo & p)215 int DataInterfaceFitsTableMatrix::read(const QString& field, DataMatrix::ReadInfo& p){
216
217 DBG qDebug() << "Entering DataInterfaceFitsTableMatrix::read() with field: " << field << endl;
218 int count = source.readMatrix(p.data->z, field);
219
220 p.data->xMin = 0;
221 p.data->yMin = 0;
222 p.data->xStepSize = 1;
223 p.data->yStepSize = 1;
224
225 return count;
226 }
227
228
isValid(const QString & field) const229 bool DataInterfaceFitsTableMatrix::isValid(const QString& field) const {
230
231 DBG qDebug() << "Entering DataInterfaceFitsTableMatrix::isValid() with field: " << field << endl;
232 return source._matrixList.contains(field);
233 }
234
235 /**********************
236 FitsTableSource - This class defines the main DataSource which derives
237 from DataSource. The key functions that this class must provide is the ability
238 to create the source, provide details about the source be able to process the
239 data.
240 ***********************/
FitsTableSource(Kst::ObjectStore * store,QSettings * cfg,const QString & filename,const QString & type,const QDomElement & e)241 FitsTableSource::FitsTableSource(Kst::ObjectStore *store, QSettings *cfg,
242 const QString& filename, const QString& type, const QDomElement& e)
243 : Kst::DataSource(store, cfg, filename, type), _fptr(0L), _config(0L),
244 is(new DataInterfaceFitsTableScalar(*this)),
245 it(new DataInterfaceFitsTableString(*this)),
246 iv(new DataInterfaceFitsTableVector(*this)),
247 im(new DataInterfaceFitsTableMatrix(*this)){
248
249 DBG qDebug() << "Entering FitsTableSource::FitsTableSource() with filename: " << filename << endl;
250 setInterface(is);
251 setInterface(it);
252 setInterface(iv);
253 setInterface(im);
254
255 setUpdateType(None);
256
257 if (!type.isEmpty() && type != "FITS Table") {
258 return;
259 }
260
261 _valid = false;
262 _maxFrameCount = 0;
263
264 _filename = filename;
265
266 if (init()) {
267 _valid = true;
268 }
269
270 registerChange();
271 }
272
273
274
~FitsTableSource()275 FitsTableSource::~FitsTableSource() {
276
277 DBG qDebug() << "Entering FitsTableSource::~FitsTableSource()\n";
278 int status = 0;
279 if (_fptr) {
280 fits_close_file( _fptr, &status );
281 _fptr = 0L;
282 }
283 }
284
285
reset()286 void FitsTableSource::reset() {
287
288 DBG qDebug() << "Entering FitsTableSource::reset()\n";
289
290 int status = 0;
291
292 if (_fptr){
293 fits_close_file(_fptr, &status);
294 _fptr = 0L;
295 }
296 _maxFrameCount = 0;
297 _valid = init();
298 }
299
validField(int typecode)300 int FitsTableSource::validField(int typecode){
301 /* check to see if typcode is one of the supported ones. If yes, return 1,
302 else return 0 */
303
304 int tmp;
305
306 switch(typecode){
307 case TINT:
308 case TLONG: /* also covers TINT32BIT */
309 case TLONGLONG:
310 case TFLOAT:
311 case TDOUBLE:
312 case TLOGICAL:
313 tmp = 1;
314 break;
315 default:
316 tmp = 0;
317 }
318 return tmp;
319 }
320
321 // If the datasource has any predefined fields they should be populated here.
init()322 bool FitsTableSource::init() {
323 int status = 0; /* cfitsio status flag */
324 int colnum; /* column number in table */
325 int ncol; /* number of columns in table */
326 long nrow; /* number of rows in table */
327 char colname[512]; /* Name for column */
328 int typecode; /* data type of column in FITS table */
329 long width; /* width codes from fits_get_coltype(). not used */
330 int hdutype; /* FITS HDU type */
331 long repeat; /* used to determine if matrix or vector */
332 long i,j,k; /* loop variables */
333 char coltemplate[512]; /* template for reading column names */
334 QString tmp; /* format string for bolometer array */
335 int numHDU; /* total number of HDUs in FITS file */
336 int idx; /* used to get index of elements in lists, so we don't
337 add the same scalar name, vector name, etc to the
338 relevant list twice */
339 int naxis; /* length of naxes variable. Used for reading TDIM */
340 long *naxes; /* array to contain dimensionality of FITS columns */
341 int maxdim; /* maximum allowed array size of naxes */
342 QStringList allcols; /* list of all column names */
343 int nkeys; /* Number of header keywords in current HDU */
344 int keylength; /* length of keyword in characters (not used) */
345 int keyclass; /* class of FITS header keyword, e.g., TYP_USER_KEY */
346 char keyname[10]; /* array to hold FITS keyword name */
347 char keyvalue[128]; /* array to hold FITS keyword value */
348 char keycomment[128]; /* array to hold FITS keyword comment */
349 char fitsrecord[128]; /* array to hold one line of FITS header */
350 char keytype; /* FITS key type (string, float, int, etc) */
351
352 maxdim = 2;
353 naxes = (long *) malloc(maxdim*sizeof(long));
354
355 DBG qDebug() << "Entering FitsTableSource::init() with filename: " << _filename.toAscii() << endl;
356 // First, try to open the file
357 if(fits_open_file( &_fptr, _filename.toAscii(), READONLY, &status )){
358 fits_close_file( _fptr, &status );
359 _fptr = 0L;
360 _valid = false;
361 free(naxes);
362 return false;
363 }
364 _scalars.clear();
365 _fieldList.clear();
366 _matrixList.clear();
367 _strings.clear();
368 _colName.clear();
369 _colRepeat.clear();
370 _colType.clear();
371 _colOffset.clear();
372 tableHDU.clear();
373 tableRow.clear();
374
375 // Some standard stuff
376 _fieldList += "INDEX";
377 _colName += "INDEX";
378 _colRepeat << 1;
379 _colType << 1;
380 _colOffset << 0;
381
382 _strings = fileMetas();
383 _maxFrameCount = 0;
384
385 /* get total number of HDUs */
386 if (fits_get_num_hdus(_fptr, &numHDU, &status)){
387 fits_report_error(stderr,status);
388 _fptr = 0L;
389 _valid = false;
390 free(naxes);
391 return false;
392 } /* can't read number of HDUs, so quit */
393
394 nrow = 0;
395 for (i=1; i<= numHDU; i++){ /* loop over all HDUs */
396 if (fits_movabs_hdu(_fptr, i, &hdutype, &status)){
397 fits_report_error(stderr,status);
398 status = 0;
399 continue;
400 } /* failed moving to an HDU, so try to skip and go to next one */
401 /* read header to assign string and scalar values. For now only
402 read USER_KEYs. May want to add UNIT_KEYs eventually, to get the
403 units for every vector */
404 if(fits_get_hdrspace(_fptr, &nkeys, NULL, &status)){ /* total # of keys */
405 fits_report_error(stderr,status);
406 status = 0;
407 continue;
408 }
409 for (j=1; j<= nkeys; j++){ /* loop over keys */
410 if(fits_read_record(_fptr, j, fitsrecord, &status)){
411 fprintf(stderr,"Failed to read record number %ld\n",j);
412 fits_report_error(stderr,status);
413 status = 0;
414 continue;
415 } /* failed to read a record, so skip and continue */
416 if (fits_get_keyname(fitsrecord, keyname, &keylength, &status)){
417 fprintf(stderr,"Failed to read keyword from record = %s\n",fitsrecord);
418 fits_report_error(stderr,status);
419 status = 0;
420 continue;
421 } /* failed to read keyword name, so skip and continue */
422 if (strcmp(keyname,"HISTORY") == 0) /* skip HISTORY keywords */
423 continue;
424 if (fits_parse_value(fitsrecord, keyvalue, keycomment, &status)){
425 fprintf(stderr,"Failed to read value and comment from key = %s\n",keyname);
426 fits_report_error(stderr,status);
427 status = 0;
428 continue;
429 } /* failed to read keyword value or comment, so skip and continue */
430 if (fits_get_keytype(keyvalue, &keytype, &status)){
431 fprintf(stderr,"Failed to read type (string, int, float) for key = %s, value = %s\n",keyname,keyvalue);
432 fits_report_error(stderr,status);
433 status = 0;
434 continue;
435 } /* failed to read keytype, so skip and continue */
436 keyclass = fits_get_keyclass(fitsrecord);
437 if (keyclass == TYP_USER_KEY){
438 if (keytype == 'C' || keytype == 'L'){ /* string or logical */
439 _strings[QString(keyname)] = QString(keyvalue);
440 } else if (keytype == 'I' || keytype == 'F'){ /* int or float */
441 _scalars[QString(keyname)] = atof(keyvalue);
442 }
443 }
444 }
445
446 /* check if table HDU, and skip if not */
447 if (hdutype == ASCII_TBL || hdutype == BINARY_TBL){
448 if(fits_get_num_cols(_fptr, &ncol, &status)){ /* read # of columns */
449 fprintf(stderr,"Failed to read # of columns in HDU = %ld\n",i);
450 fits_report_error(stderr,status);
451 status = 0;
452 continue;
453 } /* failed to read number of columns, so skip and continue */
454 if(fits_get_num_rows(_fptr, &nrow, &status)){ /* read # of rows */
455 fprintf(stderr,"Failed to read # of rows in HDU = %ld\n",i);
456 fits_report_error(stderr,status);
457 status = 0;
458 continue;
459 } /* failed to read number of rows, so skip and continue */
460 tableRow << nrow;
461 tableHDU << i;
462 } else
463 continue; /* skip non-table HDUs for reading table columns */
464 for (j=1; j<= ncol; j++){
465 sprintf(coltemplate,"%ld",j);
466 if(fits_get_colname(_fptr, CASEINSEN, coltemplate, colname, &colnum, &status)){
467 fprintf(stderr,"Failed to read column name for column = %ld\n",j);
468 fits_report_error(stderr,status);
469 status = 0;
470 continue;
471 } /* failed to read column name, so skip and continue */
472 if(fits_get_coltype(_fptr, colnum, &typecode,&repeat,&width, &status)){
473 fprintf(stderr,"Failed to read column type for column = %s\n",colname);
474 fits_report_error(stderr,status);
475 status = 0;
476 continue;
477 } /* failed to read column type, so skip and continue */
478 if (validField(typecode)){
479 if (repeat == 1){ /* not an array of values */
480 idx = _fieldList.indexOf(QString(colname));
481 if (idx == -1){ /* not present in list already */
482 _fieldList << QString(colname);
483 _colName << QString(colname);
484 _colRepeat << repeat;
485 _colType << typecode;
486 _colOffset << 0;
487 }
488 _frameCounts[QString(colname)] += nrow;
489 if (_frameCounts[QString(colname)] > _maxFrameCount)
490 _maxFrameCount = _frameCounts[QString(colname)];
491 } else{ /* is an array of values */
492 /* TODO: should these be matrices instead (or perhaps as well?)
493 I don't understand how matrices should work with KST */
494 if(fits_read_tdim(_fptr,colnum,maxdim, &naxis, naxes, &status)){
495 fprintf(stderr,"Failed to read dimensions of column = %s\n",colname);
496 fits_report_error(stderr,status);
497 status = 0;
498 continue;
499 }
500 for (k=0; k < repeat; k++){
501 if (naxis == 1)
502 tmp.sprintf("%s_%02ld",colname,k%naxes[0]+1);
503 else if (naxis == 2)
504 tmp.sprintf("%s_%02ld_%02ld",colname,k/naxes[0]+1,k%naxes[0]+1);
505 else /* 3-D arrays not supported right now */
506 break;
507 idx = _fieldList.indexOf(tmp);
508 if (idx == -1){ /* not present already */
509 _fieldList << tmp;
510 _colName << QString(colname);
511 _colRepeat << repeat;
512 _colType << typecode;
513 _colOffset << k;
514 }
515 _frameCounts[tmp] += nrow;
516 if (_frameCounts[QString(colname)] > _maxFrameCount)
517 _maxFrameCount = _frameCounts[QString(colname)];
518 }
519 }
520 } else /* end if(validField) */
521 fprintf(stderr,"Skipping unsupported type for column = %s\n",colname);
522 } /* end loop over columns */
523 } /* end loop over HDUs */
524
525 /* set all fields to have _maxFrameCount size */
526 allcols = _frameCounts.keys();
527 for (i=0; i < allcols.size(); i++)
528 _frameCounts[allcols[i]] = _maxFrameCount;
529 registerChange();
530 free(naxes);
531 return true; // false if something went wrong
532 }
533
534 /* Check if the data in the from the source has updated. Considering how FITS
535 files are built up we can consider that they are always fixed */
536
internalDataSourceUpdate()537 Kst::Object::UpdateType FitsTableSource::internalDataSourceUpdate() {
538
539 DBG qDebug() << "Entering FitsTableSource::internalDataSourceUpdate()\n";
540 return Kst::Object::NoChange;
541 }
542
readScalar(double * v,const QString & field)543 int FitsTableSource::readScalar(double *v, const QString& field){
544 DBG qDebug() << "Entering FitsTableSource::readScalar() with field: " << field << endl;
545
546 *v = _scalars[field];
547 return 1;
548 }
549
readString(QString * stringValue,const QString & stringName)550 int FitsTableSource::readString(QString *stringValue, const QString& stringName){
551 DBG qDebug() << "Entering FitsTableSource::readString() with field: " << stringName << endl;
552 *stringValue = _strings[stringName];
553 return 1;
554 }
555
readField(double * v,const QString & field,int s,int n)556 int FitsTableSource::readField(double *v, const QString& field, int s, int n) {
557 int status = 0; /* cfitsio status flag */
558 int colnum; /* column number in table */
559 int anynul; /* Number of null values read by fits_read_col() */
560 int typecode; /* data type of column in FITS table */
561 long repeat; /* used to determine if matrix or vector */
562 void *data; /* empty pointer for reading data */
563 long nelements; /* size of data to read */
564 long offset; /* offset for data when repeat > 1 */
565 long idx; /* used when reading from Pixel Readout and tracking which
566 section of data array is being read by fits_read_col */
567 long totalidx; /* to keep track of our location in the v array */
568 long i,j,k; /* loop variables */
569 long nrow; /* number of rows read for each data chunk */
570 long maxrow; /* maximum number of rows to read at once. Later re-used
571 to signify the number of rows to read for the current
572 HDU, which may not be tableRow[i], due to the fact
573 that the offset, s, may not be zero. */
574 long currow; /* current row index (when looping over data chunks */
575 int hdutype; /* FITS HDU type */
576 char *colname; /* Name for column */
577 int firstHDU; /* Flag to control whether we are reading the first HDU
578 containing data for a field. Necessary to keep track of
579 s, the offset from the beginning of data */
580 QByteArray ba; /* needed to convert a QString to char array */
581
582 DBG qDebug() << "Entering FitsTableSource::readField() with params: " << field << ", from " << s << " for " << n << " frames" << endl;
583
584 /* For INDEX field */
585 if (field.toLower() == "index") {
586 if (n < 0) {
587 v[0] = double(s);
588 return 1;
589 }
590 for (int i = 0; i < n; ++i) {
591 v[i] = double(s + i);
592 }
593 return n;
594 }
595
596 idx = _fieldList.indexOf(field); /* get index of field in list */
597 repeat = _colRepeat[idx];
598 typecode = _colType[idx]; /* FITS type code for field */
599 ba = _colName[idx].toLocal8Bit(); /* FITS column name of field */
600 colname = ba.data();
601 offset = _colOffset[idx]; /* data offset */
602
603 firstHDU = 1;
604 maxrow = 100000/repeat; /* cap on max rows to read at once */
605 nelements = maxrow * repeat; /* so we read an integer number of 'repeat' at a time */
606 if (typecode == TINT)
607 data = (int *) malloc(nelements*sizeof(int));
608 else if (typecode == TLONG || typecode == TINT32BIT)
609 data = (long *) malloc(nelements*sizeof(long));
610 else if (typecode == TLONGLONG)
611 data = (long long *) malloc(nelements*sizeof(long long));
612 else if (typecode == TFLOAT)
613 data = (float *) malloc(nelements*sizeof(float));
614 else if (typecode == TDOUBLE)
615 data = (double *) malloc(nelements*sizeof(double));
616 else if (typecode == TLOGICAL)
617 data = (bool *) malloc(nelements*sizeof(bool));
618
619 totalidx = 0;
620 for (i=0; i < tableHDU.size(); i++){ /* loop over table HDUs */
621 if (totalidx >= n) /* quit when we have read all data requested */
622 break;
623 if (fits_movabs_hdu(_fptr, tableHDU[i], &hdutype, &status)){
624 fits_report_error(stderr,status);
625 status = 0;
626 continue;
627 } /* failed moving to an HDU, so try to skip and go to next one */
628 if(fits_get_colnum(_fptr, CASEINSEN, colname, &colnum, &status)){
629 status = 0; /* column not found in this HDU, so skip. */
630 continue;
631 }
632 if (firstHDU == 1){
633 maxrow = tableRow[i] - s;
634 firstHDU = 0;
635 currow = s + 1; /* FITS starts counting from row 1 */
636 } else{
637 maxrow = tableRow[i];
638 currow = 1; /* FITS starts counting from row 1 */
639 }
640
641 /* figure out how many chunks we need to read the rows in this HDU */
642 idx = (maxrow*repeat)%nelements;
643 if (idx == 0) /* divides evenly */
644 idx = (maxrow*repeat)/nelements;
645 else
646 idx = (maxrow*repeat)/nelements + 1;
647 for (j=0; j < idx; j++){ /* loop over row chunks */
648 if (totalidx >= n) /* quit when we have read all data requested */
649 break;
650 if (j == (idx - 1)) /* last iteration */
651 nrow = (maxrow*repeat - j*nelements)/repeat;
652 else
653 nrow = nelements/repeat;
654 /* check to make sure we don't read more data than requested */
655 if (totalidx + nrow > n)
656 nrow = n - totalidx;
657 if (typecode == TINT){
658 if (fits_read_col(_fptr, typecode, colnum, currow, 1,
659 nrow*repeat, NULL, &(((int *)data)[0]), &anynul, &status)){
660 fprintf(stderr,"Failed to read column = %s, filling with NaNs\n",colname);
661 fits_report_error(stderr,status);
662 status = 0;
663 for (k=0; k < nrow; k++)
664 v[totalidx+k] = sqrt(-1);
665 } else {
666 for (k=0; k < nrow; k++){
667 v[totalidx+k] = (double) ((int *)data)[k*repeat+offset];
668 }
669 }
670 } else if (typecode == TLONG || typecode == TINT32BIT){
671 if (fits_read_col(_fptr, typecode, colnum, currow, 1,
672 nrow*repeat, NULL, &(((long *)data)[0]), &anynul, &status)){
673 fprintf(stderr,"Failed to read column = %s, filling with NaNs\n",colname);
674 fits_report_error(stderr,status);
675 status = 0;
676 for (k=0; k < nrow; k++)
677 v[totalidx+k] = sqrt(-1);
678 } else {
679 for (k=0; k < nrow; k++){
680 v[totalidx+k] = (double) ((long *)data)[k*repeat+offset];
681 }
682 }
683 } else if (typecode == TLONGLONG){
684 if (fits_read_col(_fptr, typecode, colnum, currow, 1,
685 nrow*repeat, NULL, &(((long long *)data)[0]), &anynul, &status)){
686 fprintf(stderr,"Failed to read column = %s, filling with NaNs\n",colname);
687 fits_report_error(stderr,status);
688 status = 0;
689 for (k=0; k < nrow; k++)
690 v[totalidx+k] = sqrt(-1);
691 } else {
692 for (k=0; k < nrow; k++){
693 v[totalidx+k] = (double) ((long long *)data)[k*repeat+offset];
694 }
695 }
696 }else if (typecode == TFLOAT){
697 if (fits_read_col(_fptr, typecode, colnum, currow, 1,
698 nrow*repeat, NULL, &(((float *)data)[0]), &anynul, &status)){
699 fprintf(stderr,"Failed to read column = %s, filling with NaNs\n",colname);
700 fits_report_error(stderr,status);
701 status = 0;
702 for (k=0; k < nrow; k++)
703 v[totalidx+k] = sqrt(-1);
704 } else {
705 for (k=0; k < nrow; k++)
706 v[totalidx+k] = (double) ((float *)data)[k*repeat+offset];
707 }
708 }else if (typecode == TDOUBLE){
709 if (fits_read_col(_fptr, typecode, colnum, currow, 1,
710 nrow*repeat, NULL, &(((double *)data)[0]), &anynul, &status)){
711 fprintf(stderr,"Failed to read column = %s, filling with NaNs\n",colname);
712 fits_report_error(stderr,status);
713 status = 0;
714 for (k=0; k < nrow; k++)
715 v[totalidx+k] = sqrt(-1);
716 } else {
717 for (k=0; k < nrow; k++)
718 v[totalidx+k] = (double) ((double *)data)[k*repeat+offset];
719 }
720 }else if (typecode == TLOGICAL){
721 if (fits_read_col(_fptr, typecode, colnum, currow, 1,
722 nrow*repeat, NULL, &(((bool *)data)[0]), &anynul, &status)){
723 fprintf(stderr,"Failed to read column = %s, filling with NaNs\n",colname);
724 fits_report_error(stderr,status);
725 status = 0;
726 for (k=0; k < nrow; k++)
727 v[totalidx+k] = sqrt(-1);
728 } else {
729 for (k=0; k < nrow; k++)
730 v[totalidx+k] = (double) ((bool *)data)[k*repeat+offset];
731 }
732 }
733 totalidx += nrow;
734 currow += nrow;
735 } /* end loop over row chunks */
736 } /* end loop over HDUs */
737
738 for (i=totalidx; i< n; i++) /* fill remainder with NaNs */
739 v[i] = sqrt(-1);
740 free(data);
741 return n;
742 }
743
readMatrix(double * v,const QString & field)744 int FitsTableSource::readMatrix(double *v, const QString& field){
745 int status = 0; /* cfitsio status flag */
746 int colnum; /* column number in table */
747 int anynul; /* Number of null values read by fits_read_col() */
748 int typecode; /* data type of column in FITS table */
749 long width; /* width code from fits_get_coltype(). not used */
750 long repeat; /* used to determine if matrix or vector */
751 long n; /* total size */
752 long nrow; /* number of rows */
753 void *data; /* empty pointer for reading data */
754
755 /* TODO: The code in this function is probably all wrong. Since fitstable
756 does not currently implement matrices, this function never gets called */
757 DBG qDebug() << "Entering FitsTableSource::readMatrix() with field: " << field << endl;
758 if (fits_get_colnum(_fptr, CASEINSEN, field.toAscii().data(), &colnum, &status))
759 fits_report_error(stderr,status);
760 if (fits_get_coltype(_fptr, colnum, &typecode, &repeat, &width, &status))
761 fits_report_error(stderr,status);
762 if (fits_get_num_rows(_fptr, &nrow, &status))
763 fits_report_error(stderr,status);
764
765 n = repeat*nrow;
766 if (typecode == TLONG || typecode == TINT32BIT)
767 data = (int *) malloc(n*sizeof(int));
768 else if (typecode == TLONGLONG)
769 data = (long long *) malloc(n*sizeof(long long));
770 else if (typecode == TFLOAT)
771 data = (float *) malloc(n*sizeof(float));
772 else if (typecode == TDOUBLE)
773 data = (double *) malloc(n*sizeof(double));
774 else if (typecode == TLOGICAL)
775 data = (bool *) malloc(n*sizeof(bool));
776
777 if (fits_read_col(_fptr, typecode, colnum, 1, 1, n, NULL, data, &anynul, &status))
778 fits_report_error(stderr,status);
779 for (int i=0; i < n; i++){
780 if (typecode == TLONG || typecode == TINT32BIT)
781 v[i] = (double) ((int *)data)[i];
782 else if (typecode == TLONGLONG)
783 v[i] = (double) ((long long *)data)[i];
784 else if (typecode == TFLOAT)
785 v[i] = (double) ((float *)data)[i];
786 else if (typecode == TDOUBLE)
787 v[i] = (double) ((double *)data)[i];
788 else if (typecode == TLOGICAL)
789 v[i] = (double) ((bool *)data)[i];
790 }
791
792 free(data);
793 return n;
794 }
795
frameCount(const QString & field) const796 int FitsTableSource::frameCount(const QString& field) const {
797
798 DBG qDebug() << "Entering FitsTableSource::frameCount() with field: " << field << endl;
799 if (field.isEmpty() || field.toLower() == "index") {
800 return _maxFrameCount;
801 } else {
802 return _frameCounts[field];
803 }
804 }
805
fileType() const806 QString FitsTableSource::fileType() const {
807 return "FITS Table Datasource";
808 }
809
810
save(QXmlStreamWriter & streamWriter)811 void FitsTableSource::save(QXmlStreamWriter &streamWriter) {
812 Kst::DataSource::save(streamWriter);
813 }
814
samplesPerFrame(const QString & field)815 int FitsTableSource::samplesPerFrame(const QString& field) {
816 return 1;
817 }
818
819
820 // Name used to identify the plugin. Used when loading the plugin.
pluginName() const821 QString FitsTableSourcePlugin::pluginName() const { return "FITS Table Datasource Reader"; }
pluginDescription() const822 QString FitsTableSourcePlugin::pluginDescription() const { return "FITS Table Datasource Reader"; }
823
824 /**********************
825 FitsTablesourcePlugin - This class defines the plugin interface to the DataSource
826 defined by the plugin. The primary requirements of this class are to provide the
827 necessary connections to create the object which includes providing access to
828 the configuration widget.
829 ***********************/
830
create(Kst::ObjectStore * store,QSettings * cfg,const QString & filename,const QString & type,const QDomElement & element) const831 Kst::DataSource *FitsTableSourcePlugin::create(Kst::ObjectStore *store,
832 QSettings *cfg,
833 const QString &filename,
834 const QString &type,
835 const QDomElement &element) const {
836
837 return new FitsTableSource(store, cfg, filename, type, element);
838 }
839
840
841 // Provides the matrix list that this dataSource can provide from the provided filename.
842 // This function should use understands to validate the file and then open and calculate the
843 // list of matrices.
matrixList(QSettings * cfg,const QString & filename,const QString & type,QString * typeSuggestion,bool * complete) const844 QStringList FitsTableSourcePlugin::matrixList(QSettings *cfg,
845 const QString& filename,
846 const QString& type,
847 QString *typeSuggestion,
848 bool *complete) const {
849
850 DBG qDebug() << "Entering FitsTableSourcePlugin::matrixList()\n";
851 if (typeSuggestion) {
852 *typeSuggestion = "FITS Table Datasource";
853 }
854 if ((!type.isEmpty() && !provides().contains(type)) || 0 == understands(cfg, filename)) {
855 if (complete) {
856 *complete = false;
857 }
858 return QStringList();
859 }
860 QStringList matrixList;
861
862 return matrixList;
863
864 }
865
866
867 /* Provides the scalar list that this dataSource can provide from the provided
868 filename. This function should use understands to validate the file and then
869 open and calculate the list of scalars if necessary.
870 */
scalarList(QSettings * cfg,const QString & filename,const QString & type,QString * typeSuggestion,bool * complete) const871 QStringList FitsTableSourcePlugin::scalarList(QSettings *cfg,
872 const QString& filename,
873 const QString& type,
874 QString *typeSuggestion,
875 bool *complete) const {
876
877 QStringList scalarList;
878
879 DBG qDebug() << "Entering FitsTableSourcePlugin::scalarList()\n";
880 if ((!type.isEmpty() && !provides().contains(type)) || 0 == understands(cfg, filename)) {
881 if (complete) {
882 *complete = false;
883 }
884 return QStringList();
885 }
886
887 if (typeSuggestion) {
888 *typeSuggestion = "FITS Table Datasource";
889 }
890
891 scalarList.append("FRAMES");
892 return scalarList;
893 }
894
895
896 /* Provides the string list that this dataSource can provide from the provided
897 filename. This function should use understands to validate the file and then
898 open and calculate the list of strings if necessary.
899 */
stringList(QSettings * cfg,const QString & filename,const QString & type,QString * typeSuggestion,bool * complete) const900 QStringList FitsTableSourcePlugin::stringList(QSettings *cfg,
901 const QString& filename,
902 const QString& type,
903 QString *typeSuggestion,
904 bool *complete) const {
905
906 QStringList stringList;
907
908 DBG qDebug() << "Entering FitsTableSourcePlugin::stringList()\n";
909 if ((!type.isEmpty() && !provides().contains(type)) || 0 == understands(cfg, filename)) {
910 if (complete) {
911 *complete = false;
912 }
913 return QStringList();
914 }
915
916 if (typeSuggestion) {
917 *typeSuggestion = "FITS Table Datasource";
918 }
919
920 stringList.append("FILENAME");
921 return stringList;
922 }
923
924
925 /* Provides the field list that this dataSource can provide from the provided
926 filename. This function should use understands to validate the file and then
927 open and calculate the list of fields if necessary.
928 */
fieldList(QSettings * cfg,const QString & filename,const QString & type,QString * typeSuggestion,bool * complete) const929 QStringList FitsTableSourcePlugin::fieldList(QSettings *cfg,
930 const QString& filename,
931 const QString& type,
932 QString *typeSuggestion,
933 bool *complete) const {
934 Q_UNUSED(cfg)
935 Q_UNUSED(filename)
936 Q_UNUSED(type)
937
938 DBG qDebug() << "Entering FitsTableSourcePlugin::fieldList()\n";
939 if (complete) {
940 *complete = true;
941 }
942
943 if (typeSuggestion) {
944 *typeSuggestion = "FITS Table Datasource";
945 }
946
947 QStringList fieldList;
948 return fieldList;
949 }
950
951 /* The main function used to determine if this plugin knows how to process the
952 provided file. Each datasource plugin should check the file and return a number
953 between 0 and 100 based on the likelyhood of the file being this type. 100
954 should only be returned if there is no way that the file could be any datasource
955 other than this one.
956 */
understands(QSettings * cfg,const QString & filename) const957 int FitsTableSourcePlugin::understands(QSettings *cfg, const QString& filename) const {
958 /* This function will attempt to open the file as a FITS file, then loop
959 through the HDUs to see if any are binary or ascii tables. If so, and
960 if any table has > 0 rows, then this is a FITS table that can be read
961 by this plugin. A value of 80 is returned, otherwise zero */
962 Q_UNUSED(cfg)
963
964 fitsfile *ff;
965 int status = 0; /* FITS error status */
966 int i; /* loop variable */
967 int ret_val = 0;
968 int numHDU; /* total number of HDUs in FITS file */
969 int hdutype; /* FITS HDU type */
970 long nrow; /* number of rows in table */
971
972 DBG qDebug() << "Entering FitsTableSourcePlugin::understands()\n";
973 if(fits_open_file(&ff, filename.toAscii(), READONLY, &status)){
974 fits_close_file(ff,&status);
975 return 0;
976 }
977 if (fits_get_num_hdus(ff, &numHDU, &status)){
978 fits_close_file(ff,&status);
979 return 0;
980 }
981 for(i=1; i<= numHDU; i++){
982 if (fits_movabs_hdu(ff, i, &hdutype, &status)){
983 fits_report_error(stderr,status);
984 status = 0;
985 continue;
986 }
987 if (hdutype == ASCII_TBL || hdutype == BINARY_TBL){
988 if(fits_get_num_rows(ff, &nrow, &status)){
989 fits_report_error(stderr,status);
990 status = 0;
991 continue;
992 }
993 if (nrow > 0){
994 ret_val = 80;
995 break;
996 }
997 }
998 }
999 status = 0;
1000 fits_close_file(ff,&status);
1001 return ret_val;
1002 }
1003
supportsTime(QSettings * cfg,const QString & filename) const1004 bool FitsTableSourcePlugin::supportsTime(QSettings *cfg, const QString& filename) const {
1005 //FIXME
1006 Q_UNUSED(cfg)
1007 Q_UNUSED(filename)
1008 return false;
1009 }
1010
1011
provides() const1012 QStringList FitsTableSourcePlugin::provides() const {
1013 QStringList rc;
1014 rc += "FITS Table Datasource";
1015 return rc;
1016 }
1017
1018
1019 // Request for this plugins configuration widget.
configWidget(QSettings * cfg,const QString & filename) const1020 Kst::DataSourceConfigWidget *FitsTableSourcePlugin::configWidget(QSettings *cfg,
1021 const QString& filename) const {
1022
1023 Q_UNUSED(cfg)
1024 Q_UNUSED(filename)
1025 return 0;
1026 }
1027
1028
1029 #ifndef QT5
1030 Q_EXPORT_PLUGIN2(kstdata_fitstable, FitsTableSourcePlugin)
1031 #endif
1032
1033 // vim: ts=2 sw=2 et
1034