Skip to content

Commit e4e50de

Browse files
astronomerdaveDavid Hale
andauthored
fixes bug between get_keytype (which could set type "DOUBLE") and add_key (#58)
(which didn't accept type "DOUBLE"). Also adds support for LONG and FLOAT. Co-authored-by: David Hale <dhale@caltech.edu>
1 parent 16c7221 commit e4e50de

File tree

2 files changed

+109
-61
lines changed

2 files changed

+109
-61
lines changed

camerad/fits.h

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -189,20 +189,20 @@ class FITS_file {
189189
return;
190190
}
191191

192-
// Write the user keys on close, if specified
193-
//
194-
if (writekeys) {
195-
logwrite(function, "writing user-defined keys after exposure");
196-
Common::FitsKeys::fits_key_t::iterator keyit;
197-
for (keyit = info.userkeys.keydb.begin();
198-
keyit != info.userkeys.keydb.end();
199-
keyit++) {
200-
this->add_key(keyit->second.keyword, keyit->second.keytype, keyit->second.keyvalue,
201-
keyit->second.keycomment);
192+
try {
193+
// Write the user keys on close, if specified
194+
//
195+
if (writekeys) {
196+
logwrite(function, "writing user-defined keys after exposure");
197+
Common::FitsKeys::fits_key_t::iterator keyit;
198+
for (keyit = info.userkeys.keydb.begin();
199+
keyit != info.userkeys.keydb.end();
200+
keyit++) {
201+
this->add_key(keyit->second.keyword, keyit->second.keytype, keyit->second.keyvalue,
202+
keyit->second.keycomment);
203+
}
202204
}
203-
}
204205

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

571571
/**************** FITS_file::add_key **************************************/
572572
/**
573-
* @fn add_key
574573
* @brief wrapper to write keywords to the FITS file header
575574
* @param[in] std::string keyword
576575
* @param[in] std::string type
577576
* @param[in] std::string value
578577
* @param[in] std::string comment
579-
* @return nothing
580578
*
579+
* This can throw an exception so use try-catch when calling.
581580
* Uses CCFits
581+
*
582582
*/
583583
void add_key(std::string keyword, std::string type, std::string value, std::string comment) {
584-
std::string function = "FITS_file::add_key";
584+
const std::string function("FITS_file::add_key");
585585
std::stringstream message;
586586

587587
// The file must have been opened first
@@ -597,23 +597,33 @@ class FITS_file {
597597
this->pFits->pHDU().addKey(keyword, boolvalue, comment);
598598
} else if (type.compare("INT") == 0) {
599599
this->pFits->pHDU().addKey(keyword, std::stoi(value), comment);
600+
} else if (type.compare("LONG") == 0) {
601+
this->pFits->pHDU().addKey(keyword, std::stol(value), comment);
600602
} else if (type.compare("FLOAT") == 0) {
601603
this->pFits->pHDU().addKey(keyword, std::stof(value), comment);
604+
} else if (type.compare("DOUBLE") == 0) {
605+
this->pFits->pHDU().addKey(keyword, std::stod(value), comment);
602606
} else if (type.compare("STRING") == 0) {
603607
this->pFits->pHDU().addKey(keyword, value, comment);
604608
} else {
605609
message.str("");
606610
message << "ERROR unknown type: " << type << " for user keyword: " << keyword << "=" << value
607-
<< ": expected {INT,FLOAT,STRING,BOOL}";
611+
<< ": expected {INT,LONG,FLOAT,DOUBLE,STRING,BOOL}";
608612
logwrite(function, message.str());
609613
}
614+
} catch (const std::exception &e) {
615+
message.str("");
616+
message << "ERROR parsing value " << value << " for keyword " << keyword << ": " << e.what();
617+
logwrite( function, message.str() );
618+
message.str("");
619+
message << function << ": parsing value " << value << " for keyword " << keyword << ": " << e.what();
620+
throw std::runtime_error( message.str() );
610621
} catch (CCfits::FitsError &err) {
611622
message.str("");
612623
message << "ERROR adding key " << keyword << "=" << value << " / " << comment << " (" << type << ") : "
613624
<< err.message();
614625
logwrite(function, message.str());
615626
}
616627
}
617-
618628
/**************** FITS_file::add_key **************************************/
619629
};

common/common.cpp

Lines changed: 82 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -52,73 +52,98 @@ namespace Common {
5252

5353
/** Common::FitsKeys::get_keytype *******************************************/
5454
/**
55-
* @fn get_keytype
5655
* @brief return the keyword type based on the keyvalue
5756
* @param std::string value
58-
* @return std::string type: "BOOL", "STRING", "DOUBLE", "INT"
57+
* @return string { "BOOL", "STRING", "DOUBLE", "FLOAT", "INT", "LONG" }
5958
*
60-
* This function looks at the contents of the value string to determine if it
61-
* contains an INT, DOUBLE, BOOL or STRING, and returns a string identifying the type.
62-
* That type is used in FITS_file::add_user_key() for adding keywords to the header.
59+
* This function looks at the contents of the value string to determine if
60+
* it contains an INT, DOUBLE, FLOAT, BOOL or STRING, and returns a string
61+
* identifying the type. That type is used in FITS_file::add_user_key()
62+
* for adding keywords to the header.
63+
*
64+
* To differentiate between double and float, the keyvalue string must end
65+
* with an "f", as in "3.14f" which returns "FLOAT" or "3.14" which returns
66+
* "DOUBLE". Decimal point is required ("1f" is not a float but "1.0f" is)
67+
*
68+
* To differentiate between int and long, keyvalue string must end with "l"
69+
* as in "100l" which returns "LONG" or "100" which returns "INT". Since a
70+
* long can't have a decimal, using 'l' with a decimal point is a string.
6371
*
6472
*/
6573
std::string FitsKeys::get_keytype(std::string keyvalue) {
6674
std::size_t pos(0);
6775

76+
// If it's empty then what else can it be but string
77+
if ( keyvalue.empty() ) return std::string("STRING");
78+
6879
// if the entire string is either (exactly) T or F then it's a boolean
6980
if (keyvalue == "T" || keyvalue == "F") {
7081
return std::string("BOOL");
7182
}
7283

7384
// skip the whitespaces
74-
pos = keyvalue.find_first_not_of(' ');
75-
if (pos == keyvalue.size()) return std::string("STRING"); // all spaces, so it's a string
76-
77-
// check the significand
78-
if (keyvalue[pos] == '+' || keyvalue[pos] == '-') ++pos; // skip the sign if exist
79-
80-
// count the number of digits and number of decimal points
81-
int n_nm, n_pt;
82-
for (n_nm = 0, n_pt = 0; std::isdigit(keyvalue[pos]) || keyvalue[pos] == '.'; ++pos) {
83-
keyvalue[pos] == '.' ? ++n_pt : ++n_nm;
84-
}
85-
86-
if (n_pt > 1 || n_nm < 1 || pos < keyvalue.size()) {
87-
// no more than one point, no numbers, or a non-digit character
88-
return std::string("STRING"); // then it's a string
89-
}
90-
91-
// skip the trailing whitespaces
92-
while (keyvalue[pos] == ' ') {
93-
++pos;
85+
if (keyvalue.find_first_not_of(' ') != std::string::npos) {
86+
pos = keyvalue.find_first_not_of(' ');
9487
}
88+
if (pos == keyvalue.size()) return std::string("STRING"); // all spaces, so it's a string
9589

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

107-
// If it's an INT or a DOUBLE then try to convert the value to INT or DOUBLE.
108-
// If that conversion fails then set the type to STRING.
93+
// count the different types of characters
94+
//
95+
// number of decimal points
96+
auto n_pt = std::count(keyvalue.begin(), keyvalue.end(), '.');
97+
// number of numerics
98+
auto n_nm = std::count_if(keyvalue.begin(), keyvalue.end(), [](unsigned char c) {
99+
return std::isdigit(c);
100+
} );
101+
// number of string chars, non-numerics, non-decimals
102+
auto n_ch = std::count_if(keyvalue.begin(), keyvalue.end(), [](unsigned char c) {
103+
return (!std::isdigit(c) && c!='.');
104+
} );
105+
106+
// number of decimals points, numeric and non-numeric chars determines the type
107+
// verify by trying to convert to that type and return string on conversion failure
109108
//
110109
try {
111-
if (check_type == "INT") std::stoi(keyvalue);
112-
if (check_type == "DOUBLE") std::stod(keyvalue);
113-
} catch (std::invalid_argument &) {
110+
// no numbers or more than one decimal point can't be any type of number, so string
111+
if ( n_nm==0 || n_pt > 1 ) {
114112
return std::string("STRING");
115-
}
116-
catch (std::out_of_range &) {
113+
}
114+
else
115+
// at least one digit, exactly one decimal point and only and last character is "f" is a float
116+
if ( n_nm > 0 && n_pt==1 && n_ch==1 && keyvalue.back()=='f' ) {
117+
std::stof(keyvalue);
118+
return std::string("FLOAT");
119+
}
120+
else
121+
// at least one digit, exactly one decimal point and no chars is double
122+
if ( n_nm > 0 && n_pt==1 && n_ch==0 ) {
123+
std::stod(keyvalue);
124+
return std::string("DOUBLE");
125+
}
126+
else
127+
// at least one digit, no decimal point and no chars is int
128+
if ( n_nm > 0 && n_pt==0 && n_ch==0 ) {
129+
std::stoi(keyvalue);
130+
return std::string("INT");
131+
}
132+
else
133+
// at least one digit, no decimal point and only and last char is "l" is long
134+
if ( n_nm > 0 && n_pt==0 && n_ch==1 && keyvalue.back()=='l' ) {
135+
std::stol(keyvalue);
136+
return std::string("LONG");
137+
}
138+
else
139+
// what else can it be but string
117140
return std::string("STRING");
118141
}
119-
return check_type;
142+
catch (const std::exception &) {
143+
// any numeric conversion failure sets type as string
144+
return std::string("STRING");
145+
}
120146
}
121-
122147
/** Common::FitsKeys::get_keytype *******************************************/
123148

124149

@@ -230,6 +255,19 @@ namespace Common {
230255
this->keydb[keyword].keyvalue = keyvalue;
231256
this->keydb[keyword].keycomment = keycomment;
232257

258+
// long and float are designated by a modifier char which must be removed
259+
//
260+
if (this->keydb[keyword].keytype=="LONG") {
261+
if (!this->keydb[keyword].keyvalue.empty() && this->keydb[keyword].keyvalue.back()=='l') {
262+
this->keydb[keyword].keyvalue.pop_back();
263+
}
264+
}
265+
if (this->keydb[keyword].keytype=="FLOAT") {
266+
if (!this->keydb[keyword].keyvalue.empty() && this->keydb[keyword].keyvalue.back()=='f') {
267+
this->keydb[keyword].keyvalue.pop_back();
268+
}
269+
}
270+
233271
#ifdef LOGLEVEL_DEBUG
234272
message.str(""); message << "[DEBUG] added key: " << keyword << "=" << keyvalue << " (" << this->keydb[keyword].keytype << ") // " << keycomment;
235273
logwrite( function, message.str() );

0 commit comments

Comments
 (0)