diff --git a/CRVResponse/fcl/prolog_v11.fcl b/CRVResponse/fcl/prolog_v11.fcl index c4e908b2ed..f13998cb4b 100644 --- a/CRVResponse/fcl/prolog_v11.fcl +++ b/CRVResponse/fcl/prolog_v11.fcl @@ -87,9 +87,6 @@ BEGIN_PROLOG //-may be changed for testing purposes, // if a database table of a random gaussian distribution is used // (current random table uses a sigma equal to 5% of the nominal photon yied) - photonYieldVariationCutoffLow :-0.2 //the photon yield variation is cut off at 20% below the mean - photonYieldVariationCutoffHigh : 0.2 //the photon yield variation is cut off at 20% above the mean - //note: if measured deviations are used, the cutoffs should be set to the maximum values digitizationStart : @local::DigitizationStart digitizationEnd : @local::DigitizationEnd digitizationStartMargin : 100.0 //ns start recording earlier to account for photon travel times and electronics response times diff --git a/CRVResponse/fcl/prolog_v12.fcl b/CRVResponse/fcl/prolog_v12.fcl index 135aeaa51f..b88c2ed019 100644 --- a/CRVResponse/fcl/prolog_v12.fcl +++ b/CRVResponse/fcl/prolog_v12.fcl @@ -86,9 +86,6 @@ BEGIN_PROLOG //-may be changed for testing purposes, // if a database table of a random gaussian distribution is used // (current random table uses a sigma equal to 5% of the nominal photon yied) - photonYieldVariationCutoffLow :-0.2 //the photon yield variation is cut off at 20% below the mean - photonYieldVariationCutoffHigh : 0.2 //the photon yield variation is cut off at 20% above the mean - //note: if measured deviations are used, the cutoffs should be set to the maximum values digitizationStart : @local::DigitizationStart digitizationEnd : @local::DigitizationEnd digitizationStartMargin : 100.0 //ns start recording earlier to account for photon travel times and electronics response times diff --git a/CRVResponse/src/CrvPhotonGenerator_module.cc b/CRVResponse/src/CrvPhotonGenerator_module.cc index ff72d235b9..24e4f3f586 100644 --- a/CRVResponse/src/CrvPhotonGenerator_module.cc +++ b/CRVResponse/src/CrvPhotonGenerator_module.cc @@ -68,8 +68,6 @@ namespace mu2e fhicl::Sequence scintillationYields{ Name("scintillationYields"), Comment("scintillation yields at Crv sectors")}; fhicl::Atom photonYieldScaleFactor{ Name("photonYieldScaleFactor"), Comment("global scale factor for the photon yield")}; fhicl::Atom photonYieldVariationScale{ Name("photonYieldVariationScale"),Comment("scale factor of the photon yield variation")}; - fhicl::Atom photonYieldVariationCutoffLow{ Name("photonYieldVariationCutoffLow"),Comment("lower cutoff at photon yield variation")}; - fhicl::Atom photonYieldVariationCutoffHigh{ Name("photonYieldVariationCutoffHigh"),Comment("upper cutoff at photon yield variation")}; fhicl::Atom digitizationStart{ Name("digitizationStart"), Comment("start of digitization after DAQ event window start")}; fhicl::Atom digitizationEnd{ Name("digitizationEnd"), Comment("end of digitization after DAQ event window start")}; fhicl::Atom digitizationStartMargin{ Name("digitizationStartMargin"), @@ -100,8 +98,6 @@ namespace mu2e double _photonYieldScaleFactor; mu2e::ProditionsHandle _photonYieldVariationVector; double _photonYieldVariationScale; - double _photonYieldVariationCutoffLow; - double _photonYieldVariationCutoffHigh; //On-spill //-Event length: 1695ns (microbunch period) @@ -164,8 +160,6 @@ namespace mu2e _scintillationYields(conf().scintillationYields()), _photonYieldScaleFactor(conf().photonYieldScaleFactor()), _photonYieldVariationScale(conf().photonYieldVariationScale()), - _photonYieldVariationCutoffLow(conf().photonYieldVariationCutoffLow()), - _photonYieldVariationCutoffHigh(conf().photonYieldVariationCutoffHigh()), _digitizationStart(conf().digitizationStart()), _digitizationEnd(conf().digitizationEnd()), _digitizationStartMargin(conf().digitizationStartMargin()), @@ -347,8 +341,6 @@ namespace mu2e size_t channel = step.barIndex().asUint()*CRVId::nChanPerBar + SiPM; float photonYieldDeviation = photonYieldVariationVector.photonYieldDeviation(channel); photonYieldDeviation *= _photonYieldVariationScale; //scale factor for the variation - if(photonYieldDeviation<_photonYieldVariationCutoffLow) photonYieldDeviation=_photonYieldVariationCutoffLow; - if(photonYieldDeviation>_photonYieldVariationCutoffHigh) photonYieldDeviation=_photonYieldVariationCutoffHigh; photonYieldDeviation = (photonYieldDeviation+1.0)*_photonYieldScaleFactor; //global photon yield scale factor for e.g. aging photonMaker->SetPhotonYieldDeviation(photonYieldDeviation,SiPM); } diff --git a/CRVResponse/src/MakeCrvPhotons.cc b/CRVResponse/src/MakeCrvPhotons.cc index 790d0fb239..aa2cf6cb91 100644 --- a/CRVResponse/src/MakeCrvPhotons.cc +++ b/CRVResponse/src/MakeCrvPhotons.cc @@ -538,22 +538,20 @@ double MakeCrvPhotons::GetAverageNumberOfCerenkovPhotons(double beta, double cha { if(charge==0) return 0; - bool first=true; double prevBeta=0; double prevNumberPhotons=0; std::map::const_iterator i; for(i=photons.begin(); i!=photons.end(); i++) { - if(beta<=i->first) + if(beta<=i->first) //first beta in the Cerenkov map is always greater than 0.0 and has number of photons of 0. { - if(first) return 0; //this shouldn't happen + if(i->first==0) return i->second; //beta=0 in Cerenkov map shouldn't happen. if it did and was the first entry, i->first-prevBeta would result in a division by 0. double numberPhotons=prevNumberPhotons+(i->second-prevNumberPhotons)/(i->first-prevBeta)*(beta-prevBeta); numberPhotons*=fabs(charge/eplus); return numberPhotons; } prevBeta=i->first; prevNumberPhotons=i->second; - first=false; } return photons.rbegin()->second*fabs(charge/eplus); //this shouldn't happen } diff --git a/DAQ/CMakeLists.txt b/DAQ/CMakeLists.txt index e87a5a57d6..ecf5e14a61 100644 --- a/DAQ/CMakeLists.txt +++ b/DAQ/CMakeLists.txt @@ -197,7 +197,9 @@ cet_build_plugin(CrvDigisFromArtdaqFragments art::module REG_SOURCE src/CrvDigisFromArtdaqFragments_module.cc LIBRARIES REG Offline::DAQ + Offline::CRVConditions Offline::DataProducts + Offline::ProditionsService Offline::RecoDataProducts artdaq-core-mu2e::Data artdaq-core-mu2e::Overlays @@ -207,7 +209,9 @@ cet_build_plugin(CrvDigisFromArtdaqFragmentsFEBII art::module REG_SOURCE src/CrvDigisFromArtdaqFragmentsFEBII_module.cc LIBRARIES REG Offline::DAQ + Offline::CRVConditions Offline::DataProducts + Offline::ProditionsService Offline::RecoDataProducts artdaq-core-mu2e::Data artdaq-core-mu2e::Data_dict @@ -225,6 +229,25 @@ cet_build_plugin(CrvGRdataFromArtdaqFragments art::module artdaq-core-mu2e::Overlays ) +cet_build_plugin(CrvDigisToFragments art::module + REG_SOURCE src/CrvDigisToFragments_module.cc + LIBRARIES REG + Offline::DAQ + Offline::CRVConditions + Offline::DataProducts + Offline::ProditionsService + Offline::RecoDataProducts + artdaq-core-mu2e::Data + artdaq-core-mu2e::Data_dict +) + +cet_build_plugin(CrvDigiCollectionsComparator art::module + REG_SOURCE src/CrvDigiCollectionsComparator_module.cc + LIBRARIES REG + Offline::RecoDataProducts + Offline::DataProducts +) + cet_build_plugin(MSDHitsFromDTCEvents art::module REG_SOURCE src/MSDHitsFromDTCEvents_module.cc LIBRARIES REG diff --git a/DAQ/src/CrvDigiCollectionsComparator_module.cc b/DAQ/src/CrvDigiCollectionsComparator_module.cc new file mode 100644 index 0000000000..d011655fee --- /dev/null +++ b/DAQ/src/CrvDigiCollectionsComparator_module.cc @@ -0,0 +1,210 @@ +// ====================================================================== +// +// CrvDigiCollectionsComparator_module: Compare two CrvDigiCollections +// +// ====================================================================== + +#include "art/Framework/Core/EDAnalyzer.h" +#include "art/Framework/Core/ModuleMacros.h" +#include "art/Framework/Principal/Event.h" +#include "cetlib_except/exception.h" +#include "fhiclcpp/ParameterSet.h" + +#include "Offline/RecoDataProducts/inc/CrvDigi.hh" + +#include +#include +#include +#include +#include +#include + +namespace mu2e { + +class CrvDigiCollectionsComparator : public art::EDAnalyzer { +public: + struct Config { + fhicl::Atom referenceTag{fhicl::Name("referenceTag"), + fhicl::Comment("Reference CrvDigiCollection")}; + fhicl::Atom candidateTag{fhicl::Name("candidateTag"), + fhicl::Comment("Candidate CrvDigiCollection")}; + fhicl::Atom failOnMismatch{fhicl::Name("failOnMismatch"), + fhicl::Comment("Throw if any mismatch is found"), + false}; + fhicl::Atom diagLevel{fhicl::Name("diagLevel"), + fhicl::Comment("Diagnostic verbosity"), + 0}; + fhicl::Atom maxMismatchesToPrint{fhicl::Name("maxMismatchesToPrint"), + fhicl::Comment("Maximum mismatch entries to print"), + 10}; + fhicl::Atom maxWaveformSamplesToPrint{ + fhicl::Name("maxWaveformSamplesToPrint"), + fhicl::Comment("Maximum waveform samples to print in mismatch diagnostics"), + 64}; + }; + + using Parameters = art::EDAnalyzer::Table; + + explicit CrvDigiCollectionsComparator(Parameters const& config); + + void analyze(art::Event const& event) override; + void endJob() override; + +private: + struct FlatDigi { + uint32_t scintillatorBarIndex; + uint16_t sipm; + uint16_t startTdc; + std::vector waveform; + + bool operator<(FlatDigi const& other) const { + return std::tie(scintillatorBarIndex, sipm, startTdc, waveform) < + std::tie(other.scintillatorBarIndex, other.sipm, other.startTdc, other.waveform); + } + + bool operator==(FlatDigi const& other) const { + return scintillatorBarIndex == other.scintillatorBarIndex && sipm == other.sipm && startTdc == other.startTdc && + waveform == other.waveform; + } + }; + + static std::vector normalize(mu2e::CrvDigiCollection const& digis); + static std::string describe(FlatDigi const& digi, size_t maxWaveformSamplesToPrint); + + art::InputTag referenceTag_; + art::InputTag candidateTag_; + bool failOnMismatch_; + int diagLevel_; + size_t maxMismatchesToPrint_; + size_t maxWaveformSamplesToPrint_; + + size_t totalEvents_{0}; + size_t matchingEvents_{0}; + size_t mismatchedEvents_{0}; +}; + +CrvDigiCollectionsComparator::CrvDigiCollectionsComparator(Parameters const& config) + : art::EDAnalyzer{config} + , referenceTag_(config().referenceTag()) + , candidateTag_(config().candidateTag()) + , failOnMismatch_(config().failOnMismatch()) + , diagLevel_(config().diagLevel()) + , maxMismatchesToPrint_(config().maxMismatchesToPrint()) + , maxWaveformSamplesToPrint_(config().maxWaveformSamplesToPrint()) {} + +std::vector +CrvDigiCollectionsComparator::normalize(mu2e::CrvDigiCollection const& digis) { + std::vector out; + out.reserve(digis.size()); + for (auto const& digi : digis) { + if(digi.IsNZS()) continue; + out.push_back(FlatDigi{digi.GetScintillatorBarIndex().asUint(), digi.GetSiPMNumber(), digi.GetStartTDC(), digi.GetADCs()}); + } + // Sort so comparison is robust against ordering differences between producers. + std::sort(out.begin(), out.end()); + return out; +} + +std::string CrvDigiCollectionsComparator::describe(FlatDigi const& digi, + size_t maxWaveformSamplesToPrint) { + std::ostringstream os; + os << "scintillatorBarIndex=" << digi.scintillatorBarIndex << ", sipm=" << digi.sipm << ", startTdc=" << digi.startTdc + << ", waveformSize=" << digi.waveform.size(); + if (!digi.waveform.empty()) { + os << ", waveform={"; + size_t const n = digi.waveform.size(); + if (n <= maxWaveformSamplesToPrint) { + for (size_t i = 0; i < n; ++i) { + if (i != 0) { + os << ","; + } + os << digi.waveform[i]; + } + } else { + size_t const head = maxWaveformSamplesToPrint / 2; + size_t const tail = maxWaveformSamplesToPrint - head; + for (size_t i = 0; i < head; ++i) { + if (i != 0) { + os << ","; + } + os << digi.waveform[i]; + } + os << ",...,"; + for (size_t i = n - tail; i < n; ++i) { + if (i != n - tail) { + os << ","; + } + os << digi.waveform[i]; + } + } + os << "}"; + } + return os.str(); +} + +void CrvDigiCollectionsComparator::analyze(art::Event const& event) { + ++totalEvents_; + + auto const& referenceHandle = event.getValidHandle(referenceTag_); + auto const& candidateHandle = event.getValidHandle(candidateTag_); + + auto reference = normalize(*referenceHandle); + auto candidate = normalize(*candidateHandle); + + std::vector mismatches; + if (reference.size() != candidate.size()) { + std::ostringstream os; + os << "Collection sizes differ: reference=" << reference.size() + << ", candidate=" << candidate.size(); + mismatches.push_back(os.str()); + } + + size_t compareCount = std::min(reference.size(), candidate.size()); + for (size_t i = 0; i < compareCount && mismatches.size() < maxMismatchesToPrint_; ++i) { + if (!(reference[i] == candidate[i])) { + std::ostringstream os; + os << "Digi mismatch at sorted index " << i << "\n" + << " reference: " << describe(reference[i], maxWaveformSamplesToPrint_) << "\n" + << " candidate: " << describe(candidate[i], maxWaveformSamplesToPrint_); + mismatches.push_back(os.str()); + } + } + + if (mismatches.empty()) { + ++matchingEvents_; + if (diagLevel_ > 1) { + std::cout << "[CrvDigiCollectionsComparator] Event " << event.id() + << " collections are equivalent (" << reference.size() << " digis)" << std::endl; + } + return; + } + + ++mismatchedEvents_; + + std::ostringstream summary; + summary << "[CrvDigiCollectionsComparator] Event " << event.id() << " mismatch between " + << referenceTag_ << " and " << candidateTag_ << "\n"; + for (auto const& mismatch : mismatches) { + summary << mismatch << "\n"; + } + + if (diagLevel_ > 0 || failOnMismatch_) { + // Keep human-readable details even when failOnMismatch is false. + std::cout << summary.str(); + } + + if (failOnMismatch_) { + throw cet::exception("CRVDIGI_COMPARE") << summary.str(); + } +} + +void CrvDigiCollectionsComparator::endJob() { + std::cout << "\n ----- [CrvDigiCollectionsComparator] Summary ----- " << std::endl; + std::cout << "Total events: " << totalEvents_ << std::endl; + std::cout << "Matching events: " << matchingEvents_ << std::endl; + std::cout << "Mismatched events: " << mismatchedEvents_ << std::endl; +} + +} // namespace mu2e + +DEFINE_ART_MODULE(mu2e::CrvDigiCollectionsComparator) diff --git a/DAQ/src/CrvDigisToFragments_module.cc b/DAQ/src/CrvDigisToFragments_module.cc new file mode 100644 index 0000000000..81469854ba --- /dev/null +++ b/DAQ/src/CrvDigisToFragments_module.cc @@ -0,0 +1,416 @@ +// ====================================================================== +// +// CrvDigisToFragments_module: Create DTCEVT artdaq::Fragment collection +// from CrvDigiCollection +// +// ====================================================================== + +#include "art/Framework/Core/EDProducer.h" +#include "art/Framework/Principal/Event.h" +#include "art/Framework/Principal/Handle.h" +#include "fhiclcpp/ParameterSet.h" + +#include "artdaq-core-mu2e/Overlays/Decoders/CRVDataDecoder.hh" +#include "artdaq-core-mu2e/Overlays/DTC_Packets/DTC_Event.h" +#include "artdaq-core-mu2e/Overlays/DTC_Packets/DTC_DataHeaderPacket.h" +#include "artdaq-core-mu2e/Overlays/DTC_Packets/DTC_EventHeader.h" +#include "artdaq-core-mu2e/Overlays/DTC_Packets/DTC_SubEventHeader.h" +#include "artdaq-core-mu2e/Overlays/FragmentType.hh" +#include "artdaq-core-mu2e/Overlays/DTCEventFragment.hh" +#include + +#include "Offline/CosmicRayShieldGeom/inc/CosmicRayShield.hh" +#include "Offline/GeometryService/inc/GeomHandle.hh" +#include "Offline/ProditionsService/inc/ProditionsHandle.hh" +#include "Offline/RecoDataProducts/inc/CrvDigi.hh" +#include "Offline/CRVConditions/inc/CRVOrdinal.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mu2e +{ + +constexpr uint8_t kFormatVersion = 1; +constexpr uint8_t kBytesPerPacket = 16; +constexpr size_t kDtcEventHeaderBytes = 24; +constexpr size_t kDtcSubEventHeaderBytes = 48; +static_assert(sizeof(DTCLib::DTC_EventHeader) == kDtcEventHeaderBytes, "Unexpected DTC_EventHeader size"); +static_assert(sizeof(DTCLib::DTC_SubEventHeader) == kDtcSubEventHeaderBytes, "Unexpected DTC_SubEventHeader size"); + +struct CrvBlockData +{ + CRVDataDecoder::CRVROCStatusPacketFEBII crvROCstatus; + std::vector crvHits; +}; + +class CrvDigisToFragments : public art::EDProducer +{ + public: + struct Config + { + using Name = fhicl::Name; + using Comment = fhicl::Comment; + fhicl::Atom diagLevel{Name("diagLevel"), Comment("diagnostic level"), 0}; + fhicl::Atom crvDigiTag{Name("CrvDigiTag"), Comment("Input CrvDigiCollection"), art::InputTag("makeSD")}; + fhicl::Atom crvDtcIdStart{Name("CrvDtcIdStart"), Comment("First CRV DTC ID"), 0}; //FIXME: Which DTC IDs do the CRV use? + fhicl::Atom fragmentIdOffset{Name("FragmentIdOffset"), Comment("Fragment ID offset"), 0}; //FIXME: Is this the correct fragment ID offset for the CRV? + }; + + explicit CrvDigisToFragments(const art::EDProducer::Table& config); + virtual ~CrvDigisToFragments() {} + + virtual void produce(art::Event&); + virtual void endJob(); + + private: + int _diagLevel; + art::InputTag _crvDigiTag; + uint8_t _crvDtcIdStart; + uint8_t _fragmentIdOffset; + + long int _totalEvents; + long int _totalDigis; + long int _totalDigisEncoded; + + std::set _ROCs; + + ProditionsHandle _crvChannelMap_h; + + CRVDataDecoder::CRVHitRawFEBII encodeCrvHit(CrvDigi const& digi); + + static void putBlockInEvent(DTCLib::DTC_Event& currentEvent, uint8_t dtcID, + DTCLib::DTC_DataBlock const& thisBlock); + + void buildDtcEventsFromDigis(art::Event const& event, + mu2e::CrvDigiCollection const& crvDigis, + std::map& dtcEvents); +}; + +CrvDigisToFragments::CrvDigisToFragments(const art::EDProducer::Table& config) + : art::EDProducer{config} + , _diagLevel(config().diagLevel()) + , _crvDigiTag(config().crvDigiTag()) + , _crvDtcIdStart(config().crvDtcIdStart()) + , _fragmentIdOffset(config().fragmentIdOffset()) +{ + produces(); + _totalEvents = 0; + _totalDigis = 0; + _totalDigisEncoded = 0; +} + +CRVDataDecoder::CRVHitRawFEBII CrvDigisToFragments::encodeCrvHit(CrvDigi const& digi) +{ + CRVDataDecoder::CRVHitRawFEBII hitPacket; + hitPacket.hitInfo.portNumber = digi.GetFEB(); + hitPacket.hitInfo.fpgaNumber = digi.GetFEBchannel()/CRVId::nChanPerFPGA; + hitPacket.hitInfo.fpgaChannel = digi.GetFEBchannel()%CRVId::nChanPerFPGA; + hitPacket.hitInfo.hitTime = digi.GetStartTDC()*2; //online: period of 6.25ns, offline: period of 12.5ns. + if(digi.GetStartTDC()>0x7fff) + { + hitPacket.hitInfo.hitTime=0xffff; + if(_diagLevel > 0) + { + std::cout << "[CrvDigisToFragments::encodeCrvHit] WARNING: hit time (in 6.25ns) exceeds 16 bits - setting it 0xffff" << std::endl; + } + } + + const std::vector &adcs = digi.GetADCs(); //check for correct number of samples in calling function + for(size_t adcBlock=0; adcBlock> 4; //upper 8 bit + uint16_t ADCsample2 = adcs.at(adcBlock*CRVDataDecoder::nADCsamplesPerBlock+2); + hitPacket.adcBlocks[adcBlock].ADCsample2a = ADCsample2 & 0xff; //lower 8 bit + hitPacket.adcBlocks[adcBlock].ADCsample2b = (ADCsample2 & 0xf00) >> 8; //upper 4 bit + hitPacket.adcBlocks[adcBlock].ADCsample3 = adcs.at(adcBlock*CRVDataDecoder::nADCsamplesPerBlock+3); + } + + return hitPacket; +} + +void CrvDigisToFragments::putBlockInEvent(DTCLib::DTC_Event& currentEvent, uint8_t dtcID, + DTCLib::DTC_DataBlock const& thisBlock) +{ + auto subEvt = currentEvent.GetSubEventByDTCID(dtcID, DTCLib::DTC_Subsystem_CRV); + if (subEvt == nullptr) + { + DTCLib::DTC_SubEvent newSubEvt; + newSubEvt.SetEventWindowTag(currentEvent.GetEventWindowTag()); + newSubEvt.SetSourceDTC(dtcID, DTCLib::DTC_Subsystem_CRV); + newSubEvt.AddDataBlock(thisBlock); + currentEvent.AddSubEvent(newSubEvt); + } + else + { + subEvt->AddDataBlock(thisBlock); + currentEvent.UpdateHeader(); //update the byte count + } +} + +void CrvDigisToFragments::buildDtcEventsFromDigis(art::Event const& event, mu2e::CrvDigiCollection const& crvDigis, + std::map& dtcEvents) +{ + std::map> byDtcAndLink; + + for(size_t i = 0; i < crvDigis.size(); ++i) + { + auto const& digi = crvDigis[i]; + + if(digi.IsNZS()) continue; //FEBs can't handle NZS data, yet + + uint16_t ROC = digi.GetROC(); + if(ROC==0) //shouldn't happen + { + if(_diagLevel > 0) + { + std::cout << "[CrvDigisToFragments::buildDtcEventsFromDigis] WARNING: Invalid ROC - skipping digi" << std::endl; + } + continue; + } + + if(digi.GetADCs().size()!=CRVDataDecoder::nADCsamples) + { + if(_diagLevel > 0) + { + std::cout << "[CrvDigisToFragments::buildDtcEventsFromDigis] WARNING: Encountered waveform with " << digi.GetADCs().size() << " samples. " + << "Can handle only " << CRVDataDecoder::nADCsamples << " samples. - skipping digi" << std::endl; + } + continue; + } + + uint8_t dtcID = _crvDtcIdStart + (ROC-1)/CRVId::nROCPerDTC; + uint8_t linkID = (ROC-1)%CRVId::nROCPerDTC; + + auto& block = byDtcAndLink[dtcID][linkID]; + + block.crvHits.emplace_back(encodeCrvHit(digi)); + ++_totalDigisEncoded; + } + + for(auto& [dtcID, linkMap] : byDtcAndLink) + { + auto& dtcEvent = dtcEvents[dtcID]; + dtcEvent.SetEventWindowTag(DTCLib::DTC_EventWindowTag(static_cast(event.event()))); + + for(uint8_t linkID = 0; linkID < CRVId::nROCPerDTC; ++linkID) + { + auto& block = linkMap[linkID]; + + // Calculate packet and byte counts based on actual hits + // packetCount in DTC_DataHeaderPacket excludes the 16-byte data header + // packet itself; it counts only hit payload packets. + size_t byteCount = block.crvHits.size()*sizeof(CRVDataDecoder::CRVHitRawFEBII) + sizeof(CRVDataDecoder::CRVROCStatusPacketFEBII); + size_t packetCount = (byteCount+kBytesPerPacket-1) / kBytesPerPacket; //integer ceiling division of byteCount/bytesPerPacket + size_t transferByteCount = (packetCount + 1) * kBytesPerPacket; + DTCLib::DTC_Subsystem subsystem = DTCLib::DTC_Subsystem_CRV; + + uint16_t ROC = (dtcID-_crvDtcIdStart)*CRVId::nROCPerDTC + linkID + 1; + if(_ROCs.find(ROC)==_ROCs.end()) // link (ROC) not used, but still have a data block written out. + { + byteCount=0; + packetCount=0; + transferByteCount=kBytesPerPacket; + subsystem=static_cast(0); + } + + if(byteCount%2!=0) + { + throw cet::exception("CRVDIGI_TO_FRAGMENT") << "Byte count is not a multiple of 2"; + } + + block.crvROCstatus.ControllerEventWordCount=byteCount/2; + block.crvROCstatus.EventWindowTag0=event.event() & 0xffff; + block.crvROCstatus.EventWindowTag1=(event.event()>>16) & 0xffff; + + if(_diagLevel > 1) + { + std::cout << "[buildDtcEventsFromDigis] DTC " << static_cast(dtcID) + << " Link " << static_cast(linkID) + << " hits: " << block.crvHits.size() + << " numPackets: " << packetCount + << " transferByteCount: " << transferByteCount << std::endl; + } + + DTCLib::DTC_DataBlock thisBlock(transferByteCount); + if(thisBlock.blockPointer == nullptr) + { + if(_diagLevel > 0) + { + std::cout << "[buildDtcEventsFromDigis] Failed to allocate DTC_DataBlock with size " + << transferByteCount << std::endl; + } + continue; + } + std::fill(thisBlock.allocBytes->begin(), thisBlock.allocBytes->end(), 0xFF); + + auto const ts = DTCLib::DTC_EventWindowTag(static_cast(event.event())); + DTCLib::DTC_DataHeaderPacket hdr(static_cast(linkID), + packetCount, + 0, + dtcID, + subsystem, + kFormatVersion, + ts, + 0); + + auto hdrPkt = hdr.ConvertToDataPacket(); //this function has a bug. the subsystem ID gets written to byte 3 of the DTC_DataPacket, but later read back from byte 5. + hdrPkt.SetByte(5, static_cast(((packetCount & 0x0700) >> 8) | (subsystem << 5))); //this is a temporary bug fix. + + size_t pos = 0; + std::memcpy(thisBlock.allocBytes->data() + pos, hdrPkt.GetData(), kBytesPerPacket); + pos += kBytesPerPacket; + + if(packetCount>0) //no ROC status packet, if ROC is not used + { + std::memcpy(thisBlock.allocBytes->data() + pos, &block.crvROCstatus, sizeof(CRVDataDecoder::CRVROCStatusPacketFEBII)); + pos += sizeof(CRVDataDecoder::CRVROCStatusPacketFEBII); + + for(auto const& hit : block.crvHits) + { + std::memcpy(thisBlock.allocBytes->data() + pos, &hit, sizeof(CRVDataDecoder::CRVHitRawFEBII)); + pos += sizeof(CRVDataDecoder::CRVHitRawFEBII); + } + } + + if(_diagLevel > 1) + { + std::cout << " After filling: pos = " << pos << ", transferByteCount = " << transferByteCount << std::endl; + } + + putBlockInEvent(dtcEvent, dtcID, thisBlock); + } + } +} + +void CrvDigisToFragments::produce(art::Event& event) +{ + if(_ROCs.empty()) + { + auto const& crvChannelMap = _crvChannelMap_h.get(event.id()); + GeomHandle CRS; + const std::vector > &counters = CRS->getAllCRSScintillatorBars(); + for(size_t channel=0; channel fragments = std::make_unique(); + auto crvDigiHandle = event.getHandle(_crvDigiTag); + + if(!crvDigiHandle.isValid()) + { + if(_diagLevel > 0) + { + std::cout << "[CrvDigisToFragments::produce] Missing input CRV collection" << std::endl; + } + event.put(std::move(fragments)); + return; + } + + auto const& crvDigis = *crvDigiHandle; + _totalDigis += crvDigis.size(); + + std::map dtcEvents; + buildDtcEventsFromDigis(event, crvDigis, dtcEvents); + + for(auto& [dtcID, dtcEvent] : dtcEvents) + { + auto const eventBytes = dtcEvent.GetEventByteCount(); + + if(_diagLevel > 1) + { + std::cout << "[CrvDigisToFragments::produce] DTC " << static_cast(dtcID) + << " eventBytes: " << eventBytes + << ", subEventCount: " << dtcEvent.GetSubEventCount() << std::endl; + for(size_t i = 0; i < dtcEvent.GetSubEvents().size(); ++i) + { + auto const& subEvt = dtcEvent.GetSubEvents()[i]; + std::cout << " SubEvent " << i << " blockCount: " << subEvt.GetDataBlocks().size() << std::endl; + for (size_t j = 0; j < subEvt.GetDataBlocks().size(); ++j) + { + auto const& block = subEvt.GetDataBlocks()[j]; + std::cout << " Block " << j << " byteSize: " << block.byteSize << std::endl; + } + } + } + + if(eventBytes == 0 || dtcEvent.GetSubEventCount() == 0) continue; + + std::vector packed; + packed.reserve(eventBytes); + + auto const* evHdr = dtcEvent.GetHeader(); + packed.insert(packed.end(), reinterpret_cast(evHdr), + reinterpret_cast(evHdr) + kDtcEventHeaderBytes); + + for(auto const& subEvt : dtcEvent.GetSubEvents()) + { + auto const* subHdr = subEvt.GetHeader(); + packed.insert(packed.end(), reinterpret_cast(subHdr), + reinterpret_cast(subHdr) + kDtcSubEventHeaderBytes); + + for(auto const& block : subEvt.GetDataBlocks()) + { + auto const* blk = reinterpret_cast(block.GetRawBufferPointer()); + packed.insert(packed.end(), blk, blk + block.byteSize); + } + } + + if(_diagLevel > 0 && packed.size() != eventBytes) + { + std::cout << "[CrvDigisToFragments::produce] WARNING: DTC " << static_cast(dtcID) + << " packed size " << packed.size() + << " differs from event header size " << eventBytes << std::endl; + } + + auto fragPtr = artdaq::Fragment::FragmentBytes(packed.size()); + fragPtr->setUserType(mu2e::FragmentType::DTCEVT); + fragPtr->setSequenceID(static_cast(event.event())); + fragPtr->setFragmentID(static_cast(dtcID + _fragmentIdOffset)); + fragPtr->setTimestamp(static_cast(event.time().value())); + if (!packed.empty()) std::memcpy(fragPtr->dataBeginBytes(), packed.data(), packed.size()); + + fragments->emplace_back(std::move(*fragPtr)); + } + + if(_diagLevel > 0) + { + std::cout << "[CrvDigisToFragments::produce] Run " << event.run() << ", subrun " + << event.subRun() << ", event " << event.event() << " has " << crvDigis.size() + << " CrvDigis and created " << fragments->size() << " DTCEVT fragment(s)" + << std::endl; + } + + event.put(std::move(fragments)); +} + +void CrvDigisToFragments::endJob() +{ + if(_diagLevel > 0) + { + std::cout << "\n ----- [CrvDigisToFragments] Summary ----- " << std::endl; + std::cout << "Total events: " << _totalEvents << std::endl; + std::cout << "Total CrvDigis found: " << _totalDigis << std::endl; + std::cout << "Total CrvDigis encoded: " << _totalDigisEncoded << std::endl; + } +} + +} //namespace mu2e + +DEFINE_ART_MODULE(mu2e::CrvDigisToFragments) diff --git a/DAQ/test/compareCrvDigiCollections_extracted.fcl b/DAQ/test/compareCrvDigiCollections_extracted.fcl new file mode 100644 index 0000000000..6eb8a42b3c --- /dev/null +++ b/DAQ/test/compareCrvDigiCollections_extracted.fcl @@ -0,0 +1,78 @@ +#Produces artFragments from CrvDigis and converts them back to CrvDigis. Both CrvDigi collections are compared at the end. + +#contact: R. Ehrlich + +#include "mu2e-trig-config/core/trigProducers.fcl" +#include "Offline/fcl/standardProducers.fcl" +#include "Offline/fcl/standardServices.fcl" +#include "Offline/CRVResponse/fcl/prolog.fcl" + +process_name : CrvDigiCollectionComparison + +services : +{ + @table::Services.Core + @table::Services.Reco +} + +source : +{ + module_type : RootInput + fileNames : @nil + maxEvents : -1 +} + +physics : +{ + producers : + { + CrvFragment : + { + module_type : CrvDigisToFragments + CrvDigiTag : "CrvDigi" + diagLevel : 0 + } + CrvDigiNew : + { + module_type : CrvDigisFromArtdaqFragmentsFEBII + diagLevel : 0 + } + } + analyzers: + { + CrvDigiComparison : + { + module_type : CrvDigiCollectionsComparator + diagLevel : 10 + referenceTag : "CrvDigi" + candidateTag : "CrvDigiNew" + } + } + + t1 : [ CrvFragment, CrvDigiNew ] + e1 : [ CrvDigiComparison, outfileReco ] + + trigger_paths : [t1] + end_paths : [e1] + +} + +outputs: +{ + outfileReco : + { + module_type : RootOutput + fileName : "crvRecoExtractedComparison.art" + } +} + +services.TFileService.fileName : "crvRecoExtracted_DQM.root" +services.GeometryService.inputFile: "Offline/Mu2eG4/geom/geom_common_extracted.txt" +services.ProditionsService.crvStatus.useDb: true +services.ProditionsService.crvCalib.useDb: true +#current status and calibration until we can use the database +services.DbService.textFile : ["Offline/CRVConditions/data/status_extracted_20260221.txt","Offline/CRVConditions/data/calib_extracted_20260221.txt"] +services.DbService.verbose : 0 +services.TimeTracker.printSummary: true +services.scheduler.wantSummary: true +services.message.destinations.log.outputStatistics : true diff --git a/DAQ/test/generateCrvDigiFromFragment.fcl b/DAQ/test/generateCrvDigiFromFragment.fcl deleted file mode 100644 index 5116281b9b..0000000000 --- a/DAQ/test/generateCrvDigiFromFragment.fcl +++ /dev/null @@ -1,49 +0,0 @@ -#include "mu2e-trig-config/core/trigProducers.fcl" -#include "Offline/fcl/standardServices.fcl" - -process_name : FragmentToDigi - -services : -{ - @table::Services.Core -} - -source : -{ - module_type : RootInput - fileNames : @nil - maxEvents : -1 -} - -physics : -{ - producers : - { - CrvDigi : - { - module_type : CrvDigisFromArtdaqFragments - diagLevel : 10 - useSubsystem0 : true - } - } - - t1 : [ CrvDigi] - e1 : [ outfile ] - - trigger_paths : [t1] - end_paths : [e1] - -} - -outputs: -{ - outfile : - { - module_type : RootOutput - fileName : "crvDigis.art" - } -} - -#services.message.destinations.log.categories.ArtReport.reportEvery : 1 -#services.message.destinations.log.categories.ArtReport.limit : 1 -#services.message.destinations.log.categories.ArtReport.timespan : 300 diff --git a/DAQ/test/generateCrvDigisFromFragments_extracted.fcl b/DAQ/test/generateCrvDigisFromFragments_extracted.fcl new file mode 100644 index 0000000000..1bf154655f --- /dev/null +++ b/DAQ/test/generateCrvDigisFromFragments_extracted.fcl @@ -0,0 +1,64 @@ +#CrvDigi generator from artFragments at KPP + +#contact: R. Ehrlich + +#include "mu2e-trig-config/core/trigProducers.fcl" +#include "Offline/fcl/standardProducers.fcl" +#include "Offline/fcl/standardServices.fcl" +#include "Offline/CRVResponse/fcl/prolog.fcl" + +process_name : CrvPass1 + +services : +{ + @table::Services.Core + @table::Services.Reco +} + +source : +{ + module_type : RootInput + fileNames : @nil + maxEvents : -1 +} + +physics : +{ + producers : + { + CrvDigi : + { + module_type : CrvDigisFromArtdaqFragmentsFEBII + diagLevel : 10 + } + } + + t1 : [ CrvDigi ] + e1 : [ outfileReco ] + + trigger_paths : [t1] + end_paths : [e1] + +} + +outputs: +{ + outfileReco : + { + module_type : RootOutput + fileName : "crvRecoExtracted.art" + outputCommands : [ "keep *_*_*_*" , + "drop artdaq::*_*_*_*" ] + } +} + +services.TFileService.fileName : "crvRecoExtracted_DQM.root" +services.GeometryService.inputFile: "Offline/Mu2eG4/geom/geom_common_extracted.txt" +services.ProditionsService.crvStatus.useDb: true +services.ProditionsService.crvCalib.useDb: true +#current status and calibration until we can use the database +services.DbService.textFile : ["Offline/CRVConditions/data/status_extracted_20260221.txt","Offline/CRVConditions/data/calib_extracted_20260221.txt"] +services.DbService.verbose : 0 +services.TimeTracker.printSummary: true +services.scheduler.wantSummary: true +services.message.destinations.log.outputStatistics : true diff --git a/DAQ/test/generateCrvGlobalRunDataFromFragments.fcl b/DAQ/test/generateCrvGlobalRunDataFromFragments.fcl deleted file mode 100644 index 6dcd285dac..0000000000 --- a/DAQ/test/generateCrvGlobalRunDataFromFragments.fcl +++ /dev/null @@ -1,43 +0,0 @@ -# Reads CRV Global Run Info -# Contact person R. Ehrlich - -#include "mu2e-trig-config/core/trigProducers.fcl" -#include "Offline/fcl/standardServices.fcl" - -process_name : CrvGlobalRun - -services : -{ - @table::Services.Core -} - -source : -{ - module_type : RootInput - fileNames : @nil - maxEvents : -1 -} - -physics : -{ - producers : - { - CrvGlobalRunData : - { - module_type : CrvGRdataFromArtdaqFragments - diagLevel : 10 - csvFileName : "crvGlobalRun.txt" - writeCsvFile : true - useSubsystem0 : false - } - } - - t1 : [ CrvGlobalRunData ] - e1 : [ ] - - trigger_paths : [t1] - end_paths : [e1] - -} - -services.message.destinations.log.categories.ArtReport.limit : 0