File indexing completed on 2025-08-03 08:21:08
0001 #include "TpotMon.h"
0002 #include "TpotMonDefs.h"
0003
0004 #include <onlmon/OnlMon.h> // for OnlMon
0005 #include <onlmon/OnlMonServer.h>
0006
0007 #include <Event/msg_profile.h>
0008 #include <Event/Event.h>
0009
0010 #include <micromegas/MicromegasDefs.h>
0011
0012 #include <TH1.h>
0013 #include <TH2.h>
0014 #include <TH2Poly.h>
0015
0016 #include <cmath>
0017 #include <cstdio> // for printf
0018 #include <cstdlib>
0019 #include <fstream>
0020 #include <iostream>
0021 #include <memory>
0022 #include <sstream>
0023 #include <string> // for allocator, string, char_traits
0024
0025 namespace
0026 {
0027
0028 enum
0029 {
0030 TRGMESSAGE = 1,
0031 FILLMESSAGE = 2
0032 };
0033
0034
0035 std::vector<double> get_x( const MicromegasGeometry::point_list_t& point_list )
0036 {
0037 std::vector<double> out;
0038 std::transform( point_list.begin(), point_list.end(), std::back_inserter( out ), []( const auto& p ) { return p.first; } );
0039 return out;
0040 }
0041
0042
0043 std::vector<double> get_y( const MicromegasGeometry::point_list_t& point_list )
0044 {
0045 std::vector<double> out;
0046 std::transform( point_list.begin(), point_list.end(), std::back_inserter( out ), []( const auto& p ) { return p.second; } );
0047 return out;
0048 }
0049
0050
0051 std::ostream& operator << ( std::ostream&o, const TpotMon::sample_window_t& window )
0052 {
0053 o << "{ " << window.first << ", " << window.second << "}";
0054 return o;
0055 }
0056
0057
0058 static constexpr int m_nsampa_fee = 8;
0059
0060
0061 enum ModeBitType
0062 {
0063 BX_COUNTER_SYNC_T = 0,
0064 ELINK_HEARTBEAT_T = 1,
0065 SAMPA_EVENT_TRIGGER_T = 2,
0066 CLEAR_LV1_LAST_T = 6,
0067 CLEAR_LV1_ENDAT_T = 7
0068 };
0069
0070
0071
0072
0073
0074 bool is_masked( int fee_id, int channel )
0075 { return fee_id==8 && channel>=128; }
0076
0077 }
0078
0079
0080 TpotMon::TpotMon(const std::string &name)
0081 : OnlMon(name)
0082 {
0083
0084
0085 const auto tpotcalib = getenv("TPOTCALIB");
0086 if (!tpotcalib)
0087 {
0088 std::cout << "TpotMon::TpotMon - TPOTCALIB environment variable not set" << std::endl;
0089 exit(1);
0090 }
0091
0092 m_calibration_filename = std::string(tpotcalib) + "/" + "TPOT_Pedestal-000.root";
0093 }
0094
0095
0096 int TpotMon::Init()
0097 {
0098
0099 if( Verbosity() )
0100 {
0101 std::cout << "TpotMon::Init - m_calibration_filename: " << m_calibration_filename << std::endl;
0102 std::cout << "TpotMon::Init - m_max_sample: " << m_max_sample << std::endl;
0103 std::cout << "TpotMon::Init - m_sample_window_signal: " << m_sample_window_signal << std::endl;
0104 std::cout << "TpotMon::Init - m_n_sigma: " << m_n_sigma << std::endl;
0105 }
0106
0107
0108 if( std::ifstream( m_calibration_filename.c_str() ).good() )
0109 {
0110 m_calibration_data.read( m_calibration_filename );
0111 } else {
0112 std::cout << "TpotMon::Init -"
0113 << " file " << m_calibration_filename << " cannot be opened."
0114 << " No calibration loaded"
0115 << std::endl;
0116 }
0117
0118
0119 auto se = OnlMonServer::instance();
0120
0121
0122 const auto fee_id_list = m_mapping.get_fee_id_list();
0123 for( const auto& fee_id:fee_id_list )
0124 {
0125 const auto tile_id = MicromegasDefs::getTileId( m_mapping.get_hitsetkey(fee_id));
0126 m_tile_centers.emplace(fee_id, m_geometry.get_tile_center(tile_id));
0127 }
0128
0129
0130
0131 m_counters = new TH1F( "m_counters", "counters", 10, 0, 10 );
0132 m_counters->GetXaxis()->SetBinLabel(TpotMonDefs::kEventCounter, "RCDAQ frame" );
0133 m_counters->GetXaxis()->SetBinLabel(TpotMonDefs::kValidEventCounter, "valid RCDAQ frames" );
0134 m_counters->GetXaxis()->SetBinLabel(TpotMonDefs::kTriggerCounter, "triggers" );
0135 m_counters->GetXaxis()->SetBinLabel(TpotMonDefs::kHeartBeatCounter, "heartbeats" );
0136 se->registerHisto(this, m_counters);
0137
0138
0139 m_detector_multiplicity_phi = new TH2Poly( "m_detector_multiplicity_phi", "multiplicity (#phi); z (cm); x (cm)", -120, 120, -60, 60 );
0140 m_detector_occupancy_phi = new TH2Poly( "m_detector_occupancy_phi", "occupancy (#phi); z (cm); x (cm);occupancy (%)", -120, 120, -60, 60 );
0141 se->registerHisto(this, m_detector_occupancy_phi);
0142
0143 m_detector_multiplicity_z = new TH2Poly( "m_detector_multiplicity_z", "multiplicity (z); z (cm); x (cm)", -120, 120, -60, 60 );
0144 m_detector_occupancy_z = new TH2Poly( "m_detector_occupancy_z", "occupancy (z); z (cm); x(cm);occupancy (%)", -120, 120, -60, 60 );
0145 se->registerHisto(this, m_detector_occupancy_z);
0146
0147
0148 for( auto&& h:{m_detector_multiplicity_phi, m_detector_occupancy_phi, m_detector_multiplicity_z, m_detector_occupancy_z } )
0149 {
0150 setup_detector_bins( h );
0151 h->SetMinimum(0);
0152 }
0153
0154
0155 m_resist_multiplicity_phi = new TH2Poly( "m_resist_multiplicity_phi", "multiplicity (#phi); z (cm); x (cm)", -120, 120, -60, 60 );
0156 m_resist_occupancy_phi = new TH2Poly( "m_resist_occupancy_phi", "occupancy (#phi); z (cm); x (cm);occupancy (%)", -120, 120, -60, 60 );
0157 se->registerHisto(this, m_resist_occupancy_phi);
0158
0159 m_resist_multiplicity_z = new TH2Poly( "m_resist_multiplicity_z", "multiplicity (z); z (cm); x (cm)", -120, 120, -60, 60 );
0160 m_resist_occupancy_z = new TH2Poly( "m_resist_occupancy_z", "occupancy (z); z (cm); x(cm);occupancy (%)", -120, 120, -60, 60 );
0161 se->registerHisto(this, m_resist_occupancy_z);
0162
0163
0164 for( auto&& h:{m_resist_multiplicity_z, m_resist_occupancy_z } )
0165 {
0166 setup_resist_bins( h, MicromegasDefs::SegmentationType::SEGMENTATION_Z );
0167 h->SetMinimum(0);
0168 }
0169
0170 for( auto&& h:{m_resist_multiplicity_phi, m_resist_occupancy_phi } )
0171 {
0172 setup_resist_bins( h, MicromegasDefs::SegmentationType::SEGMENTATION_PHI );
0173 h->SetMinimum(0);
0174 }
0175
0176 for( const auto& fee_id:fee_id_list )
0177 {
0178
0179 const auto& detector_name=m_mapping.get_detname_sphenix(fee_id);
0180
0181 detector_histograms_t detector_histograms;
0182
0183 detector_histograms.m_counts_sample = new TH1I(
0184 Form( "m_counts_sample_%s", detector_name.c_str() ),
0185 Form( "hit count vs sample (%s);sample;counts", detector_name.c_str() ),
0186 m_max_sample, 0, m_max_sample );
0187 se->registerHisto(this, detector_histograms.m_counts_sample);
0188
0189 static constexpr int max_adc = 1100;
0190 detector_histograms.m_adc_sample = new TH2I(
0191 Form( "m_adc_sample_%s", detector_name.c_str() ),
0192 Form( "adc count vs sample (%s);sample;adc", detector_name.c_str() ),
0193 m_max_sample, 0, m_max_sample,
0194 max_adc, 0, max_adc );
0195 se->registerHisto(this, detector_histograms.m_adc_sample);
0196
0197 detector_histograms.m_adc_channel = new TH2I(
0198 Form( "m_adc_channel_%s", detector_name.c_str() ),
0199 Form( "adc count vs strip (%s);strip;adc", detector_name.c_str() ),
0200 MicromegasDefs::m_nchannels_fee, 0, MicromegasDefs::m_nchannels_fee,
0201 max_adc, 0, max_adc );
0202 se->registerHisto(this, detector_histograms.m_adc_channel);
0203
0204
0205 static constexpr int max_sample = 25;
0206 detector_histograms.m_sample_channel = new TH2I(
0207 Form("m_sample_channel_%s", detector_name.c_str() ),
0208 Form("strip vs sample (%s);strip;sample", detector_name.c_str()),
0209 MicromegasDefs::m_nchannels_fee, 0, MicromegasDefs::m_nchannels_fee,
0210 max_sample, 0 , max_sample );
0211 se->registerHisto(this, detector_histograms.m_sample_channel);
0212
0213
0214 static constexpr double max_hit_charge = 1100;
0215 detector_histograms.m_hit_charge = new TH1I(
0216 Form( "m_hit_charge_%s", detector_name.c_str() ),
0217 Form( "hit charge distribution (%s);adc", detector_name.c_str() ),
0218 100, 0, max_hit_charge );
0219 se->registerHisto(this, detector_histograms.m_hit_charge);
0220
0221
0222 detector_histograms.m_hit_multiplicity = new TH1I(
0223 Form( "m_hit_multiplicity_%s", detector_name.c_str() ),
0224 Form( "hit multiplicity (%s);#hits", detector_name.c_str() ),
0225 MicromegasDefs::m_nchannels_fee+5, 0, MicromegasDefs::m_nchannels_fee+5 );
0226 se->registerHisto(this, detector_histograms.m_hit_multiplicity);
0227
0228
0229 detector_histograms.m_wf_vs_channel = new TH1F(
0230 Form( "m_wf_vs_channel_%s", detector_name.c_str() ),
0231 Form( "waveform profile (%s);strip", detector_name.c_str() ),
0232 MicromegasDefs::m_nchannels_fee, 0, MicromegasDefs::m_nchannels_fee );
0233 se->registerHisto(this, detector_histograms.m_wf_vs_channel);
0234
0235
0236 detector_histograms.m_hit_vs_channel = new TH1F(
0237 Form( "m_hit_vs_channel_%s", detector_name.c_str() ),
0238 Form( "hit profile (%s);strip", detector_name.c_str() ),
0239 MicromegasDefs::m_nchannels_fee, 0, MicromegasDefs::m_nchannels_fee );
0240 se->registerHisto(this, detector_histograms.m_hit_vs_channel);
0241
0242
0243 detector_histograms.m_heartbeat_vs_channel = new TH1F(
0244 Form( "m_heartbeat_vs_channel_%s", detector_name.c_str() ),
0245 Form( "heartbeat profile (%s);channel", detector_name.c_str() ),
0246 m_nsampa_fee, 0, MicromegasDefs::m_nchannels_fee );
0247 se->registerHisto(this, detector_histograms.m_heartbeat_vs_channel);
0248
0249
0250 m_detector_histograms.emplace( fee_id, std::move( detector_histograms ) );
0251
0252 }
0253
0254
0255 Reset();
0256 return 0;
0257 }
0258
0259
0260 int TpotMon::BeginRun(const int )
0261 {
0262
0263
0264 return 0;
0265 }
0266
0267
0268 int TpotMon::process_event(Event* event)
0269 {
0270
0271
0272 auto increment = []( TH1* h, int bin, double weight = 1.0 )
0273 { h->SetBinContent(bin, h->GetBinContent(bin)+weight ); };
0274
0275
0276 increment( m_counters, TpotMonDefs::kEventCounter );
0277
0278
0279 if( !event ) { return 0; }
0280 if(event->getEvtType() >= 8) { return 0; }
0281
0282
0283 ++m_evtcnt;
0284
0285 increment( m_counters, TpotMonDefs::kValidEventCounter );
0286
0287
0288 std::map<int, int> multiplicity;
0289
0290
0291 for( const auto& packet_id:MicromegasDefs::m_packet_ids )
0292 {
0293 std::unique_ptr<Packet> packet(event->getPacket(packet_id));
0294 if( !packet )
0295 {
0296
0297 if( Verbosity() )
0298 { std::cout << "TpotMon::process_event - packet " << packet_id << " not found" << std::endl; }
0299 continue;
0300 }
0301
0302
0303 const int n_tagger = packet->lValue(0, "N_TAGGER");
0304 int n_lvl1_tagger = 0;
0305 int n_heartbeat_tagger = 0;
0306 for (int t = 0; t < n_tagger; t++)
0307 {
0308 const bool is_lvl1_tagger( static_cast<uint8_t>(packet->lValue(t, "IS_LEVEL1_TRIGGER" )));
0309 if( is_lvl1_tagger )
0310 { ++n_lvl1_tagger; }
0311
0312
0313 const bool is_modebit = static_cast<uint8_t>(packet->lValue(t, "IS_MODEBIT"));
0314 if( is_modebit )
0315 {
0316
0317 uint64_t modebits = static_cast<uint8_t>(packet->lValue(t, "MODEBITS"));
0318 if( modebits&(1<<ELINK_HEARTBEAT_T) )
0319 { ++n_heartbeat_tagger; }
0320 }
0321 }
0322
0323
0324 increment( m_counters, TpotMonDefs::kTriggerCounter, double(n_lvl1_tagger)/MicromegasDefs::m_npackets_active );
0325 increment( m_counters, TpotMonDefs::kHeartBeatCounter, double(n_heartbeat_tagger)/MicromegasDefs::m_npackets_active );
0326
0327 m_triggercnt += double(n_lvl1_tagger)/MicromegasDefs::m_npackets_active;
0328
0329
0330 const auto n_waveforms = packet->iValue(0, "NR_WF" );
0331
0332 if( Verbosity() )
0333 { std::cout << "TpotMon::process_event - n_waveforms: " << n_waveforms << std::endl; }
0334 for( int i=0; i<n_waveforms; ++i )
0335 {
0336
0337
0338 const int type = packet->iValue(i, "TYPE");
0339
0340
0341 const auto channel = packet->iValue( i, "CHANNEL" );
0342
0343
0344 if( channel < 0 || channel >= MicromegasDefs::m_nchannels_fee )
0345 {
0346 if( Verbosity() )
0347 { std::cout << "TpotMon::process_event - invalid channel: " << channel << std::endl; }
0348 continue;
0349 }
0350
0351
0352 const int fee_id = packet->iValue(i, "FEE");
0353
0354
0355 const auto iter = m_detector_histograms.find( fee_id );
0356 if( iter == m_detector_histograms.end() )
0357 {
0358 std::cout << "TpotMon::process_event - invalid fee_id: " << fee_id << std::endl;
0359 continue;
0360 }
0361 const auto& detector_histograms = iter->second;
0362
0363
0364 const auto strip_index = m_mapping.get_physical_strip(fee_id, channel );
0365
0366
0367 if( type == MicromegasDefs::HEARTBEAT_T )
0368 {
0369
0370 detector_histograms.m_heartbeat_vs_channel->Fill( channel );
0371 continue;
0372 }
0373
0374
0375 const double pedestal = m_calibration_data.get_pedestal( fee_id, channel );
0376 const double rms = m_calibration_data.get_rms( fee_id, channel );
0377
0378
0379 const auto& [tile_x, tile_y] = m_tile_centers.at(fee_id);
0380 const auto segmentation = MicromegasDefs::getSegmentationType( m_mapping.get_hitsetkey(fee_id));
0381
0382
0383 const int samples = packet->iValue( i, "SAMPLES" );
0384 for( int is = 0; is < samples; ++is )
0385 {
0386 const uint16_t adc = packet->iValue( i, is );
0387 if( adc == MicromegasDefs::m_adc_invalid ) continue;
0388 const bool is_signal = rms>0 && (adc > m_min_adc) && (adc> pedestal+m_n_sigma*rms);
0389 if( is_signal )
0390 {
0391 if( !is_masked(fee_id,channel) )
0392 { detector_histograms.m_counts_sample->Fill( is ); }
0393
0394 detector_histograms.m_sample_channel->Fill( strip_index, is);
0395 }
0396
0397 if( !is_masked(fee_id, channel) )
0398 {
0399 detector_histograms.m_adc_sample->Fill( is, adc );
0400 detector_histograms.m_hit_charge->Fill( adc );
0401 }
0402
0403 detector_histograms.m_adc_channel->Fill( strip_index, adc );
0404
0405 }
0406
0407
0408 detector_histograms.m_wf_vs_channel->Fill( strip_index );
0409
0410
0411 bool is_signal = false;
0412 for( int is = std::max<int>(0,m_sample_window_signal.first); is < std::min<int>(samples,m_sample_window_signal.second); ++is )
0413 {
0414 const uint16_t adc = packet->iValue( i, is );
0415 if( adc == MicromegasDefs::m_adc_invalid ) continue;
0416 if( rms>0 && (adc > m_min_adc) && (adc>pedestal + m_n_sigma*rms) )
0417 {
0418 is_signal = true;
0419 break;
0420 }
0421 }
0422
0423
0424 if( is_signal )
0425 {
0426 detector_histograms.m_hit_vs_channel->Fill( strip_index );
0427
0428
0429 if( !is_masked( fee_id, channel ) )
0430 {
0431 ++multiplicity[fee_id];
0432
0433 switch( segmentation )
0434 {
0435 case MicromegasDefs::SegmentationType::SEGMENTATION_Z:
0436 m_detector_multiplicity_z->Fill( tile_x, tile_y );
0437 m_resist_multiplicity_z->Fill( tile_x + MicromegasGeometry::m_tile_length*( double(strip_index)/MicromegasDefs::m_nchannels_fee - 0.5), tile_y );
0438 break;
0439
0440 case MicromegasDefs::SegmentationType::SEGMENTATION_PHI:
0441 m_detector_multiplicity_phi->Fill( tile_x, tile_y );
0442 m_resist_multiplicity_phi->Fill( tile_x, tile_y + MicromegasGeometry::m_tile_width*( double(strip_index)/MicromegasDefs::m_nchannels_fee - 0.5) );
0443 break;
0444 }
0445 }
0446
0447 }
0448 }
0449 }
0450
0451
0452 for( auto&& [fee_id, detector_histograms]:m_detector_histograms )
0453 { detector_histograms.m_hit_multiplicity->Fill( multiplicity[fee_id] ); }
0454
0455
0456 auto copy_content = []( TH2Poly* source, TH2Poly* destination, double scale )
0457 {
0458 for( int bin = 0; bin < source->GetNumberOfBins(); ++bin )
0459 { destination->SetBinContent( bin+1, source->GetBinContent( bin+1 )*scale ); }
0460 };
0461
0462 copy_content( m_detector_multiplicity_z, m_detector_occupancy_z, 100./(m_triggercnt*MicromegasDefs::m_nchannels_fee) );
0463 copy_content( m_detector_multiplicity_phi, m_detector_occupancy_phi, 100./(m_triggercnt*MicromegasDefs::m_nchannels_fee) );
0464 copy_content( m_resist_multiplicity_z, m_resist_occupancy_z, 400./(m_triggercnt*MicromegasDefs::m_nchannels_fee) );
0465 copy_content( m_resist_multiplicity_phi, m_resist_occupancy_phi, 400./(m_triggercnt*MicromegasDefs::m_nchannels_fee) );
0466
0467 return 0;
0468 }
0469
0470
0471 int TpotMon::Reset()
0472 {
0473
0474 m_evtcnt = 0;
0475 m_triggercnt = 0;
0476
0477
0478 for( TH1* h:{
0479 m_detector_multiplicity_z, m_detector_multiplicity_phi,
0480 m_detector_occupancy_z, m_detector_occupancy_phi,
0481 m_resist_multiplicity_z, m_resist_multiplicity_phi,
0482 m_resist_occupancy_z, m_resist_occupancy_phi } )
0483 { h->Reset(); }
0484
0485 for( const auto& [fee_id,hlist]:m_detector_histograms )
0486 {
0487 for( TH1* h:std::initializer_list<TH1*>{
0488 hlist.m_counts_sample,
0489 hlist.m_adc_sample,
0490 hlist.m_adc_channel,
0491 hlist.m_sample_channel,
0492 hlist.m_hit_charge,
0493 hlist.m_hit_multiplicity,
0494 hlist.m_wf_vs_channel,
0495 hlist.m_hit_vs_channel } )
0496 { h->Reset(); }
0497 }
0498
0499 return 0;
0500 }
0501
0502
0503 void TpotMon::setup_detector_bins(TH2Poly* h2)
0504 {
0505
0506 for( size_t i = 0; i < m_geometry.get_ntiles(); ++i )
0507 {
0508 const auto boundaries = m_geometry.get_tile_boundaries( i );
0509 h2->AddBin( boundaries.size(), &get_x( boundaries )[0], &get_y( boundaries )[0] );
0510 }
0511 }
0512
0513
0514 void TpotMon::setup_resist_bins(TH2Poly* h2, MicromegasDefs::SegmentationType segmentation)
0515 {
0516
0517 for( size_t itile = 0; itile < m_geometry.get_ntiles(); ++itile )
0518 {
0519 for( size_t iresist = 0; iresist < MicromegasGeometry::m_nresist; ++iresist )
0520 {
0521 const auto boundaries = m_geometry.get_resist_boundaries( itile, iresist, segmentation );
0522 h2->AddBin( boundaries.size(), &get_x( boundaries )[0], &get_y( boundaries )[0] );
0523 }
0524 }
0525 }