Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 27 additions & 17 deletions camerad/fits.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,20 +189,20 @@ class FITS_file {
return;
}

// Write the user keys on close, if specified
//
if (writekeys) {
logwrite(function, "writing user-defined keys after exposure");
Common::FitsKeys::fits_key_t::iterator keyit;
for (keyit = info.userkeys.keydb.begin();
keyit != info.userkeys.keydb.end();
keyit++) {
this->add_key(keyit->second.keyword, keyit->second.keytype, keyit->second.keyvalue,
keyit->second.keycomment);
try {
// Write the user keys on close, if specified
//
if (writekeys) {
logwrite(function, "writing user-defined keys after exposure");
Common::FitsKeys::fits_key_t::iterator keyit;
for (keyit = info.userkeys.keydb.begin();
keyit != info.userkeys.keydb.end();
keyit++) {
this->add_key(keyit->second.keyword, keyit->second.keytype, keyit->second.keyvalue,
keyit->second.keycomment);
}
}
}

try {
// Add a header keyword for the time the file was written (right now!)
//
this->pFits->pHDU().addKey("DATE", get_timestamp(), "FITS file write time");
Expand Down Expand Up @@ -570,18 +570,18 @@ class FITS_file {

/**************** FITS_file::add_key **************************************/
/**
* @fn add_key
* @brief wrapper to write keywords to the FITS file header
* @param[in] std::string keyword
* @param[in] std::string type
* @param[in] std::string value
* @param[in] std::string comment
* @return nothing
*
* This can throw an exception so use try-catch when calling.
* Uses CCFits
*
*/
void add_key(std::string keyword, std::string type, std::string value, std::string comment) {
std::string function = "FITS_file::add_key";
const std::string function("FITS_file::add_key");
std::stringstream message;

// The file must have been opened first
Expand All @@ -597,23 +597,33 @@ class FITS_file {
this->pFits->pHDU().addKey(keyword, boolvalue, comment);
} else if (type.compare("INT") == 0) {
this->pFits->pHDU().addKey(keyword, std::stoi(value), comment);
} else if (type.compare("LONG") == 0) {
this->pFits->pHDU().addKey(keyword, std::stol(value), comment);
} else if (type.compare("FLOAT") == 0) {
this->pFits->pHDU().addKey(keyword, std::stof(value), comment);
} else if (type.compare("DOUBLE") == 0) {
this->pFits->pHDU().addKey(keyword, std::stod(value), comment);
} else if (type.compare("STRING") == 0) {
this->pFits->pHDU().addKey(keyword, value, comment);
} else {
message.str("");
message << "ERROR unknown type: " << type << " for user keyword: " << keyword << "=" << value
<< ": expected {INT,FLOAT,STRING,BOOL}";
<< ": expected {INT,LONG,FLOAT,DOUBLE,STRING,BOOL}";
logwrite(function, message.str());
}
} catch (const std::exception &e) {
message.str("");
message << "ERROR parsing value " << value << " for keyword " << keyword << ": " << e.what();
logwrite( function, message.str() );
message.str("");
message << function << ": parsing value " << value << " for keyword " << keyword << ": " << e.what();
throw std::runtime_error( message.str() );
} catch (CCfits::FitsError &err) {
message.str("");
message << "ERROR adding key " << keyword << "=" << value << " / " << comment << " (" << type << ") : "
<< err.message();
logwrite(function, message.str());
}
}

/**************** FITS_file::add_key **************************************/
};
126 changes: 82 additions & 44 deletions common/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,73 +52,98 @@ namespace Common {

/** Common::FitsKeys::get_keytype *******************************************/
/**
* @fn get_keytype
* @brief return the keyword type based on the keyvalue
* @param std::string value
* @return std::string type: "BOOL", "STRING", "DOUBLE", "INT"
* @return string { "BOOL", "STRING", "DOUBLE", "FLOAT", "INT", "LONG" }
*
* This function looks at the contents of the value string to determine if it
* contains an INT, DOUBLE, BOOL or STRING, and returns a string identifying the type.
* That type is used in FITS_file::add_user_key() for adding keywords to the header.
* This function looks at the contents of the value string to determine if
* it contains an INT, DOUBLE, FLOAT, BOOL or STRING, and returns a string
* identifying the type. That type is used in FITS_file::add_user_key()
* for adding keywords to the header.
*
* To differentiate between double and float, the keyvalue string must end
* with an "f", as in "3.14f" which returns "FLOAT" or "3.14" which returns
* "DOUBLE". Decimal point is required ("1f" is not a float but "1.0f" is)
*
* To differentiate between int and long, keyvalue string must end with "l"
* as in "100l" which returns "LONG" or "100" which returns "INT". Since a
* long can't have a decimal, using 'l' with a decimal point is a string.
*
*/
std::string FitsKeys::get_keytype(std::string keyvalue) {
std::size_t pos(0);

// If it's empty then what else can it be but string
if ( keyvalue.empty() ) return std::string("STRING");

// if the entire string is either (exactly) T or F then it's a boolean
if (keyvalue == "T" || keyvalue == "F") {
return std::string("BOOL");
}

// skip the whitespaces
pos = keyvalue.find_first_not_of(' ');
if (pos == keyvalue.size()) return std::string("STRING"); // all spaces, so it's a string

// check the significand
if (keyvalue[pos] == '+' || keyvalue[pos] == '-') ++pos; // skip the sign if exist

// count the number of digits and number of decimal points
int n_nm, n_pt;
for (n_nm = 0, n_pt = 0; std::isdigit(keyvalue[pos]) || keyvalue[pos] == '.'; ++pos) {
keyvalue[pos] == '.' ? ++n_pt : ++n_nm;
}

if (n_pt > 1 || n_nm < 1 || pos < keyvalue.size()) {
// no more than one point, no numbers, or a non-digit character
return std::string("STRING"); // then it's a string
}

// skip the trailing whitespaces
while (keyvalue[pos] == ' ') {
++pos;
if (keyvalue.find_first_not_of(' ') != std::string::npos) {
pos = keyvalue.find_first_not_of(' ');
}
if (pos == keyvalue.size()) return std::string("STRING"); // all spaces, so it's a string

std::string check_type;

if (pos == keyvalue.size()) {
// If it's an INT or DOUBLE, don't return that type until it has been checked, below
//
if (keyvalue.find(".") == std::string::npos) // all numbers and no decimals, it's an integer
check_type = "INT";
else // otherwise numbers with a decimal, it's a float
check_type = "DOUBLE";
} else return std::string("STRING"); // lastly, must be a string
// remove sign if it exists
if (keyvalue[pos] == '+' || keyvalue[pos] == '-') keyvalue.erase(0,1);

// If it's an INT or a DOUBLE then try to convert the value to INT or DOUBLE.
// If that conversion fails then set the type to STRING.
// count the different types of characters
//
// number of decimal points
auto n_pt = std::count(keyvalue.begin(), keyvalue.end(), '.');
// number of numerics
auto n_nm = std::count_if(keyvalue.begin(), keyvalue.end(), [](unsigned char c) {
return std::isdigit(c);
} );
// number of string chars, non-numerics, non-decimals
auto n_ch = std::count_if(keyvalue.begin(), keyvalue.end(), [](unsigned char c) {
return (!std::isdigit(c) && c!='.');
} );

// number of decimals points, numeric and non-numeric chars determines the type
// verify by trying to convert to that type and return string on conversion failure
//
try {
if (check_type == "INT") std::stoi(keyvalue);
if (check_type == "DOUBLE") std::stod(keyvalue);
} catch (std::invalid_argument &) {
// no numbers or more than one decimal point can't be any type of number, so string
if ( n_nm==0 || n_pt > 1 ) {
return std::string("STRING");
}
catch (std::out_of_range &) {
}
else
// at least one digit, exactly one decimal point and only and last character is "f" is a float
if ( n_nm > 0 && n_pt==1 && n_ch==1 && keyvalue.back()=='f' ) {
std::stof(keyvalue);
return std::string("FLOAT");
}
else
// at least one digit, exactly one decimal point and no chars is double
if ( n_nm > 0 && n_pt==1 && n_ch==0 ) {
std::stod(keyvalue);
return std::string("DOUBLE");
}
else
// at least one digit, no decimal point and no chars is int
if ( n_nm > 0 && n_pt==0 && n_ch==0 ) {
std::stoi(keyvalue);
return std::string("INT");
}
else
// at least one digit, no decimal point and only and last char is "l" is long
if ( n_nm > 0 && n_pt==0 && n_ch==1 && keyvalue.back()=='l' ) {
std::stol(keyvalue);
return std::string("LONG");
}
else
// what else can it be but string
return std::string("STRING");
}
return check_type;
catch (const std::exception &) {
// any numeric conversion failure sets type as string
return std::string("STRING");
}
}

/** Common::FitsKeys::get_keytype *******************************************/


Expand Down Expand Up @@ -230,6 +255,19 @@ namespace Common {
this->keydb[keyword].keyvalue = keyvalue;
this->keydb[keyword].keycomment = keycomment;

// long and float are designated by a modifier char which must be removed
//
if (this->keydb[keyword].keytype=="LONG") {
if (!this->keydb[keyword].keyvalue.empty() && this->keydb[keyword].keyvalue.back()=='l') {
this->keydb[keyword].keyvalue.pop_back();
}
}
if (this->keydb[keyword].keytype=="FLOAT") {
if (!this->keydb[keyword].keyvalue.empty() && this->keydb[keyword].keyvalue.back()=='f') {
this->keydb[keyword].keyvalue.pop_back();
}
}

#ifdef LOGLEVEL_DEBUG
message.str(""); message << "[DEBUG] added key: " << keyword << "=" << keyvalue << " (" << this->keydb[keyword].keytype << ") // " << keycomment;
logwrite( function, message.str() );
Expand Down
Loading