diff --git a/src/datasources/fitstable/fitstable.cpp b/src/datasources/fitstable/fitstable.cpp new file mode 100644 index 000000000..b6fca27b5 --- /dev/null +++ b/src/datasources/fitstable/fitstable.cpp @@ -0,0 +1,986 @@ +/*************************************************************************** + * * + * copyright : (C) 2014 Northwestern University * + * nchapman@u.northwestern.edu * + * g-novak@northwestern.edu * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "fitstable.h" + +#include +#include + +using namespace Kst; + +/* Scalar interface */ + +class DataInterfaceFitsTableScalar : public DataSource::DataInterface{ + public: + DataInterfaceFitsTableScalar(FitsTableSource& s) : source(s) {} + + // read one element + int read(const QString&, DataScalar::ReadInfo&); + + // named elements + QStringList list() const { return source._scalars.keys(); } + bool isListComplete() const { return true; } + bool isValid(const QString&) const; + + // T specific + const DataScalar::DataInfo dataInfo(const QString&) const { return DataScalar::DataInfo(); } + void setDataInfo(const QString&, const DataScalar::DataInfo&) {} + + // meta data + QMap metaScalars(const QString&) { return QMap(); } + QMap metaStrings(const QString&) { return QMap(); } + + private: + FitsTableSource& source; +}; + + +int DataInterfaceFitsTableScalar::read(const QString& scalar, DataScalar::ReadInfo& p){ + + qDebug() << "Entering DataInterfaceFitsTableScalar::read() with scalar: " << scalar << endl; + return source.readScalar(p.value, scalar); +} + + +bool DataInterfaceFitsTableScalar::isValid(const QString& scalar) const{ + + qDebug() << "Entering DataInterfaceFitsTableScalar::isValid() with scalar: " << scalar << endl; + return source._scalars.contains( scalar ); +} + +/* String interface */ + +class DataInterfaceFitsTableString : public DataSource::DataInterface{ + public: + DataInterfaceFitsTableString(FitsTableSource& s) : source(s) {} + + // read one element + int read(const QString&, DataString::ReadInfo&); + + // named elements + QStringList list() const { return source._strings.keys(); } + bool isListComplete() const { return true; } + bool isValid(const QString&) const; + + // T specific + const DataString::DataInfo dataInfo(const QString&) const { return DataString::DataInfo(); } + void setDataInfo(const QString&, const DataString::DataInfo&) {} + + // meta data + QMap metaScalars(const QString&) { return QMap(); } + QMap metaStrings(const QString&) { return QMap(); } + + private: + FitsTableSource& source; +}; + + +int DataInterfaceFitsTableString::read(const QString& string, DataString::ReadInfo& p){ + + qDebug() << "Entering DataInterfaceFitsTableString::read() with string: " << string << endl; + if (isValid(string) && p.value) { + *p.value = source._strings[string]; + return 1; + } + return 0; +} + + +bool DataInterfaceFitsTableString::isValid(const QString& string) const{ + + qDebug() << "Entering DataInterfaceFitsTableString::isValid() with string: " << string << endl; + return source._strings.contains( string ); +} + +/* Vector interface */ + +class DataInterfaceFitsTableVector : public DataSource::DataInterface{ + public: + DataInterfaceFitsTableVector(FitsTableSource& s) : source(s) {} + + // read one element + int read(const QString&, DataVector::ReadInfo&); + + // named elements + QStringList list() const { return source._fieldList; } + bool isListComplete() const { return true; } + bool isValid(const QString&) const; + + // T specific + const DataVector::DataInfo dataInfo(const QString&) const; + void setDataInfo(const QString&, const DataVector::DataInfo&) {} + + // meta data + QMap metaScalars(const QString&); + QMap metaStrings(const QString&); + + private: + FitsTableSource& source; +}; + + +const DataVector::DataInfo DataInterfaceFitsTableVector::dataInfo(const QString &field) const{ + + qDebug() << "Entering DataInterfaceFitsTableVector::dataInfo() with field: " << field << endl; + if (!source._fieldList.contains(field)) + return DataVector::DataInfo(); + + return DataVector::DataInfo(source.frameCount(field), source.samplesPerFrame(field)); +} + + + +int DataInterfaceFitsTableVector::read(const QString& field, DataVector::ReadInfo& p){ + + qDebug() << "Entering DataInterfaceFitsTableVector::read() with field: " << field << endl; + return source.readField(p.data, field, p.startingFrame, p.numberOfFrames); +} + + +bool DataInterfaceFitsTableVector::isValid(const QString& field) const{ + + qDebug() << "Entering DataInterfaceFitsTableVector::isValid() with field: " << field << endl; + return source._fieldList.contains(field); +} + +QMap DataInterfaceFitsTableVector::metaScalars(const QString& field){ + Q_UNUSED(field); + QMap fieldScalars; + + qDebug() << "Entering DataInterfaceFitsTableVector::metaScalars() with field: " << field << endl; + return fieldScalars; +} + +QMap DataInterfaceFitsTableVector::metaStrings(const QString& field){ + + QMap fieldStrings; + qDebug() << "Entering DataInterfaceFitsTableVector::metaStrings() with field: " << field << endl; + return fieldStrings; +} + +/* Matrix interface */ + +class DataInterfaceFitsTableMatrix : public DataSource::DataInterface{ + public: + + DataInterfaceFitsTableMatrix(FitsTableSource& s) : source(s) {} + + // read one element + int read(const QString&, DataMatrix::ReadInfo&); + + // named elements + QStringList list() const { return source._matrixList; } + bool isListComplete() const { return true; } + bool isValid(const QString&) const; + + // T specific + const DataMatrix::DataInfo dataInfo (const QString&) const; + void setDataInfo(const QString&, const DataMatrix::DataInfo&) {} + + // meta data + QMap metaScalars(const QString&) { return QMap(); } + QMap metaStrings(const QString&) { return QMap(); } + + private: + FitsTableSource& source; +}; + + +const DataMatrix::DataInfo DataInterfaceFitsTableMatrix::dataInfo(const QString& matrix) const{ + + qDebug() << "Entering DataInterfaceFitsTableMatrix::dataInfo() with matrix: " << matrix << endl; + if (!source._matrixList.contains( matrix ) ) { + return DataMatrix::DataInfo(); + } + + DataMatrix::DataInfo info; + info.samplesPerFrame = 1; + info.xSize = 32; + info.ySize = 12; + + return info; +} + + +int DataInterfaceFitsTableMatrix::read(const QString& field, DataMatrix::ReadInfo& p){ + + qDebug() << "Entering DataInterfaceFitsTableMatrix::read() with field: " << field << endl; + int count = source.readMatrix(p.data->z, field); + + p.data->xMin = 0; + p.data->yMin = 0; + p.data->xStepSize = 1; + p.data->yStepSize = 1; + + return count; +} + + +bool DataInterfaceFitsTableMatrix::isValid(const QString& field) const { + + qDebug() << "Entering DataInterfaceFitsTableMatrix::isValid() with field: " << field << endl; + return source._matrixList.contains(field); +} + +/********************** +FitsTableSource - This class defines the main DataSource which derives +from DataSource. The key functions that this class must provide is the ability +to create the source, provide details about the source be able to process the +data. +***********************/ +FitsTableSource::FitsTableSource(Kst::ObjectStore *store, QSettings *cfg, + const QString& filename, const QString& type, const QDomElement& e) + : Kst::DataSource(store, cfg, filename, type), _fptr(0L), _config(0L), + is(new DataInterfaceFitsTableScalar(*this)), + it(new DataInterfaceFitsTableString(*this)), + iv(new DataInterfaceFitsTableVector(*this)), + im(new DataInterfaceFitsTableMatrix(*this)){ + + qDebug() << "Entering FitsTableSource::FitsTableSource() with filename: " << filename << endl; + setInterface(is); + setInterface(it); + setInterface(iv); + setInterface(im); + + setUpdateType(None); + + if (!type.isEmpty() && type != "FITS Table") { + return; + } + + _valid = false; + _maxFrameCount = 0; + + _filename = filename; + + if (init()) { + _valid = true; + } + + registerChange(); +} + + + +FitsTableSource::~FitsTableSource() { + + qDebug() << "Entering FitsTableSource::~FitsTableSource()\n"; + int status = 0; + if (_fptr) { + fits_close_file( _fptr, &status ); + _fptr = 0L; + } +} + + +void FitsTableSource::reset() { + + qDebug() << "Entering FitsTableSource::reset()\n"; + + int status = 0; + + if (_fptr){ + fits_close_file(_fptr, &status); + _fptr = 0L; + } + _maxFrameCount = 0; + _valid = init(); +} + +int FitsTableSource::validField(int typecode){ + /* check to see if typcode is one of the supported ones. If yes, return 1, + else return 0 */ + + int tmp; + + switch(typecode){ + case TINT: + case TLONG: /* also covers TINT32BIT */ + case TFLOAT: + case TDOUBLE: + case TLOGICAL: + tmp = 1; + break; + default: + tmp = 0; + } + return tmp; +} + +// If the datasource has any predefined fields they should be populated here. +bool FitsTableSource::init() { + int status = 0; /* cfitsio status flag */ + int colnum; /* column number in table */ + int ncol; /* number of columns in table */ + long nrow; /* number of rows in table */ + char colname[512]; /* Name for column */ + int typecode; /* data type of column in FITS table */ + long width; /* width codes from fits_get_coltype(). not used */ + int hdutype; /* FITS HDU type */ + long repeat; /* used to determine if matrix or vector */ + long i,j,k; /* loop variables */ + char coltemplate[512]; /* template for reading column names */ + QString tmp; /* format string for bolometer array */ + int numHDU; /* total number of HDUs in FITS file */ + int idx; /* used to get index of elements in lists, so we don't + add the same scalar name, vector name, etc to the + relevant list twice */ + int naxis; /* length of naxes variable. Used for reading TDIM */ + long *naxes; /* array to contain dimensionality of FITS columns */ + int maxdim; /* maximum allowed array size of naxes */ + QStringList allcols; /* list of all column names */ + int nkeys; /* Number of header keywords in current HDU */ + int keylength; /* length of keyword in characters (not used) */ + int keyclass; /* class of FITS header keyword, e.g., TYP_USER_KEY */ + char keyname[10]; /* array to hold FITS keyword name */ + char keyvalue[128]; /* array to hold FITS keyword value */ + char keycomment[128]; /* array to hold FITS keyword comment */ + char fitsrecord[128]; /* array to hold one line of FITS header */ + char keytype; /* FITS key type (string, float, int, etc) */ + + maxdim = 2; + naxes = (long *) malloc(maxdim*sizeof(long)); + + qDebug() << "Entering FitsTableSource::init() with filename: " << _filename.toAscii() << endl; + // First, try to open the file + if(fits_open_file( &_fptr, _filename.toAscii(), READONLY, &status )){ + fits_close_file( _fptr, &status ); + _fptr = 0L; + _valid = false; + return false; + } + _scalars.clear(); + _fieldList.clear(); + _matrixList.clear(); + _strings.clear(); + _colName.clear(); + _colRepeat.clear(); + _colType.clear(); + _colOffset.clear(); + tableHDU.clear(); + tableRow.clear(); + + // Some standard stuff + _fieldList += "INDEX"; + _colName += "INDEX"; + _colRepeat << 1; + _colType << 1; + _colOffset << 0; + + _strings = fileMetas(); + _maxFrameCount = 0; + + /* get total number of HDUs */ + if (fits_get_num_hdus(_fptr, &numHDU, &status)){ + fits_report_error(stderr,status); + _fptr = 0L; + _valid = false; + return false; + } /* can't read number of HDUs, so quit */ + + nrow = 0; + for (i=1; i<= numHDU; i++){ /* loop over all HDUs */ + if (fits_movabs_hdu(_fptr, i, &hdutype, &status)){ + fits_report_error(stderr,status); + status = 0; + continue; + } /* failed moving to an HDU, so try to skip and go to next one */ + /* read header to assign string and scalar values. For now only + read USER_KEYs. May want to add UNIT_KEYs eventually, to get the + units for every vector */ + if(fits_get_hdrspace(_fptr, &nkeys, NULL, &status)){ /* total # of keys */ + fits_report_error(stderr,status); + status = 0; + continue; + } + for (j=1; j<= nkeys; j++){ /* loop over keys */ + if(fits_read_record(_fptr, j, fitsrecord, &status)){ + fprintf(stderr,"Failed to read record number %ld\n",j); + fits_report_error(stderr,status); + status = 0; + continue; + } /* failed to read a record, so skip and continue */ + if (fits_get_keyname(fitsrecord, keyname, &keylength, &status)){ + fprintf(stderr,"Failed to read keyword from record = %s\n",fitsrecord); + fits_report_error(stderr,status); + status = 0; + continue; + } /* failed to read keyword name, so skip and continue */ + if (fits_parse_value(fitsrecord, keyvalue, keycomment, &status)){ + fprintf(stderr,"Failed to read value and comment from key = %s\n",keyname); + fits_report_error(stderr,status); + status = 0; + continue; + } /* failed to read keyword value or comment, so skip and continue */ + if (fits_get_keytype(keyvalue, &keytype, &status)){ + fprintf(stderr,"Failed to read type (string, int, float) for key = %s, value = %s\n",keyname,keyvalue); + fits_report_error(stderr,status); + status = 0; + continue; + } /* failed to read keytype, so skip and continue */ + keyclass = fits_get_keyclass(fitsrecord); + if (keyclass == TYP_USER_KEY){ + if (keytype == 'C' || keytype == 'L'){ /* string or logical */ + _strings[QString(keyname)] = QString(keyvalue); + } else if (keytype == 'I' || keytype == 'F'){ /* int or float */ + _scalars[QString(keyname)] = atof(keyvalue); + } + } + } + + /* check if table HDU, and skip if not */ + if (hdutype == ASCII_TBL || hdutype == BINARY_TBL){ + if(fits_get_num_cols(_fptr, &ncol, &status)){ /* read # of columns */ + fprintf(stderr,"Failed to read # of columns in HDU = %ld\n",i); + fits_report_error(stderr,status); + status = 0; + continue; + } /* failed to read number of columns, so skip and continue */ + if(fits_get_num_rows(_fptr, &nrow, &status)){ /* read # of rows */ + fprintf(stderr,"Failed to read # of rows in HDU = %ld\n",i); + fits_report_error(stderr,status); + status = 0; + continue; + } /* failed to read number of rows, so skip and continue */ + tableRow << nrow; + tableHDU << i; + } else + continue; /* skip non-table HDUs for reading table columns */ + for (j=1; j<= ncol; j++){ + sprintf(coltemplate,"%ld",j); + if(fits_get_colname(_fptr, CASEINSEN, coltemplate, colname, &colnum, &status)){ + fprintf(stderr,"Failed to read column name for column = %ld\n",j); + fits_report_error(stderr,status); + status = 0; + continue; + } /* failed to read column name, so skip and continue */ + if(fits_get_coltype(_fptr, colnum, &typecode,&repeat,&width, &status)){ + fprintf(stderr,"Failed to read column type for column = %s\n",colname); + fits_report_error(stderr,status); + status = 0; + continue; + } /* failed to read column type, so skip and continue */ + if (validField(typecode)){ + if (repeat == 1){ /* not an array of values */ + idx = _fieldList.indexOf(QString(colname)); + if (idx == -1){ /* not present in list already */ + _fieldList << QString(colname); + _colName << QString(colname); + _colRepeat << repeat; + _colType << typecode; + _colOffset << 0; + } + _frameCounts[QString(colname)] += nrow; + if (_frameCounts[QString(colname)] > _maxFrameCount) + _maxFrameCount = _frameCounts[QString(colname)]; + } else{ /* is an array of values */ + /* TODO: should these be matrices instead (or perhaps as well?) + I don't understand how matrices should work with KST */ + if(fits_read_tdim(_fptr,colnum,maxdim, &naxis, naxes, &status)){ + fprintf(stderr,"Failed to read dimensions of column = %s\n",colname); + fits_report_error(stderr,status); + status = 0; + continue; + } + for (k=0; k < repeat; k++){ + if (naxis == 1) + tmp.sprintf("%s_%02ld",colname,k%naxes[0]+1); + else if (naxis == 2) + tmp.sprintf("%s_%02ld_%02ld",colname,k/naxes[0]+1,k%naxes[0]+1); + else /* 3-D arrays not supported right now */ + break; + idx = _fieldList.indexOf(tmp); + if (idx == -1){ /* not present already */ + _fieldList << tmp; + _colName << QString(colname); + _colRepeat << repeat; + _colType << typecode; + _colOffset << k; + } + _frameCounts[tmp] += nrow; + if (_frameCounts[QString(colname)] > _maxFrameCount) + _maxFrameCount = _frameCounts[QString(colname)]; + } + } + } else /* end if(validField) */ + fprintf(stderr,"Skipping unsupported type for column = %s\n",colname); + } /* end loop over columns */ + } /* end loop over HDUs */ + + /* set all fields to have _maxFrameCount size */ + allcols = _frameCounts.keys(); + for (i=0; i < allcols.size(); i++) + _frameCounts[allcols[i]] = _maxFrameCount; + registerChange(); + free(naxes); + return true; // false if something went wrong +} + +/* Check if the data in the from the source has updated. Considering how FITS + files are built up we can consider that they are always fixed */ + +Kst::Object::UpdateType FitsTableSource::internalDataSourceUpdate() { + + qDebug() << "Entering FitsTableSource::internalDataSourceUpdate()\n"; + return Kst::Object::NoChange; +} + +int FitsTableSource::readScalar(double *v, const QString& field){ + qDebug() << "Entering FitsTableSource::readScalar() with field: " << field << endl; + + *v = _scalars[field]; + return 1; +} + +int FitsTableSource::readString(QString *stringValue, const QString& stringName){ + qDebug() << "Entering FitsTableSource::readString() with field: " << stringName << endl; + *stringValue = _strings[stringName]; + return 1; +} + +int FitsTableSource::readField(double *v, const QString& field, int s, int n) { + int status = 0; /* cfitsio status flag */ + int colnum; /* column number in table */ + int anynul; /* Number of null values read by fits_read_col() */ + int typecode; /* data type of column in FITS table */ + long repeat; /* used to determine if matrix or vector */ + void *data; /* empty pointer for reading data */ + long nelements; /* size of data to read */ + int offset; /* offset for data when repeat > 1 */ + long idx; /* used when reading from Pixel Readout and tracking which + section of data array is being read by fits_read_col */ + long totalidx; /* to keep track of our location in the v array */ + int i,j,k; /* loop variables */ + long nrow; /* number of rows read for each data chunk */ + long maxrow; /* maximum number of rows to read at once */ + long currow; /* current row index (when looping over data chunks */ + int hdutype; /* FITS HDU type */ + char *colname; /* Name for column */ + QByteArray ba; /* needed to convert a QString to char array */ + + qDebug() << "Entering FitsTableSource::readField() with params: " << field << ", from " << s << " for " << n << " frames" << endl; + + /* For INDEX field */ + if (field.toLower() == "index") { + if (n < 0) { + v[0] = double(s); + return 1; + } + for (int i = 0; i < n; ++i) { + v[i] = double(s + i); + } + return n; + } + + idx = _fieldList.indexOf(field); /* get index of field in list */ + repeat = _colRepeat[idx]; + typecode = _colType[idx]; /* FITS type code for field */ + ba = _colName[idx].toLocal8Bit(); /* FITS column name of field */ + colname = ba.data(); + offset = _colOffset[idx]; /* data offset */ + + maxrow = 100000/repeat; /* cap on max rows to read at once */ + nelements = maxrow * repeat; + if (typecode == TINT) + data = (int *) malloc(nelements*sizeof(int)); + else if (typecode == TLONG || typecode == TINT32BIT) + data = (long *) malloc(nelements*sizeof(long)); + else if (typecode == TFLOAT) + data = (float *) malloc(nelements*sizeof(float)); + else if (typecode == TDOUBLE) + data = (double *) malloc(nelements*sizeof(double)); + else if (typecode == TLOGICAL) + data = (bool *) malloc(nelements*sizeof(bool)); + + totalidx = 0; + for (i=0; i < tableHDU.size(); i++){ + if (fits_movabs_hdu(_fptr, tableHDU[i], &hdutype, &status)){ + fits_report_error(stderr,status); + status = 0; + continue; + } /* failed moving to an HDU, so try to skip and go to next one */ + if(fits_get_colnum(_fptr, CASEINSEN, colname, &colnum, &status)){ + status = 0; /* column not found in this HDU, so skip. */ + continue; + } + /* figure out how many chunks we need to read the rows in this HDU */ + idx = (tableRow[i]*repeat)%nelements; + if (idx == 0) /* divides evenly */ + idx = (tableRow[i]*repeat)/nelements; + else + idx = (tableRow[i]*repeat)/nelements + 1; + currow = 1; /* FITS starts counting from row 1 */ + for (j=0; j < idx; j++){ /* loop over row chunks */ + if (j == (idx - 1)) /* last iteration */ + nelements = tableRow[i]*repeat - j*nelements; + nrow = nelements/repeat; + if (typecode == TINT){ + if (fits_read_col(_fptr, typecode, colnum, currow +s, 1, + nelements, NULL, &(((int *)data)[0]), &anynul, &status)){ + fprintf(stderr,"Failed to read column = %s, filling with NaNs\n",colname); + fits_report_error(stderr,status); + status = 0; + for (k=0; k < nrow; k++) + v[totalidx+k] = sqrt(-1); + } else { + for (k=0; k < nrow; k++){ + v[totalidx+k] = (double) ((int *)data)[k*repeat+offset]; + } + } + } else if (typecode == TLONG || typecode == TINT32BIT){ + if (fits_read_col(_fptr, typecode, colnum, currow +s, 1, + nelements, NULL, &(((long *)data)[0]), &anynul, &status)){ + fprintf(stderr,"Failed to read column = %s, filling with NaNs\n",colname); + fits_report_error(stderr,status); + status = 0; + for (k=0; k < nrow; k++) + v[totalidx+k] = sqrt(-1); + } else { + for (k=0; k < nrow; k++){ + v[totalidx+k] = (double) ((long *)data)[k*repeat+offset]; + } + } + }else if (typecode == TFLOAT){ + if (fits_read_col(_fptr, typecode, colnum, currow+s, 1, + nelements, NULL, &(((float *)data)[0]), &anynul, &status)){ + fprintf(stderr,"Failed to read column = %s, filling with NaNs\n",colname); + fits_report_error(stderr,status); + status = 0; + for (k=0; k < nrow; k++) + v[totalidx+k] = sqrt(-1); + } else { + for (k=0; k < nrow; k++) + v[totalidx+k] = (double) ((float *)data)[k*repeat+offset]; + } + }else if (typecode == TDOUBLE){ + if (fits_read_col(_fptr, typecode, colnum, currow+s, 1, + nelements, NULL, &(((double *)data)[0]), &anynul, &status)){ + fprintf(stderr,"Failed to read column = %s, filling with NaNs\n",colname); + fits_report_error(stderr,status); + status = 0; + for (k=0; k < nrow; k++) + v[totalidx+k] = sqrt(-1); + } else { + for (k=0; k < nrow; k++) + v[totalidx+k] = (double) ((double *)data)[k*repeat+offset]; + } + }else if (typecode == TLOGICAL){ + if (fits_read_col(_fptr, typecode, colnum, currow+s, 1, + nelements, NULL, &(((bool *)data)[0]), &anynul, &status)){ + fprintf(stderr,"Failed to read column = %s, filling with NaNs\n",colname); + fits_report_error(stderr,status); + status = 0; + for (k=0; k < nrow; k++) + v[totalidx+k] = sqrt(-1); + } else { + for (k=0; k < nrow; k++) + v[totalidx+k] = (double) ((bool *)data)[k*repeat+offset]; + } + } + totalidx += nrow; + currow += nrow; + } /* end loop over row chunks */ + } /* end loop over HDUs */ + + for (i=totalidx; i< _maxFrameCount; i++) /* fill remainder with NaNs */ + v[i] = sqrt(-1); + free(data); + return n; +} + +int FitsTableSource::readMatrix(double *v, const QString& field){ + int status = 0; /* cfitsio status flag */ + int colnum; /* column number in table */ + int anynul; /* Number of null values read by fits_read_col() */ + int typecode; /* data type of column in FITS table */ + long width; /* width code from fits_get_coltype(). not used */ + long repeat; /* used to determine if matrix or vector */ + long n; /* total size */ + long nrow; /* number of rows */ + void *data; /* empty pointer for reading data */ + + /* TODO: The code in this function is probably all wrong. Since fitstable + does not currently implement matrices, this function never gets called */ + qDebug() << "Entering FitsTableSource::readMatrix() with field: " << field << endl; + if (fits_get_colnum(_fptr, CASEINSEN, field.toAscii().data(), &colnum, &status)) + fits_report_error(stderr,status); + if (fits_get_coltype(_fptr, colnum, &typecode, &repeat, &width, &status)) + fits_report_error(stderr,status); + if (fits_get_num_rows(_fptr, &nrow, &status)) + fits_report_error(stderr,status); + + n = repeat*nrow; + if (typecode == TLONG || typecode == TINT32BIT) + data = (int *) malloc(n*sizeof(int)); + else if (typecode == TFLOAT) + data = (float *) malloc(n*sizeof(float)); + else if (typecode == TDOUBLE) + data = (double *) malloc(n*sizeof(double)); + else if (typecode == TLOGICAL) + data = (bool *) malloc(n*sizeof(bool)); + + if (fits_read_col(_fptr, typecode, colnum, 1, 1, n, NULL, data, &anynul, &status)) + fits_report_error(stderr,status); + for (int i=0; i < n; i++){ + if (typecode == TLONG || typecode == TINT32BIT) + v[i] = (double) ((int *)data)[i]; + else if (typecode == TFLOAT) + v[i] = (double) ((float *)data)[i]; + else if (typecode == TDOUBLE) + v[i] = (double) ((double *)data)[i]; + else if (typecode == TLOGICAL) + v[i] = (double) ((bool *)data)[i]; + } + + free(data); + return n; +} + +int FitsTableSource::frameCount(const QString& field) const { + + qDebug() << "Entering FitsTableSource::frameCount() with field: " << field << endl; + if (field.isEmpty() || field.toLower() == "index") { + return _maxFrameCount; + } else { + return _frameCounts[field]; + } +} + +QString FitsTableSource::fileType() const { + return "FITS Table Datasource"; +} + + +void FitsTableSource::save(QXmlStreamWriter &streamWriter) { + Kst::DataSource::save(streamWriter); +} + +int FitsTableSource::samplesPerFrame(const QString& field) { + return 1; +} + + +// Name used to identify the plugin. Used when loading the plugin. +QString FitsTableSourcePlugin::pluginName() const { return "FITS Table Datasource Reader"; } +QString FitsTableSourcePlugin::pluginDescription() const { return "FITS Table Datasource Reader"; } + +/********************** +FitsTablesourcePlugin - This class defines the plugin interface to the DataSource +defined by the plugin. The primary requirements of this class are to provide the +necessary connections to create the object which includes providing access to +the configuration widget. +***********************/ + +Kst::DataSource *FitsTableSourcePlugin::create(Kst::ObjectStore *store, + QSettings *cfg, + const QString &filename, + const QString &type, + const QDomElement &element) const { + + return new FitsTableSource(store, cfg, filename, type, element); +} + + +// Provides the matrix list that this dataSource can provide from the provided filename. +// This function should use understands to validate the file and then open and calculate the +// list of matrices. +QStringList FitsTableSourcePlugin::matrixList(QSettings *cfg, + const QString& filename, + const QString& type, + QString *typeSuggestion, + bool *complete) const { + + qDebug() << "Entering FitsTableSourcePlugin::matrixList()\n"; + if (typeSuggestion) { + *typeSuggestion = "FITS Table Datasource"; + } + if ((!type.isEmpty() && !provides().contains(type)) || 0 == understands(cfg, filename)) { + if (complete) { + *complete = false; + } + return QStringList(); + } + QStringList matrixList; + + return matrixList; + +} + + +/* Provides the scalar list that this dataSource can provide from the provided +filename. This function should use understands to validate the file and then +open and calculate the list of scalars if necessary. +*/ +QStringList FitsTableSourcePlugin::scalarList(QSettings *cfg, + const QString& filename, + const QString& type, + QString *typeSuggestion, + bool *complete) const { + + QStringList scalarList; + + qDebug() << "Entering FitsTableSourcePlugin::scalarList()\n"; + if ((!type.isEmpty() && !provides().contains(type)) || 0 == understands(cfg, filename)) { + if (complete) { + *complete = false; + } + return QStringList(); + } + + if (typeSuggestion) { + *typeSuggestion = "FITS Table Datasource"; + } + + scalarList.append("FRAMES"); + return scalarList; +} + + +/* Provides the string list that this dataSource can provide from the provided +filename. This function should use understands to validate the file and then +open and calculate the list of strings if necessary. +*/ +QStringList FitsTableSourcePlugin::stringList(QSettings *cfg, + const QString& filename, + const QString& type, + QString *typeSuggestion, + bool *complete) const { + + QStringList stringList; + + qDebug() << "Entering FitsTableSourcePlugin::stringList()\n"; + if ((!type.isEmpty() && !provides().contains(type)) || 0 == understands(cfg, filename)) { + if (complete) { + *complete = false; + } + return QStringList(); + } + + if (typeSuggestion) { + *typeSuggestion = "FITS Table Datasource"; + } + + stringList.append("FILENAME"); + return stringList; +} + + +/* Provides the field list that this dataSource can provide from the provided +filename. This function should use understands to validate the file and then +open and calculate the list of fields if necessary. +*/ +QStringList FitsTableSourcePlugin::fieldList(QSettings *cfg, + const QString& filename, + const QString& type, + QString *typeSuggestion, + bool *complete) const { + Q_UNUSED(cfg) + Q_UNUSED(filename) + Q_UNUSED(type) + + qDebug() << "Entering FitsTableSourcePlugin::fieldList()\n"; + if (complete) { + *complete = true; + } + + if (typeSuggestion) { + *typeSuggestion = "FITS Table Datasource"; + } + + QStringList fieldList; + return fieldList; +} + +/* The main function used to determine if this plugin knows how to process the +provided file. Each datasource plugin should check the file and return a number +between 0 and 100 based on the likelyhood of the file being this type. 100 +should only be returned if there is no way that the file could be any datasource +other than this one. +*/ +int FitsTableSourcePlugin::understands(QSettings *cfg, const QString& filename) const { + /* This function will attempt to open the file as a FITS file, then loop + through the HDUs to see if any are binary or ascii tables. If so, and + if any table has > 0 rows, then this is a FITS table that can be read + by this plugin. A value of 80 is returned, otherwise zero */ + Q_UNUSED(cfg) + + fitsfile *ff; + int status = 0; /* FITS error status */ + int i; /* loop variable */ + int ret_val = 0; + int numHDU; /* total number of HDUs in FITS file */ + int hdutype; /* FITS HDU type */ + long nrow; /* number of rows in table */ + + qDebug() << "Entering FitsTableSourcePlugin::understands()\n"; + if(fits_open_file(&ff, filename.toAscii(), READONLY, &status)){ + fits_close_file(ff,&status); + return 0; + } + if (fits_get_num_hdus(ff, &numHDU, &status)){ + fits_close_file(ff,&status); + return 0; + } + for(i=1; i<= numHDU; i++){ + if (fits_movabs_hdu(ff, i, &hdutype, &status)){ + fits_report_error(stderr,status); + status = 0; + continue; + } + if (hdutype == ASCII_TBL || hdutype == BINARY_TBL){ + if(fits_get_num_rows(ff, &nrow, &status)){ + fits_report_error(stderr,status); + status = 0; + continue; + } + if (nrow > 0){ + ret_val = 80; + break; + } + } + } + status = 0; + fits_close_file(ff,&status); + return ret_val; +} + +bool FitsTableSourcePlugin::supportsTime(QSettings *cfg, const QString& filename) const { + //FIXME + Q_UNUSED(cfg) + Q_UNUSED(filename) + return false; +} + + +QStringList FitsTableSourcePlugin::provides() const { + QStringList rc; + rc += "FITS Table Datasource"; + return rc; +} + + +// Request for this plugins configuration widget. +Kst::DataSourceConfigWidget *FitsTableSourcePlugin::configWidget(QSettings *cfg, + const QString& filename) const { + + Q_UNUSED(cfg) + Q_UNUSED(filename) + return 0; +} + + +#ifndef QT5 +Q_EXPORT_PLUGIN2(kstdata_fitstable, FitsTableSourcePlugin) +#endif + +// vim: ts=2 sw=2 et diff --git a/src/datasources/fitstable/fitstable.h b/src/datasources/fitstable/fitstable.h new file mode 100644 index 000000000..ad1a27202 --- /dev/null +++ b/src/datasources/fitstable/fitstable.h @@ -0,0 +1,146 @@ +/*************************************************************************** + * * + * copyright : (C) 2014 Northwestern University * + * nchapman@u.northwestern.edu * + * g-novak@northwestern.edu * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + + +#ifndef FITSTABLE_H +#define FITSTABLE_H + +#include +#include + +#include + +class DataInterfaceFitsTableScalar; +class DataInterfaceFitsTableString; +class DataInterfaceFitsTableVector; +class DataInterfaceFitsTableMatrix; + +class FitsTableSource : public Kst::DataSource { + Q_OBJECT + + public: + FitsTableSource(Kst::ObjectStore *store, QSettings *cfg, const QString& filename, const QString& type, const QDomElement& e); + + ~FitsTableSource(); + + bool init(); + virtual void reset(); + + Kst::Object::UpdateType internalDataSourceUpdate(); + + int readScalar(double *v, const QString& field); + + int readString(QString *stringValue, const QString& stringName); + + int readField(double *v, const QString& field, int s, int n); + + int readMatrix(double *v, const QString& field); + + int samplesPerFrame(const QString& field); + int frameCount(const QString& field = QString()) const; + + int validField(int typecode); + + QString fileType() const; + + void save(QXmlStreamWriter &streamWriter); + + class Config; + + + private: + QMap _frameCounts; + int _maxFrameCount; + + // FITS file object + fitsfile *_fptr; + mutable Config *_config; + + // Primitive lists + QMap _strings; /* dictionary-like container of strings */ + QMap _scalars; /* dictionary-like container of scalars */ + QStringList _fieldList; /* list of vector names */ + QStringList _matrixList; /* list of matrix names (not implemented) */ + + QStringList _colName; /* Name in FITS file of each item in _fieldList */ + QList _colRepeat; /* FITS repeat count of each item in _fieldList */ + QList _colType; /* FITS typecode for each item in _fieldList */ + QList _colOffset; /* Offset of data, useful when repeat > 1 */ + QList tableHDU; /* Array of HDU numbers with FITS tables */ + QList tableRow; /* number of rows for each HDU in tableHDU */ + + friend class DataInterfaceFitsTableScalar; + friend class DataInterfaceFitsTableString; + friend class DataInterfaceFitsTableVector; + friend class DataInterfaceFitsTableMatrix; + DataInterfaceFitsTableScalar* is; + DataInterfaceFitsTableString* it; + DataInterfaceFitsTableVector* iv; + DataInterfaceFitsTableMatrix* im; +}; + + +class FitsTableSourcePlugin : public QObject, public Kst::DataSourcePluginInterface { + Q_OBJECT + Q_INTERFACES(Kst::DataSourcePluginInterface) + Q_PLUGIN_METADATA(IID "com.kst.DataSourcePluginInterface/2.0") + public: + virtual ~FitsTableSourcePlugin() {} + + virtual QString pluginName() const; + virtual QString pluginDescription() const; + + virtual bool hasConfigWidget() const { return false; } + + virtual Kst::DataSource *create(Kst::ObjectStore *store, + QSettings *cfg, + const QString &filename, + const QString &type, + const QDomElement &element) const; + + virtual QStringList matrixList(QSettings *cfg, + const QString& filename, + const QString& type, + QString *typeSuggestion, + bool *complete) const; + + virtual QStringList fieldList(QSettings *cfg, + const QString& filename, + const QString& type, + QString *typeSuggestion, + bool *complete) const; + + virtual QStringList scalarList(QSettings *cfg, + const QString& filename, + const QString& type, + QString *typeSuggestion, + bool *complete) const; + + virtual QStringList stringList(QSettings *cfg, + const QString& filename, + const QString& type, + QString *typeSuggestion, + bool *complete) const; + + virtual int understands(QSettings *cfg, const QString& filename) const; + + virtual bool supportsTime(QSettings *cfg, const QString& filename) const; + + virtual QStringList provides() const; + + virtual Kst::DataSourceConfigWidget *configWidget(QSettings *cfg, const QString& filename) const; +}; + + +#endif +// vim: ts=2 sw=2 et diff --git a/src/datasources/fitstable/fitstable.pro b/src/datasources/fitstable/fitstable.pro new file mode 100644 index 000000000..5524742ee --- /dev/null +++ b/src/datasources/fitstable/fitstable.pro @@ -0,0 +1,12 @@ +TOPOUT_REL=../../.. +include($$PWD/$$TOPOUT_REL/kst.pri) +include($$PWD/../../../datasourceplugin.pri) + +TARGET = $$kstlib(kst2data_fitstable) +INCLUDEPATH += $$OUTPUT_DIR/src/datasources/fitstable/tmp + +SOURCES += \ + fitstable.cpp + +HEADERS += \ + fitstable.h diff --git a/src/datasources/fitstable/kstdata_fitstable.desktop b/src/datasources/fitstable/kstdata_fitstable.desktop new file mode 100644 index 000000000..a53f4e989 --- /dev/null +++ b/src/datasources/fitstable/kstdata_fitstable.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Kst Data Source +X-KDE-ModuleType=Plugin +X-KDE-Library=fitstable +X-Kst-Plugin-Author=Nicholas Chapman +Name=FITS Table reader +Comment=FITS table reader plugin for Kst