Skip to content

Commit 9d15094

Browse files
committed
Add CAEN Hexagon ".gxml" file format.
Only tested with a very limited number of example files.
1 parent a100733 commit 9d15094

File tree

3 files changed

+209
-5
lines changed

3 files changed

+209
-5
lines changed

SpecUtils/SpecFile.h

+8-1
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,11 @@ enum class ParserType : int
173173
Lzs,
174174
/** Scan Data XML */
175175
ScanDataXml,
176-
/** Bridgeport MCA-3000 JSON files */
176+
/** Bridgeport MCA-3000 JSON files. */
177177
Json,
178+
/** CAEN Hexagon MCA gxml format. */
179+
CaenHexagonGXml,
180+
178181
/** Automatically determine format - should be safe to be used with any format
179182
that can be parsed. Will first guess format based on file extension, then
180183
on initial file contents, and if still not successfully identified, will try
@@ -1670,6 +1673,7 @@ class SpecFile
16701673
bool load_lzs_file( const std::string &filename );
16711674
bool load_xml_scan_data_file( const std::string &filename );
16721675
bool load_json_file( const std::string &filename );
1676+
bool load_caen_gxml_file(const std::string& filename);
16731677

16741678
//load_from_N42: loads spectrum from a stream. If failure, will return false
16751679
// and set the stream position back to original position.
@@ -1792,6 +1796,9 @@ class SpecFile
17921796
/** Loads Bridgeport MCA-3000 JSON files. */
17931797
bool load_from_json( std::istream &input );
17941798

1799+
/** Load from a CAEN Hexagon gxml file. */
1800+
bool load_from_caen_gxml(std::istream& input);
1801+
17951802
//cleanup_after_load(): Fixes up inconsistent calibrations, binnings and such,
17961803
// May throw exception on error.
17971804
enum CleanupAfterLoadFlags

src/SpecFile.cpp

+18-1
Original file line numberDiff line numberDiff line change
@@ -4242,6 +4242,10 @@ bool SpecFile::load_file( const std::string &filename,
42424242
success = load_xml_scan_data_file(filename);
42434243
break;
42444244

4245+
case ParserType::CaenHexagonGXml:
4246+
success = load_caen_gxml_file(filename);
4247+
break;
4248+
42454249
case ParserType::MicroRaider:
42464250
success = load_json_file( filename );
42474251
break;
@@ -4254,7 +4258,8 @@ bool SpecFile::load_file( const std::string &filename,
42544258
triedCnf = false, triedMps = false, triedSPM = false, triedMCA = false,
42554259
triedOrtecLM = false, triedMicroRaider = false, triedAram = false,
42564260
triedTka = false, triedMultiAct = false, triedPhd = false,
4257-
triedLzs = false, triedXmlScanData = false, triedJson = false;
4261+
triedLzs = false, triedXmlScanData = false, triedJson = false,
4262+
tried_gxml = false;
42584263

42594264
if( !orig_file_ending.empty() )
42604265
{
@@ -4406,6 +4411,15 @@ bool SpecFile::load_file( const std::string &filename,
44064411
success = load_json_file(filename);
44074412
if (success) break;
44084413
}//if( orig_file_ending=="xml" )
4414+
4415+
4416+
if (orig_file_ending == "gxml")
4417+
{
4418+
tried_gxml = true;
4419+
success = load_caen_gxml_file(filename);
4420+
if (success) break;
4421+
}//if( orig_file_ending=="gxml" )
4422+
44094423
}//if( !orig_file_ending.empty() ) / else
44104424

44114425
if( !success && !triedSpc )
@@ -4471,6 +4485,9 @@ bool SpecFile::load_file( const std::string &filename,
44714485
if (!success && !triedJson)
44724486
success = load_json_file(filename);
44734487

4488+
if (!success && !tried_gxml)
4489+
success = load_caen_gxml_file(filename);
4490+
44744491
break;
44754492
}//case Auto
44764493
};//switch( parser_type )

src/SpecFile_xml_other.cpp

+183-3
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,28 @@ string rsp_name( const string &name )
8888
return name;
8989
}//string rsp_name(...)
9090

91+
92+
bool is_candidate_gxml(std::istream& input)
93+
{
94+
if (!input)
95+
return false;
96+
97+
char buffer[257] = { '\0' };
98+
const istream::pos_type start_pos = input.tellg();
99+
const bool successful_read = !!input.read(buffer, 256);
100+
101+
input.clear();
102+
input.seekg(start_pos, ios::beg);
103+
104+
if (!successful_read)
105+
return false;
106+
107+
if (strstr(buffer, "<BGAMMA>") || strstr(buffer, "<bgamma>"))
108+
return true;
109+
110+
return false;
111+
}//bool is_candidate_gxml(...)
112+
91113
}//namespace
92114

93115

@@ -134,14 +156,14 @@ bool SpecFile::load_from_xml_scan_data( std::istream &input )
134156

135157
rapidxml::file<char> input_file( input );
136158

137-
rapidxml::xml_document<char> doc;
159+
std::unique_ptr<rapidxml::xml_document<char>> doc( new rapidxml::xml_document<char>() );
138160

139161
char *data = input_file.data();
140162
char *data_end = data + input_file.size();
141163

142-
doc.parse<rapidxml::parse_trim_whitespace | rapidxml::allow_sloppy_parse>( data, data_end );
164+
doc->parse<rapidxml::parse_trim_whitespace | rapidxml::allow_sloppy_parse>( data, data_end );
143165

144-
const rapidxml::xml_node<char> *scanData = XML_FIRST_NODE((&doc),"scanData");
166+
const rapidxml::xml_node<char> *scanData = XML_FIRST_NODE(doc.get(), "scanData");
145167
if( !scanData )
146168
throw runtime_error( "No scanData element" );
147169

@@ -464,4 +486,162 @@ bool SpecFile::load_from_xml_scan_data( std::istream &input )
464486
return true;
465487
}//bool SpecFile::load_from_xml_scan_data(...)
466488

489+
490+
491+
bool SpecFile::load_caen_gxml_file(const std::string& filename)
492+
{
493+
#ifdef _WIN32
494+
ifstream input(convert_from_utf8_to_utf16(filename).c_str(), ios_base::binary | ios_base::in);
495+
#else
496+
ifstream input(filename.c_str(), ios_base::binary | ios_base::in);
497+
#endif
498+
499+
if (!input.is_open())
500+
return false;
501+
502+
const bool success = load_from_caen_gxml(input);
503+
504+
if (success)
505+
filename_ = filename;
506+
507+
return success;
508+
}//bool load_caen_gxml_file(const std::string& filename)
509+
510+
511+
bool SpecFile::load_from_caen_gxml(std::istream& input)
512+
{
513+
reset();
514+
515+
if (!input.good())
516+
return false;
517+
518+
std::unique_lock<std::recursive_mutex> scoped_lock(mutex_);
519+
520+
const istream::pos_type start_pos = input.tellg();
521+
522+
try
523+
{
524+
input.unsetf(ios::skipws);
525+
526+
if (!is_candidate_gxml(input))
527+
throw runtime_error("Not GXML file candidate.");
528+
529+
rapidxml::file<char> input_file(input);
530+
531+
std::unique_ptr<rapidxml::xml_document<char>> doc( new rapidxml::xml_document<char>() );
532+
533+
char* data = input_file.data();
534+
char* data_end = data + input_file.size();
535+
536+
doc->parse<rapidxml::parse_trim_whitespace | rapidxml::allow_sloppy_parse>(data, data_end);
537+
538+
const rapidxml::xml_node<char>* const bgamma_node = XML_FIRST_INODE(doc.get(), "BGAMMA");
539+
if (!bgamma_node)
540+
throw runtime_error("No BGAMMA element");
541+
542+
const rapidxml::xml_node<char>* const spectrum_node = XML_FIRST_INODE(bgamma_node, "SPECTRUM");
543+
if (!spectrum_node)
544+
throw runtime_error("No SPECTRUM element");
545+
546+
const rapidxml::xml_node<char>* const data_node = XML_FIRST_INODE(spectrum_node, "DATA");
547+
if (!data_node || (data_node->value_size() < 16))
548+
throw runtime_error("No spectrum DATA element");
549+
550+
vector<string> warnings, remarks;
551+
auto counts = make_shared<vector<float>>();
552+
if (!SpecUtils::split_to_floats(data_node->value(), data_node->value_size(), *counts))
553+
warnings.push_back("May not have read in all channel counts.");
554+
555+
if (counts->size() < 16)
556+
throw runtime_error("No channel counts");
557+
558+
float live_time = 0.0f, real_time = 0.0f;
559+
const rapidxml::xml_node<char>* const lt_node = XML_FIRST_INODE(spectrum_node, "ELT");
560+
if (!lt_node
561+
|| !lt_node->value_size()
562+
|| !SpecUtils::parse_float(lt_node->value(), lt_node->value_size(), live_time) )
563+
{
564+
warnings.push_back("Unable to parse live time.");
565+
}
566+
567+
const rapidxml::xml_node<char>* const rt_node = XML_FIRST_INODE(spectrum_node, "ERT");
568+
if (!rt_node
569+
|| !rt_node->value_size()
570+
|| !SpecUtils::parse_float(rt_node->value(), rt_node->value_size(), real_time))
571+
{
572+
warnings.push_back("Unable to parse real time.");
573+
}
574+
575+
// From the one example I have, the <DATETIME> node is before START/STOP time, so we'll ignore for now
576+
//const rapidxml::xml_node<char>* const datetime_node = XML_FIRST_INODE(bgamma_node, "DATETIME");
577+
578+
const rapidxml::xml_node<char>* const meas_node = XML_FIRST_INODE(bgamma_node, "MEASUREMENT");
579+
580+
SpecUtils::time_point_t start_time{};
581+
if (meas_node)
582+
{
583+
const rapidxml::xml_node<char>* const operator_node = XML_FIRST_INODE(meas_node, "OPERATOR");
584+
const rapidxml::xml_node<char>* const start_node = XML_FIRST_INODE(meas_node, "START");
585+
const rapidxml::xml_node<char>* const stop_node = XML_FIRST_INODE(meas_node, "STOP");
586+
const rapidxml::xml_node<char>* const comments_node = XML_FIRST_INODE(meas_node, "COMMENTS");
587+
const rapidxml::xml_node<char>* const tags_node = XML_FIRST_INODE(meas_node, "TAGS");
588+
if (start_node && start_node->value_size())
589+
start_time = SpecUtils::time_from_string(xml_value_str(start_node));
590+
if (comments_node && comments_node->value_size())
591+
remarks.push_back(xml_value_str(comments_node));
592+
if (tags_node && tags_node->value_size())
593+
remarks.push_back("TAGS: " + xml_value_str(tags_node));
594+
if (operator_node && operator_node->value_size())
595+
remarks.push_back("Operator: " + xml_value_str(operator_node));
596+
}//if (meas_node)
597+
598+
599+
const rapidxml::xml_node<char>* const ch_start_node = XML_FIRST_INODE(spectrum_node, "CHNNLSTART");
600+
if (ch_start_node && ch_start_node->value() && !xml_value_compare(ch_start_node, "0"))
601+
warnings.push_back("File defined a channel start of '"
602+
+ xml_value_str(ch_start_node) + "' - which is not handled." );
603+
604+
const rapidxml::xml_node<char>* const ch_end_node = XML_FIRST_INODE(spectrum_node, "CHNNLEND");
605+
if (ch_end_node && ch_end_node->value())
606+
{
607+
int val = 0;
608+
parse_int(ch_end_node->value(), ch_end_node->value_size(), val);
609+
if( val != static_cast<int>(counts->size() - 1) )
610+
warnings.push_back("File defined a channel end of '"
611+
+ xml_value_str(ch_end_node) + "' - which is not handled.");
612+
}//if (ch_end_node && ch_end_node->value())
613+
614+
615+
auto meas = make_shared<Measurement>();
616+
meas->gamma_counts_ = counts;
617+
meas->start_time_ = start_time;
618+
meas->real_time_ = real_time;
619+
meas->live_time_ = live_time;
620+
meas->gamma_count_sum_ = 0.0;
621+
for (const float& v : *counts)
622+
meas->gamma_count_sum_ += v;
623+
624+
measurements_.push_back(meas);
625+
parse_warnings_ = warnings;
626+
remarks_ = remarks;
627+
manufacturer_ = "CAEN";
628+
instrument_model_ = "Hexagon";
629+
instrument_type_ = "Other";
630+
detector_type_ = DetectorType::Unknown;
631+
632+
cleanup_after_load();
633+
634+
return true;
635+
}catch (std::exception& e)
636+
{
637+
input.clear();
638+
input.seekg(start_pos, ios::beg);
639+
reset();
640+
return false;
641+
}//try/catch
642+
643+
return true;
644+
}//bool load_from_caen_gxml(std::istream& input)
645+
646+
467647
}//namespace SpecUtils

0 commit comments

Comments
 (0)