Skip to content

Commit 6568f94

Browse files
committed
Fix parsing Lynx CSV files.
The "Energy Cali..." line was being mistaken for a column header.
1 parent eebc5f1 commit 6568f94

File tree

1 file changed

+51
-15
lines changed

1 file changed

+51
-15
lines changed

src/SpecFile_csv.cpp

+51-15
Original file line numberDiff line numberDiff line change
@@ -1019,7 +1019,7 @@ void Measurement::set_info_from_txt_or_csv( std::istream& istr )
10191019
}
10201020

10211021
int channel = 0;
1022-
float energy = 0.0f, count = 0.0f; //, count2 = 0.0f;
1022+
float energy = 0.0f, count = 0.0f;
10231023
for( size_t col = 0; col < fields.size(); ++col )
10241024
{
10251025
if( column_map.count(col) )
@@ -1029,7 +1029,6 @@ void Measurement::set_info_from_txt_or_csv( std::istream& istr )
10291029
case kChannel: channel = atoi(fields[col].c_str()); break;
10301030
case kEnergy: energy = static_cast<float>(atof(fields[col].c_str())); break;
10311031
case kCounts: count = static_cast<float>(atof(fields[col].c_str())); break;
1032-
//case kCounts + 1: count2 = static_cast<float>(atof(fields[col].c_str())); break;
10331032
default:
10341033
// \TODO: Ignoring past the first record...
10351034
assert( column_map[col] > kCounts );
@@ -1040,12 +1039,9 @@ void Measurement::set_info_from_txt_or_csv( std::istream& istr )
10401039

10411040
if( IsNan(energy) || IsInf(energy) )
10421041
continue;
1043-
if( IsNan(count) || IsInf(count) /*|| IsNan(count2) || IsInf(count2)*/ )
1042+
if( IsNan(count) || IsInf(count) )
10441043
continue;
10451044

1046-
// if( errno )
1047-
// throw runtime_error( "Error converting to float" );
1048-
10491045
energy *= energy_units;
10501046

10511047
if( (energies->size() && (energies->back() > energy) )
@@ -1062,11 +1058,11 @@ void Measurement::set_info_from_txt_or_csv( std::istream& istr )
10621058
}while( SpecUtils::safe_get_line( istr, line, maxlen ) );
10631059

10641060
if( counts->empty() )
1065-
throw runtime_error( "Didnt find and channel counts" );
1061+
throw runtime_error( "Didnt find any channel counts" );
10661062

10671063
gamma_counts_ = counts;
10681064

1069-
if( (energies->size() >= counts->size()) && (energies->back()!=0.0f) )
1065+
if( (energies->size() >= counts->size()) && (energies->back() != 0.0f) )
10701066
{
10711067
try
10721068
{
@@ -1082,12 +1078,20 @@ void Measurement::set_info_from_txt_or_csv( std::istream& istr )
10821078

10831079
break;
10841080
}else if( column_map.empty()
1085-
&& ( istarts_with( fields[0], "channel" )
1081+
&& (
1082+
//Let counts header be "Channel", "Counts", "Ch", or similar non-English varients
1083+
istarts_with( fields[0], "channel" )
10861084
|| istarts_with( fields[0], "counts" )
1087-
|| istarts_with( fields[0], "data" )
1088-
|| istarts_with( fields[0], "energy" )
10891085
|| istarts_with( fields[0], "Ch" )
10901086
|| istarts_with( fields[0], "Канал" )
1087+
//For a single column data
1088+
|| istarts_with( fields[0], "data" )
1089+
//energy header can be like "Energy", "Energy (keV)", "En", or similar, but we dont
1090+
// want to mistake something like "Energy Calibration, ..." as a column header
1091+
|| istarts_with( fields[0], "energy (" )
1092+
|| iequals_ascii(fields[0], "energy" )
1093+
|| iequals_ascii(fields[0], "en" )
1094+
|| iequals_ascii(fields[0], "en (" )
10911095
|| fields[0]=="##" ) )
10921096
{
10931097
++nlines_used;
@@ -1345,21 +1349,53 @@ void Measurement::set_info_from_txt_or_csv( std::istream& istr )
13451349

13461350
if( c > 0 || b > 0 )
13471351
poly_calib_coeff = { d, c, b, a };
1352+
}else if( poly_calib_coeff.empty()
1353+
&& starts_with( fields[0], "energy calibration" )
1354+
&& (fields.size() >= 3)
1355+
&& starts_with( fields[1], "offset:" )
1356+
&& starts_with( fields[2], "slope:" ) )
1357+
{
1358+
// Canberra Lynx CSV files get here
1359+
//fields == {"energy calibration", "offset: -2.69", "slope: 0.37", "quadratic: -2.89e-7"}
1360+
//Note: because lower channel energies are also provided, this polynomial calibration we are
1361+
// parsing out here, wont be used.
1362+
map<string,float> vals_map{ {"offset", 0.0f}, {"slope", 0.0f}, {"quadratic", 0.0f} };
1363+
1364+
for( size_t i = 1; i < fields.size(); ++i )
1365+
{
1366+
float val;
1367+
vector<string> parts;
1368+
SpecUtils::split( parts, fields[i], ": \t" );
1369+
1370+
if( (parts.size() >= 2) && SpecUtils::parse_float(parts[1].c_str(), parts[1].size(), val) )
1371+
vals_map[parts[0]] = val;
1372+
}
1373+
1374+
poly_calib_coeff = vector<float>{ vals_map["offset"], vals_map["slope"], vals_map["quadratic"] };
1375+
while( !poly_calib_coeff.empty() && (poly_calib_coeff.back() == 0.0) )
1376+
poly_calib_coeff.resize( poly_calib_coeff.size() - 1 );
1377+
}else
1378+
{
1379+
// unidentified line
13481380
}
1349-
13501381
}//while( getline( istr, line ) )
13511382

1352-
if( nlines_total < 10 || nlines_used < static_cast<size_t>( ceil(0.25*nlines_total) ))
1383+
if( (nlines_total < 10) || (nlines_used < static_cast<size_t>( ceil(0.25*nlines_total) )) )
13531384
{
13541385
reset();
13551386
istr.seekg( orig_pos, ios::beg );
13561387
istr.clear( ios::failbit );
13571388
throw runtime_error( "Not enough (useful) lines in the file." );
13581389
}//
13591390

1391+
// If we have polynomial energy calibration terms, we will only use them if we dont already
1392+
// have another energy calibration - this includes when lower channel energies are specified
1393+
// (perhaps we should check if polynomial and lower channel energies are about the same, and
1394+
// if so, go with the polynomial)
13601395
const size_t nchannel = gamma_counts_ ? gamma_counts_->size() : size_t(0);
1361-
if( nchannel>=2 && !poly_calib_coeff.empty()
1362-
&& (energy_calibration_->type() == EnergyCalType::InvalidEquationType) )
1396+
if( (nchannel >= 2)
1397+
&& (poly_calib_coeff.size() >= 2)
1398+
&& (energy_calibration_->type() == EnergyCalType::InvalidEquationType) )
13631399
{
13641400
try
13651401
{

0 commit comments

Comments
 (0)