@@ -3206,7 +3206,7 @@ struct N42DecodeHelper2012
3206
3206
}//std::string concat_2012_N42_characteristic_node( const rapidxml::xml_node<char> *node )
3207
3207
3208
3208
3209
- static void set_deriv_data( shared_ptr<Measurement> &meas, const string &dd_id, const string &spec_id )
3209
+ static void set_deriv_data( const shared_ptr<Measurement> &meas, const string &dd_id, const string &spec_id )
3210
3210
{
3211
3211
typedef Measurement::DerivedDataProperties DerivedProps;
3212
3212
@@ -7927,7 +7927,7 @@ namespace SpecUtils
7927
7927
7928
7928
get_2012_N42_energy_calibrations( calibrations, rad_data_node, remarks_, parse_warnings_ );
7929
7929
7930
- //XXX - implement using RadItemInformation
7930
+ //TODO: implement using RadItemInformation
7931
7931
// for( const rapidxml::xml_node<char> *rad_item_node = XML_FIRST_NODE(rad_data_node, "RadItemInformation");
7932
7932
// rad_item_node;
7933
7933
// rad_item_node = XML_NEXT_TWIN(rad_item_node) )
@@ -8182,25 +8182,52 @@ namespace SpecUtils
8182
8182
size_t numRadMeasNodes = 0;
8183
8183
std::mutex meas_mutex, calib_mutex;
8184
8184
8185
+ // Symetrica detectors _may_ have a <RadMeasurement> node with id="ForegroundMeasureSum",
8186
+ // "BackgroundMeasure...5" (for gamma), "BackgroundMeasure...6" (for neutron),
8187
+ // "CalMeasurementGamma-...", "StabMeasurement...", and then they have a bunch
8188
+ // of ("TickMeasurement..."|"ForegroundMeasure...") that are 0.2 seconds long.
8189
+ // We probably want the "ForegroundMeasureSum" + "BackgroundMeasure..." (neut+gamma
8190
+ // combined) - so we will separate all these from the "Tick" measurements, by
8191
+ // marking them as derived (which I guess they kinda are).
8192
+ // Its not great, but maybe better than nothing.
8193
+ // A particular example where this was giving trouble can be seen in ref3Z3LPD6CY6
8194
+ const bool is_symetrica = SpecUtils::istarts_with( manufacturer_, "symetrica" );
8195
+ vector<shared_ptr<vector<shared_ptr<Measurement>>>> symetrica_special_meas;
8196
+ vector<pair<string,string>> symetrica_special_attribs;
8197
+
8198
+
8199
+
8185
8200
// See note above about system that has multiple N42 documents in a single file.
8186
8201
for( const rapidxml::xml_node<char> *rad_data_node = data_node; rad_data_node;
8187
8202
rad_data_node = rad_data_node->next_sibling("RadInstrumentData") )
8188
8203
{
8189
8204
XML_FOREACH_CHILD( meas_node, rad_data_node, "RadMeasurement" )
8190
8205
{
8191
- //see ref3Z3LPD6CY6
8192
- if( numRadMeasNodes > 32 && xml_value_compare(meas_node->first_attribute("id"), "ForegroundMeasureSum") )
8193
- {
8194
- continue;
8195
- }
8196
-
8197
8206
std::shared_ptr<std::mutex> mutexptr = std::make_shared<std::mutex>();
8198
8207
auto these_meas = std::make_shared< vector<std::shared_ptr<Measurement> > >();
8199
8208
8200
8209
++numRadMeasNodes;
8201
8210
meas_mutexs.push_back( mutexptr );
8202
8211
measurements_each_meas.push_back( these_meas );
8203
8212
8213
+ if( is_symetrica )
8214
+ {
8215
+ const rapidxml::xml_attribute<char> *id_attrib = meas_node->first_attribute("id");
8216
+ const string id_val_str = xml_value_str( id_attrib );
8217
+
8218
+ if( SpecUtils::istarts_with(id_val_str, "ForegroundMeasureSum")
8219
+ || SpecUtils::istarts_with(id_val_str, "BackgroundMeasure")
8220
+ || SpecUtils::istarts_with(id_val_str, "StabMeasurement")
8221
+ || SpecUtils::istarts_with(id_val_str, "CalMeasurementGamma")
8222
+ )
8223
+ {
8224
+ symetrica_special_meas.push_back( these_meas );
8225
+
8226
+ const string spec_id = xml_value_str( XML_FIRST_NODE(meas_node, "Spectrum") );
8227
+ symetrica_special_attribs.push_back( std::make_pair(id_val_str, spec_id) );
8228
+ }//
8229
+ }//if( is_symetrica )
8230
+
8204
8231
workerpool.post( [these_meas,meas_node,&id_to_dettype,&calibrations,mutexptr,&calib_mutex](){
8205
8232
N42DecodeHelper2012::decode_2012_N42_rad_measurement_node( *these_meas, meas_node, &id_to_dettype, &calibrations, *mutexptr, calib_mutex );
8206
8233
} );
@@ -8223,6 +8250,100 @@ namespace SpecUtils
8223
8250
8224
8251
workerpool.join();
8225
8252
8253
+
8254
+ // Now go back up and mark Symetrica special measurements as derived, so this way the
8255
+ // "raw" and "derived" datas will kinda be sensical.
8256
+ assert( symetrica_special_meas.size() == symetrica_special_attribs.size() );
8257
+ if( !symetrica_special_meas.empty() )
8258
+ {
8259
+ // We will look for a background neutron-only record, and a background gamma-only
8260
+ // record, and combine them, if found (and then remove from `measurements_each_meas`)
8261
+ // TODO: For multi-detector systems (portals) we should actually try to match the neutrons
8262
+ // to gammas for each detector, but we arent doing that yet
8263
+ bool single_gamma_neutron_back = true;
8264
+ shared_ptr<Measurement> neut_only_back, gamma_only_back;
8265
+
8266
+ for( size_t sym_index = 0;
8267
+ single_gamma_neutron_back && (sym_index < symetrica_special_meas.size()); ++sym_index )
8268
+ {
8269
+ if( !symetrica_special_meas[sym_index] || (sym_index >= symetrica_special_attribs.size()) )
8270
+ continue;
8271
+
8272
+ const shared_ptr<vector<shared_ptr<Measurement>>> &measv = symetrica_special_meas[sym_index];
8273
+ const pair<string,string> &attribs = symetrica_special_attribs[sym_index];
8274
+
8275
+ for( const shared_ptr<Measurement> &m : *measv )
8276
+ {
8277
+ N42DecodeHelper2012::set_deriv_data( m, attribs.first, attribs.second );
8278
+ if( m->source_type() == SourceType::Background )
8279
+ {
8280
+ if( (m->num_gamma_channels() > 6) && !m->contained_neutron() )
8281
+ {
8282
+ single_gamma_neutron_back = (single_gamma_neutron_back && !gamma_only_back);
8283
+ gamma_only_back = m;
8284
+ }else if( !m->num_gamma_channels() && m->contained_neutron() )
8285
+ {
8286
+ single_gamma_neutron_back = (single_gamma_neutron_back && !neut_only_back);
8287
+ neut_only_back = m;
8288
+ }
8289
+ if( !single_gamma_neutron_back )
8290
+ break;
8291
+ }//if( m->source_type() == SourceType::Background )
8292
+ }//for( const shared_ptr<Measurement> &m : *measv )
8293
+ }//for( const auto &meass : symetrica_special_meas )
8294
+
8295
+ if( single_gamma_neutron_back && gamma_only_back && neut_only_back )
8296
+ {
8297
+ // Below is commented out 20231004 - only leaving in for a short while to look at different variants
8298
+ //cout << "Symetrica\n\tNeutron:"
8299
+ //"\n\t\tstart_time=" << to_iso_string( neut_only_back->start_time() )
8300
+ //<< "\n\t\tRealTime=" << neut_only_back->real_time()
8301
+ //<< "\n\t\tLiveTime=" << neut_only_back->live_time()
8302
+ //<< "\n\tGamma:"
8303
+ //<< "\n\t\tstart_time=" << to_iso_string( gamma_only_back->start_time() )
8304
+ //<< "\n\t\tRealTime=" << gamma_only_back->real_time()
8305
+ //<< "\n\t\tLiveTime=" << gamma_only_back->live_time()
8306
+ //<< "\nDiffs:"
8307
+ //<< "\n\t\tStartTime: " << chrono::duration_cast<chrono::microseconds>(neut_only_back->start_time() - gamma_only_back->start_time()).count() << " us"
8308
+ //<< "\n\t\tRealTime: " << (neut_only_back->real_time() - gamma_only_back->real_time())
8309
+ //<< "\n\t\tLiveTime: " << (neut_only_back->live_time() - gamma_only_back->live_time())
8310
+ //<< endl;
8311
+
8312
+ // The neutron background isnt always at the same time, or same duration, so dont combine
8313
+ // in these cases
8314
+ const float gamma_rt = gamma_only_back->real_time();
8315
+ if( ((neut_only_back->start_time() - gamma_only_back->start_time()) < chrono::seconds(15)) //Arbitrary 15 seconds
8316
+ && fabs((neut_only_back->real_time() - gamma_rt) < 0.01*gamma_rt) ) //Arbitrary 1% match
8317
+ {
8318
+ gamma_only_back->contained_neutron_ = true;
8319
+ gamma_only_back->remarks_.push_back( "Neutron and gamma backgrounds have been combined into a single record." );
8320
+ gamma_only_back->remarks_.push_back( "Neutron Real Time: PT" + to_string(neut_only_back->real_time()) + "S" );
8321
+ gamma_only_back->remarks_.push_back( "Neutron Live Time: PT" + to_string(neut_only_back->live_time()) + "S" );
8322
+
8323
+ gamma_only_back->neutron_counts_ = neut_only_back->neutron_counts_;
8324
+ gamma_only_back->neutron_counts_sum_ = neut_only_back->neutron_counts_sum_;
8325
+
8326
+ if( (gamma_only_back->dose_rate_ >= 0.0) && (neut_only_back->dose_rate_ >= 0.0) )
8327
+ gamma_only_back->dose_rate_ += neut_only_back->dose_rate_;
8328
+ if( (gamma_only_back->exposure_rate_ >= 0.0) && (neut_only_back->exposure_rate_ >= 0.0) )
8329
+ gamma_only_back->exposure_rate_ += neut_only_back->exposure_rate_;
8330
+
8331
+ for( auto miter = begin(measurements_each_meas); miter != end(measurements_each_meas); ++miter )
8332
+ {
8333
+ auto &measv = **miter;
8334
+ auto pos = std::find( begin(measv), end(measv), neut_only_back );
8335
+ if( pos != end(measv) )
8336
+ {
8337
+ measv.erase( pos );
8338
+ break;
8339
+ }//if( pos != end(measv) )
8340
+ }
8341
+ }//if( gamma/neutron start and real times are reasonably close )
8342
+ }//if( gamma_only_back && neut_only_back )
8343
+ }//if( !symetrica_special_meas.empty() )
8344
+
8345
+
8346
+
8226
8347
for( size_t i = 0; i < measurements_each_meas.size(); ++i )
8227
8348
for( size_t j = 0; j < measurements_each_meas[i]->size(); ++j )
8228
8349
measurements_.push_back( (*measurements_each_meas[i])[j] );
0 commit comments