diff --git a/include/GFitsTableDblCol.hpp b/include/GFitsTableDblCol.hpp index eca2f66..f1f6e1d 100644 --- a/include/GFitsTableDblCol.hpp +++ b/include/GFitsTableDblCol.hpp @@ -73,10 +73,10 @@ private: std::string binary_format(void) const; // Private data area - int m_size; //!< Size of allocated data area (0 if offline) - double* m_data; //!< Data area - double* m_nulval; //!< NULL value - int m_anynul; //!< Number of NULLs encountered + int m_size; //!< Size of allocated data area (0 if not loaded) + double* m_data; //!< Data area + double* m_nulval; //!< NULL value + int m_anynul; //!< Number of NULLs encountered }; #endif /* GFITSTABLEDBLCOL_HPP */ diff --git a/include/GFitsTableFltCol.hpp b/include/GFitsTableFltCol.hpp index 2f605c0..4f52367 100644 --- a/include/GFitsTableFltCol.hpp +++ b/include/GFitsTableFltCol.hpp @@ -38,6 +38,9 @@ ***************************************************************************/ class GFitsTableFltCol : public GFitsTableCol { + // I/O friends + friend ostream& operator<< (ostream& os, const GFitsTableFltCol& column); + public: // Constructors and destructors GFitsTableFltCol(); @@ -48,6 +51,8 @@ public: // Operators GFitsTableFltCol& operator= (const GFitsTableFltCol& column); + float& operator() (const int& row, const int& inx = 0); + const float& operator() (const int& row, const int& inx = 0) const; // Methods void save(void); @@ -63,20 +68,15 @@ private: void init_members(void); void copy_members(const GFitsTableFltCol& column); void free_members(void); - void load(void); + void fetch_data(void); std::string ascii_format(void) const; std::string binary_format(void) const; // Private data area - int m_size; //!< Size of data area - float* m_data; //!< Data area - float* m_nulval; //!< NULL value - int m_anynul; //!< Number of NULLs encountered + int m_size; //!< Size of allocated data area (0 if not loaded) + float* m_data; //!< Data area + float* m_nulval; //!< NULL value + int m_anynul; //!< Number of NULLs encountered }; - -/*************************************************************************** - * Inline methods * - ***************************************************************************/ - #endif /* GFITSTABLEFLTCOL_HPP */ diff --git a/src/fits/GFitsTableDblCol.cpp b/src/fits/GFitsTableDblCol.cpp index 054f968..92589a6 100644 --- a/src/fits/GFitsTableDblCol.cpp +++ b/src/fits/GFitsTableDblCol.cpp @@ -78,8 +78,8 @@ GFitsTableDblCol::GFitsTableDblCol(const std::string& name, * * @param[in] column Column from which class instance should be built. ***************************************************************************/ -GFitsTableDblCol::GFitsTableDblCol(const GFitsTableDblCol& column) : - GFitsTableCol(column) +GFitsTableDblCol::GFitsTableDblCol(const GFitsTableDblCol& column) + : GFitsTableCol(column) { // Initialise class members for clean destruction init_members(); @@ -159,7 +159,7 @@ double& GFitsTableDblCol::operator() (const int& row, const int& inx) if (m_data == NULL) fetch_data(); // Calculate pixel offset - int offset = row * m_repeat + inx; + int offset = row * m_number + inx; // Return image pixel return m_data[offset]; @@ -180,13 +180,13 @@ double& GFitsTableDblCol::operator() (const int& row, const int& inx) * if range checking is required. ***************************************************************************/ const double& GFitsTableDblCol::operator() (const int& row, const int& inx) - const + const { // If data are not available then load them now if (m_data == NULL) ((GFitsTableDblCol*)this)->fetch_data(); // Calculate pixel offset - int offset = row * m_repeat + inx; + int offset = row * m_number + inx; // Return image pixel return m_data[offset]; @@ -262,7 +262,7 @@ std::string GFitsTableDblCol::string(const int& row, const int& inx) throw GException::out_of_range(G_STRING, inx, 0, m_number-1); // Get index - int offset = row * m_repeat + inx; + int offset = row * m_number + inx; // Convert double into string ostringstream s_value; @@ -298,7 +298,7 @@ double GFitsTableDblCol::real(const int& row, const int& inx) throw GException::out_of_range(G_REAL, inx, 0, m_number-1); // Get index - int offset = row * m_repeat + inx; + int offset = row * m_number + inx; // Return value return m_data[offset]; @@ -330,7 +330,7 @@ int GFitsTableDblCol::integer(const int& row, const int& inx) throw GException::out_of_range(G_INTEGER, inx, 0, m_number-1); // Get index - int offset = row * m_repeat + inx; + int offset = row * m_number + inx; // Convert double into int int value = (int)m_data[offset]; diff --git a/src/fits/GFitsTableFltCol.cpp b/src/fits/GFitsTableFltCol.cpp index ad37fdc..ee2860e 100644 --- a/src/fits/GFitsTableFltCol.cpp +++ b/src/fits/GFitsTableFltCol.cpp @@ -25,12 +25,7 @@ #define G_STRING "GFitsTableFltCol::string(const int&, const int&)" #define G_REAL "GFitsTableFltCol::real(const int&, const int&)" #define G_INTEGER "GFitsTableFltCol::integer(const int&, const int&)" -#define G_PTR_FLOAT "GFitsTableFltCol::ptr_float()" -#define G_PTR_DOUBLE "GFitsTableFltCol::ptr_double()" -#define G_PTR_SHORT "GFitsTableFltCol::ptr_short()" -#define G_PTR_LONG "GFitsTableFltCol::ptr_long()" -#define G_PTR_INT "GFitsTableFltCol::ptr_int()" -#define G_LOAD "GFitsTableFltCol::load()" +#define G_FETCH_DATA "GFitsTableFltCol::fetch_data()" /* __ Macros _____________________________________________________________ */ @@ -83,8 +78,8 @@ GFitsTableFltCol::GFitsTableFltCol(const std::string& name, * * @param[in] column Column from which class instance should be built. ***************************************************************************/ -GFitsTableFltCol::GFitsTableFltCol(const GFitsTableFltCol& column) : - GFitsTableCol(column) +GFitsTableFltCol::GFitsTableFltCol(const GFitsTableFltCol& column) + : GFitsTableCol(column) { // Initialise class members for clean destruction init_members(); @@ -145,6 +140,59 @@ GFitsTableFltCol& GFitsTableFltCol::operator= (const GFitsTableFltCol& column) } +/***********************************************************************//** + * @brief Column data access operator + * + * @param[in] row Row of column to access. + * @param[in] inx Vector index in column row to access + * + * Provides access to data in a column. No range checking is performed. + * Use one of + * GFitsTableFltCol::string(ix,iy), + * GFitsTableFltCol::real(ix;iy) or + * GFitsTableFltCol::integer(ix;iy) + * if range checking is required. + ***************************************************************************/ +float& GFitsTableFltCol::operator() (const int& row, const int& inx) +{ + // If data are not available then load them now + if (m_data == NULL) fetch_data(); + + // Calculate pixel offset + int offset = row * m_number + inx; + + // Return image pixel + return m_data[offset]; +} + + +/***********************************************************************//** + * @brief Column data access operator (const variant) + * + * @param[in] row Row of column to access. + * @param[in] inx Vector index in column row to access + * + * Provides access to data in a column. No range checking is performed. + * Use one of + * GFitsTableFltCol::string(ix,iy), + * GFitsTableFltCol::real(ix;iy) or + * GFitsTableFltCol::integer(ix;iy) + * if range checking is required. + ***************************************************************************/ +const float& GFitsTableFltCol::operator() (const int& row, const int& inx) + const +{ + // If data are not available then load them now + if (m_data == NULL) ((GFitsTableFltCol*)this)->fetch_data(); + + // Calculate pixel offset + int offset = row * m_number + inx; + + // Return image pixel + return m_data[offset]; +} + + /*========================================================================== = = = GFitsTableFltCol public methods = @@ -156,11 +204,17 @@ GFitsTableFltCol& GFitsTableFltCol::operator= (const GFitsTableFltCol& column) * * @exception GException::fits_hdu_not_found * Specified HDU not found in FITS file. + * @exception GException::fits_error + * Error occured during writing of the column data. + * + * The table column is only saved if it is linked to a FITS file and if the + * data are indeed present in the class instance. This avoids saving of data + * that have not been modified. ***************************************************************************/ void GFitsTableFltCol::save(void) { - // Continue only if a FITS file is connected - if (m_fitsfile.Fptr != NULL) { + // Continue only if a FITS file is connected and data have been loaded + if (m_fitsfile.Fptr != NULL && m_colnum > 0 && m_data != NULL) { // Move to the HDU int status = 0; @@ -170,9 +224,11 @@ void GFitsTableFltCol::save(void) throw GException::fits_hdu_not_found(G_SAVE, (m_fitsfile.HDUposition)+1, status); - // Save the column data - // TBD + status = __ffpcn(&m_fitsfile, __TFLOAT, m_colnum, 1, 1, + (long)m_size, m_data, m_nulval, &status); + if (status != 0) + throw GException::fits_error(G_SAVE, status); } // endif: FITS file was connected @@ -185,30 +241,32 @@ void GFitsTableFltCol::save(void) * @brief Get string value * * @param[in] row Table row. - * @param[in] col Table column vector index. + * @param[in] inx Table column vector index. + * + * @exception GException::out_of_range + * Table row or vector index are out of valid range. * * Returns value of specified row and vector index as string. ***************************************************************************/ -std::string GFitsTableFltCol::string(const int& row, const int& col) +std::string GFitsTableFltCol::string(const int& row, const int& inx) { - // Make sure that data are loaded - if (m_data == NULL) - load(); + // If data are not available then load them now + if (m_data == NULL) fetch_data(); // Check row value if (row < 0 || row >= m_length) throw GException::out_of_range(G_STRING, row, 0, m_length-1); - // Check col value - if (col < 0 || col >= m_number) - throw GException::out_of_range(G_STRING, col, 0, m_number-1); + // Check inx value + if (inx < 0 || inx >= m_number) + throw GException::out_of_range(G_STRING, inx, 0, m_number-1); // Get index - int inx = row * m_repeat + col; + int offset = row * m_number + inx; // Convert double into string ostringstream s_value; - s_value << scientific << m_data[inx]; + s_value << scientific << m_data[offset]; // Return value return s_value.str(); @@ -219,29 +277,31 @@ std::string GFitsTableFltCol::string(const int& row, const int& col) * @brief Get double precision value * * @param[in] row Table row. - * @param[in] col Table column vector index. + * @param[in] inx Table column vector index. + * + * @exception GException::out_of_range + * Table row or vector index are out of valid range. * * Returns value of specified row and vector index as double precision. ***************************************************************************/ -double GFitsTableFltCol::real(const int& row, const int& col) +double GFitsTableFltCol::real(const int& row, const int& inx) { - // Make sure that data are loaded - if (m_data == NULL) - load(); + // If data are not available then load them now + if (m_data == NULL) fetch_data(); // Check row value if (row < 0 || row >= m_length) throw GException::out_of_range(G_REAL, row, 0, m_length-1); - // Check col value - if (col < 0 || col >= m_number) - throw GException::out_of_range(G_REAL, col, 0, m_number-1); + // Check inx value + if (inx < 0 || inx >= m_number) + throw GException::out_of_range(G_REAL, inx, 0, m_number-1); // Get index - int inx = row * m_repeat + col; + int offset = row * m_number + inx; // Convert float into double - double value = (double)m_data[inx]; + double value = (double)m_data[offset]; // Return value return value; @@ -252,29 +312,31 @@ double GFitsTableFltCol::real(const int& row, const int& col) * @brief Get integer value * * @param[in] row Table row. - * @param[in] col Table column vector index. + * @param[in] inx Table column vector index. + * + * @exception GException::out_of_range + * Table row or vector index are out of valid range. * * Returns value of specified row and vector index as integer. ***************************************************************************/ -int GFitsTableFltCol::integer(const int& row, const int& col) +int GFitsTableFltCol::integer(const int& row, const int& inx) { - // Make sure that data are loaded - if (m_data == NULL) - load(); + // If data are not available then load them now + if (m_data == NULL) fetch_data(); // Check row value if (row < 0 || row >= m_length) throw GException::out_of_range(G_INTEGER, row, 0, m_length-1); - // Check col value - if (col < 0 || col >= m_number) - throw GException::out_of_range(G_INTEGER, col, 0, m_number-1); + // Check inx value + if (inx < 0 || inx >= m_number) + throw GException::out_of_range(G_INTEGER, inx, 0, m_number-1); // Get index - int inx = row * m_repeat + col; + int offset = row * m_number + inx; // Convert float into int - int value = (int)m_data[inx]; + int value = (int)m_data[offset]; // Return value return value; @@ -407,30 +469,54 @@ void GFitsTableFltCol::free_members(void) /***********************************************************************//** - * @brief Load column data + * @brief Fetch column data + * + * @exception GException::fits_error + * An error occured while loading column data from FITS file. + * + * If a FITS file is attached to the column the data are loaded into memory + * from the FITS file. If no FITS file is attached, memory is allocated + * to hold the column data and all cells are set to 0. ***************************************************************************/ -void GFitsTableFltCol::load(void) +void GFitsTableFltCol::fetch_data(void) { // Calculate size of memory m_size = m_number * m_length; - // Allocate memory - if (m_data != NULL) delete [] m_data; - m_data = new float[m_size]; + // Load only if the column has a positive size + if (m_size > 0) { + + // Allocate fresh memory + if (m_data != NULL) delete [] m_data; + m_data = new float[m_size]; + + // If a FITS file is attached then load column data from the FITS + // file + if (m_fitsfile.Fptr != NULL) { + int status = 0; + status = __ffmahd(&m_fitsfile, (m_fitsfile.HDUposition)+1, NULL, + &status); + status = __ffgcv(&m_fitsfile, __TFLOAT, m_colnum, 1, 1, m_size, + m_nulval, m_data, &m_anynul, &status); + if (status != 0) + throw GException::fits_error(G_FETCH_DATA, status); + } + + // ... otherwise initialise all column values to 0 + else { + for (int i = 0; i < m_size; ++i) + m_data[i] = 0.0; + } - // Load column data - int status = 0; - status = __ffmahd(&m_fitsfile, (m_fitsfile.HDUposition)+1, NULL, &status); - status = __ffgcv(&m_fitsfile, __TFLOAT, m_colnum, 1, 1, m_size, - m_nulval, m_data, &m_anynul, &status); - if (status != 0) - throw GException::fits_error(G_LOAD, status); + } // endif: column has a positive size // Return return; } + + /***********************************************************************//** * @brief Returns format string of ASCII table ***************************************************************************/ @@ -475,6 +561,52 @@ std::string GFitsTableFltCol::binary_format(void) const = = ==========================================================================*/ +/***********************************************************************//** + * @brief Output operator + * + * @param[in] os Output stream + * @param[in] column Column to put in output stream + ***************************************************************************/ +ostream& operator<< (ostream& os, const GFitsTableFltCol& column) +{ + // Put column name in stream + os << "'" << column.m_name << "'"; + + // Put FITS column number in stream + if (column.m_colnum > 0) + os << " [fits_colnum=" << column.m_colnum << "]"; + else + os << " [not linked to FITS file]"; + + // Put column type in stream + os << " " << column.ascii_format(); + os << " " << column.binary_format(); + + // Put data loading in stream + if (column.m_data == NULL) + os << " (not loaded)"; + else + os << " (loaded in memory)"; + + // Set data area size + os << " size=" << column.m_size; + + // Set vector length + os << " repeat=" << column.m_repeat; + + // Set width + os << " width=" << column.m_width; + + // Set number + os << " number=" << column.m_number; + + // Set length + os << " length=" << column.m_length; + + // Return output stream + return os; +} + /*========================================================================== = = diff --git a/test/test_GFits.cpp b/test/test_GFits.cpp index be4576e..0049f21 100644 --- a/test/test_GFits.cpp +++ b/test/test_GFits.cpp @@ -382,7 +382,9 @@ void test_create(void) GFitsBinTable table = GFitsBinTable(nrows); GFitsTableDblCol first = GFitsTableDblCol("First", nrows); GFitsTableDblCol second = GFitsTableDblCol("Second", nrows); - first(9) = 99.0; + first(0) = 1.0; + first(1) = 2.0; + first(2) = 3.0; second(0) = -99.0; cout << endl << first << endl; cout << second << endl; @@ -495,6 +497,189 @@ void test_image_double(void) /*************************************************************************** + * Test: FITS binary table test * + ***************************************************************************/ +void test_bin_table(void) +{ + // Dump header + cout << "Test GFitsBinTable: "; + + // Remove FITS file + system("rm -rf test_bintables.fits"); + + // Allocate reference sums + double sum_dbl = 0.0; + int sum_dbl_int = 0; + std::string sum_dbl_str; + float sum_flt = 0.0; + int sum_flt_int = 0; + std::string sum_flt_str; + + // Build tables + try { + // Open FITS file + GFits fits; + fits.open("test_bintables.fits"); + + // Set number of rows + int nrows = 10; + + // Initial control sums + float tot_flt = 0.0; + double tot_dbl = 0.0; + int tot_int = 0; + std::string tot_str; + + // + // ===== D O U B L E ===== + // + + // Set double precision table + GFitsTableDblCol col_dbl = GFitsTableDblCol("DOUBLE", nrows); + for (int i = 0; i < nrows; ++i) { + double val_dbl = cos(double(i)); + col_dbl(i) = val_dbl; + sum_dbl += val_dbl; + sum_dbl_int += int(val_dbl); + ostringstream s_value; + s_value << scientific << val_dbl; + sum_dbl_str += ":"+s_value.str(); + } + cout << "."; + + // Check double precision table (operator access) + tot_dbl = 0.0; + for (int i = 0; i < nrows; ++i) + tot_dbl += col_dbl(i); + if (!dequal(tot_dbl, sum_dbl)) { + cout << endl << "TEST ERROR: GFitsTableDblCol::operator()" + << endl; + throw; + } + cout << "."; + + // Check double precision table (real access) + tot_dbl = 0.0; + for (int i = 0; i < nrows; ++i) + tot_dbl += col_dbl.real(i); + if (!dequal(tot_dbl, sum_dbl)) { + cout << endl << "TEST ERROR: GFitsTableDblCol::real()" + << endl; + throw; + } + cout << "."; + + // Check double precision table (int access) + tot_int = 0.0; + for (int i = 0; i < nrows; ++i) + tot_int += col_dbl.integer(i); + if (tot_int != sum_dbl_int) { + cout << endl << "TEST ERROR: GFitsTableDblCol::integer()" + << endl; + throw; + } + cout << "."; + + // Check double precision table (string access) + tot_str.clear(); + for (int i = 0; i < nrows; ++i) + tot_str += ":"+col_dbl.string(i); + if (tot_str != sum_dbl_str) { + cout << endl << "TEST ERROR: GFitsTableDblCol::string()" + << endl; + throw; + } + cout << "."; + + // + // ===== F L O A T ===== + // + + // Set single precision table + GFitsTableFltCol col_flt = GFitsTableFltCol("FLOAT", nrows); + for (int i = 0; i < nrows; ++i) { + float val_flt = cos(0.1*float(i)); + col_flt(i) = val_flt; + sum_flt += val_flt; + sum_flt_int += int(val_flt); + ostringstream s_value; + s_value << scientific << val_flt; + sum_flt_str += ":"+s_value.str(); + } + cout << "."; + + // Check single precision table (operator access) + tot_flt = 0.0; + for (int i = 0; i < nrows; ++i) + tot_flt += col_flt(i); + if (!fequal(tot_flt, sum_flt)) { + cout << endl << "TEST ERROR: GFitsTableFltCol::operator()" + << endl; + throw; + } + cout << "."; + + // Check single precision table (real access) + tot_flt = 0.0; + for (int i = 0; i < nrows; ++i) + tot_flt += col_flt.real(i); + if (!fequal(tot_flt, sum_flt)) { + cout << endl << "TEST ERROR: GFitsTableFltCol::real()" + << endl; + throw; + } + cout << "."; + + // Check single precision table (int access) + tot_int = 0.0; + for (int i = 0; i < nrows; ++i) + tot_int += col_flt.integer(i); + if (tot_int != sum_flt_int) { + cout << endl << "TEST ERROR: GFitsTableFltCol::integer()" + << endl; + throw; + } + cout << "."; + + // Check single precision table (string access) + tot_str.clear(); + for (int i = 0; i < nrows; ++i) + tot_str += ":"+col_flt.string(i); + if (tot_str != sum_flt_str) { + cout << endl << "TEST ERROR: GFitsTableFltCol::string()" + << endl; + throw; + } + cout << "."; + + // + // ===== W R I T E T A B L E ===== + // + + // Build binary table + GFitsBinTable table = GFitsBinTable(nrows); + table.append_column(col_dbl); + table.append_column(col_flt); + + // Create HDU and append to FILE file + GFitsHDU hdu(table); + fits.append_hdu(hdu); + + // Save FITS file + fits.save(); + cout << "."; + } + catch (exception &e) { + cout << endl << "TEST ERROR: Unable to build tables." << endl; + cout << e.what() << endl; + throw; + } + cout << ". ok." << endl; + + +} + +/*************************************************************************** * Main test function * ***************************************************************************/ int main(void) @@ -506,8 +691,9 @@ int main(void) std::cout << "***********************" << std::endl; // Execute the tests - test_image_double(); test_create(); + test_image_double(); + test_bin_table(); //test_open(); //test_columns();