-
The most non ASCII characters were used in comments but some were also used for screen output. The most common non ASCII character was µ which was exchanged by mu or micro.
The most non ASCII characters were used in comments but some were also used for screen output. The most common non ASCII character was µ which was exchanged by mu or micro.
UnpackMS.cxx 6.16 KiB
/* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
SPDX-License-Identifier: GPL-3.0-only
Authors: Pierre-Alain Loizeau, Volker Friese [committer] */
#include "UnpackMS.h"
#include "StsXyterMessage.h"
#include <cassert>
#include <cmath>
#include <utility>
#include <vector>
using std::unique_ptr;
using std::vector;
namespace cbm::algo::much
{
UnpackMS::UnpackMS(const UnpackPar& pars) : fParams(pars) {}
UnpackMS::~UnpackMS() = default;
// ---- Algorithm execution ---------------------------------------------
UnpackMS::Result_t UnpackMS::operator()(const uint8_t* msContent, const fles::MicrosliceDescriptor& msDescr,
const uint64_t tTimeslice) const
{
// --- Output data
Result_t result = {};
TimeSpec time;
// --- 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;
time.currentTsTime = tTimeslice / epochLengthInNs;
// --- Current TS_MSB epoch cycle
auto const msTime = msDescr.idx; // Unix time of MS in ns
time.currentCycle = std::ldiv(msTime, fkCycleLength).quot;
time.currentEpoch = 0; // Needed to make each MS independent of the previous! Will be updated in message 1 if MS OK
time.currentEpochTime =
0; // Needed to make each MS independent of the previous! Will be updated in message 1 if MS OK
// --- Number of messages in microslice
auto msSize = msDescr.size;
if (msSize % sizeof(stsxyter::Message) != 0) {
std::get<1>(result).fNumErrInvalidMsSize++;
return result;
}
const uint32_t numMessages = msSize / sizeof(stsxyter::Message);
if (numMessages < 2) {
std::get<1>(result).fNumErrInvalidMsSize++;
return result;
}
const uint32_t maxDigis = numMessages - 2; // -2 for the TS_MSB and EPOCH messages
std::get<0>(result).reserve(maxDigis);
// --- 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) {
std::get<1>(result).fNumErrInvalidFirstMessage++;
return result;
}
// --- The second message must be of type ts_msb.
if (message[1].GetMessType() != stsxyter::MessType::TsMsb) {
std::get<1>(result).fNumErrInvalidFirstMessage++;
return result;
}
ProcessTsmsbMessage(message[1], time);
// --- 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], time, std::get<0>(result), std::get<1>(result));
break;
}
case stsxyter::MessType::TsMsb: {
ProcessTsmsbMessage(message[messageNr], time);
break;
}
default: {
std::get<1>(result).fNumNonHitOrTsbMessage++;
break;
}
} //? Message type
} //# Messages
return result;
}
// --------------------------------------------------------------------------
// ----- Process hit message --------------------------------------------
inline void UnpackMS::ProcessHitMessage(const stsxyter::Message& message, const TimeSpec& time,
vector<CbmMuchDigi>& digiVec, UnpackMonitorData& monitor) const
{
// --- Check eLink and get parameters
uint16_t elink = message.GetLinkIndexHitBinning();
if (elink >= fParams.fElinkParams.size()) {
monitor.fNumErrElinkOutOfRange++;
return;
}
const UnpackElinkPar& elinkPar = fParams.fElinkParams.at(elink);
uint16_t channel = message.GetHitChannel();
// --- Check for masked channel
if (!elinkPar.fChanMask.empty() && elinkPar.fChanMask[channel] == true) {
return;
}
uint32_t address = (elinkPar.fAddress)[channel];
// --- Expand time stamp to time within timeslice (in clock cycle)
uint64_t messageTime = message.GetHitTimeBinning() + time.currentEpochTime;
// --- 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;
// --- Charge
double charge = message.GetHitAdc();
// --- Create output digi
digiVec.emplace_back(address, charge, messageTime);
}
// --------------------------------------------------------------------------
// ----- Process an epoch (TS_MSB) message ------------------------------
inline void UnpackMS::ProcessTsmsbMessage(const stsxyter::Message& message, TimeSpec& time) const
{
// 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 < time.currentEpoch) time.currentCycle++;
// --- Update current epoch counter
time.currentEpoch = epoch;
// --- Calculate epoch time in clocks cycles relative to timeslice start time
time.currentEpochTime = (time.currentCycle * fkEpochsPerCycle + epoch - time.currentTsTime) * fkEpochLength;
}
// --------------------------------------------------------------------------
} // namespace cbm::algo::much