File indexing completed on 2025-01-05 04:12:04

0001 /***************************************************************************
0002  *                                                                         *
0003  *   copyright : (C) 2014 Northwestern University                          *
0004  *                   nchapman@u.northwestern.edu                           *
0005  *                   g-novak@northwestern.edu                              *
0006  *                                                                         *
0007  *   This program is free software; you can redistribute it and/or modify  *
0008  *   it under the terms of the GNU General Public License as published by  *
0009  *   the Free Software Foundation; either version 2 of the License, or     *
0010  *   (at your option) any later version.                                   *
0011  *                                                                         *
0012  ***************************************************************************/
0013 
0014 #include "fitstable.h"
0015 
0016 #include <QXmlStreamWriter>
0017 #include <QFileInfo>
0018 
0019 #define DBG if(0)
0020 
0021 using namespace Kst;
0022 
0023 /* Scalar interface */
0024 
0025 class DataInterfaceFitsTableScalar : public DataSource::DataInterface<DataScalar>{
0026    public:
0027       DataInterfaceFitsTableScalar(FitsTableSource& s) : source(s) {}
0028 
0029       // read one element
0030       int read(const QString&, DataScalar::ReadInfo&);
0031 
0032       // named elements
0033       QStringList list() const { return source._scalars.keys(); }
0034       bool isListComplete() const { return true; }
0035       bool isValid(const QString&) const;
0036 
0037       // T specific
0038       const DataScalar::DataInfo dataInfo(const QString&) const { return DataScalar::DataInfo(); }
0039       void setDataInfo(const QString&, const DataScalar::DataInfo&) {}
0040 
0041       // meta data
0042       QMap<QString, double> metaScalars(const QString&) { return QMap<QString, double>(); }
0043       QMap<QString, QString> metaStrings(const QString&) { return QMap<QString, QString>(); }
0044 
0045    private:
0046       FitsTableSource& source;
0047 };
0048 
0049 
0050 int DataInterfaceFitsTableScalar::read(const QString& scalar, DataScalar::ReadInfo& p){
0051 
0052    DBG qDebug() << "Entering DataInterfaceFitsTableScalar::read() with scalar: " << scalar << endl;
0053    return source.readScalar(p.value, scalar);
0054 }
0055 
0056 
0057 bool DataInterfaceFitsTableScalar::isValid(const QString& scalar) const{
0058 
0059    DBG qDebug() << "Entering DataInterfaceFitsTableScalar::isValid() with scalar: " << scalar << endl;
0060    return  source._scalars.contains( scalar );
0061 }
0062 
0063 /* String interface */
0064 
0065 class DataInterfaceFitsTableString : public DataSource::DataInterface<DataString>{
0066    public:
0067       DataInterfaceFitsTableString(FitsTableSource& s) : source(s) {}
0068 
0069       // read one element
0070       int read(const QString&, DataString::ReadInfo&);
0071 
0072       // named elements
0073       QStringList list() const { return source._strings.keys(); }
0074       bool isListComplete() const { return true; }
0075       bool isValid(const QString&) const;
0076 
0077       // T specific
0078       const DataString::DataInfo dataInfo(const QString&) const { return DataString::DataInfo(); }
0079       void setDataInfo(const QString&, const DataString::DataInfo&) {}
0080 
0081       // meta data
0082       QMap<QString, double> metaScalars(const QString&) { return QMap<QString, double>(); }
0083       QMap<QString, QString> metaStrings(const QString&) { return QMap<QString, QString>(); }
0084 
0085    private:
0086       FitsTableSource& source;
0087 };
0088 
0089 
0090 int DataInterfaceFitsTableString::read(const QString& string, DataString::ReadInfo& p){
0091 
0092    DBG qDebug() << "Entering DataInterfaceFitsTableString::read() with string: " << string << endl;
0093    if (isValid(string) && p.value) {
0094       *p.value = source._strings[string];
0095       return 1;
0096    }
0097    return 0;
0098 }
0099 
0100 
0101 bool DataInterfaceFitsTableString::isValid(const QString& string) const{
0102 
0103    DBG qDebug() << "Entering DataInterfaceFitsTableString::isValid() with string: " << string << endl;
0104    return source._strings.contains( string );
0105 }
0106 
0107 /* Vector interface */
0108 
0109 class DataInterfaceFitsTableVector : public DataSource::DataInterface<DataVector>{
0110    public:
0111       DataInterfaceFitsTableVector(FitsTableSource& s) : source(s) {}
0112 
0113       // read one element
0114       int read(const QString&, DataVector::ReadInfo&);
0115 
0116       // named elements
0117       QStringList list() const { return source._fieldList; }
0118       bool isListComplete() const { return true; }
0119       bool isValid(const QString&) const;
0120 
0121       // T specific
0122       const DataVector::DataInfo dataInfo(const QString&) const;
0123       void setDataInfo(const QString&, const DataVector::DataInfo&) {}
0124 
0125       // meta data
0126       QMap<QString, double> metaScalars(const QString&);
0127       QMap<QString, QString> metaStrings(const QString&);
0128 
0129    private:
0130       FitsTableSource& source;
0131 };
0132 
0133 
0134 const DataVector::DataInfo DataInterfaceFitsTableVector::dataInfo(const QString &field) const{
0135 
0136    DBG qDebug() << "Entering DataInterfaceFitsTableVector::dataInfo() with field: " << field << endl;
0137    if (!source._fieldList.contains(field))
0138       return DataVector::DataInfo();
0139 
0140    return DataVector::DataInfo(source.frameCount(field), source.samplesPerFrame(field));
0141 }
0142 
0143 int DataInterfaceFitsTableVector::read(const QString& field, DataVector::ReadInfo& p){
0144 
0145    DBG qDebug() << "Entering DataInterfaceFitsTableVector::read() with field: " << field << endl;
0146    return source.readField(p.data, field, p.startingFrame, p.numberOfFrames);
0147 }
0148 
0149 
0150 bool DataInterfaceFitsTableVector::isValid(const QString& field) const{
0151 
0152    DBG qDebug() << "Entering DataInterfaceFitsTableVector::isValid() with field: " << field << endl;
0153    return  source._fieldList.contains(field);
0154 }
0155 
0156 QMap<QString, double> DataInterfaceFitsTableVector::metaScalars(const QString& field){
0157    Q_UNUSED(field);
0158    QMap<QString, double> fieldScalars;
0159 
0160    DBG qDebug() << "Entering DataInterfaceFitsTableVector::metaScalars() with field: " << field << endl;
0161    return fieldScalars;
0162 }
0163 
0164 QMap<QString, QString> DataInterfaceFitsTableVector::metaStrings(const QString& field){
0165 
0166    QMap<QString, QString> fieldStrings;
0167    DBG qDebug() << "Entering DataInterfaceFitsTableVector::metaStrings() with field: " << field << endl;
0168    return fieldStrings;
0169 }
0170 
0171 /* Matrix interface */
0172 
0173 class DataInterfaceFitsTableMatrix : public DataSource::DataInterface<DataMatrix>{
0174    public:
0175 
0176       DataInterfaceFitsTableMatrix(FitsTableSource& s) : source(s) {}
0177 
0178       // read one element
0179       int read(const QString&, DataMatrix::ReadInfo&);
0180 
0181       // named elements
0182       QStringList list() const { return source._matrixList; }
0183       bool isListComplete() const { return true; }
0184       bool isValid(const QString&) const;
0185 
0186       // T specific
0187       const DataMatrix::DataInfo dataInfo   (const QString&) const;
0188       void setDataInfo(const QString&, const DataMatrix::DataInfo&) {}
0189 
0190       // meta data
0191       QMap<QString, double> metaScalars(const QString&) { return QMap<QString, double>(); }
0192       QMap<QString, QString> metaStrings(const QString&) { return QMap<QString, QString>(); }
0193 
0194    private:
0195       FitsTableSource& source;
0196 };
0197 
0198 
0199 const DataMatrix::DataInfo DataInterfaceFitsTableMatrix::dataInfo(const QString& matrix) const{
0200 
0201    DBG qDebug() << "Entering DataInterfaceFitsTableMatrix::dataInfo() with matrix: " << matrix << endl;
0202    if (!source._matrixList.contains( matrix ) ) {
0203       return DataMatrix::DataInfo();
0204    }
0205 
0206    DataMatrix::DataInfo info;
0207    info.samplesPerFrame = 1;
0208    info.xSize = 32;
0209    info.ySize = 12;
0210 
0211    return info;
0212 }
0213 
0214 
0215 int DataInterfaceFitsTableMatrix::read(const QString& field, DataMatrix::ReadInfo& p){
0216 
0217    DBG qDebug() << "Entering DataInterfaceFitsTableMatrix::read() with field: " << field << endl;
0218    int count = source.readMatrix(p.data->z, field);
0219 
0220    p.data->xMin = 0;
0221    p.data->yMin = 0;
0222    p.data->xStepSize = 1;
0223    p.data->yStepSize = 1;
0224 
0225    return count;
0226 }
0227 
0228 
0229 bool DataInterfaceFitsTableMatrix::isValid(const QString& field) const {
0230 
0231    DBG qDebug() << "Entering DataInterfaceFitsTableMatrix::isValid() with field: " << field << endl;
0232    return source._matrixList.contains(field);
0233 }
0234 
0235 /**********************
0236 FitsTableSource - This class defines the main DataSource which derives
0237 from DataSource. The key functions that this class must provide is the ability
0238 to create the source, provide details about the source be able to process the
0239 data.
0240 ***********************/
0241 FitsTableSource::FitsTableSource(Kst::ObjectStore *store, QSettings *cfg,
0242    const QString& filename, const QString& type, const QDomElement& e)
0243    : Kst::DataSource(store, cfg, filename, type), _fptr(0L), _config(0L),
0244    is(new DataInterfaceFitsTableScalar(*this)),
0245    it(new DataInterfaceFitsTableString(*this)),
0246    iv(new DataInterfaceFitsTableVector(*this)),
0247    im(new DataInterfaceFitsTableMatrix(*this)){
0248 
0249    DBG qDebug() << "Entering FitsTableSource::FitsTableSource() with filename: " << filename << endl;
0250    setInterface(is);
0251    setInterface(it);
0252    setInterface(iv);
0253    setInterface(im);
0254 
0255    setUpdateType(None);
0256 
0257    if (!type.isEmpty() && type != "FITS Table") {
0258       return;
0259    }
0260 
0261    _valid = false;
0262    _maxFrameCount = 0;
0263 
0264    _filename = filename;
0265 
0266    if (init()) {
0267       _valid = true;
0268    }
0269 
0270    registerChange();
0271 }
0272 
0273 
0274 
0275 FitsTableSource::~FitsTableSource() {
0276 
0277    DBG qDebug() << "Entering FitsTableSource::~FitsTableSource()\n";
0278    int status = 0;
0279    if (_fptr) {
0280       fits_close_file( _fptr, &status );
0281       _fptr = 0L;
0282    }
0283 }
0284 
0285 
0286 void FitsTableSource::reset() {
0287 
0288    DBG qDebug() << "Entering FitsTableSource::reset()\n";
0289 
0290    int status = 0;
0291 
0292    if (_fptr){
0293       fits_close_file(_fptr, &status);
0294       _fptr = 0L;
0295    }
0296    _maxFrameCount = 0;
0297    _valid = init();
0298 }
0299 
0300 int FitsTableSource::validField(int typecode){
0301    /* check to see if typcode is one of the supported ones.  If yes, return 1,
0302       else return 0 */
0303 
0304    int tmp;
0305 
0306    switch(typecode){
0307       case TINT:
0308       case TLONG: /* also covers TINT32BIT */
0309       case TLONGLONG:
0310       case TFLOAT:
0311       case TDOUBLE:
0312       case TLOGICAL:
0313          tmp = 1;
0314          break;
0315       default:
0316          tmp = 0;
0317    }
0318    return tmp;
0319 }
0320 
0321 // If the datasource has any predefined fields they should be populated here.
0322 bool FitsTableSource::init() {
0323    int  status = 0;   /* cfitsio status flag */
0324    int  colnum;       /* column number in table */
0325    int  ncol;         /* number of columns in table */
0326    long nrow;         /* number of rows in table */
0327    char colname[512]; /* Name for column */
0328    int  typecode;     /* data type of column in FITS table */
0329    long width;        /* width codes from fits_get_coltype(). not used */
0330    int  hdutype;      /* FITS HDU type */
0331    long repeat;       /* used to determine if matrix or vector */
0332    long i,j,k;        /* loop variables */
0333    char coltemplate[512]; /* template for reading column names */
0334    QString tmp;       /* format string for bolometer array */
0335    int numHDU;        /* total number of HDUs in FITS file */
0336    int idx;           /* used to get index of elements in lists, so we don't
0337                          add the same scalar name, vector name, etc to the
0338                          relevant list twice */
0339    int naxis;           /* length of naxes variable.  Used for reading TDIM */
0340    long *naxes;         /* array to contain dimensionality of FITS columns */
0341    int maxdim;          /* maximum allowed array size of naxes */
0342    QStringList allcols; /* list of all column names */
0343    int nkeys;           /* Number of header keywords in current HDU */
0344    int keylength;       /* length of keyword in characters (not used) */
0345    int keyclass;        /* class of FITS header keyword, e.g., TYP_USER_KEY */
0346    char keyname[10];    /* array to hold FITS keyword name */
0347    char keyvalue[128];   /* array to hold FITS keyword value */
0348    char keycomment[128]; /* array to hold FITS keyword comment */
0349    char fitsrecord[128]; /* array to hold one line of FITS header */
0350    char keytype;        /* FITS key type (string, float, int, etc) */
0351 
0352    maxdim = 2;
0353    naxes = (long *) malloc(maxdim*sizeof(long));
0354 
0355    DBG qDebug() << "Entering FitsTableSource::init() with filename: " << _filename.toAscii() << endl;
0356    // First, try to open the file
0357    if(fits_open_file( &_fptr, _filename.toAscii(), READONLY, &status )){
0358       fits_close_file( _fptr, &status );
0359       _fptr = 0L;
0360       _valid = false;
0361       free(naxes);
0362       return false;
0363    }
0364    _scalars.clear();
0365    _fieldList.clear();
0366    _matrixList.clear();
0367    _strings.clear();
0368    _colName.clear();
0369    _colRepeat.clear();
0370    _colType.clear();
0371    _colOffset.clear();
0372    tableHDU.clear();
0373    tableRow.clear();
0374 
0375    // Some standard stuff
0376    _fieldList += "INDEX";
0377    _colName += "INDEX";
0378    _colRepeat << 1;
0379    _colType << 1;
0380    _colOffset << 0;
0381 
0382    _strings = fileMetas();
0383    _maxFrameCount = 0;
0384 
0385    /* get total number of HDUs */
0386    if (fits_get_num_hdus(_fptr, &numHDU, &status)){
0387       fits_report_error(stderr,status);
0388       _fptr = 0L;
0389       _valid = false;
0390       free(naxes);
0391       return false;
0392    } /* can't read number of HDUs, so quit */
0393 
0394    nrow = 0;
0395    for (i=1; i<= numHDU; i++){ /* loop over all HDUs */
0396       if (fits_movabs_hdu(_fptr, i, &hdutype, &status)){
0397          fits_report_error(stderr,status);
0398          status = 0;
0399          continue;
0400       } /* failed moving to an HDU, so try to skip and go to next one */
0401       /* read header to assign string and scalar values.  For now only
0402          read USER_KEYs.  May want to add UNIT_KEYs eventually, to get the
0403          units for every vector */
0404       if(fits_get_hdrspace(_fptr, &nkeys, NULL, &status)){ /* total # of keys */
0405          fits_report_error(stderr,status);
0406          status = 0;
0407          continue;
0408       }
0409       for (j=1; j<= nkeys; j++){ /* loop over keys */
0410          if(fits_read_record(_fptr, j, fitsrecord, &status)){
0411             fprintf(stderr,"Failed to read record number %ld\n",j);
0412             fits_report_error(stderr,status);
0413             status = 0;
0414             continue;
0415          } /* failed to read a record, so skip and continue */
0416          if (fits_get_keyname(fitsrecord, keyname, &keylength, &status)){
0417             fprintf(stderr,"Failed to read keyword from record = %s\n",fitsrecord);
0418             fits_report_error(stderr,status);
0419             status = 0;
0420             continue;
0421          } /* failed to read keyword name, so skip and continue */
0422          if (strcmp(keyname,"HISTORY") == 0) /* skip HISTORY keywords */
0423             continue;
0424          if (fits_parse_value(fitsrecord, keyvalue, keycomment, &status)){
0425             fprintf(stderr,"Failed to read value and comment from key = %s\n",keyname);
0426             fits_report_error(stderr,status);
0427             status = 0;
0428             continue;
0429          } /* failed to read keyword value or comment, so skip and continue */
0430          if (fits_get_keytype(keyvalue, &keytype, &status)){
0431             fprintf(stderr,"Failed to read type (string, int, float) for key = %s, value = %s\n",keyname,keyvalue);
0432             fits_report_error(stderr,status);
0433             status = 0;
0434             continue;
0435          } /* failed to read keytype, so skip and continue */
0436          keyclass = fits_get_keyclass(fitsrecord);
0437          if (keyclass == TYP_USER_KEY){
0438             if (keytype == 'C' || keytype == 'L'){ /* string or logical */
0439                _strings[QString(keyname)] = QString(keyvalue);
0440             } else if (keytype == 'I' || keytype == 'F'){ /* int or float */
0441                _scalars[QString(keyname)] = atof(keyvalue);
0442             }
0443          }
0444       }
0445 
0446       /* check if table HDU, and skip if not */
0447       if (hdutype == ASCII_TBL || hdutype == BINARY_TBL){
0448          if(fits_get_num_cols(_fptr, &ncol, &status)){ /* read # of columns */
0449             fprintf(stderr,"Failed to read # of columns in HDU = %ld\n",i);
0450             fits_report_error(stderr,status);
0451             status = 0;
0452             continue;
0453          } /* failed to read number of columns, so skip and continue */
0454          if(fits_get_num_rows(_fptr, &nrow, &status)){ /* read # of rows */
0455             fprintf(stderr,"Failed to read # of rows in HDU = %ld\n",i);
0456             fits_report_error(stderr,status);
0457             status = 0;
0458             continue;
0459          } /* failed to read number of rows, so skip and continue */
0460          tableRow << nrow;
0461          tableHDU << i;
0462       } else
0463          continue; /* skip non-table HDUs for reading table columns */
0464       for (j=1; j<= ncol; j++){
0465          sprintf(coltemplate,"%ld",j);
0466          if(fits_get_colname(_fptr, CASEINSEN, coltemplate, colname, &colnum, &status)){
0467             fprintf(stderr,"Failed to read column name for column = %ld\n",j);
0468             fits_report_error(stderr,status);
0469             status = 0;
0470             continue;
0471          } /* failed to read column name, so skip and continue */
0472          if(fits_get_coltype(_fptr, colnum, &typecode,&repeat,&width, &status)){
0473             fprintf(stderr,"Failed to read column type for column = %s\n",colname);
0474             fits_report_error(stderr,status);
0475             status = 0;
0476             continue;
0477          } /* failed to read column type, so skip and continue */
0478          if (validField(typecode)){
0479             if (repeat == 1){ /* not an array of values */
0480                idx = _fieldList.indexOf(QString(colname));
0481                if (idx == -1){ /* not present in list already */
0482                   _fieldList << QString(colname);
0483                   _colName << QString(colname);
0484                   _colRepeat << repeat;
0485                   _colType << typecode;
0486                   _colOffset << 0;
0487                }
0488                _frameCounts[QString(colname)] += nrow;
0489                if (_frameCounts[QString(colname)] > _maxFrameCount)
0490                   _maxFrameCount = _frameCounts[QString(colname)];
0491             } else{ /* is an array of values */
0492                /* TODO: should these be matrices instead (or perhaps as well?)
0493                   I don't understand how matrices should work with KST */
0494                if(fits_read_tdim(_fptr,colnum,maxdim, &naxis, naxes, &status)){
0495                   fprintf(stderr,"Failed to read dimensions of column = %s\n",colname);
0496                   fits_report_error(stderr,status);
0497                   status = 0;
0498                   continue;
0499                }
0500                for (k=0; k < repeat; k++){
0501                   if (naxis == 1)
0502                      tmp.sprintf("%s_%02ld",colname,k%naxes[0]+1);
0503                   else if (naxis == 2)
0504                      tmp.sprintf("%s_%02ld_%02ld",colname,k/naxes[0]+1,k%naxes[0]+1);
0505                   else /* 3-D arrays not supported right now */
0506                      break;
0507                   idx = _fieldList.indexOf(tmp);
0508                   if (idx == -1){ /* not present already */
0509                      _fieldList << tmp;
0510                      _colName << QString(colname);
0511                      _colRepeat << repeat;
0512                      _colType << typecode;
0513                      _colOffset << k;
0514                   }
0515                   _frameCounts[tmp] += nrow;
0516                   if (_frameCounts[QString(colname)] > _maxFrameCount)
0517                      _maxFrameCount = _frameCounts[QString(colname)];
0518                }
0519             }
0520          } else /* end if(validField) */
0521             fprintf(stderr,"Skipping unsupported type for column = %s\n",colname);
0522       } /* end loop over columns */
0523    } /* end loop over HDUs */
0524 
0525    /* set all fields to have _maxFrameCount size */
0526    allcols = _frameCounts.keys();
0527    for (i=0; i < allcols.size(); i++)
0528       _frameCounts[allcols[i]] = _maxFrameCount;
0529    registerChange();
0530    free(naxes);
0531    return true; // false if something went wrong
0532 }
0533 
0534 /* Check if the data in the from the source has updated. Considering how FITS
0535  files are built up we can consider that they are always fixed */
0536 
0537 Kst::Object::UpdateType FitsTableSource::internalDataSourceUpdate() {
0538 
0539    DBG qDebug() << "Entering FitsTableSource::internalDataSourceUpdate()\n";
0540    return Kst::Object::NoChange;
0541 }
0542 
0543 int FitsTableSource::readScalar(double *v, const QString& field){
0544    DBG qDebug() << "Entering FitsTableSource::readScalar() with field: " << field << endl;
0545 
0546    *v = _scalars[field];
0547    return 1;
0548 }
0549 
0550 int FitsTableSource::readString(QString *stringValue, const QString& stringName){
0551    DBG qDebug() << "Entering FitsTableSource::readString() with field: " << stringName << endl;
0552    *stringValue = _strings[stringName];
0553    return 1;
0554 }
0555 
0556 int FitsTableSource::readField(double *v, const QString& field, int s, int n) {
0557    int status = 0; /* cfitsio status flag */
0558    int colnum;     /* column number in table */
0559    int anynul;     /* Number of null values read by fits_read_col() */
0560    int typecode;   /* data type of column in FITS table */
0561    long repeat;    /* used to determine if matrix or vector */
0562    void *data;     /* empty pointer for reading data */
0563    long nelements; /* size of data to read */
0564    long offset;    /* offset for data when repeat > 1 */
0565    long idx;       /* used when reading from Pixel Readout and tracking which
0566                       section of data array is being read by fits_read_col */
0567    long totalidx;  /* to keep track of our location in the v array */
0568    long i,j,k;     /* loop variables */
0569    long nrow;      /* number of rows read for each data chunk */
0570    long maxrow;    /* maximum number of rows to read at once.  Later re-used
0571                       to signify the number of rows to read for the current
0572                       HDU, which may not be tableRow[i], due to the fact
0573                       that the offset, s, may not be zero. */
0574    long currow;    /* current row index (when looping over data chunks */
0575    int hdutype;    /* FITS HDU type */
0576    char *colname;  /* Name for column */
0577    int firstHDU;   /* Flag to control whether we are reading the first HDU
0578                       containing data for a field.  Necessary to keep track of
0579                       s, the offset from the beginning of data */
0580    QByteArray ba;  /* needed to convert a QString to char array */
0581 
0582    DBG qDebug() << "Entering FitsTableSource::readField() with params: " << field << ", from " << s << " for " << n << " frames" << endl;
0583 
0584    /* For INDEX field */
0585    if (field.toLower() == "index") {
0586       if (n < 0) {
0587          v[0] = double(s);
0588          return 1;
0589       }
0590       for (int i = 0; i < n; ++i) {
0591          v[i] = double(s + i);
0592       }
0593       return n;
0594    }
0595 
0596    idx       = _fieldList.indexOf(field); /* get index of field in list */
0597    repeat    = _colRepeat[idx];
0598    typecode  = _colType[idx];   /* FITS type code for field */
0599    ba        = _colName[idx].toLocal8Bit(); /* FITS column name of field */
0600    colname   = ba.data();
0601    offset    = _colOffset[idx]; /* data offset */
0602 
0603    firstHDU = 1;
0604    maxrow = 100000/repeat; /* cap on max rows to read at once */
0605    nelements = maxrow * repeat; /* so we read an integer number of 'repeat' at a time */
0606    if (typecode == TINT)
0607       data = (int *) malloc(nelements*sizeof(int));
0608    else if (typecode == TLONG || typecode == TINT32BIT)
0609       data = (long *) malloc(nelements*sizeof(long));
0610    else if (typecode == TLONGLONG)
0611       data = (long long *) malloc(nelements*sizeof(long long));
0612    else if (typecode == TFLOAT)
0613       data = (float *) malloc(nelements*sizeof(float));
0614    else if (typecode == TDOUBLE)
0615       data = (double *) malloc(nelements*sizeof(double));
0616    else if (typecode == TLOGICAL)
0617       data = (bool *) malloc(nelements*sizeof(bool));
0618 
0619    totalidx = 0;
0620    for (i=0; i < tableHDU.size(); i++){ /* loop over table HDUs */
0621       if (totalidx >= n) /* quit when we have read all data requested */
0622          break;
0623       if (fits_movabs_hdu(_fptr, tableHDU[i], &hdutype, &status)){
0624          fits_report_error(stderr,status);
0625          status = 0;
0626          continue;
0627       } /* failed moving to an HDU, so try to skip and go to next one */
0628       if(fits_get_colnum(_fptr, CASEINSEN, colname, &colnum, &status)){
0629          status = 0; /* column not found in this HDU, so skip. */
0630          continue;
0631       }
0632       if (firstHDU == 1){
0633          maxrow = tableRow[i] - s;
0634          firstHDU = 0;
0635          currow = s + 1; /* FITS starts counting from row 1 */
0636       } else{
0637          maxrow = tableRow[i];
0638          currow = 1; /* FITS starts counting from row 1 */
0639       }
0640 
0641       /* figure out how many chunks we need to read the rows in this HDU */
0642       idx = (maxrow*repeat)%nelements;
0643       if (idx == 0) /* divides evenly */
0644          idx = (maxrow*repeat)/nelements;
0645       else
0646          idx = (maxrow*repeat)/nelements + 1;
0647       for (j=0; j < idx; j++){ /* loop over row chunks */
0648          if (totalidx >= n) /* quit when we have read all data requested */
0649             break;
0650          if (j == (idx - 1)) /* last iteration */
0651             nrow = (maxrow*repeat - j*nelements)/repeat;
0652          else
0653             nrow = nelements/repeat;
0654          /* check to make sure we don't read more data than requested */
0655          if (totalidx + nrow > n)
0656             nrow = n - totalidx;
0657          if (typecode == TINT){
0658             if (fits_read_col(_fptr, typecode, colnum, currow, 1,
0659                nrow*repeat, NULL, &(((int *)data)[0]), &anynul, &status)){
0660                fprintf(stderr,"Failed to read column = %s, filling with NaNs\n",colname);
0661                fits_report_error(stderr,status);
0662                status = 0;
0663                for (k=0; k < nrow; k++)
0664                   v[totalidx+k] = sqrt(-1);
0665             } else {
0666                for (k=0; k < nrow; k++){
0667                   v[totalidx+k] = (double) ((int *)data)[k*repeat+offset];
0668                }
0669             }
0670          } else if (typecode == TLONG || typecode == TINT32BIT){
0671             if (fits_read_col(_fptr, typecode, colnum, currow, 1,
0672                nrow*repeat, NULL, &(((long *)data)[0]), &anynul, &status)){
0673                fprintf(stderr,"Failed to read column = %s, filling with NaNs\n",colname);
0674                fits_report_error(stderr,status);
0675                status = 0;
0676                for (k=0; k < nrow; k++)
0677                   v[totalidx+k] = sqrt(-1);
0678             } else {
0679                for (k=0; k < nrow; k++){
0680                   v[totalidx+k] = (double) ((long *)data)[k*repeat+offset];
0681                }
0682             }
0683          } else if (typecode == TLONGLONG){
0684             if (fits_read_col(_fptr, typecode, colnum, currow, 1,
0685                nrow*repeat, NULL, &(((long long *)data)[0]), &anynul, &status)){
0686                fprintf(stderr,"Failed to read column = %s, filling with NaNs\n",colname);
0687                fits_report_error(stderr,status);
0688                status = 0;
0689                for (k=0; k < nrow; k++)
0690                   v[totalidx+k] = sqrt(-1);
0691             } else {
0692                for (k=0; k < nrow; k++){
0693                   v[totalidx+k] = (double) ((long long *)data)[k*repeat+offset];
0694                }
0695             }
0696          }else if (typecode == TFLOAT){
0697             if (fits_read_col(_fptr, typecode, colnum, currow, 1,
0698                nrow*repeat, NULL, &(((float *)data)[0]), &anynul, &status)){
0699                fprintf(stderr,"Failed to read column = %s, filling with NaNs\n",colname);
0700                fits_report_error(stderr,status);
0701                status = 0;
0702                for (k=0; k < nrow; k++)
0703                   v[totalidx+k] = sqrt(-1);
0704             } else {
0705                for (k=0; k < nrow; k++)
0706                   v[totalidx+k] = (double) ((float *)data)[k*repeat+offset];
0707             }
0708          }else if (typecode == TDOUBLE){
0709             if (fits_read_col(_fptr, typecode, colnum, currow, 1,
0710                nrow*repeat, NULL, &(((double *)data)[0]), &anynul, &status)){
0711                fprintf(stderr,"Failed to read column = %s, filling with NaNs\n",colname);
0712                fits_report_error(stderr,status);
0713                status = 0;
0714                for (k=0; k < nrow; k++)
0715                   v[totalidx+k] = sqrt(-1);
0716             } else {
0717                for (k=0; k < nrow; k++)
0718                   v[totalidx+k] = (double) ((double *)data)[k*repeat+offset];
0719             }
0720          }else if (typecode == TLOGICAL){
0721             if (fits_read_col(_fptr, typecode, colnum, currow, 1,
0722                nrow*repeat, NULL, &(((bool *)data)[0]), &anynul, &status)){
0723                fprintf(stderr,"Failed to read column = %s, filling with NaNs\n",colname);
0724                fits_report_error(stderr,status);
0725                status = 0;
0726                for (k=0; k < nrow; k++)
0727                   v[totalidx+k] = sqrt(-1);
0728             } else {
0729                for (k=0; k < nrow; k++)
0730                   v[totalidx+k] = (double) ((bool *)data)[k*repeat+offset];
0731             }
0732          }
0733          totalidx += nrow;
0734          currow += nrow;
0735       } /* end loop over row chunks */
0736    } /* end loop over HDUs */
0737 
0738    for (i=totalidx; i< n; i++) /* fill remainder with NaNs */
0739       v[i] = sqrt(-1);
0740    free(data);
0741    return n;
0742 }
0743 
0744 int FitsTableSource::readMatrix(double *v, const QString& field){
0745    int status = 0; /* cfitsio status flag */
0746    int colnum;     /* column number in table */
0747    int anynul;     /* Number of null values read by fits_read_col() */
0748    int typecode;   /* data type of column in FITS table */
0749    long width;     /* width code from fits_get_coltype(). not used */
0750    long repeat;    /* used to determine if matrix or vector */
0751    long n;          /* total size */
0752    long nrow;      /* number of rows */
0753    void *data;     /* empty pointer for reading data */
0754 
0755    /* TODO: The code in this function is probably all wrong.  Since fitstable
0756       does not currently implement matrices, this function never gets called */
0757    DBG qDebug() << "Entering FitsTableSource::readMatrix() with field: " << field << endl;
0758    if (fits_get_colnum(_fptr, CASEINSEN, field.toAscii().data(), &colnum, &status))
0759       fits_report_error(stderr,status);
0760    if (fits_get_coltype(_fptr, colnum, &typecode, &repeat, &width, &status))
0761       fits_report_error(stderr,status);
0762    if (fits_get_num_rows(_fptr, &nrow, &status))
0763       fits_report_error(stderr,status);
0764 
0765    n = repeat*nrow;
0766    if (typecode == TLONG || typecode == TINT32BIT)
0767       data = (int *) malloc(n*sizeof(int));
0768    else if (typecode == TLONGLONG)
0769       data = (long long *) malloc(n*sizeof(long long));
0770    else if (typecode == TFLOAT)
0771       data = (float *) malloc(n*sizeof(float));
0772    else if (typecode == TDOUBLE)
0773       data = (double *) malloc(n*sizeof(double));
0774    else if (typecode == TLOGICAL)
0775       data = (bool *) malloc(n*sizeof(bool));
0776 
0777    if (fits_read_col(_fptr, typecode, colnum, 1, 1, n, NULL, data, &anynul, &status))
0778       fits_report_error(stderr,status);
0779    for (int i=0; i < n; i++){
0780       if (typecode == TLONG || typecode == TINT32BIT)
0781          v[i] = (double) ((int *)data)[i];
0782       else if (typecode == TLONGLONG)
0783          v[i] = (double) ((long long *)data)[i];
0784       else if (typecode == TFLOAT)
0785          v[i] = (double) ((float *)data)[i];
0786       else if (typecode == TDOUBLE)
0787          v[i] = (double) ((double *)data)[i];
0788       else if (typecode == TLOGICAL)
0789          v[i] = (double) ((bool *)data)[i];
0790    }
0791 
0792    free(data);
0793    return n;
0794 }
0795 
0796 int FitsTableSource::frameCount(const QString& field) const {
0797 
0798    DBG qDebug() << "Entering FitsTableSource::frameCount() with field: " << field << endl;
0799    if (field.isEmpty() || field.toLower() == "index") {
0800       return _maxFrameCount;
0801    } else {
0802       return _frameCounts[field];
0803    }
0804 }
0805 
0806 QString FitsTableSource::fileType() const {
0807    return "FITS Table Datasource";
0808 }
0809 
0810 
0811 void FitsTableSource::save(QXmlStreamWriter &streamWriter) {
0812    Kst::DataSource::save(streamWriter);
0813 }
0814 
0815 int FitsTableSource::samplesPerFrame(const QString& field) {
0816    return 1;
0817 }
0818 
0819 
0820 // Name used to identify the plugin.  Used when loading the plugin.
0821 QString FitsTableSourcePlugin::pluginName() const { return "FITS Table Datasource Reader"; }
0822 QString FitsTableSourcePlugin::pluginDescription() const { return "FITS Table Datasource Reader"; }
0823 
0824 /**********************
0825 FitsTablesourcePlugin - This class defines the plugin interface to the DataSource
0826 defined by the plugin. The primary requirements of this class are to provide the
0827 necessary connections to create the object which includes providing access to
0828 the configuration widget.
0829 ***********************/
0830 
0831 Kst::DataSource *FitsTableSourcePlugin::create(Kst::ObjectStore *store,
0832                                            QSettings *cfg,
0833                                            const QString &filename,
0834                                            const QString &type,
0835                                            const QDomElement &element) const {
0836 
0837    return new FitsTableSource(store, cfg, filename, type, element);
0838 }
0839 
0840 
0841 // Provides the matrix list that this dataSource can provide from the provided filename.
0842 // This function should use understands to validate the file and then open and calculate the
0843 // list of matrices.
0844 QStringList FitsTableSourcePlugin::matrixList(QSettings *cfg,
0845                                           const QString& filename,
0846                                           const QString& type,
0847                                           QString *typeSuggestion,
0848                                           bool *complete) const {
0849 
0850    DBG qDebug() << "Entering FitsTableSourcePlugin::matrixList()\n";
0851    if (typeSuggestion) {
0852       *typeSuggestion = "FITS Table Datasource";
0853    }
0854    if ((!type.isEmpty() && !provides().contains(type)) || 0 == understands(cfg, filename)) {
0855       if (complete) {
0856          *complete = false;
0857       }
0858       return QStringList();
0859    }
0860    QStringList matrixList;
0861 
0862    return matrixList;
0863 
0864 }
0865 
0866 
0867 /* Provides the scalar list that this dataSource can provide from the provided
0868 filename. This function should use understands to validate the file and then
0869 open and calculate the list of scalars if necessary.
0870 */
0871 QStringList FitsTableSourcePlugin::scalarList(QSettings *cfg,
0872                                           const QString& filename,
0873                                           const QString& type,
0874                                           QString *typeSuggestion,
0875                                           bool *complete) const {
0876 
0877    QStringList scalarList;
0878 
0879    DBG qDebug() << "Entering FitsTableSourcePlugin::scalarList()\n";
0880    if ((!type.isEmpty() && !provides().contains(type)) || 0 == understands(cfg, filename)) {
0881       if (complete) {
0882          *complete = false;
0883       }
0884       return QStringList();
0885    }
0886 
0887    if (typeSuggestion) {
0888       *typeSuggestion = "FITS Table Datasource";
0889    }
0890 
0891    scalarList.append("FRAMES");
0892    return scalarList;
0893 }
0894 
0895 
0896 /* Provides the string list that this dataSource can provide from the provided
0897 filename. This function should use understands to validate the file and then
0898 open and calculate the list of strings if necessary.
0899 */
0900 QStringList FitsTableSourcePlugin::stringList(QSettings *cfg,
0901                                           const QString& filename,
0902                                           const QString& type,
0903                                           QString *typeSuggestion,
0904                                           bool *complete) const {
0905 
0906    QStringList stringList;
0907 
0908    DBG qDebug() << "Entering FitsTableSourcePlugin::stringList()\n";
0909    if ((!type.isEmpty() && !provides().contains(type)) || 0 == understands(cfg, filename)) {
0910       if (complete) {
0911          *complete = false;
0912       }
0913       return QStringList();
0914    }
0915 
0916    if (typeSuggestion) {
0917       *typeSuggestion = "FITS Table Datasource";
0918    }
0919 
0920    stringList.append("FILENAME");
0921    return stringList;
0922 }
0923 
0924 
0925 /* Provides the field list that this dataSource can provide from the provided
0926 filename. This function should use understands to validate the file and then
0927 open and calculate the list of fields if necessary.
0928 */
0929 QStringList FitsTableSourcePlugin::fieldList(QSettings *cfg,
0930                                          const QString& filename,
0931                                          const QString& type,
0932                                          QString *typeSuggestion,
0933                                          bool *complete) const {
0934    Q_UNUSED(cfg)
0935    Q_UNUSED(filename)
0936    Q_UNUSED(type)
0937 
0938    DBG qDebug() << "Entering FitsTableSourcePlugin::fieldList()\n";
0939    if (complete) {
0940       *complete = true;
0941    }
0942 
0943    if (typeSuggestion) {
0944       *typeSuggestion = "FITS Table Datasource";
0945    }
0946 
0947    QStringList fieldList;
0948    return fieldList;
0949 }
0950 
0951 /* The main function used to determine if this plugin knows how to process the
0952 provided file. Each datasource plugin should check the file and return a number
0953 between 0 and 100 based on the likelyhood of the file being this type.  100
0954 should only be returned if there is no way that the file could be any datasource
0955 other than this one.
0956 */
0957 int FitsTableSourcePlugin::understands(QSettings *cfg, const QString& filename) const {
0958    /* This function will attempt to open the file as a FITS file, then loop
0959       through the HDUs to see if any are binary or ascii tables.  If so, and
0960       if any table has > 0 rows, then this is a FITS table that can be read
0961       by this plugin.  A value of 80 is returned, otherwise zero */
0962    Q_UNUSED(cfg)
0963 
0964    fitsfile *ff;
0965    int status = 0;    /* FITS error status */
0966    int i;             /* loop variable */
0967    int ret_val = 0;
0968    int numHDU;        /* total number of HDUs in FITS file */
0969    int hdutype;       /* FITS HDU type */
0970    long nrow;         /* number of rows in table */
0971 
0972    DBG qDebug() << "Entering FitsTableSourcePlugin::understands()\n";
0973    if(fits_open_file(&ff, filename.toAscii(), READONLY, &status)){
0974       fits_close_file(ff,&status);
0975       return 0;
0976    }
0977    if (fits_get_num_hdus(ff, &numHDU, &status)){
0978       fits_close_file(ff,&status);
0979       return 0;
0980    }
0981    for(i=1; i<= numHDU; i++){
0982       if (fits_movabs_hdu(ff, i, &hdutype, &status)){
0983          fits_report_error(stderr,status);
0984          status = 0;
0985          continue;
0986       }
0987       if (hdutype == ASCII_TBL || hdutype == BINARY_TBL){
0988          if(fits_get_num_rows(ff, &nrow, &status)){
0989             fits_report_error(stderr,status);
0990             status = 0;
0991             continue;
0992          }
0993          if (nrow > 0){
0994             ret_val = 80;
0995             break;
0996          }
0997       }
0998    }
0999    status = 0;
1000    fits_close_file(ff,&status);
1001    return ret_val;
1002 }
1003 
1004 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 
1012 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.
1020 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