@@ -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