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