/*************************************************************************** * GFitsHeaderCard.cpp - FITS header card class * * ----------------------------------------------------------------------- * * copyright (C) 2008-2013 by Juergen Knoedlseder * * ----------------------------------------------------------------------- * * * * 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 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ /** * @file GFitsHeaderCard.cpp * @brief FITS header card class implementation * @author Juergen Knoedlseder */ /* __ Includes ___________________________________________________________ */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "GException.hpp" #include "GTools.hpp" #include "GFitsCfitsio.hpp" #include "GFits.hpp" #include "GFitsHeaderCard.hpp" /* __ Method name definitions ____________________________________________ */ #define G_COPY_DTYPE "GFitsHeaderCard::copy_dtype(GFitsHeaderCard&)" #define G_FREE_DTYPE "GFitsHeaderCard::free_dtype()" #define G_SET_DTYPE "GFitsHeaderCard::set_dtype(std::string&)" #define G_READ_NUM "GFitsHeaderCard::read(void*, int&)" #define G_READ_STR "GFitsHeaderCard::read(void*, std::string&)" #define G_WRITE "GFitsHeaderCard::write(void*)" /* __ Definitions ________________________________________________________ */ /* __ Macros _____________________________________________________________ */ /* __ Coding definitions _________________________________________________ */ /* __ Enumerations _______________________________________________________ */ /* __ Debug definitions __________________________________________________ */ /*========================================================================== = = = Constructors/destructors = = = ==========================================================================*/ /***********************************************************************//** * @brief Void constructor ***************************************************************************/ GFitsHeaderCard::GFitsHeaderCard(void) { // Initialise class members init_members(); // Return return; } /***********************************************************************//** * @brief Constructor for string cards * * @param[in] keyname Card name. * @param[in] value Card string value. * @param[in] comment Card comment. * * This constructor builds a header card from the keyname, value and comment. ***************************************************************************/ GFitsHeaderCard::GFitsHeaderCard(const std::string& keyname, const std::string& value, const std::string& comment) { // Initialise class members init_members(); // Set members this->keyname(keyname); this->value("'"+value+"'"); this->comment(comment); // Return return; } /***********************************************************************//** * @brief Constructor for floating point cards * * @param[in] keyname Card name. * @param[in] value Card floating point value. * @param[in] comment Card comment. * * This constructor builds a header card from the keyname, value and comment. ***************************************************************************/ GFitsHeaderCard::GFitsHeaderCard(const std::string& keyname, const double& value, const std::string& comment) { // Initialise class members init_members(); // Set members this->keyname(keyname); this->value(value); this->comment(comment); // Return return; } /***********************************************************************//** * @brief Constructor for integer cards * * @param[in] keyname Card name. * @param[in] value Card integer value. * @param[in] comment Card comment. * * This constructor builds a header card from the keyname, value and comment. ***************************************************************************/ GFitsHeaderCard::GFitsHeaderCard(const std::string& keyname, const int& value, const std::string& comment) { // Initialise class members init_members(); // Set members this->keyname(keyname); this->value(value); this->comment(comment); // Return return; } /***********************************************************************//** * @brief Copy constructor * * @param[in] card Header card. ***************************************************************************/ GFitsHeaderCard::GFitsHeaderCard(const GFitsHeaderCard& card) { // Initialise class members for clean destruction init_members(); // Copy members copy_members(card); // Return return; } /***********************************************************************//** * @brief Destructor ***************************************************************************/ GFitsHeaderCard::~GFitsHeaderCard(void) { // Free members free_members(); // Return return; } /*========================================================================== = = = Operators = = = ==========================================================================*/ /***********************************************************************//** * @brief Assignment operator * * @param[in] card Header card. * @return Header card. ***************************************************************************/ GFitsHeaderCard& GFitsHeaderCard::operator=(const GFitsHeaderCard& card) { // Execute only if object is not identical if (this != &card) { // Free members free_members(); // Initialise private members for clean destruction init_members(); // Copy members copy_members(card); } // endif: object was not identical // Return this object return *this; } /*========================================================================== = = = Public methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Clear header card ***************************************************************************/ void GFitsHeaderCard::clear(void) { // Free members free_members(); // Initialise members init_members(); // Return return; } /***********************************************************************//** * @brief Clone header card * * @return Pointer to deep copy of header card. ***************************************************************************/ GFitsHeaderCard* GFitsHeaderCard::clone(void) const { return new GFitsHeaderCard(*this); } /***********************************************************************//** * @brief Set name of header card * * @param[in] keyname Card name. ***************************************************************************/ void GFitsHeaderCard::keyname(const std::string& keyname) { // Set name of card m_keyname = keyname; // Return return; } /***********************************************************************//** * @brief Set string value of header card * * @param[in] value Card string value. * * This method sets the value of a string header card. The internal string * representation contains hyphens, yet for keyword writing the hyphens are * stripped. ***************************************************************************/ void GFitsHeaderCard::value(const std::string& value) { // Free data type free_dtype(); // Set value m_value = value; // Attach hyphens to internal string representation if required if (m_value[0] != '\x27') { m_value = "'" + m_value; } if (m_value[m_value.length()-1] != '\x27') { m_value = m_value + "'"; } // Strip hyphens and whitespace from datatype value that is used for // keyword writing std::string value_dtype = gammalib::strip_whitespace(m_value.substr(1, m_value.length() - 2)); // Set data type m_dtype = __TSTRING; m_value_dtype = new std::string(value_dtype); // Return return; } /***********************************************************************//** * @brief Set boolean value of header card * * @param[in] value Card boolean value. ***************************************************************************/ void GFitsHeaderCard::value(const bool& value) { // Free data type free_dtype(); // Set value and data type m_value = (value) ? "T" : "F"; m_dtype = __TLOGICAL; m_value_dtype = new bool(value); // Return return; } /***********************************************************************//** * @brief Set single precision value of header card * * @param[in] value Card single precision value. ***************************************************************************/ void GFitsHeaderCard::value(const float& value) { // Free data type free_dtype(); // Set value and data type m_value = gammalib::str(value); m_dtype = __TFLOAT; m_value_dtype = new float(value); // Return return; } /***********************************************************************//** * @brief Set double precision value of header card * * @param[in] value Card double precision value. ***************************************************************************/ void GFitsHeaderCard::value(const double& value) { // Free data type free_dtype(); // Set value and data type m_value = gammalib::str(value); m_dtype = __TDOUBLE; m_value_dtype = new double(value); // Return return; } /***********************************************************************//** * @brief Set unsigned short integer value of header card * * @param[in] value Card unsigned short integer value. ***************************************************************************/ void GFitsHeaderCard::value(const unsigned short& value) { // Free data type free_dtype(); // Set value and data type m_value = gammalib::str(value); m_dtype = __TUSHORT; m_value_dtype = new unsigned short(value); // Return return; } /***********************************************************************//** * @brief Set short integer value of header card * * @param[in] value Card short integer value. ***************************************************************************/ void GFitsHeaderCard::value(const short& value) { // Free data type free_dtype(); // Set value and data type m_value = gammalib::str(value); m_dtype = __TSHORT; m_value_dtype = new short(value); // Return return; } /***********************************************************************//** * @brief Set unsigned integer value of header card * * @param[in] value Card unsigned integer value. ***************************************************************************/ void GFitsHeaderCard::value(const unsigned int& value) { // Free data type free_dtype(); // Set value and data type m_value = gammalib::str(value); m_dtype = __TUINT; m_value_dtype = new unsigned int(value); // Return return; } /***********************************************************************//** * @brief Set integer value of header card * * @param[in] value Card integer value. ***************************************************************************/ void GFitsHeaderCard::value(const int& value) { // Free data type free_dtype(); // Set value and data type m_value = gammalib::str(value); m_dtype = __TINT; m_value_dtype = new int(value); // Return return; } /***********************************************************************//** * @brief Set long integer value of header card * * @param[in] value Card long integer value. ***************************************************************************/ void GFitsHeaderCard::value(const long& value) { // Free data type free_dtype(); // Set value and data type m_value = gammalib::str(value); m_dtype = __TLONG; m_value_dtype = new long(value); // Return return; } /***********************************************************************//** * @brief Set unsigned integer long value of header card * * @param[in] value Card unsigned long integer value. ***************************************************************************/ void GFitsHeaderCard::value(const unsigned long& value) { // Free data type free_dtype(); // Set value and data type m_value = gammalib::str(value); m_dtype = __TULONG; m_value_dtype = new unsigned long(value); // Return return; } /***********************************************************************//** * @brief Set long long integer value of header card * * @param[in] value Card long long integer value. ***************************************************************************/ void GFitsHeaderCard::value(const long long& value) { // Free data type free_dtype(); // Set value and data type m_value = gammalib::str(value); m_dtype = __TLONGLONG; m_value_dtype = new long long(value); // Return return; } /***********************************************************************//** * @brief Return header card value as string * * Convert header card value into a string. * Any hyphens that may occur in the FITS card will be automatically stripped. ***************************************************************************/ std::string GFitsHeaderCard::string(void) const { // Initialize return value to actual value string std::string result = m_value; // Type dependent conversion if (m_value_dtype != NULL) { switch (m_dtype) { case __TSTRING: if (m_value.length() > 2) { result = gammalib::strip_whitespace(m_value.substr(1, m_value.length() - 2)); } break; default: break; } } // Return string return result; } /***********************************************************************//** * @brief Return header card value as double precision * * Convert header card value into a double precision value. In case that the * card did not contain a numerical value, 0 will be returned by the * method. ***************************************************************************/ double GFitsHeaderCard::real(void) const { // Initialize return value to 0.0 double result = 0.0; // Type dependent conversion if (m_value_dtype != NULL) { switch (m_dtype) { case __TSTRING: if (m_value.length() > 2) { result = gammalib::todouble(m_value.substr(1, m_value.length() - 2)); } break; case __TLOGICAL: result = (m_value == "T") ? 1.0 : 0.0; break; default: result = gammalib::todouble(m_value); break; } } // Return string return result; } /***********************************************************************//** * @brief Return header card value as integer * * Convert header card value into a integer value. In case that the * card did not contain a numerical value, 0 will be returned by the * method. ***************************************************************************/ int GFitsHeaderCard::integer(void) const { // Initialize return value to 0 int result = 0; // Type dependent conversion if (m_value_dtype != NULL) { switch (m_dtype) { case __TSTRING: if (m_value.length() > 2) { result = gammalib::toint(m_value.substr(1, m_value.length() - 2)); } break; case __TLOGICAL: result = (m_value == "T") ? 1 : 0; break; default: result = gammalib::toint(m_value); break; } } // Return string return result; } /***********************************************************************//** * @brief Print header card information * * @param[in] chatter Chattiness (defaults to NORMAL). * @return String containing header card information. ***************************************************************************/ std::string GFitsHeaderCard::print(const GChatter& chatter) const { // Initialise result string std::string result; // Continue only if chatter is not silent if (chatter != SILENT) { // Append keyname result.append(gammalib::left(m_keyname,8)); // Format values if (m_keyname != "COMMENT" && m_keyname != "HISTORY" && m_keyname != "") { if (m_unit.length() > 0) { result.append(" ="+gammalib::right(m_value,21)+" / ["+m_unit+"] "+m_comment); } else { result.append(" ="+gammalib::right(m_value,21)+" / "+m_comment); } } else { result.append(" "+m_comment); } // Attach card type if (m_value_dtype != NULL) { switch (m_dtype) { case __TNULL: result.append(" "); break; case __TBIT: result.append(" "); break; case __TBYTE: result.append(" "); break; case __TSBYTE: result.append(" "); break; case __TLOGICAL: result.append(" "); break; case __TSTRING: result.append(" "); break; case __TUSHORT: result.append(" "); break; case __TSHORT: result.append(" "); break; case __TUINT: result.append(" "); break; case __TINT: result.append(" "); break; case __TULONG: result.append(" "); break; case __TLONG: result.append(" "); break; case __TLONGLONG: result.append(" "); break; case __TFLOAT: result.append(" "); break; case __TDOUBLE: result.append(" "); break; case __TCOMPLEX: result.append(" "); break; case __TDBLCOMPLEX: result.append(" "); break; default: result.append(" "); break; } } /* else { switch (m_dtype) { case __TNULL: result.append(" "); break; default: result.append(" "); break; } } */ } // endif: chatter was not silent // Return result return result; } /*========================================================================== = = = Private methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Initialise class members ***************************************************************************/ void GFitsHeaderCard::init_members(void) { // Initialise members m_keyname.clear(); m_value.clear(); m_unit.clear(); m_comment.clear(); m_value_dtype = NULL; m_dtype = __TNULL; m_value_decimals = 10; m_comment_write = true; // Was false before, not sure why ... // Return return; } /***********************************************************************//** * @brief Copy class members * * @param[in] card Header card to be copied ***************************************************************************/ void GFitsHeaderCard::copy_members(const GFitsHeaderCard& card) { // Copy members m_keyname = card.m_keyname; m_value = card.m_value; m_value_decimals = card.m_value_decimals; m_unit = card.m_unit; m_comment = card.m_comment; m_comment_write = card.m_comment_write; // Copy native data types copy_dtype(card); // Return return; } /***********************************************************************//** * @brief Delete class members ***************************************************************************/ void GFitsHeaderCard::free_members(void) { // Free members free_dtype(); // Return return; } /***********************************************************************//** * @brief Copy dtype * * @param[in] card Header card. * * Copies the data type of a header card. ***************************************************************************/ void GFitsHeaderCard::copy_dtype(const GFitsHeaderCard& card) { // Copy data type if (card.m_value_dtype != NULL) { m_dtype = card.m_dtype; switch (m_dtype) { case __TLOGICAL: m_value_dtype = new bool(*((bool*)card.m_value_dtype)); break; case __TSTRING: m_value_dtype = new std::string(*((std::string*)card.m_value_dtype)); break; case __TUSHORT: m_value_dtype = new unsigned short(*((unsigned short*)card.m_value_dtype)); break; case __TSHORT: m_value_dtype = new short(*((short*)card.m_value_dtype)); break; case __TUINT: m_value_dtype = new unsigned int(*((unsigned int*)card.m_value_dtype)); break; case __TINT: m_value_dtype = new int(*((int*)card.m_value_dtype)); break; case __TULONG: m_value_dtype = new unsigned long(*((unsigned long*)card.m_value_dtype)); break; case __TLONG: m_value_dtype = new long(*((long*)card.m_value_dtype)); break; case __TLONGLONG: m_value_dtype = new long long(*((long long*)card.m_value_dtype)); break; case __TFLOAT: m_value_dtype = new float(*((float*)card.m_value_dtype)); break; case __TDOUBLE: m_value_dtype = new double(*((double*)card.m_value_dtype)); break; default: std::string msg = "Invalid data type code "+ gammalib::str(m_dtype)+" encountered."; gammalib::warning(G_COPY_DTYPE, msg); break; } } // Return return; } /***********************************************************************//** * @brief Free dtype ***************************************************************************/ void GFitsHeaderCard::free_dtype(void) { // Free data type if (m_value_dtype != NULL) { switch (m_dtype) { case __TLOGICAL: delete (bool*)m_value_dtype; break; case __TSTRING: delete (std::string*)m_value_dtype; break; case __TUSHORT: delete (unsigned short*)m_value_dtype; break; case __TSHORT: delete (short*)m_value_dtype; break; case __TUINT: delete (unsigned int*)m_value_dtype; break; case __TINT: delete (int*)m_value_dtype; break; case __TULONG: delete (unsigned long*)m_value_dtype; break; case __TLONG: delete (long*)m_value_dtype; break; case __TLONGLONG: delete (long long*)m_value_dtype; break; case __TFLOAT: delete (float*)m_value_dtype; break; case __TDOUBLE: delete (double*)m_value_dtype; break; default: std::string msg = "Invalid data type code "+ gammalib::str(m_dtype)+" encountered."; gammalib::warning(G_FREE_DTYPE, msg); break; } } // Return return; } /***********************************************************************//** * @brief Set native data type from card string * * @param[in] value Card string. * * The native data type is determined from the card string. This method sets * the members m_dtype and m_value_type. The card type is determined by * analyzing the card value. * * @todo Implement syntax checking for integer or float values. ***************************************************************************/ void GFitsHeaderCard::set_dtype(const std::string& value) { // Free data type free_dtype(); // Main loop to allow fall through do { // Get index of last string element int last = m_value.length() - 1; // If value is empty and there is a COMMENT, HISTORY or BLANKFIELD // (empty) keyword then we have a commentary card. We can just // skip this card here. Otherwise we have an undefined value, also // called NULL value. if (last < 0) { if (m_keyname == "COMMENT" || m_keyname == "HISTORY" || m_keyname == "") { continue; } else { m_dtype = __TNULL; continue; } } // If value is enclosed in parentheses then we have a string if (m_value[0] == '\x27' && m_value[last] == '\x27') { this->value(value); continue; } // If value has only one digit and is either 'F' or 'T' we have a // Boolean if (last == 0 && (m_value[0] == 'F' || m_value[0] == 'T')) { this->value((m_value[0] == 'T')); continue; } // Conversion double value_dbl = gammalib::todouble(m_value); long long value_ll = gammalib::tolonglong(m_value); unsigned long long value_ull = gammalib::toulonglong(m_value); // Check if we have an integer if ((value_dbl >= 0 && value_dbl == value_ull) || (value_dbl < 0 && value_dbl == value_ll)) { // Consider positive integers as unsigned if (value_dbl >= 0) { if (value_ull > ULONG_MAX) { m_dtype = __TLONGLONG; m_value_dtype = new long long(value_ll); } else if (value_ull > USHRT_MAX) { m_dtype = __TULONG; m_value_dtype = new unsigned long(value_ull); } else { m_dtype = __TUSHORT; m_value_dtype = new unsigned short(value_ull); } } // ... otherwise consider signed else { if (value_ll > LONG_MAX || value_ll < LONG_MIN) { m_dtype = __TLONGLONG; m_value_dtype = new long long(value_ll); } else if (value_ll > SHRT_MAX || value_ll < SHRT_MIN) { m_dtype = __TLONG; m_value_dtype = new long(value_ll); } else { m_dtype = __TSHORT; m_value_dtype = new short(value_ll); } } } // endif: handled integers // ... otherwise handle floats else { if (value_dbl > FLT_MAX || value_dbl < FLT_MIN) { m_dtype = __TDOUBLE; m_value_dtype = new double(value_dbl); } else { m_dtype = __TFLOAT; m_value_dtype = new float(value_dbl); } } } while(0); // Return return; } /***********************************************************************//** * @brief Read header card from FITS file * * @param[in] vptr FITS file void pointer. * @param[in] keynum Number of the header card. ***************************************************************************/ void GFitsHeaderCard::read(void* vptr, const int& keynum) { // Initialise status int status = 0; // Move to HDU gammalib::fits_move_to_hdu(G_READ_NUM, vptr); // Read keyword char keyname[80]; char value[80]; char comment[80]; status = __ffgkyn(FPTR(vptr), keynum, keyname, value, comment, &status); if (status != 0) { throw GException::fits_error(G_READ_NUM, status); } // Store result m_keyname.assign(keyname); m_value.assign(value); m_comment.assign(comment); // Determine card type set_dtype(m_value); // Return return; } /***********************************************************************//** * @brief Read header card from FITS file * * @param[in] vptr FITS file void pointer. * @param[in] keyname Name of the header card. * * @exception GException::invalid_value * Specified @p keyname not found in FITS header. * @exception GException::fits_error * cfitsio error occured. ***************************************************************************/ void GFitsHeaderCard::read(void* vptr, const std::string& keyname) { // Initialise FITS status int status = 0; // Move to HDU gammalib::fits_move_to_hdu(G_READ_STR, vptr); // Read keyword char value[80]; char comment[80]; status = __ffgkey(FPTR(vptr), (char*)keyname.c_str(), value, comment, &status); // Catch error if (status == 202) { // Keyword not found std::string msg = "Header card with keyword \""+keyname+"\" not" " found in FITS header (status=202)."; throw GException::invalid_value(G_READ_STR, msg); } else if (status != 0) { // Any other error std::string msg = "Unable to read keyword \""+keyname+"\" from" " FITS extension "+ gammalib::str((FPTR(vptr)->HDUposition))+ " header."; throw GException::fits_error(G_READ_STR, status, msg); } // Store result m_keyname = keyname; m_value.assign(value); m_comment.assign(comment); // Determine card type set_dtype(m_value); // Return return; } /***********************************************************************//** * @brief Write header card * * @param[in] vptr FITS file void pointer. * * Writes any kind of header card to a FITS file. ***************************************************************************/ void GFitsHeaderCard::write(void* vptr) const { // Initialise status int status = 0; // Move to HDU gammalib::fits_move_to_hdu(G_WRITE, vptr); // If card is comment then write comment if (m_keyname == "COMMENT") { if (m_comment_write) { status = __ffpcom(FPTR(vptr), (char*)m_comment.c_str(), &status); } } // If card is history then write history else if (m_keyname == "HISTORY") { if (m_comment_write) { status = __ffphis(FPTR(vptr), (char*)m_comment.c_str(), &status); } } // If card is BLANKFIELD (empty keyword) then write empty line else if (m_keyname == "") { if (m_comment_write) { status = __ffprec(FPTR(vptr), "", &status); } } // If card holds a native data type then write it else if (m_value_dtype != NULL) { switch (m_dtype) { case __TLOGICAL: { int value = (*((bool*)m_value_dtype)) ? 1 : 0; status = __ffukyl(FPTR(vptr), (char*)m_keyname.c_str(), value, (char*)m_comment.c_str(), &status); } break; case __TSTRING: status = __ffukys(FPTR(vptr), (char*)m_keyname.c_str(), (char*)((std::string*)m_value_dtype)->c_str(), (char*)m_comment.c_str(), &status); break; case __TFLOAT: status = __ffukye(FPTR(vptr), (char*)m_keyname.c_str(), *((float*)m_value_dtype), decimals(), (char*)m_comment.c_str(), &status); break; case __TDOUBLE: status = __ffukyd(FPTR(vptr), (char*)m_keyname.c_str(), *((double*)m_value_dtype), decimals(), (char*)m_comment.c_str(), &status); break; default: status = __ffuky(FPTR(vptr), m_dtype, (char*)m_keyname.c_str(), m_value_dtype, (char*)m_comment.c_str(), &status); break; } } // endif: had native data type // ... otherwise if card holds a NULL value then write it else if (m_dtype == __TNULL) { status = __ffukyu(FPTR(vptr), (char*)m_keyname.c_str(), (char*)m_comment.c_str(), &status); } // ... capture all other stuff else { std::string msg = "The code should never arrive at this point!\n" "The keyname \""+m_keyname+"\" is neither" " \"COMMENT\" nor \"HISTRORY\" but it has a" " NULL data type value and a data type of" " "+gammalib::str(m_dtype)+".\n" "Maybe an invalid FITS header card has been" " encountered in reading a file?"; gammalib::warning(G_WRITE, msg); } // Throw exception in case of a FITS error if (status != 0) { throw GException::fits_error(G_WRITE, status); } // Return return; }