-
Dominik Smith authoredDominik Smith authored
UnpackSts.cxx 6.12 KiB
/* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
SPDX-License-Identifier: GPL-3.0-only
Authors: Pierre-Alain Loizeau, Volker Friese [committer] */
#include "UnpackSts.h"
#include <cassert>
#include <utility>
#include <vector>
#include <cmath>
#include "StsXyterMessage.h"
using std::unique_ptr;
using std::vector;
namespace cbm::algo
{
// ---- Algorithm execution ---------------------------------------------
UnpackSts::resultType UnpackSts::operator()(const uint8_t* msContent, const fles::MicrosliceDescriptor& msDescr,
const uint64_t tTimeslice)
{
// --- Output data
resultType result = {};
// --- Current Timeslice start time in epoch units. Note that it is always a multiple of epochs
// --- and the epoch is a multiple of ns.
const uint64_t epochLengthInNs = fkEpochLength * fkClockCycleNom / fkClockCycleDen;
fCurrentTsTime = tTimeslice / epochLengthInNs;
// --- Current TS_MSB epoch cycle
auto const msTime = msDescr.idx; // Unix time of MS in ns
fCurrentCycle = std::ldiv(msTime, fkCycleLength).quot;
// --- Number of messages in microslice
auto msSize = msDescr.size;
if (msSize % sizeof(stsxyter::Message) != 0) {
result.second.fNumErrInvalidMsSize++;
return result;
}
const uint32_t numMessages = msSize / sizeof(stsxyter::Message);
if (numMessages < 2) {
result.second.fNumErrInvalidMsSize++;
return result;
}
// --- Interpret MS content as sequence of SMX messages
auto message = reinterpret_cast<const stsxyter::Message*>(msContent);
// --- The first message in the MS is expected to be of type EPOCH and can be ignored.
if (message[0].GetMessType() != stsxyter::MessType::Epoch) {
result.second.fNumErrInvalidFirstMessage++;
return result;
}
// --- The second message must be of type ts_msb.
if (message[1].GetMessType() != stsxyter::MessType::TsMsb) {
result.second.fNumErrInvalidFirstMessage++;
return result;
}
ProcessTsmsbMessage(message[1]);
// --- Message loop
for (uint32_t messageNr = 2; messageNr < numMessages; messageNr++) {
// --- Action depending on message type
switch (message[messageNr].GetMessType()) {
case stsxyter::MessType::Hit: {
ProcessHitMessage(message[messageNr], result.first, result.second);
break;
}
case stsxyter::MessType::TsMsb: {
ProcessTsmsbMessage(message[messageNr]);
break;
}
default: {
result.second.fNumNonHitOrTsbMessage++;
break;
}
} //? Message type
} //# Messages
return result;
}
// --------------------------------------------------------------------------
// ----- Process hit message --------------------------------------------
inline void UnpackSts::ProcessHitMessage(const stsxyter::Message& message, vector<CbmStsDigi>& digiVec,
UnpackStsMonitorData& monitor) const
{
// --- Check eLink and get parameters
uint16_t elink = message.GetLinkIndexHitBinning();
if (elink >= fParams.fElinkParams.size()) {
monitor.fNumErrElinkOutOfRange++;
return;
}
const UnpackStsElinkPar& elinkPar = fParams.fElinkParams.at(elink);
uint32_t asicNr = elinkPar.fAsicNr;
// --- Hardware-to-software address
uint32_t numChansPerModule = fParams.fNumAsicsPerModule * fParams.fNumChansPerAsic;
uint32_t address = elinkPar.fAddress;
uint32_t channel = 0;
if (asicNr < fParams.fNumAsicsPerModule / 2) { // front side (n side)
channel = message.GetHitChannel() + fParams.fNumChansPerAsic * asicNr;
}
else { // back side (p side)
channel = numChansPerModule - message.GetHitChannel()
- fParams.fNumChansPerAsic * (asicNr - fParams.fNumAsicsPerModule / 2) - 1;
}
// --- Expand time stamp to time within timeslice (in clock cycle)
uint64_t messageTime = message.GetHitTimeBinning() + fCurrentEpochTime;
// --- Convert time stamp from clock cycles to ns. Round to nearest full ns.
messageTime = (messageTime * fkClockCycleNom + fkClockCycleDen / 2) / fkClockCycleDen;
// --- Correct ASIC-wise offsets
messageTime -= elinkPar.fTimeOffset;
// --- Apply walk correction if applicable
if (message.GetHitAdc() <= elinkPar.fWalk.size()) { messageTime += elinkPar.fWalk[message.GetHitAdc() - 1]; }
// --- Charge
double charge = elinkPar.fAdcOffset + (message.GetHitAdc() - 1) * elinkPar.fAdcGain;
// --- Create output digi
digiVec.emplace_back(address, channel, messageTime, charge);
}
// --------------------------------------------------------------------------
// ----- Process an epoch (TS_MSB) message ------------------------------
inline void UnpackSts::ProcessTsmsbMessage(const stsxyter::Message& message)
{
// The compression of time is based on the hierarchy epoch cycle - epoch - message time.
// Cycles are counted from the start of Unix time and are multiples of an epoch (ts_msb).
// The epoch number is counted within each cycle. The time in the hit message is expressed
// in units of the readout clock cycle relative to the current epoch.
// The ts_msb message indicates the start of a new epoch. Its basic information is the epoch
// number within the current cycle. A cycle wrap resets the epoch number to zero, so it is
// indicated by the epoch number being smaller than the previous one (epoch messages are
// seemingly not consecutively in the data stream, but only if there are hit messages in between).
auto epoch = message.GetTsMsbValBinning();
// --- Cycle wrap
if (epoch < fCurrentEpoch) fCurrentCycle++;
// --- Update current epoch counter
fCurrentEpoch = epoch;
// --- Calculate epoch time in clocks cycles relative to timeslice start time
fCurrentEpochTime = (fCurrentCycle * fkEpochsPerCycle + epoch - fCurrentTsTime) * fkEpochLength;
}
// --------------------------------------------------------------------------
} /* namespace cbm::algo */