Skip to content

Commit

Permalink
Trigger handling with dense format for non-interleaved data
Browse files Browse the repository at this point in the history
  • Loading branch information
wiechula authored and davidrohr committed Aug 15, 2023
1 parent adef678 commit 2ae8ce1
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 28 deletions.
19 changes: 19 additions & 0 deletions DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppression.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,25 @@ struct ZeroSuppressedContainer { // Struct for the TPC zero suppressed data form
TPCZSHDR hdr; // ZS header
};

/// Trigger word for dense link-base ZS
///
/// Trigger word is always 128bit and occurs always in the last page of a HBF before the meta header
struct TriggerWordDLBZS {
static constexpr uint16_t MaxTriggerEntries = 8; ///< Maximum number of trigger information

/// trigger types as in the ttype bits
enum TriggerType : uint8_t {
PhT = 1, ///< Physics Trigger
PP = 2, ///< Pre Pulse for calibration
Cal = 4, ///< Laser (Calibration trigger)
};
uint16_t triggerEntries[MaxTriggerEntries] = {};

uint16_t getTriggerBC(int entry = 0) const { return triggerEntries[entry] & 0xFFF; }
uint16_t getTriggerType(int entry = 0) const { return (triggerEntries[entry] >> 12) & 0x7; }
bool isValid(int entry = 0) const { return triggerEntries[entry] & 0x8000; }
};

} // namespace tpc
} // namespace o2
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class CalibLaserTracksDevice : public o2::framework::Task
const auto nTFs = mCalib.getCalibData().processedTFs;
const auto nMatchA = mCalib.getMatchedPairsA();
const auto nMatchC = mCalib.getMatchedPairsC();
LOGP(error, "Calibration data was not published, laser track calibration might have enough statistics: {} ({}) matched tracks in {} TFs on the A (C) < {} min TFs * {} min matches per side per TF ", nMatchA, nMatchC, nTFs, mMinNumberTFs, CalibLaserTracks::MinTrackPerSidePerTF);
LOGP(error, "Calibration data was not published, laser track calibration might not have enough statistics: {} ({}) matched tracks in {} TFs on the A (C) < {} min TFs * {} min matches per side per TF? Or eos was not called by the framework.", nMatchA, nMatchC, nTFs, mMinNumberTFs, CalibLaserTracks::MinTrackPerSidePerTF);
}
};
ic.services().get<CallbackService>().set<CallbackService::Id::Stop>(finishFunction);
Expand Down Expand Up @@ -136,7 +136,7 @@ class CalibLaserTracksDevice : public o2::framework::Task

const auto now = std::chrono::system_clock::now();
const long timeStart = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
//const auto timeStart = ltrCalib.firstTime; //TODO: use once it is a correct time not TFid
// const auto timeStart = ltrCalib.firstTime; //TODO: use once it is a correct time not TFid
const long timeEnd = o2::ccdb::CcdbObjectInfo::INFINITE_TIMESTAMP;

w.setPath("TPC/Calib/LaserTracks");
Expand Down
18 changes: 15 additions & 3 deletions Detectors/TPC/workflow/include/TPCWorkflow/CalibProcessingHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@

#include "Framework/InputRecord.h"

using o2::tpc::rawreader::RawReaderCRU;

namespace o2
{
namespace tpc
Expand All @@ -26,10 +24,24 @@ namespace rawreader
{
class RawReaderCRU;
}

namespace calib_processing_helper
{

uint64_t processRawData(o2::framework::InputRecord& inputs, std::unique_ptr<RawReaderCRU>& reader, bool useOldSubspec = false, const std::vector<int>& sectors = {}, size_t* nerrors = nullptr, uint32_t syncOffsetReference = 144, uint32_t decoderType = 1);
uint64_t processRawData(o2::framework::InputRecord& inputs, std::unique_ptr<o2::tpc::rawreader::RawReaderCRU>& reader, bool useOldSubspec = false, const std::vector<int>& sectors = {}, size_t* nerrors = nullptr, uint32_t syncOffsetReference = 144, uint32_t decoderType = 1, bool useTrigger = true);

/// absolute BC relative to TF start (firstOrbit)
std::vector<o2::framework::InputSpec> getFilter(o2::framework::InputRecord& inputs);

/// absolute BC relative to TF start (firstOrbit)
int getTriggerBCoffset(o2::framework::InputRecord& inputs, std::vector<o2::framework::InputSpec> filter = {}, bool slowScan = false);

/// absolute BC relative to TF start (firstOrbit)
/// \param data full raw page (incl. RDH)
/// \param size size of raw page
/// \param firstOrbit first orbit of the TF
int getTriggerBCoffset(const char* data, size_t size, uint32_t firstOrbit);

} // namespace calib_processing_helper
} // namespace tpc
} // namespace o2
Expand Down
174 changes: 166 additions & 8 deletions Detectors/TPC/workflow/src/CalibProcessingHelper.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "Headers/DataHeaderHelpers.h"
#include "Headers/RDHAny.h"
#include "DataFormatsTPC/ZeroSuppressionLinkBased.h"
#include "DataFormatsTPC/ZeroSuppression.h"
#include "DataFormatsTPC/RawDataTypes.h"
#include "DataFormatsTPC/Digit.h"

Expand All @@ -40,15 +41,15 @@
using namespace o2::tpc;
using namespace o2::framework;
using RDHUtils = o2::raw::RDHUtils;
using RDHAny = o2::header::RDHAny;

void processGBT(o2::framework::RawParser<>& parser, std::unique_ptr<RawReaderCRU>& reader, const rdh_utils::FEEIDType feeID);
void processLinkZS(o2::framework::RawParser<>& parser, std::unique_ptr<RawReaderCRU>& reader, uint32_t firstOrbit, uint32_t syncOffsetReference, uint32_t decoderType);
void processGBT(o2::framework::RawParser<>& parser, std::unique_ptr<o2::tpc::rawreader::RawReaderCRU>& reader, const rdh_utils::FEEIDType feeID);
void processLinkZS(o2::framework::RawParser<>& parser, std::unique_ptr<o2::tpc::rawreader::RawReaderCRU>& reader, uint32_t firstOrbit, uint32_t syncOffsetReference, uint32_t decoderType, int triggerBC);
uint32_t getBCsyncOffsetReference(InputRecord& inputs, const std::vector<InputSpec>& filter);

uint64_t calib_processing_helper::processRawData(o2::framework::InputRecord& inputs, std::unique_ptr<RawReaderCRU>& reader, bool useOldSubspec, const std::vector<int>& sectors, size_t* nerrors, uint32_t syncOffsetReference, uint32_t decoderType)
std::vector<o2::framework::InputSpec> calib_processing_helper::getFilter(o2::framework::InputRecord& inputs)
{
std::vector<InputSpec> filter = {{"check", ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, "RAWDATA"}, Lifetime::Timeframe}};
size_t errorCount = 0;
// TODO: check if presence of data sampling can be checked in another way
bool sampledData = true;
for ([[maybe_unused]] auto const& ref : InputRecordWalker(inputs, filter)) {
Expand All @@ -69,11 +70,22 @@ uint64_t calib_processing_helper::processRawData(o2::framework::InputRecord& inp
LOGP(info, "Using sampled data");
}

return filter;
}

uint64_t calib_processing_helper::processRawData(o2::framework::InputRecord& inputs, std::unique_ptr<rawreader::RawReaderCRU>& reader, bool useOldSubspec, const std::vector<int>& sectors, size_t* nerrors, uint32_t syncOffsetReference, uint32_t decoderType, bool useTrigger)
{
std::vector<InputSpec> filter = getFilter(inputs);

size_t errorCount = 0;

uint64_t activeSectors = 0;
uint32_t firstOrbit = 0;
bool readFirst = false;
bool readFirstZS = false;

const auto triggerBC = ((decoderType == 1 && useTrigger)) ? getTriggerBCoffset(inputs, filter) : -1;

// for LinkZS data the maximum sync offset is needed to align the data properly.
// getBCsyncOffsetReference only works, if the full TF is seen. Alternatively, this value could be set
// fixed to e.g. 144 or 152 which is the maximum sync delay expected
Expand Down Expand Up @@ -168,7 +180,7 @@ uint64_t calib_processing_helper::processRawData(o2::framework::InputRecord& inp
}

if (isLinkZS) {
processLinkZS(parser, reader, firstOrbit, syncOffsetReference, decoderType);
processLinkZS(parser, reader, firstOrbit, syncOffsetReference, decoderType, triggerBC);
} else {
processGBT(parser, reader, feeID);
}
Expand Down Expand Up @@ -209,7 +221,7 @@ uint64_t calib_processing_helper::processRawData(o2::framework::InputRecord& inp
return activeSectors;
}

void processGBT(o2::framework::RawParser<>& parser, std::unique_ptr<RawReaderCRU>& reader, const rdh_utils::FEEIDType feeID)
void processGBT(o2::framework::RawParser<>& parser, std::unique_ptr<o2::tpc::rawreader::RawReaderCRU>& reader, const rdh_utils::FEEIDType feeID)
{
// TODO: currently this will only work for HBa1, since the sync is in the first packet and
// the decoder expects all packets of one link to be processed at once
Expand Down Expand Up @@ -249,7 +261,7 @@ void processGBT(o2::framework::RawParser<>& parser, std::unique_ptr<RawReaderCRU
reader->runADCDataCallback(rawData);
}

void processLinkZS(o2::framework::RawParser<>& parser, std::unique_ptr<RawReaderCRU>& reader, uint32_t firstOrbit, uint32_t syncOffsetReference, uint32_t decoderType)
void processLinkZS(o2::framework::RawParser<>& parser, std::unique_ptr<o2::tpc::rawreader::RawReaderCRU>& reader, uint32_t firstOrbit, uint32_t syncOffsetReference, uint32_t decoderType, int triggerBC)
{
for (auto it = parser.begin(), end = parser.end(); it != end; ++it) {
auto rdhPtr = reinterpret_cast<const o2::header::RDHAny*>(it.raw());
Expand Down Expand Up @@ -277,7 +289,7 @@ void processLinkZS(o2::framework::RawParser<>& parser, std::unique_ptr<RawReader
std::vector<Digit> digits;
static o2::gpu::GPUParam gpuParam;
static o2::gpu::GPUReconstructionZSDecoder gpuDecoder;
gpuDecoder.DecodePage(digits, (const void*)it.raw(), firstOrbit, gpuParam);
gpuDecoder.DecodePage(digits, (const void*)it.raw(), firstOrbit, gpuParam, static_cast<unsigned int>((triggerBC > 0) ? triggerBC : 0));
for (const auto& digit : digits) {
reader->getManager()->getLinkZSCallback()(digit.getCRU(), digit.getRow(), digit.getPad(), digit.getTimeStamp(), digit.getChargeFloat());
}
Expand Down Expand Up @@ -355,3 +367,149 @@ uint32_t getBCsyncOffsetReference(InputRecord& inputs, const std::vector<InputSp
LOGP(info, "syncOffsetReference in this TF: {}", syncOffsetReference);
return syncOffsetReference;
}

int getTriggerInfoLBZS(const char* data, size_t size, uint32_t firstOrbit)
{
auto rdh = (const RDHAny*)data;
const auto orbit = RDHUtils::getTriggerOrbit(rdh);
int triggerBCOffset = -1;
zerosupp_link_based::ContainerZS* zsdata = (zerosupp_link_based::ContainerZS*)(data + sizeof(RDHAny));
const auto& header = zsdata->cont.header;
if (!header.hasCorrectMagicWord()) {
return -1;
}

if (header.isTriggerInfo()) {
// for the moment only skip the trigger info
const auto triggerInfo = (zerosupp_link_based::TriggerContainer*)zsdata;
const auto triggerOrbit = triggerInfo->triggerInfo.getOrbit();
const auto triggerBC = triggerInfo->triggerInfo.bunchCrossing;
triggerBCOffset = int(triggerOrbit - firstOrbit) * o2::constants::lhc::LHCMaxBunches + triggerBC;
LOGP(debug, "TriggerInfoV1: orbit: {}, firstOrbit: {}, triggerOrbit: {}, triggerBC: {}, triggerBCOffset: {}", orbit, firstOrbit, triggerOrbit, triggerBC, triggerBCOffset);
zsdata = zsdata->next();
return triggerBCOffset;
} else if (header.isTriggerInfoV2()) {
// for the moment only skip the trigger info
const auto triggerInfo = (zerosupp_link_based::TriggerInfoV2*)zsdata;
const auto triggerOrbit = triggerInfo->orbit;
const auto triggerBC = triggerInfo->bunchCrossing;
triggerBCOffset = int(triggerOrbit - firstOrbit) * o2::constants::lhc::LHCMaxBunches + triggerBC;
LOGP(debug, "TriggerInfoV2: orbit: {}, firstOrbit: {}, triggerOrbit: {}, triggerBC: {}, triggerBCOffset: {}", orbit, firstOrbit, triggerOrbit, triggerBC, triggerBCOffset);
zsdata = zsdata->next();
return triggerBCOffset;
} else if (header.isMetaHeader()) {
const auto& metaHDR = *((TPCZSHDRV2*)zsdata);

const auto& triggerInfo = *(zerosupp_link_based::TriggerInfoV3*)((const char*)&metaHDR + sizeof(metaHDR));
if (triggerInfo.hasTrigger()) {
const auto triggerBC = triggerInfo.getFirstBC();
const auto triggerOrbit = orbit;
triggerBCOffset = int(triggerOrbit - firstOrbit) * o2::constants::lhc::LHCMaxBunches + triggerBC;
LOGP(debug, "TriggerInfoV3: orbit: {}, firstOrbit: {}, triggerOrbit: {}, triggerBC: {}, triggerBCOffset: {}", orbit, firstOrbit, triggerOrbit, triggerBC, triggerBCOffset);
}

return triggerBCOffset;
}

return -1;
}

int calib_processing_helper::getTriggerBCoffset(const char* data, size_t size, uint32_t firstOrbit)
{
auto rdh = (const RDHAny*)data;
const auto feeId = RDHUtils::getFEEID(rdh);
const auto link = rdh_utils::getLink(feeId);

if (link != rdh_utils::DLBZSLinkID) {
return getTriggerInfoLBZS(data, size, firstOrbit);
}

// treatment for dense link-based
TPCZSHDR* const headerV1 = (TPCZSHDR*)(link == rdh_utils::DLBZSLinkID ? (data + size - sizeof(TPCZSHDRV2)) : data);
const auto header = (TPCZSHDRV2*)headerV1;
if (header->magicWord != 0xfd) {
LOGP(error, "Wrong magic word: {}, wrong data type?", header->magicWord);
return -1;
}
if (!(header->flags & TPCZSHDRV2::ZSFlags::TriggerWordPresent)) {
return -1;
}
const auto triggerWord = (TriggerWordDLBZS*)(data + size - sizeof(TPCZSHDRV2) - sizeof(TriggerWordDLBZS));
// Check only first trigger word assuming we will only have laser triggers
const int iTrg = 0;
if (!triggerWord->isValid(iTrg)) {
return -1;
}

const auto triggerBC = triggerWord->getTriggerBC(0);
const auto orbit = RDHUtils::getHeartBeatOrbit(rdh);
const int orbitBC = ((orbit - firstOrbit) * o2::constants::lhc::LHCMaxBunches) + triggerBC;
LOGP(debug, "TriggerWordDLBZS {}: Type = {}, orbit = {}, firstOrbit = {}, BC = {}, oribitBC = {}", iTrg, triggerWord->getTriggerType(iTrg), orbit, firstOrbit, triggerWord->getTriggerBC(iTrg), orbitBC);
return orbitBC;
}

int calib_processing_helper::getTriggerBCoffset(o2::framework::InputRecord& inputs, std::vector<o2::framework::InputSpec> filter, bool slowScan)
{
if (filter.size() == 0) {
filter = getFilter(inputs);
}

int triggerBC = -1;
uint32_t firstOrbit = 0;
for (auto const& ref : InputRecordWalker(inputs, filter)) {
const auto* dh = DataRefUtils::getHeader<o2::header::DataHeader*>(ref);
auto payloadSize = DataRefUtils::getPayloadSize(ref);
// skip empty HBF
if (payloadSize == 2 * sizeof(o2::header::RAWDataHeader)) {
continue;
}

firstOrbit = dh->firstTForbit;

try {
const gsl::span<const char> raw = inputs.get<gsl::span<char>>(ref);
std::unique_ptr<o2::framework::RawParser<8192>> rawparserPtr;

o2::framework::RawParser parser(raw.data(), raw.size());
for (auto it = parser.begin(), end = parser.end(); it != end; ++it) {
auto rdhPtr = reinterpret_cast<const o2::header::RDHAny*>(it.raw());
const auto rdhVersion = RDHUtils::getVersion(rdhPtr);
if (!rdhPtr || rdhVersion < 6) {
throw std::runtime_error(fmt::format("could not get RDH from packet, or version {} < 6", rdhVersion).data());
}

// only process LinkZSdata, only supported for data where this is already set in the UL
const auto link = RDHUtils::getLinkID(*rdhPtr);
const auto detField = RDHUtils::getDetectorField(*rdhPtr);
const auto feeID = RDHUtils::getFEEID(*rdhPtr);
const auto linkID = rdh_utils::getLink(feeID);
if (!((detField == raw_data_types::LinkZS) ||
((detField == raw_data_types::RAWDATA || detField == 0xdeadbeef) && (link == rdh_utils::UserLogicLinkID)) ||
((linkID == rdh_utils::ILBZSLinkID || linkID == rdh_utils::DLBZSLinkID) && (detField == raw_data_types::Type::ZS)))) {
continue;
}

const auto data = (const char*)it.raw();
const auto size = it.sizeTotal();

const auto thisTrigger = getTriggerBCoffset(data, size, firstOrbit);
if (thisTrigger >= 0 && !slowScan) {
triggerBC = thisTrigger;
break;
}
// slow scan, check consistency of trigger info in all pages
if (triggerBC >= 0 && triggerBC != thisTrigger) {
LOGP(error, "inconsistent trigger information in raw pages of this TF");
}
}
} catch (...) {
}
if (triggerBC >= 0 && !slowScan) {
break;
}
}
if (triggerBC >= 0) {
LOGP(info, "Found triggerBCoffset: {}", triggerBC);
}
return triggerBC;
}
5 changes: 4 additions & 1 deletion Detectors/TPC/workflow/src/RawToDigitsSpec.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class TPCDigitDumpDevice : public o2::framework::Task
mMaxEvents = static_cast<uint32_t>(ic.options().get<int>("max-events"));
mSyncOffsetReference = ic.options().get<uint32_t>("sync-offset-reference");
mDecoderType = ic.options().get<uint32_t>("decoder-type");
mUseTrigger = !ic.options().get<bool>("ignore-trigger");
mUseOldSubspec = ic.options().get<bool>("use-old-subspec");
const bool createOccupancyMaps = ic.options().get<bool>("create-occupancy-maps");
mForceQuit = ic.options().get<bool>("force-quit");
Expand Down Expand Up @@ -143,7 +144,7 @@ class TPCDigitDumpDevice : public o2::framework::Task
}

auto& reader = mRawReader.getReaders()[0];
mActiveSectors = calib_processing_helper::processRawData(pc.inputs(), reader, mUseOldSubspec, std::vector<int>(), nullptr, mSyncOffsetReference, mDecoderType);
mActiveSectors = calib_processing_helper::processRawData(pc.inputs(), reader, mUseOldSubspec, std::vector<int>(), nullptr, mSyncOffsetReference, mDecoderType, mUseTrigger);

mDigitDump.incrementNEvents();
if (mClusterQC) {
Expand Down Expand Up @@ -194,6 +195,7 @@ class TPCDigitDumpDevice : public o2::framework::Task
bool mRemoveDuplicates{false};
bool mRemoveCEdigits{false};
bool mSendCEdigits{false};
bool mUseTrigger{false};
uint64_t mActiveSectors{0}; ///< bit mask of active sectors
std::vector<int> mSectors{}; ///< tpc sector configuration

Expand Down Expand Up @@ -283,6 +285,7 @@ DataProcessorSpec getRawToDigitsSpec(int channel, const std::string inputSpec, b
{"ignore-grp", VariantType::Bool, false, {"ignore GRP file"}},
{"sync-offset-reference", VariantType::UInt32, 144u, {"Reference BCs used for the global sync offset in the CRUs"}},
{"decoder-type", VariantType::UInt32, 1u, {"Decoder to use: 0 - TPC, 1 - GPU"}},
{"ignore-trigger", VariantType::Bool, false, {"Ignore the trigger information"}},
} // end Options
}; // end DataProcessorSpec
}
Expand Down
Loading

0 comments on commit 2ae8ce1

Please sign in to comment.