// $Id$ // // File: JEventProcessor_L3proc.cc // Created: Wed Aug 21 17:22:31 EDT 2013 // Creator: davidl (on Darwin harriet.local 11.4.2 i386) // // // (See hdl3.cc for detailed description) // #include #include #include using namespace std; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "JEventProcessor_L3proc.h" using namespace jana; extern bool USE_PRESSURE_RELIEF; extern float PRESSURE_RELIEF_LIMIT; extern uint64_t AUTO_KEEP_PERIOD; //------------------ // JEventProcessor_L3proc (Constructor) //------------------ JEventProcessor_L3proc::JEventProcessor_L3proc(L3farm_out *l3out):l3out(l3out) { Nevents=0; ofs_debug_input = NULL; ofs_debug_output = NULL; } //------------------ // ~JEventProcessor_L3proc (Destructor) //------------------ JEventProcessor_L3proc::~JEventProcessor_L3proc() { // l3out is not owned by this class so don't delete it. } //------------------ // init //------------------ jerror_t JEventProcessor_L3proc::init(void) { COMPACT = true; PREFER_EMULATED = false; DEBUG_FILES = false; // n.b. also defined in L3farm_out APPLY_HIT_FILTERS = false; CUT_UNKNOWNS = false; string save_events_str = ""; string detector_cut_str = ""; FDC_T_MIN = -10000.0; FDC_T_MAX = +10000.0; CDC_T_MIN = -10000.0; CDC_T_MAX = +10000.0; TOF_T_MIN = -10000.0; TOF_T_MAX = +10000.0; TAG_T_MIN = -10000.0; TAG_T_MAX = +10000.0; BCAL_T_MIN = -10000.0; BCAL_T_MAX = +10000.0; FCAL_T_MIN = -10000.0; FCAL_T_MAX = +10000.0; PS_T_MIN = -10000.0; PS_T_MAX = +10000.0; ST_T_MIN = -10000.0; ST_T_MAX = +10000.0; L1_BCAL_FAC = 1.0; L1_FCAL_FAC = 1.0; L1_THRESH = 1.0; // GeV f250_PEAK_MIN = -10000.0; TOF_PEAK_MIN = -10000.0; TAGM_PEAK_MIN = -10000.0; TAGH_PEAK_MIN = -10000.0; TAGH_MAX_FADC_CHANNEL = 10000; TAGH_MAX_TDC_CHANNEL = 10000; gPARMS->SetDefaultParameter("L3:COMPACT" , COMPACT, "Drop words where we can to reduce output file size. This shouldn't loose any vital information, but can be turned off to help with debugging."); gPARMS->SetDefaultParameter("L3:PREFER_EMULATED" , PREFER_EMULATED, "If true, then sample data will not be written to output, but emulated hits will. Otherwise, do exactly the opposite."); gPARMS->SetDefaultParameter("L3:DEBUG_FILES" , DEBUG_FILES, "Write input and output debug files in addition to the standard output."); gPARMS->SetDefaultParameter("L3:SAVE_EVENTS", save_events_str ,"Comma separated list of physics event numbers to write to output (for debugging only)"); gPARMS->SetDefaultParameter("L3:CUT_DETECTOR", detector_cut_str ,"Comma separated list of the names of detectors to cut from output (e.g 'BCAL,FCAL,PS' for debugging only)"); gPARMS->SetDefaultParameter("L3:CUT_UNKNOWNS", CUT_UNKNOWNS ,"Set this to 1 to cut low level hits found in the file that don't seem to be connected to a digi-hit"); vector p; p.push_back( gPARMS->SetDefaultParameter("L3:FDC_T_MIN", FDC_T_MIN ,"")); p.push_back( gPARMS->SetDefaultParameter("L3:FDC_T_MAX", FDC_T_MAX ,"")); p.push_back( gPARMS->SetDefaultParameter("L3:CDC_T_MIN", CDC_T_MIN ,"")); p.push_back( gPARMS->SetDefaultParameter("L3:CDC_T_MAX", CDC_T_MAX ,"")); p.push_back( gPARMS->SetDefaultParameter("L3:TOF_T_MIN", TOF_T_MIN ,"")); p.push_back( gPARMS->SetDefaultParameter("L3:TOF_T_MAX", TOF_T_MAX ,"")); p.push_back( gPARMS->SetDefaultParameter("L3:TAG_T_MIN", TAG_T_MIN ,"")); p.push_back( gPARMS->SetDefaultParameter("L3:TAG_T_MAX", TAG_T_MAX ,"")); p.push_back( gPARMS->SetDefaultParameter("L3:BCAL_T_MIN", BCAL_T_MIN ,"")); p.push_back( gPARMS->SetDefaultParameter("L3:BCAL_T_MAX", BCAL_T_MAX ,"")); p.push_back( gPARMS->SetDefaultParameter("L3:FCAL_T_MIN", FCAL_T_MIN ,"")); p.push_back( gPARMS->SetDefaultParameter("L3:FCAL_T_MAX", FCAL_T_MAX ,"")); p.push_back( gPARMS->SetDefaultParameter("L3:PS_T_MIN", PS_T_MIN ,"")); p.push_back( gPARMS->SetDefaultParameter("L3:PS_T_MAX", PS_T_MAX ,"")); p.push_back( gPARMS->SetDefaultParameter("L3:ST_T_MIN", ST_T_MIN ,"")); p.push_back( gPARMS->SetDefaultParameter("L3:ST_T_MAX", ST_T_MAX ,"")); p.push_back( gPARMS->SetDefaultParameter("L3:L1_BCAL_FAC", L1_BCAL_FAC ,"")); p.push_back( gPARMS->SetDefaultParameter("L3:L1_FCAL_FAC", L1_FCAL_FAC ,"")); p.push_back( gPARMS->SetDefaultParameter("L3:L1_THRESH", L1_THRESH ,"")); p.push_back( gPARMS->SetDefaultParameter("L3:f250_PEAK_MIN",f250_PEAK_MIN,"")); p.push_back( gPARMS->SetDefaultParameter("L3:TOF_PEAK_MIN", TOF_PEAK_MIN,"")); p.push_back( gPARMS->SetDefaultParameter("L3:TAGM_PEAK_MIN",TAGM_PEAK_MIN,"")); p.push_back( gPARMS->SetDefaultParameter("L3:TAGH_PEAK_MIN",TAGH_PEAK_MIN,"")); p.push_back( gPARMS->SetDefaultParameter("L3:TAGH_MAX_FADC_CHANNEL",TAGH_MAX_FADC_CHANNEL,"")); p.push_back( gPARMS->SetDefaultParameter("L3:TAGH_MAX_TDC_CHANNEL",TAGH_MAX_TDC_CHANNEL,"")); for(auto par : p) if(par->GetDefault()!=par->GetValue()) APPLY_HIT_FILTERS = true; if(TOF_PEAK_MIN<0 ) TOF_PEAK_MIN = f250_PEAK_MIN; if(TAGM_PEAK_MIN<0) TAGM_PEAK_MIN = f250_PEAK_MIN; if(TAGH_PEAK_MIN<0) TAGH_PEAK_MIN = f250_PEAK_MIN; SYSTEMS_TO_CUT = 0; if(detector_cut_str.length()>0){ stringstream ss(detector_cut_str); string item; while(getline(ss, item, ',')) SYSTEMS_TO_CUT |= NameToSystem(item.c_str()); if(SYSTEMS_TO_CUT != 0 ) APPLY_HIT_FILTERS = true; } if(save_events_str.length()>0){ stringstream ss(save_events_str); string item; while(getline(ss, item, ',')) SAVE_EVENTS.push_back(atoi(item.c_str())); jout << " Will write out the following " << SAVE_EVENTS.size() << " physics events: "; for(uint32_t i=0;iGetDefault()==par->GetValue()) continue; string name = par->GetKey(); if(name.find("L3:")!=0) continue; name.erase(0, 3); cout << string(25-name.length(), ' ') << name << ": " << par->GetValue() << endl; } cout << " SYSTEMS_TO_CUT: 0x" << hex << SYSTEMS_TO_CUT << dec <<" (see GlueX.h)" << endl; } // If cutting by detector system, add all rocids // for that system to global list. #define CutROCids(A,...) if(SYSTEMS_TO_CUT & A) {set s={__VA_ARGS__};ROCIDS_TO_REMOVE.insert(s.begin(),s.end());} #define CutSLOTs(A,...) if(SYSTEMS_TO_CUT & A) {set s={__VA_ARGS__};SLOTS_TO_REMOVE.insert(s.begin(),s.end());} CutROCids(SYS_FDC, 51,52,53,54,55,56,57,58,59,60,61,62,63,64); CutROCids(SYS_CDC, 25,26,27,28); CutROCids(SYS_BCAL, 31,32,33,34,35,36,37,38,39,40,41,42); CutROCids(SYS_FCAL, 11,12,13,14,15,16,17,18,19,20,21,22); CutROCids(SYS_TOF, 77,78); CutROCids(SYS_TAGM, 71,72); CutROCids(SYS_TAGH, 73,74); CutROCids(SYS_PS, 83); CutSLOTs(SYS_PS, 8403,8404,8405); CutSLOTs(SYS_PSC, 8406); CutSLOTs(SYS_RF, 9410); CutSLOTs(SYS_TPOL, 8413,8414); CutSLOTs(SYS_START, 9403,9404); CutSLOTs(SYS_TAGH, 7507,7508,7509,7510,7513,7514,7515,7516); //n.b. RF uses 1 channel from 7516!! CutSLOTs(SYS_TAGM, 7503,7504,7505,7506); if(CUT_UNKNOWNS){ SLOTS_TO_REMOVE.insert(9503); SLOTS_TO_REMOVE.insert(9504); } if(!ROCIDS_TO_REMOVE.empty()){ cout<<"ROCIDs to cut: "; for(auto r:ROCIDS_TO_REMOVE)cout<is_open() ){ jerr << "Unable to open \"hdl3_debug_input.evio\"!" << endl; delete ofs_debug_input; ofs_debug_input = NULL; }else{ jout << "Opened \"hdl3_debug_input.evio\" for debug output" << endl; } ofs_debug_output = new ofstream("hdl3_debug_output_preswap.evio"); if( !ofs_debug_output->is_open() ){ jerr << "Unable to open \"hdl3_debug_output_preswap.evio\"!" << endl; delete ofs_debug_output; ofs_debug_output = NULL; }else{ jout << "Opened \"hdl3_debug_output_preswap.evio\" for debug output" << endl; } } return NOERROR; } //------------------ // brun //------------------ jerror_t JEventProcessor_L3proc::brun(JEventLoop *loop, int32_t runnumber) { // This is called whenever the run number changes return NOERROR; } //------------------ // evnt //------------------ jerror_t JEventProcessor_L3proc::evnt(JEventLoop *loop, uint64_t eventnumber) { // Optionally write input buffer to a debug file if(DEBUG_FILES){ JEvent &jevent = loop->GetJEvent(); JEventSource *jes = jevent.GetJEventSource(); JEventSource_EVIO *jesevio = dynamic_cast(jes); if(!jesevio){ static bool warned = false; if(!warned) jerr << "Event source not a JEventSource_EVIO type!" << endl; warned = true; }else{ uint32_t *buff; uint32_t buff_size; jesevio->GetEVIOBuffer(jevent, buff, buff_size); if(ofs_debug_input) ofs_debug_input->write((char*)buff, buff_size*sizeof(uint32_t)); } } // Keep track of number of events we've seen. // (this will also be used for itrigger when it can't be obtained otherwise) Nevents++; // Initially set to false. Once we decide to keep it for any reason // we can skip additional expensive checks. bool keep_event = false; // If this is not a physics event, then automatically keep it if( !loop->GetJEvent().GetStatusBit(kSTATUS_PHYSICS_EVENT) ) keep_event = true; // Record unbiased sample // We use the eventnumber passed to us here because keeping our own // counter would require a mutex lock to guarantee we don't have // race conditions causing values to be repeated. if(AUTO_KEEP_PERIOD>0) if( (eventnumber%AUTO_KEEP_PERIOD)==0 ) keep_event = true; // If we haven't already decided to keep the event then check // the DL3Trigger object. if( !keep_event ){ // Get DL3Trigger Object try{ const DL3Trigger *l3trig = NULL; loop->GetSingle(l3trig); if(l3trig->L3_decision != DL3Trigger::kDISCARD_EVENT) keep_event = true; }catch(...){ keep_event = true; static uint32_t Nwarnings = 0; if(Nwarnings<10){ jout << "No DL3Trigger object. Event kept by default." << endl; if(++Nwarnings==10) jout << "Last Warning!" << endl; } } } // For debugging only! if(!SAVE_EVENTS.empty()){ keep_event = false; if(loop->GetJEvent().GetStatusBit(kSTATUS_PHYSICS_EVENT)){ for(uint32_t i=0; i *buff = l3out->GetBufferFromPool(); WriteEventToBuffer(loop, *buff); // Optionally write buffer to output file if(ofs_debug_output) ofs_debug_output->write((const char*)&(*buff)[0], buff->size()*sizeof(uint32_t)); // Add event to output queue l3out->AddBufferToOutput(buff); return NOERROR; } //------------------ // WriteEventToBuffer //------------------ void JEventProcessor_L3proc::WriteEventToBuffer(JEventLoop *loop, vector &buff) { /// This method will grab certain low-level objects and write them /// into EVIO banks in a format compatible with the DAQ library. // Clear output buffer buff.clear(); // First, grab all of the low level objects vector f250tts; vector f250pds; vector f250pis; vector f250wrds; vector f250configs; vector f125tts; vector f125pis; vector f125cdcpulses; vector f125fdcpulses; vector f125wrds; vector f125configs; vector caen1290hits; vector caen1290configs; vector F1hits; vector F1tts; vector F1configs; vector epicsValues; vector coda_events; vector coda_rocinfos; vector l1infos; loop->Get(f250tts); loop->Get(f250pds); loop->Get(f250pis); loop->Get(f250wrds); loop->Get(f250configs); loop->Get(f125tts); loop->Get(f125pis); loop->Get(f125cdcpulses); loop->Get(f125fdcpulses); loop->Get(f125wrds); loop->Get(f125configs); loop->Get(caen1290hits); loop->Get(caen1290configs); loop->Get(F1hits); loop->Get(F1tts); loop->Get(F1configs); loop->Get(epicsValues); loop->Get(coda_events); loop->Get(coda_rocinfos); loop->Get(l1infos); // Get the DL3Trigger object for the event. We go to some trouble // here not to activate the factory ourselves and only check if the // object already exists. This is because we may have skipped creating // the object due to this being an unbiased event. const DL3Trigger *l3trigger = NULL; JFactory_base *fac = loop->GetFactory("DL3Trigger"); if(fac){ int nobjs = fac->GetNrows(false, true); // don't create objects if not already existing if(nobjs>0){ loop->GetSingle(l3trigger, "", false); // don't throw exception if nobjs>1 } } // If there are any EPICS values then asume this is an EPICS event // with no CODA data. In this case, write the EPICS banks and then // return before writing the Physics Bank if( !epicsValues.empty() ){ WriteEPICSData(buff, epicsValues); return; } // If this is a BOR event, then just write it and return if(loop->GetJEvent().GetStatusBit(kSTATUS_BOR_EVENT)){ WriteBORData(buff, loop); return; } // If hit filters were specified, then apply them if(APPLY_HIT_FILTERS) ApplyHitFilters(loop, f250pds, f250pis, f250wrds, f125cdcpulses, f125fdcpulses, caen1290hits, F1hits); // Get list of rocids with hits set rocids; rocids.insert(1); // This *may* be from TS (don't know). There is such a id in run 2391 for(uint32_t i=0; irocid ); for(uint32_t i=0; irocid ); for(uint32_t i=0; irocid ); for(uint32_t i=0; irocid ); for(uint32_t i=0; irocid); for(uint32_t i=0; irocid); for(uint32_t i=0; irocid ); for(uint32_t i=0; irocid ); for(uint32_t i=0; irocid ); // If COMPACT is true, filter coda_rocinfos to only have rocids with hits if(COMPACT){ vector my_coda_rocinfos; for(uint32_t i=0; irocid)!=rocids.end()){ my_coda_rocinfos.push_back(rocinfo); } } // Replace coda_rocinfos with filtered list coda_rocinfos = my_coda_rocinfos; } // Physics Bank Header uint32_t physics_bank_len_idx = buff.size(); buff.push_back(0); // Physics Event Length (must be updated at the end) buff.push_back( 0xFF701001);// 0xFF70=SEB in single event mode, 0x10=bank of banks, 0x01=1event // Built Trigger Bank uint32_t built_trigger_bank_len_idx = buff.size(); buff.push_back(0); // Length buff.push_back(0xFF232000 + coda_rocinfos.size()); // 0xFF23=Trigger Bank Tag, 0x20=segment //--- Common Data Segments --- // uint64_t segments for Event Number, avg. timestamp, Run Number, and Run Type uint32_t run_number = loop->GetJEvent().GetRunNumber(); uint32_t run_type = 1; // observed in run 2931. Not sure shat it should be uint64_t event_number = loop->GetJEvent().GetEventNumber(); uint64_t avg_timestamp = time(NULL); if(!coda_events.empty()){ run_number = coda_events[0]->run_number; run_type = coda_events[0]->run_type; event_number = coda_events[0]->event_number; avg_timestamp = coda_events[0]->avg_timestamp; } buff.push_back(0xD30A0006); // 0xD3=EB id (D3 stands for hallD level 3), 0x0A=64bit segment, 0006=length of segment (in 32bit words!) buff.push_back(event_number & 0xFFFFFFFF); // low 32 bits of event number buff.push_back(event_number>>32); // high 32 bits of event number buff.push_back(avg_timestamp & 0xFFFFFFFF); // low 32 bits of avg. timestamp buff.push_back(avg_timestamp>>32); // high 32 bits of avg. timestamp buff.push_back(run_type); buff.push_back(run_number); // uint16_t segment for Event Types uint16_t event_type = 1; // observed in run 2931. Not sure shat it should be if(!coda_events.empty()){ event_type = coda_events[0]->event_type; } buff.push_back(0xD3850001); // 0xD3=EB id (D3 stands for hallD level 3), 0x85=16bit segment(8 is for padding), 0001=length of segment buff.push_back((uint32_t)event_type); //--- ROC Data --- // uint32_t segments for ROC timestamp and misc. data for(uint32_t i=0; imisc.size(); // 2 = 2 words for 64bit timestamp buff.push_back( (rocinfo->rocid<<24) + 0x00010000 + len); // 0x01=32bit segment buff.push_back(rocinfo->timestamp & 0xFFFFFFFF); // low 32 bits of timestamp buff.push_back(rocinfo->timestamp>>32); // high 32 bits of timestamp if(!rocinfo->misc.empty()){ buff.insert(buff.end(), rocinfo->misc.begin(), rocinfo->misc.end()); // add all misc words } } // Update Built trigger bank length buff[built_trigger_bank_len_idx] = buff.size() - built_trigger_bank_len_idx - 1; // Write sync event data if this is a sync event if(!l1infos.empty()) WriteTSSyncData(buff, l1infos[0]); // Write EventTag WriteEventTagData(buff, loop->GetJEvent().GetStatus(), l3trigger); // Write CAEN1290TDC hits WriteCAEN1290Data(buff, caen1290hits, caen1290configs); // Write F1TDC hits WriteF1Data(buff, F1hits, F1tts, F1configs); // Write f250 hits Writef250Data(buff, f250pds, f250pis, f250tts, f250wrds, f250configs); // Write f125 hits Writef125Data(buff, f125pis, f125cdcpulses, f125fdcpulses, f125tts, f125wrds, f125configs); // Update global header length if(!buff.empty()) buff[physics_bank_len_idx] = buff.size() - physics_bank_len_idx - 1; } //------------------ // RemoveAllHits //------------------ template static inline void RemoveAllHits(JEventLoop *loop, vector &rmv) { vector digihits; loop->Get(digihits); for(auto h : digihits){ vector hh; h->Get(hh); if(!hh.empty()) rmv.insert(rmv.end(), hh.begin(), hh.end()); } } //------------------ // ApplyHitFilters //------------------ void JEventProcessor_L3proc::ApplyHitFilters(JEventLoop *loop ,vector &f250pds ,vector &f250pis ,vector &f250wrds ,vector &f125cdcpulses ,vector &f125fdcpulses ,vector &caen1290hits ,vector &F1hits) { /// This allows one to specify hit filters which can be used /// to drop hits before writing the disentangled event. This /// is useful if trying to estimate file sizes if the timing /// windows are more restricted than what was used in an /// existing data set. // Get list of objects to drop. Since we want to cut on the // calibrated values (in ns) we first make a list of the // objects to remove from the respective lists vector rmf250pds; vector rmf250pis; vector rmf250wrds; vector rmf125cdcpulses; vector rmf125fdcpulses; vector rmcaen1290hits; vector rmF1hits; // Whole detector systems we drop by ROCID or slot #define FilterROCID(A) {\ for(auto obj:A) if(ROCIDS_TO_REMOVE.find(obj->rocid) != ROCIDS_TO_REMOVE.end()) rm ## A.push_back(obj); \ for(auto obj:A) if(SLOTS_TO_REMOVE.find(obj->rocid*100+obj->slot) != SLOTS_TO_REMOVE.end()) rm ## A.push_back(obj); \ } FilterROCID(f250pds); FilterROCID(f250pis); FilterROCID(f250wrds); FilterROCID(f125cdcpulses); FilterROCID(f125fdcpulses); FilterROCID(caen1290hits); FilterROCID(F1hits); // Apply cut to pulse_peak for f250 if specified bool cut_pulse_peak = (f250_PEAK_MIN >= 0.0); cut_pulse_peak |= (TOF_PEAK_MIN >= 0.0); cut_pulse_peak |= (TAGM_PEAK_MIN >= 0.0); cut_pulse_peak |= (TAGH_PEAK_MIN >= 0.0); if(cut_pulse_peak){ for(auto pi:f250pis){ const Df250PulsePedestal *pp = NULL; pi->GetSingle(pp); if(pp){ double peak_min = f250_PEAK_MIN; switch(pp->rocid){ case 77: peak_min = TOF_PEAK_MIN; break; case 71: peak_min = TAGM_PEAK_MIN; break; case 73: peak_min = TAGH_PEAK_MIN; break; } if((double)pp->pulse_peak < peak_min) rmf250pis.push_back(pi); } } } // Optionally cut out some TAGH slots if(TAGH_MAX_FADC_CHANNEL<10000){ for(auto pi:f250pis){ if(pi->rocid!=73) continue; uint32_t slot_chan = pi->slot*100 + pi->channel; if(slot_chan>TAGH_MAX_FADC_CHANNEL) rmf250pis.push_back(pi); } } if(TAGH_MAX_TDC_CHANNEL<10000){ for(auto h:F1hits){ if(h->rocid!=75) continue; uint32_t slot_chan = h->slot*100 + h->channel; if(slot_chan>TAGH_MAX_TDC_CHANNEL) rmF1hits.push_back(h); } } // FDC if( !(SYSTEMS_TO_CUT & SYS_FDC) ){ vector fdchits; loop->Get(fdchits); for(auto h : fdchits){ if( (h->t < FDC_T_MIN) || (h->t >FDC_T_MAX) ){ vector hf1; vector hfadc; h->Get(hf1); h->Get(hfadc); if(!hf1.empty() ) rmF1hits.insert(rmF1hits.end(), hf1.begin(), hf1.end()); if(!hfadc.empty()) rmf125fdcpulses.insert(rmf125fdcpulses.end(), hfadc.begin(), hfadc.end()); } } } // CDC if( !(SYSTEMS_TO_CUT & SYS_CDC) ){ vector cdchits; loop->Get(cdchits); for(auto h : cdchits){ if( (h->t < CDC_T_MIN) || (h->t >CDC_T_MAX) ){ vector hfadc; h->Get(hfadc); if(!hfadc.empty()) rmf125cdcpulses.insert(rmf125cdcpulses.end(), hfadc.begin(), hfadc.end()); } } } // TOF if( !(SYSTEMS_TO_CUT & SYS_TOF) ){ vector tofhits; loop->Get(tofhits); for(auto h : tofhits){ if( (h->t < TOF_T_MIN) || (h->t >TOF_T_MAX) ){ vector h1290; vector hfadc_pd; vector hfadc_pi; h->Get(h1290); h->Get(hfadc_pd); h->Get(hfadc_pi); if(!h1290.empty()) rmcaen1290hits.insert(rmcaen1290hits.end(), h1290.begin(), h1290.end()); if(!hfadc_pd.empty()) rmf250pds.insert(rmf250pds.end(), hfadc_pd.begin(), hfadc_pd.end()); if(!hfadc_pi.empty()) rmf250pis.insert(rmf250pis.end(), hfadc_pi.begin(), hfadc_pi.end()); } } } // FCAL if( !(SYSTEMS_TO_CUT & SYS_FCAL) ){ vector fcalhits; loop->Get(fcalhits); for(auto h : fcalhits){ if( (h->t < FCAL_T_MIN) || (h->t >FCAL_T_MAX) ){ vector hfadc_pd; vector hfadc_pi; h->Get(hfadc_pd); h->Get(hfadc_pi); if(!hfadc_pd.empty()) rmf250pds.insert(rmf250pds.end(), hfadc_pd.begin(), hfadc_pd.end()); if(!hfadc_pi.empty()) rmf250pis.insert(rmf250pis.end(), hfadc_pi.begin(), hfadc_pi.end()); } } } // BCAL if( !(SYSTEMS_TO_CUT & SYS_BCAL) ){ vector bcalhits; vector bcaltdchits; loop->Get(bcalhits); loop->Get(bcaltdchits); for(auto h : bcalhits){ if( (h->t < FCAL_T_MIN) || (h->t >FCAL_T_MAX) ){ vector hfadc_pd; vector hfadc_pi; h->Get(hfadc_pd); h->Get(hfadc_pi); if(!hfadc_pd.empty()) rmf250pds.insert(rmf250pds.end(), hfadc_pd.begin(), hfadc_pd.end()); if(!hfadc_pi.empty()) rmf250pis.insert(rmf250pis.end(), hfadc_pi.begin(), hfadc_pi.end()); } } for(auto h : bcaltdchits){ if( (h->t < FCAL_T_MIN) || (h->t >FCAL_T_MAX) ){ vector hf1; h->Get(hf1); if(!hf1.empty()) rmF1hits.insert(rmF1hits.end(), hf1.begin(), hf1.end()); } } } // PSC if( !(SYSTEMS_TO_CUT & SYS_PS) ){ vector pschits; loop->Get(pschits); for(auto h : pschits){ if(h->has_fADC){ if( (h->time_fadc < PS_T_MIN) || (h->time_fadc > PS_T_MAX) ){ vector pds; vector pis; h->Get(pds); h->Get(pis); if(!pds.empty()) rmf250pds.insert(rmf250pds.end(), pds.begin(), pds.end()); if(!pis.empty()) rmf250pis.insert(rmf250pis.end(), pis.begin(), pis.end()); } } if(h->has_TDC){ if( (h->time_tdc < PS_T_MIN) || (h->time_tdc > PS_T_MAX) ){ vector f1hits; h->Get(f1hits); if(!f1hits.empty()) rmF1hits.insert(rmF1hits.end(), f1hits.begin(), f1hits.end()); } } } } // PS if( !(SYSTEMS_TO_CUT & SYS_PS) ){ vector pshits; loop->Get(pshits); for(auto h : pshits){ if( (h->t < PS_T_MIN) || (h->t >PS_T_MAX) ){ vector pds; vector pis; h->Get(pds); h->Get(pis); if(!pds.empty()) rmf250pds.insert(rmf250pds.end(), pds.begin(), pds.end()); if(!pis.empty()) rmf250pis.insert(rmf250pis.end(), pis.begin(), pis.end()); } } } // TAGM // n.b. the non="Calib" factory takes the ones from "Calib" and drops // hits from adjacent, in-time columns. Thus, we need to use the "Calib" // list of hits since there is one for every digi hit. if( !(SYSTEMS_TO_CUT & SYS_TAGM) ){ vector tagmhits; loop->Get(tagmhits, "Calib"); for(auto h : tagmhits){ if(h->has_fADC){ if( (h->time_fadc < TAG_T_MIN) || (h->time_fadc > TAG_T_MAX) ){ vector pds; vector pis; h->Get(pds); h->Get(pis); if(!pds.empty()) rmf250pds.insert(rmf250pds.end(), pds.begin(), pds.end()); if(!pis.empty()) rmf250pis.insert(rmf250pis.end(), pis.begin(), pis.end()); } } if(h->has_TDC){ if( (h->time_tdc < TAG_T_MIN) || (h->time_tdc > TAG_T_MAX) ){ vector f1hits; h->Get(f1hits); if(!f1hits.empty()) rmF1hits.insert(rmF1hits.end(), f1hits.begin(), f1hits.end()); } } } } // TAGH // n.b. the non="Calib" factory takes the ones from "Calib" and drops // hits from adjacent, in-time columns. Thus, we need to use the "Calib" // list of hits since there is one for every digi hit. if( !(SYSTEMS_TO_CUT & SYS_TAGH) ){ vector taghhits; loop->Get(taghhits, "Calib"); for(auto h : taghhits){ if(h->has_fADC){ if( (h->time_fadc < TAG_T_MIN) || (h->time_fadc > TAG_T_MAX) ){ vector pds; vector pis; h->Get(pds); h->Get(pis); if(!pds.empty()) rmf250pds.insert(rmf250pds.end(), pds.begin(), pds.end()); if(!pis.empty()) rmf250pis.insert(rmf250pis.end(), pis.begin(), pis.end()); } } if(h->has_TDC){ if( (h->time_tdc < TAG_T_MIN) || (h->time_tdc >TAG_T_MAX) ){ vector f1hits; h->Get(f1hits); if(!f1hits.empty()) rmF1hits.insert(rmF1hits.end(), f1hits.begin(), f1hits.end()); } } } } // Use macro to make code more concise #define rmelements(v) for(auto e: rm ## v) v.erase(remove(v.begin(), v.end(), e), v.end()); rmelements(f250pds); rmelements(f250pis); rmelements(f250wrds); rmelements(f125cdcpulses); rmelements(f125fdcpulses); rmelements(caen1290hits); rmelements(F1hits); } //------------------ // WriteCAEN1290Data //------------------ void JEventProcessor_L3proc::WriteCAEN1290Data(vector &buff , vector &caen1290hits , vector &caen1290configs) { // Create lists of F1 hit objects indexed by rocid,slot // At same time, make map of module types (32channel or 48 channel) map > > modules; // outer map index is rocid, inner map index is slot map > configs; for(uint32_t i=0; irocid][hit->slot].push_back(hit); } // Copy config pointers into map indexed by rocid for(uint32_t i=0; irocid].insert(config); } // Loop over rocids map > >::iterator it; for(it=modules.begin(); it!=modules.end(); it++){ uint32_t rocid = it->first; // Write Physics Event's Data Bank Header for this rocid uint32_t data_bank_idx = buff.size(); buff.push_back(0); // Total bank length (will be overwritten later) buff.push_back( (rocid<<16) + 0x1001 ); // 0x10=bank of banks, 0x01=1 event // Write Config Bank set &confs = configs[rocid]; if(!confs.empty()){ uint32_t config_bank_idx = buff.size(); buff.push_back(0); // Total bank length (will be overwritten later) buff.push_back( 0x00550101 ); // 0x55=config bank, 0x01=uint32_t bank, 0x01=1 event set::iterator it_conf; for(it_conf=confs.begin(); it_conf!=confs.end(); it_conf++){ const DCAEN1290TDCConfig *conf = *it_conf; uint32_t header_idx = buff.size(); buff.push_back(0); // Nvals and slot mask (will be filled below) uint32_t Nvals = 0; // keep count of how many params we write if(conf->WINWIDTH != 0xFFFF) {buff.push_back( (kPARAMCAEN1290_WINWIDTH <<16) + conf->WINWIDTH ); Nvals++;} if(conf->WINOFFSET != 0xFFFF) {buff.push_back( (kPARAMCAEN1290_WINOFFSET <<16) + conf->WINOFFSET ); Nvals++;} buff[header_idx] = (Nvals<<24) + conf->slot_mask; } // Update Config Bank length buff[config_bank_idx] = buff.size() - config_bank_idx - 1; } // Write Data Block Bank Header uint32_t data_block_bank_idx = buff.size(); buff.push_back(0); // Total bank length (will be overwritten later) buff.push_back( 0x00140101 ); // 0x00=status w/ little endian, 0x14=CAEN1290TDC, 0x01=uint32_t bank, 0x01=1 event // Loop over slots map >::iterator it2; for(it2=it->second.begin(); it2!=it->second.end(); it2++){ uint32_t slot = it2->first; // Write global header uint32_t global_header_idx = buff.size(); buff.push_back( 0x40000100 + (0x01<<5) + slot ); // Global Header 0x04=global header, 0x01<<5=event count // Write module data vector &hits = it2->second; uint32_t last_id = 0x0; for(uint32_t i=0; itdc_num<<24) + (hit->event_id<<12) + (hit->bunch_id); if(id != last_id){ // Write event header buff.push_back( 0x08000000 + id ); // TDC Header last_id = id; } // Write Hit buff.push_back( (hit->edge<<26) + (hit->channel<<21) + (hit->time&0x1fffff) ); } // Write module block trailer uint32_t Nwords_in_block = buff.size()-global_header_idx+1; buff.push_back( 0x80000000 + (Nwords_in_block<<5) + (slot) ); } // Update Data Block Bank length buff[data_block_bank_idx] = buff.size() - data_block_bank_idx - 1; // Update Physics Event's Data Bank length buff[data_bank_idx] = buff.size() - data_bank_idx - 1; } } //------------------ // F1sortByChipChan //------------------ bool F1sortByChipChan(const DF1TDCHit* const &hit1, const DF1TDCHit* const &hit2) { uint32_t chip_chan1 = (hit1->data_word>>16)&0x3F; uint32_t chip_chan2 = (hit2->data_word>>16)&0x3F; return chip_chan1 < chip_chan2; } //------------------ // WriteF1Data //------------------ void JEventProcessor_L3proc::WriteF1Data(vector &buff , vector &F1hits , vector &F1tts , vector &F1configs) { // Create lists of F1 hit objects indexed by rocid,slot // At same time, make map of module types (32channel or 48 channel) map > > modules; // outer map index is rocid, inner map index is slot map > mod_types; for(uint32_t i=0; irocid][hit->slot].push_back(hit); mod_types[hit->rocid][hit->slot] = hit->modtype; } // Copy F1 config pointers into map indexed by rocid map > configs; for(uint32_t i=0; irocid].insert(config); } // Loop over rocids map > >::iterator it; for(it=modules.begin(); it!=modules.end(); it++){ uint32_t rocid = it->first; // Write Physics Event's Data Bank Header for this rocid uint32_t data_bank_idx = buff.size(); buff.push_back(0); // Total bank length (will be overwritten later) buff.push_back( (rocid<<16) + 0x1001 ); // 0x10=bank of banks, 0x01=1 event // Write Config Bank set &confs = configs[rocid]; if(!confs.empty()){ uint32_t config_bank_idx = buff.size(); buff.push_back(0); // Total bank length (will be overwritten later) buff.push_back( 0x00550101 ); // 0x55=config bank, 0x01=uint32_t bank, 0x01=1 event set::iterator it_conf; for(it_conf=confs.begin(); it_conf!=confs.end(); it_conf++){ const DF1TDCConfig *conf = *it_conf; uint32_t header_idx = buff.size(); buff.push_back(0); // Nvals and slot mask (will be filled below) uint32_t Nvals = 0; // keep count of how many params we write if(conf->REFCNT != 0xFFFF) {buff.push_back( (kPARAMF1_REFCNT <<16) + conf->REFCNT ); Nvals++;} if(conf->TRIGWIN != 0xFFFF) {buff.push_back( (kPARAMF1_TRIGWIN <<16) + conf->TRIGWIN ); Nvals++;} if(conf->TRIGLAT != 0xFFFF) {buff.push_back( (kPARAMF1_TRIGLAT <<16) + conf->TRIGLAT ); Nvals++;} if(conf->HSDIV != 0xFFFF) {buff.push_back( (kPARAMF1_HSDIV <<16) + conf->HSDIV ); Nvals++;} if(conf->BINSIZE != 0xFFFF) {buff.push_back( (kPARAMF1_BINSIZE <<16) + conf->BINSIZE ); Nvals++;} if(conf->REFCLKDIV != 0xFFFF) {buff.push_back( (kPARAMF1_REFCLKDIV <<16) + conf->REFCLKDIV ); Nvals++;} buff[header_idx] = (Nvals<<24) + conf->slot_mask; } // Update Config Bank length buff[config_bank_idx] = buff.size() - config_bank_idx - 1; } // Write Data Block Bank Header // In principle, we could write one of these for each module, but // we write all modules into the same data block bank to save space. // n.b. the documentation mentions Single Event Mode (SEM) and that // the values in the header and even the first couple of words depend // on whether it is in that mode. It appears this mode is an alternative // to having a Built Trigger Bank. Our data all seems to have been taken // *not* in SEM. Empirically, it looks like the data also doesn't have // the initial "Starting Event Number" though the documentation does not // declare that as optional. We omit it here as well. uint32_t data_block_bank_idx = buff.size(); buff.push_back(0); // Total bank length (will be overwritten later) buff.push_back( 0x001A0101 ); // 0x00=status w/ little endian, 0x1A=F1TDC, 0x01=uint32_t bank, 0x01=1 event // Loop over slots map >::iterator it2; for(it2=it->second.begin(); it2!=it->second.end(); it2++){ uint32_t slot = it2->first; MODULE_TYPE modtype = mod_types[rocid][slot]; // Find Trigger Time object const DF1TDCTriggerTime *tt = NULL; for(uint32_t i=0; irocid==rocid) && (F1tts[i]->slot==slot) ){ tt = F1tts[i]; break; } } // Set itrigger number and trigger time uint32_t itrigger = (tt==NULL) ? (Nevents&0x3FFFFF):tt->DDAQAddress::itrigger; uint64_t trig_time = (tt==NULL) ? time(NULL):tt->time; // Write module block and event headers uint32_t block_header_idx = buff.size(); buff.push_back( 0x80000101 + (modtype<<18) + (slot<<22) ); // Block Header 0x80=data defining, modtype=F1TDC, 0x01=event block number,0x01=number of events in block buff.push_back( 0x90000000 + (slot<<22) + itrigger); // Event Header // Write Trigger Time buff.push_back(0x98000000 + ((trig_time>>0 )&0x00FFFFFF)); buff.push_back(0x00000000 + ((trig_time>>24)&0x00FFFFFF)); // Write module data vector &hits = it2->second; sort(hits.begin(), hits.end(), F1sortByChipChan); uint32_t last_chip = 0xFF; for(uint32_t i=0; idata_word; uint32_t chip_chan = (data_word>>16)&0x3F; uint32_t chip = chip_chan>>3; if(chip != last_chip){ uint32_t chip_header = 0xC0000000; chip_header += (data_word&0x07000000); // Resolution status, overflow statuses chip_header += (hits[i]->trig_time<<7)&0x01FF; // 9bit trigger time chip_header += chip_chan; // chip number and channel on chip buff.push_back( chip_header ); last_chip = chip; } // Write Hit buff.push_back( data_word ); // original data word for F1 is conveniently recorded! } // Write module block trailer uint32_t Nwords_in_block = buff.size()-block_header_idx+1; buff.push_back( 0x88000000 + (slot<<22) + Nwords_in_block ); } // Update Data Block Bank length buff[data_block_bank_idx] = buff.size() - data_block_bank_idx - 1; // Update Physics Event's Data Bank length buff[data_bank_idx] = buff.size() - data_bank_idx - 1; } } //------------------ // Writef250Data //------------------ void JEventProcessor_L3proc::Writef250Data(vector &buff , vector &f250pds , vector &f250pis , vector &f250tts , vector &f250wrds , vector &f250configs) { // Make map of rocid,slot values that have some hit data. // Simultaneously make map for each flavor of hit indexed by rocid,slot map > modules; // outer map index is rocid, inner map index is slot map > > > pd_hits; // outer map index is rocid, middle map index is slot, inner index is channel map > > pi_hits; // outer map index is rocid, inner map index is slot map > > wrd_hits; // outer map index is rocid, inner map index is slot for(auto hit : f250pds){ modules[hit->rocid].insert(hit->slot); pd_hits[hit->rocid][hit->slot][hit->channel].push_back( hit ); } for(auto hit : f250pis){ modules[hit->rocid].insert(hit->slot); pi_hits[hit->rocid][hit->slot].push_back( hit ); } for(auto hit : f250wrds){ modules[hit->rocid].insert(hit->slot); wrd_hits[hit->rocid][hit->slot].push_back( hit ); } // Copy f250 config pointers into map indexed by just rocid map > configs; for(auto config : f250configs){ configs[config->rocid].insert(config); } // Loop over rocids for(auto m : modules){ uint32_t rocid = m.first; // Write Physics Event's Data Bank Header for this rocid uint32_t data_bank_idx = buff.size(); buff.push_back(0); // Total bank length (will be overwritten later) buff.push_back( (rocid<<16) + 0x1001 ); // 0x10=bank of banks, 0x01=1 event // Write Config Bank set &confs = configs[rocid]; if(!confs.empty()){ uint32_t config_bank_idx = buff.size(); buff.push_back(0); // Total bank length (will be overwritten later) buff.push_back( 0x00550101 ); // 0x55=config bank, 0x01=uint32_t bank, 0x01=1 event for(auto conf : confs){ uint32_t header_idx = buff.size(); buff.push_back(0); // Nvals and slot mask (will be filled below) uint32_t Nvals = 0; // keep count of how many params we write if(conf->NSA != 0xFFFF) {buff.push_back( (kPARAM250_NSA <<16) + conf->NSA ); Nvals++;} if(conf->NSB != 0xFFFF) {buff.push_back( (kPARAM250_NSB <<16) + conf->NSB ); Nvals++;} if(conf->NSA_NSB != 0xFFFF) {buff.push_back( (kPARAM250_NSA_NSB<<16) + conf->NSA_NSB); Nvals++;} if(conf->NPED != 0xFFFF) {buff.push_back( (kPARAM250_NPED <<16) + conf->NPED ); Nvals++;} buff[header_idx] = (Nvals<<24) + conf->slot_mask; } // Update Config Bank length buff[config_bank_idx] = buff.size() - config_bank_idx - 1; } // Write Data Block Bank Header // In principle, we could write one of these for each module, but // we write all modules into the same data block bank to save space. // n.b. the documentation mentions Single Event Mode (SEM) and that // the values in the header and even the first couple of words depend // on whether it is in that mode. It appears this mode is an alternative // to having a Built Trigger Bank. Our data all seems to have been taken // *not* in SEM. Empirically, it looks like the data also doesn't have // the initial "Starting Event Number" though the documentation does not // declare that as optional. We omit it here as well. uint32_t data_block_bank_idx = buff.size(); buff.push_back(0); // Total bank length (will be overwritten later) buff.push_back(0x00060101); // 0x00=status w/ little endian, 0x06=f250, 0x01=uint32_t bank, 0x01=1 event // Loop over slots for(auto slot : m.second){ // Find Trigger Time object const Df250TriggerTime *tt = NULL; for(auto mytt : f250tts ){ if( (mytt->rocid==rocid) && (mytt->slot==slot) ){ tt = mytt; break; } } // Should we print a warning if no Trigger Time object found? // Set itrigger number and trigger time uint32_t itrigger = (tt==NULL) ? (Nevents&0x3FFFFF):tt->itrigger; uint64_t trig_time = (tt==NULL) ? time(NULL):tt->time; // Write module block and event headers // n.b. format of lowest 27 bits of event header changed in firmware // upgrade of Fall 2016. The newer format also allows this word to // be omitted completely (so called "compressed mode"). We only // write it out here if there are Df250Pulseintegral or // Df250WindowRawData objects. uint32_t block_header_idx = buff.size(); buff.push_back( 0x80040101 + (slot<<22) ); // Block Header 0x80=data defining, 0x04=FADC250, 0x01=event block number,0x01=number of events in block if( (!pi_hits[rocid][slot].empty()) || (!f250wrds.empty()) ){ buff.push_back( 0x90000000 + ((0x3FF&trig_time)<<12) + (0xFFF&itrigger) ); // Event Header (see above) } // Write Trigger Time if(tt != NULL){ buff.push_back(0x98000000 + ((trig_time>>0 )&0x00FFFFFF)); buff.push_back(0x00000000 + ((trig_time>>24)&0x03FFFFFF)); } // Write pulse data (new format) // n.b. the format allows multiple events to be written // at this level. Here, we are always writing out single // events so event_number_within_block should always be // the same. for( auto pd_pair : pd_hits[rocid][slot] ){ // loop over channels (word 1) if(pd_pair.second.empty()) continue; auto pd = pd_pair.second[0]; // use first Df250PulseData object for word 1 uint32_t word1 = 0xC8000000; // Pulse Parameters defining word word1 += 1<<19; // event number within block (always 1 for single event) word1 += pd->channel<<15; // channel number if(pd->QF_pedestal) word1 += 1<<14; // pedestal quality word1 += pd->pedestal & 0x3FFF; // pdestal sum buff.push_back( word1 ); // loop over hits (words 2 and 3) for( auto pd : pd_pair.second){ uint32_t word2 = 1<<30; word2 += (0x3FFFF&pd->integral)<<12; if(pd->QF_NSA_beyond_PTW) word2 += 1<<11; if(pd->QF_overflow ) word2 += 1<<10; if(pd->QF_underflow ) word2 += 1<< 9; word2 += 0x1FF&pd->nsamples_over_threshold; uint32_t word3 = 0<<30; word3 += (0x1FF&pd->course_time)<<21; word3 += (0x3F&pd->fine_time)<<15; word3 += (0xFFF&pd->pulse_peak)<<3; if(pd->QF_vpeak_beyond_NSA) word3 += 1<<2; if(pd->QF_vpeak_not_found ) word3 += 1<<1; if(pd->QF_bad_pedestal ) word3 += 1<<0; buff.push_back( word2 ); buff.push_back( word3 ); } } // Write Pulse Integral, Pulse Time, and Pulse Pedestal (old format) vector &pis = pi_hits[rocid][slot]; //it2->second; for(uint32_t i=0; iGetSingle(pt); pi->GetSingle(pp); // Pulse Integral if(pi->emulated == PREFER_EMULATED){ buff.push_back(0xB8000000 + (pi->channel<<23) + (pi->pulse_number<<21) + (pi->quality_factor<<19) + (pi->integral&0x7FFFF) ); } // Pulse Time if(pt && (pt->emulated == PREFER_EMULATED) ){ buff.push_back(0xC0000000 + (pt->channel<<23) + (pt->pulse_number<<21) + (pt->quality_factor<<19) + (pt->time&0x7FFFF) ); } // Pulse Pedestal if(pp && (pp->emulated == PREFER_EMULATED) ){ buff.push_back(0xD0000000 + (pp->channel<<23) + (pp->pulse_number<<21) + (pp->pedestal<<12) + (pp->pulse_peak&0x0FFF) ); } } // Write Window Raw Data // This is not the most efficient, but is needed to get these under // the correct block header. if(!PREFER_EMULATED){ for(uint32_t i=0; irocid!=rocid || wrd->slot!=slot) continue; // IMPORTANT: At this time, the individual "not valid" bits for // the samples is not preserved in the Df250WindowRawData class. // We set them here only to indicate if the last sample is not // valid due to there being an odd number of samples. buff.push_back(0xA0000000 + (wrd->channel<<23) + (wrd->samples.size()) ); for(uint32_t j=0; j<(wrd->samples.size()+1)/2; j++){ uint32_t idx1 = 2*j; uint32_t idx2 = idx1 + 1; uint32_t sample_1 = wrd->samples[idx1]; uint32_t sample_2 = idx2samples.size() ? wrd->samples[idx2]:0; uint32_t invalid1 = 0; uint32_t invalid2 = idx2>=wrd->samples.size(); buff.push_back( (invalid1<<29) + (sample_1<<16) + (invalid2<<13) + (sample_2<<0) ); } } } // Write module block trailer uint32_t Nwords_in_block = buff.size()-block_header_idx+1; buff.push_back( 0x88000000 + (slot<<22) + Nwords_in_block ); } // Update Data Block Bank length buff[data_block_bank_idx] = buff.size() - data_block_bank_idx - 1; // Update Physics Event's Data Bank length buff[data_bank_idx] = buff.size() - data_bank_idx - 1; } } //------------------ // Writef125Data //------------------ void JEventProcessor_L3proc::Writef125Data(vector &buff , vector &f125pis , vector &f125cdcpulses , vector &f125fdcpulses , vector &f125tts , vector &f125wrds , vector &f125configs) { // Make map of rocid,slot values that have some hit data. // Simultaneously make map for each flavor of hit indexed by rocid,slot map > modules; // outer map index is rocid, inner map index is slot map > > pi_hits; // outer map index is rocid, inner map index is slot map > > cdc_hits; // outer map index is rocid, inner map index is slot map > > fdc_hits; // outer map index is rocid, inner map index is slot map > > wrd_hits; // outer map index is rocid, inner map index is slot for(uint32_t i=0; irocid].insert(hit->slot); pi_hits[hit->rocid][hit->slot].push_back( hit ); } for(uint32_t i=0; irocid].insert(hit->slot); cdc_hits[hit->rocid][hit->slot].push_back( hit ); } for(uint32_t i=0; irocid].insert(hit->slot); fdc_hits[hit->rocid][hit->slot].push_back( hit ); } for(uint32_t i=0; irocid].insert(hit->slot); wrd_hits[hit->rocid][hit->slot].push_back( hit ); } // Copy f125 config pointers into map indexed by just rocid map > configs; for(uint32_t i=0; irocid].insert(config); } // Loop over rocids map >::iterator it; for(it=modules.begin(); it!=modules.end(); it++){ uint32_t rocid = it->first; // Write Physics Event's Data Bank Header for this rocid uint32_t data_bank_idx = buff.size(); buff.push_back(0); // Total bank length (will be overwritten later) buff.push_back( (rocid<<16) + 0x1001 ); // 0x10=bank of banks, 0x01=1 event // Write Config Bank set &confs = configs[rocid]; if(!confs.empty()){ uint32_t config_bank_idx = buff.size(); buff.push_back(0); // Total bank length (will be overwritten later) buff.push_back( 0x00550101 ); // 0x55=config bank, 0x01=uint32_t bank, 0x01=1 event set::iterator it_conf; for(it_conf=confs.begin(); it_conf!=confs.end(); it_conf++){ const Df125Config *conf = *it_conf; uint32_t header_idx = buff.size(); buff.push_back(0); // Nvals and slot mask (will be filled below) uint32_t Nvals = 0; // keep count of how many params we write if(conf->NSA != 0xFFFF) {buff.push_back( (kPARAM125_NSA <<16) + conf->NSA ); Nvals++;} if(conf->NSB != 0xFFFF) {buff.push_back( (kPARAM125_NSB <<16) + conf->NSB ); Nvals++;} if(conf->NSA_NSB != 0xFFFF) {buff.push_back( (kPARAM125_NSA_NSB <<16) + conf->NSA_NSB ); Nvals++;} if(conf->NPED != 0xFFFF) {buff.push_back( (kPARAM125_NPED <<16) + conf->NPED ); Nvals++;} if(conf->WINWIDTH!= 0xFFFF) {buff.push_back( (kPARAM125_WINWIDTH<<16) + conf->WINWIDTH); Nvals++;} // See GlueX-doc-2274 if(conf->PL != 0xFFFF) {buff.push_back( (kPARAM125_PL <<16) + conf->PL ); Nvals++;} if(conf->NW != 0xFFFF) {buff.push_back( (kPARAM125_NW <<16) + conf->NW ); Nvals++;} if(conf->NPK != 0xFFFF) {buff.push_back( (kPARAM125_NPK <<16) + conf->NPK ); Nvals++;} if(conf->P1 != 0xFFFF) {buff.push_back( (kPARAM125_P1 <<16) + conf->P1 ); Nvals++;} if(conf->P2 != 0xFFFF) {buff.push_back( (kPARAM125_P2 <<16) + conf->P2 ); Nvals++;} if(conf->PG != 0xFFFF) {buff.push_back( (kPARAM125_PG <<16) + conf->PG ); Nvals++;} if(conf->IE != 0xFFFF) {buff.push_back( (kPARAM125_IE <<16) + conf->IE ); Nvals++;} if(conf->H != 0xFFFF) {buff.push_back( (kPARAM125_H <<16) + conf->H ); Nvals++;} if(conf->TH != 0xFFFF) {buff.push_back( (kPARAM125_TH <<16) + conf->TH ); Nvals++;} if(conf->TL != 0xFFFF) {buff.push_back( (kPARAM125_TL <<16) + conf->TL ); Nvals++;} if(conf->IBIT != 0xFFFF) {buff.push_back( (kPARAM125_IBIT <<16) + conf->IBIT ); Nvals++;} if(conf->ABIT != 0xFFFF) {buff.push_back( (kPARAM125_ABIT <<16) + conf->ABIT ); Nvals++;} if(conf->PBIT != 0xFFFF) {buff.push_back( (kPARAM125_PBIT <<16) + conf->PBIT ); Nvals++;} buff[header_idx] = (Nvals<<24) + conf->slot_mask; } // Update Config Bank length buff[config_bank_idx] = buff.size() - config_bank_idx - 1; } // Write Data Block Bank Header // In principle, we could write one of these for each module, but // we write all modules into the same data block bank to save space. // n.b. the documentation mentions Single Event Mode (SEM) and that // the values in the header and even the first couple of words depend // on whether it is in that mode. It appears this mode is an alternative // to having a Built Trigger Bank. Our data all seems to have been taken // *not* in SEM. Empirically, it looks like the data also doesn't have // the initial "Starting Event Number" though the documentation does not // declare that as optional. We omit it here as well. uint32_t data_block_bank_idx = buff.size(); buff.push_back(0); // Total bank length (will be overwritten later) buff.push_back(0x00100101); // 0x00=status w/ little endian, 0x10=f125, 0x01=uint32_t bank, 0x01=1 event // Loop over slots set::iterator it2; for(it2=it->second.begin(); it2!=it->second.end(); it2++){ uint32_t slot = *it2; // Find Trigger Time object const Df125TriggerTime *tt = NULL; for(uint32_t i=0; irocid==rocid) && (f125tts[i]->slot==slot) ){ tt = f125tts[i]; break; } } // Should we print a warning if no Trigger Time object found? // Set itrigger number and trigger time uint32_t itrigger = (tt==NULL) ? (Nevents&0x3FFFFF):tt->DDAQAddress::itrigger; uint64_t trig_time = (tt==NULL) ? time(NULL):tt->time; // Write module block and event headers uint32_t block_header_idx = buff.size(); buff.push_back( 0x80080101 + (slot<<22) ); // Block Header 0x80=data defining, 0x08=FADC125, 0x01=event block number,0x01=number of events in block buff.push_back( 0x90000000 + itrigger); // Event Header // Write Trigger Time buff.push_back(0x98000000 + ((trig_time>>0 )&0x00FFFFFF)); buff.push_back(0x00000000 + ((trig_time>>24)&0x00FFFFFF)); // Write Pulse Integral (+Pedestal and Time) data vector &pis = pi_hits[rocid][slot]; for(uint32_t i=0; iGetSingle(pt); pi->GetSingle(pp); // Pulse Integral if(pi->emulated == PREFER_EMULATED){ buff.push_back(0xB8000000 + (pi->channel<<20) + (pi->integral&0x7FFFF) ); } // Pulse Time if(pt && (pt->emulated == PREFER_EMULATED) ){ buff.push_back(0xC0000000 + (pt->channel<<20) + (pt->pulse_number<<18) + (pt->time&0x7FFFF) ); } // Pulse Pedestal if(pp && (pp->emulated == PREFER_EMULATED) ){ buff.push_back(0xD0000000 + (pp->pulse_number<<21) + (pp->pedestal<<12) + (pp->pulse_peak&0x0FFF) ); } } // Write CDC Pulse data vector &cdcpulses = cdc_hits[rocid][slot]; for(uint32_t i=0; iemulated == PREFER_EMULATED){ buff.push_back( pulse->word1 ); buff.push_back( pulse->word2 ); } } // Write FDC Pulse data vector &fdcpulses = fdc_hits[rocid][slot]; for(uint32_t i=0; iemulated == PREFER_EMULATED){ buff.push_back( pulse->word1 ); buff.push_back( pulse->word2 ); } } // Write Window Raw Data // This is not the most efficient, but is needed to get these under // the correct block header. if(!PREFER_EMULATED){ vector &wrds = wrd_hits[rocid][slot]; for(uint32_t i=0; ichannel<<20) + (wrd->channel<<15) + (wrd->samples.size()) ); for(uint32_t j=0; j<(wrd->samples.size()+1)/2; j++){ uint32_t idx1 = 2*j; uint32_t idx2 = idx1 + 1; uint32_t sample_1 = wrd->samples[idx1]; uint32_t sample_2 = idx2samples.size() ? wrd->samples[idx2]:0; uint32_t invalid1 = 0; uint32_t invalid2 = idx2>=wrd->samples.size(); buff.push_back( (invalid1<<29) + (sample_1<<16) + (invalid2<<13) + (sample_2<<0) ); } } } // Write module block trailer uint32_t Nwords_in_block = buff.size()-block_header_idx+1; buff.push_back( 0x88000000 + (slot<<22) + Nwords_in_block ); } // Update Data Block Bank length buff[data_block_bank_idx] = buff.size() - data_block_bank_idx - 1; // Update Physics Event's Data Bank length buff[data_bank_idx] = buff.size() - data_bank_idx - 1; } } //------------------ // WriteEPICSData //------------------ void JEventProcessor_L3proc::WriteEPICSData(vector &buff , vector epicsValues) { // Store the EPICS event in EVIO as a bank of SEGMENTs // The first segment is a 32-bit unsigned int for the current // time. This is followed by additional SEGMENTs that are // 8-bit unsigned integer types, one for each variable being // written. All variables are written as strings in the form: // // varname=value // // where "varname" is the EPICS variable name and "value" its // value in string form. If there are multiple elements for // the PV, then value will be a comma separated list. The // contents of "value" are set in GetEPICSvalue() while // the "varname=value" string is formed here. // If no values to write then return now if(epicsValues.size() == 0) return; // Outermost EVIO header is a bank of segments. uint32_t epics_bank_idx = buff.size(); buff.push_back(0); // Total bank length (will be overwritten later) buff.push_back( (0x60<<16) + (0xD<<8) + (0x1<<0) ); // Time word (unsigned 32bit SEGMENT) buff.push_back( (0x61<<24) + (0x1<<16) + (1<<0) ); buff.push_back( (uint32_t)epicsValues[0]->timestamp ); // Loop over PVs, writing each as a SEGMENT to the buffer for(uint32_t i=0; inameval; uint32_t Nbytes = str.size()+1; // +1 is for terminating 0 uint32_t Nwords = (Nbytes+3)/4; // bytes needed for padding uint32_t Npad = (Nwords*4) - Nbytes; buff.push_back( (0x62<<24) + (Npad<<22) + (0x7<<16) + (Nwords<<0) ); // 8bit unsigned char segment // Increase buffer enough to hold this string uint32_t buff_str_idx = buff.size(); buff.resize( buff_str_idx + Nwords ); unsigned char *ichar = (unsigned char*)&buff[buff_str_idx]; for(unsigned int j=0; j void WriteBORSingle(vector &buff, M m, F&& modFunc) { /// Write BOR config objects for all crates containing /// a single module type. This is called from WriteBORData /// below. // This templated function saves duplicating this code for all // 4 BORconfig data types. It is complicated slightly by the // fact that the F1TDC comes in 2 types and so the last argument // must be a lambda function in order to set the module type // based on a value in object "c" in the innermost loop. // It is also a bit tricky since the DXXXBORConfig data types // use multiple inheritance with one type being the actual // data structure from bor_roc.h. This is really only needed // for the data structure length so we make it the first // template parameter so that it can be specified using the // template syntax when calling this. for(auto p : m){ uint32_t rocid = p.first; uint32_t crate_idx = buff.size(); buff.push_back(0); // crate bank length (will be overwritten later) buff.push_back( (0x71<<16) + (0x0C<<8) + (rocid<<0) ); // 0x71=crate config, 0x0c=tag segment, num=rocid for(auto c : p.second){ uint32_t slot = c->slot; uint32_t modtype = modFunc(c); uint32_t tag = (slot<<5) + (modtype); uint32_t Nwords = sizeof(T)/sizeof(uint32_t); buff.push_back( (tag<<20) + (0x01<<16) + (Nwords)); // 0x1=uint32 uint32_t *d = (uint32_t*)&c->rocid; for(uint32_t j=0; j &buff, JEventLoop *loop) { // Write the BOR data into EVIO format. vector vDf250BORConfig; vector vDf125BORConfig; vector vDF1TDCBORConfig; vector vDCAEN1290TDCBORConfig; loop->Get(vDf250BORConfig); loop->Get(vDf125BORConfig); loop->Get(vDF1TDCBORConfig); loop->Get(vDCAEN1290TDCBORConfig); uint32_t Nconfig_objs = vDf250BORConfig.size() + vDf125BORConfig.size() + vDF1TDCBORConfig.size() + vDCAEN1290TDCBORConfig.size(); if(Nconfig_objs == 0) return; // Sort config. objects by rocid into containers map mDf250BORConfig; map mDf125BORConfig; map mDF1TDCBORConfig; map mDCAEN1290TDCBORConfig; for(auto c : vDf250BORConfig) mDf250BORConfig[c->rocid].push_back(c); for(auto c : vDf125BORConfig) mDf125BORConfig[c->rocid].push_back(c); for(auto c : vDF1TDCBORConfig) mDF1TDCBORConfig[c->rocid].push_back(c); for(auto c : vDCAEN1290TDCBORConfig) mDCAEN1290TDCBORConfig[c->rocid].push_back(c); // Outermost EVIO header for BOR event uint32_t bor_bank_idx = buff.size(); buff.push_back(0); // Total bank length (will be overwritten later) buff.push_back( (0x70<<16) + (0x0E<<8) + (0x1<<0) ); // 0x70=BOR event 0x0E=bank of banks 0x1=num // Write all config objects for each BOR data type // using the templated function above. WriteBORSingle(buff, mDf250BORConfig, [](const Df250BORConfig *c){return DModuleType::FADC250;}); WriteBORSingle(buff, mDf125BORConfig, [](const Df125BORConfig *c){return DModuleType::FADC125;}); WriteBORSingle(buff, mDF1TDCBORConfig, [](const DF1TDCBORConfig *c){return c->nchips==8 ? 0x3:0x4;}); WriteBORSingle(buff, mDCAEN1290TDCBORConfig, [](const DCAEN1290TDCBORConfig *c){return DModuleType::CAEN1290;}); // Update BOR Bank length buff[bor_bank_idx] = buff.size() - bor_bank_idx - 1; } //------------------ // WriteTSSyncData //------------------ void JEventProcessor_L3proc::WriteTSSyncData(vector &buff, const DL1Info *l1info) { // This was copied from the evio_writer plugin and modified // The Trigger Supervisor (TS) inserts information into the data stream // during periodic "sync events". The data is stored in one bank // so we build it here // TS data block bank uint32_t data_bank_len_idx = buff.size(); buff.push_back(0); // will be updated later buff.push_back(0x00011001); // Data bank header: 0001=TS rocid , 10=Bank of Banks, 01=1 event // TS data bank uint32_t trigger_bank_len_idx = buff.size(); buff.push_back(0); // Length buff.push_back(0xEE020100); // 0xEE02=TS Bank Tag, 0x01=u32int // Save header information buff.push_back(l1info->nsync); buff.push_back(l1info->trig_number); buff.push_back(l1info->live_time); buff.push_back(l1info->busy_time); buff.push_back(l1info->live_inst); buff.push_back(l1info->unix_time); // save GTP scalars for(auto a : l1info->gtp_sc ) buff.push_back(a); // save FP scalars for(auto a : l1info->fp_sc ) buff.push_back(a); // Save GTP rates for(auto a : l1info->gtp_rate ) buff.push_back(a); // save FP rates for(auto a : l1info->fp_rate ) buff.push_back(a); // Update bank length buff[trigger_bank_len_idx] = buff.size() - trigger_bank_len_idx - 1; buff[data_bank_len_idx] = buff.size() - data_bank_len_idx - 1; } //------------------ // WriteEventTagData //------------------ void JEventProcessor_L3proc::WriteEventTagData(vector &buff , uint64_t event_status , const DL3Trigger* l3trigger) { // Here we purposefully write the DEventTag data into a bank // based only on data extracted from other data objects, but // NOT the DEventTag object. This is so whenever an event is // written, it is guaranteed to have event tag data based on // current information as opposed to data passed in from a // previously run algorithm. uint32_t eventtag_bank_idx = buff.size(); uint64_t L3_status = 0; uint32_t L3_decision = 0; uint32_t L3_algorithm = 0; double L3_mva_response = 2E-6; if(l3trigger){ L3_status = l3trigger->status; L3_decision = l3trigger->L3_decision; L3_algorithm = l3trigger->algorithm; L3_mva_response = l3trigger->mva_response; } // Set L3 pass/fail status in event_status word to be written // (this is redundant with the L3_decision word written below // but makes the bits in the status word valid and costs nothing) switch(L3_decision){ case DL3Trigger::kKEEP_EVENT : event_status |= kSTATUS_L3PASS; break; case DL3Trigger::kDISCARD_EVENT: event_status |= kSTATUS_L3FAIL; break; case DL3Trigger::kNO_DECISION : break; } // Length and Header words // This must fit the format of ROC data so we add a bank of banks // header with rocid=0xF56 buff.push_back(0); // Total bank length (will be overwritten later) buff.push_back( 0x0F561001 ); // rocid=0xF56 buff.push_back(0); // Bank length (will be overwritten later) buff.push_back( 0x00560101 ); // 0x56=event tag bank, 0x01=uint32_t bank, 0x01=1 event // event_status buff.push_back( (event_status>> 0)&0xFFFFFFFF ); // low order word buff.push_back( (event_status>>32)&0xFFFFFFFF ); // high order word // L3_status buff.push_back( (L3_status>> 0)&0xFFFFFFFF ); // low order word buff.push_back( (L3_status>>32)&0xFFFFFFFF ); // high order word // L3_decision buff.push_back( L3_decision ); // L3_algorithm buff.push_back( L3_algorithm ); // L3_mva_response uint32_t mva = 0x7FFFFFFF & (uint32_t)fabs(L3_mva_response*1000.0); if(L3_mva_response < 0.0 ) mva |= 0x80000000; buff.push_back( mva ); // Update event tag Bank lengths buff[eventtag_bank_idx] = buff.size() - eventtag_bank_idx - 1; buff[eventtag_bank_idx+2] = buff[eventtag_bank_idx] - 2; } //------------------ // fini //------------------ jerror_t JEventProcessor_L3proc::fini(void) { l3out->Quit(); if(ofs_debug_input){ ofs_debug_input->close(); delete ofs_debug_input; } if(ofs_debug_output){ ofs_debug_output->close(); delete ofs_debug_output; } return NOERROR; }