20#include <fmt/format.h>
33int32_t CMVPerTF::cmvToSigned(uint16_t raw)
35 const int32_t mag = raw & 0x7FFF;
36 return (raw >> 15) ? mag : -mag;
39uint16_t CMVPerTF::quantizeBelowThreshold(uint16_t raw,
float quantizationMean,
float quantizationSigma)
45 if (quantizationSigma <= 0.f) {
49 const float adc = (raw & 0x7FFFu) / 128.f;
50 const float distance = (
adc - quantizationMean) / quantizationSigma;
54 float quantizedADC =
adc;
55 if (lossStrength > 0.85f) {
56 quantizedADC = std::round(
adc * 10.f) / 10.f;
57 }
else if (lossStrength > 0.60f) {
58 quantizedADC = std::round(
adc * 100.f) / 100.f;
59 }
else if (lossStrength > 0.30f) {
60 quantizedADC = std::round(
adc * 1000.f) / 1000.f;
61 }
else if (lossStrength > 0.12f) {
62 quantizedADC = std::round(
adc * 10000.f) / 10000.f;
63 }
else if (lossStrength > 0.03f) {
64 quantizedADC = std::round(
adc * 1000000.f) / 1000000.f;
68 const uint16_t quantizedMagnitude =
static_cast<uint16_t
>(std::clamp(std::lround(quantizedADC * 128.f), 0l, 0x7FFFl));
69 return static_cast<uint16_t
>((raw & 0x8000u) | quantizedMagnitude);
72uint32_t CMVPerTF::zigzagEncode(int32_t
value)
74 return (
static_cast<uint32_t
>(
value) << 1) ^
static_cast<uint32_t
>(
value >> 31);
77void CMVPerTF::encodeVarintInto(uint32_t
value, std::vector<uint8_t>& out)
79 while (
value > 0x7F) {
80 out.push_back(
static_cast<uint8_t>((
value & 0x7F) | 0x80));
91int32_t zigzagDecodeLocal(uint32_t
value)
93 return static_cast<int32_t
>((
value >> 1) ^ -(
value & 1));
96uint16_t signedToCmvLocal(int32_t
val)
98 const uint16_t mag =
static_cast<uint16_t
>(std::abs(
val)) & 0x7FFF;
99 return static_cast<uint16_t
>((
val >= 0 ? 0x8000u : 0u) | mag);
102uint32_t decodeVarintLocal(
const uint8_t*&
data,
const uint8_t*
end)
107 value |=
static_cast<uint32_t
>(*
data & 0x7F) << shift;
112 throw std::runtime_error(
"decodeVarintLocal: unexpected end of varint data");
114 value |=
static_cast<uint32_t
>(*data) << shift;
125void huffmanEncode(
const std::vector<uint32_t>& symbols, std::vector<uint8_t>&
buf)
128 std::map<uint32_t, uint64_t> freq;
129 for (
const uint32_t
z : symbols) {
140 std::vector<HNode>
nodes;
141 nodes.reserve(freq.size() * 2);
142 for (
const auto& [sym,
f] : freq) {
143 nodes.push_back({
f, sym, -1, -1,
true});
146 auto cmp = [&](
int a,
int b) {
149 std::vector<int> heap;
150 heap.reserve(
nodes.size());
151 for (
int i = 0; i < static_cast<int>(
nodes.size()); ++
i) {
154 std::make_heap(heap.begin(), heap.end(),
cmp);
156 while (heap.size() > 1) {
157 std::pop_heap(heap.begin(), heap.end(),
cmp);
158 const int a = heap.back();
160 std::pop_heap(heap.begin(), heap.end(),
cmp);
161 const int b = heap.back();
164 heap.push_back(
static_cast<int>(
nodes.size()) - 1);
165 std::push_heap(heap.begin(), heap.end(),
cmp);
169 std::map<uint32_t, uint8_t> codeLens;
171 const int root = heap[0];
172 std::vector<std::pair<int, int>>
stack;
173 stack.push_back({root, 0});
174 while (!
stack.empty()) {
177 if (
nodes[idx].isLeaf) {
191 std::vector<SymLen> symLens;
192 symLens.reserve(codeLens.size());
193 for (
const auto& [sym,
len] : codeLens) {
194 symLens.push_back({sym,
len});
196 std::sort(symLens.begin(), symLens.end(), [](
const SymLen&
a,
const SymLen&
b) {
197 return a.len != b.len ? a.len < b.len : a.sym < b.sym;
201 std::map<uint32_t, std::pair<uint32_t, uint8_t>> codeTable;
205 for (
const auto& sl : symLens) {
207 code = (code + 1) << (sl.len - prevLen);
209 codeTable[sl.sym] = {code, sl.len};
215 buf.reserve(
buf.size() + 4 + symLens.size() * 5 + 8 + (symbols.size() / 8 + 1));
216 const uint32_t numSym =
static_cast<uint32_t
>(symLens.size());
217 for (
int i = 0;
i < 4; ++
i) {
218 buf.push_back(
static_cast<uint8_t>((numSym >> (8 *
i)) & 0xFF));
220 for (
const auto& sl : symLens) {
221 for (
int i = 0;
i < 4; ++
i) {
222 buf.push_back(
static_cast<uint8_t>((sl.sym >> (8 *
i)) & 0xFF));
224 buf.push_back(sl.len);
228 const size_t totalBitsOffset =
buf.size();
229 for (
int i = 0;
i < 8; ++
i) {
234 uint64_t totalBits = 0;
237 for (
const uint32_t
z : symbols) {
238 const auto& [code,
len] = codeTable.at(
z);
239 for (
int b =
static_cast<int>(
len) - 1;
b >= 0; --
b) {
240 curByte =
static_cast<uint8_t>(curByte | (((code >>
b) & 1u) << (7 - bitsInByte)));
243 if (bitsInByte == 8) {
244 buf.push_back(curByte);
250 if (bitsInByte > 0) {
251 buf.push_back(curByte);
255 for (
int i = 0;
i < 8; ++
i) {
256 buf[totalBitsOffset +
i] =
static_cast<uint8_t>((totalBits >> (8 *
i)) & 0xFF);
263std::vector<uint32_t> huffmanDecode(
const uint8_t*&
ptr,
const uint8_t*
end, uint32_t N)
265 auto readU32 = [&]() -> uint32_t {
267 throw std::runtime_error(
"huffmanDecode: unexpected end reading uint32");
269 const uint32_t
v =
static_cast<uint32_t
>(
ptr[0]) | (
static_cast<uint32_t
>(
ptr[1]) << 8) |
270 (
static_cast<uint32_t
>(
ptr[2]) << 16) | (
static_cast<uint32_t
>(
ptr[3]) << 24);
275 const uint32_t numSym = readU32();
280 std::vector<SymLen> symLens(numSym);
281 for (uint32_t
i = 0;
i < numSym; ++
i) {
282 symLens[
i].sym = readU32();
284 throw std::runtime_error(
"huffmanDecode: unexpected end reading code length");
286 symLens[
i].len = *
ptr++;
289 std::map<uint8_t, uint32_t> firstCode;
290 std::map<uint8_t, std::vector<uint32_t>> symsByLen;
294 for (
const auto& sl : symLens) {
296 code = (code + 1) << (sl.len - prevLen);
298 if (!firstCode.count(sl.len)) {
299 firstCode[sl.len] = code;
301 symsByLen[sl.len].push_back(sl.sym);
307 throw std::runtime_error(
"huffmanDecode: unexpected end reading totalBits");
309 uint64_t totalBits = 0;
310 for (
int i = 0;
i < 8; ++
i) {
311 totalBits |=
static_cast<uint64_t
>(
ptr[
i]) << (8 *
i);
315 const uint8_t minLen = symLens.empty() ? 1 : symLens.front().len;
316 const uint8_t maxLen = symLens.empty() ? 1 : symLens.back().len;
317 uint64_t bitsRead = 0;
321 auto nextBit = [&]() ->
int {
324 throw std::runtime_error(
"huffmanDecode: unexpected end of bitstream");
329 const int bit = (curByte >> bitPos) & 1;
334 std::vector<uint32_t> out;
336 while (out.size() < N) {
339 for (uint8_t curLen = 1; curLen <= maxLen; ++curLen) {
340 if (bitsRead >= totalBits) {
341 throw std::runtime_error(
"huffmanDecode: bitstream exhausted before all symbols decoded");
343 accum = (accum << 1) | static_cast<uint32_t>(nextBit());
345 if (curLen < minLen) {
348 const auto fcIt = firstCode.find(curLen);
349 if (fcIt == firstCode.end()) {
352 if (accum >= fcIt->second) {
353 const uint32_t
idx = accum - fcIt->second;
354 const auto& sv = symsByLen.at(curLen);
355 if (idx < sv.size()) {
356 out.push_back(sv[idx]);
363 throw std::runtime_error(
"huffmanDecode: invalid Huffman code in bitstream");
375 if (cru < 0 || cru >=
static_cast<int>(
CRU::MaxCRU)) {
376 throw std::out_of_range(fmt::format(
"CMVPerTF::getCMV: cru {} out of range [0, {})", cru,
static_cast<int>(
CRU::MaxCRU)));
378 if (timeBin < 0 ||
static_cast<uint32_t
>(timeBin) >= cmv::NTimeBinsPerTF) {
379 throw std::out_of_range(fmt::format(
"CMVPerTF::getCMV: timeBin {} out of range [0, {})", timeBin,
static_cast<int>(cmv::NTimeBinsPerTF)));
381 return mDataPerTF[cru * cmv::NTimeBinsPerTF + timeBin];
386 const uint16_t raw =
getCMV(cru, timeBin);
387 const uint16_t mag = raw & 0x7FFF;
391 const bool positive = (raw >> 15) & 1;
392 return positive ? mag / 128.f : -mag / 128.f;
397 if (threshold <= 0.f) {
400 for (uint32_t
i = 0; i < static_cast<uint32_t>(
CRU::MaxCRU) * cmv::NTimeBinsPerTF; ++
i) {
401 const float mag = (
mDataPerTF[
i] & 0x7FFF) / 128.f;
402 if (mag < threshold) {
410 if (threshold == 0) {
413 for (uint32_t
i = 0; i < static_cast<uint32_t>(
CRU::MaxCRU) * cmv::NTimeBinsPerTF; ++
i) {
418 const uint16_t rounded =
static_cast<uint16_t
>(((raw & 0x7FFFu) + 64u) >> 7);
419 if (rounded > threshold) {
422 mDataPerTF[
i] = (rounded == 0) ? 0 :
static_cast<uint16_t
>((raw & 0x8000u) | (rounded << 7));
432 for (uint32_t
i = 0; i < static_cast<uint32_t>(
CRU::MaxCRU) * cmv::NTimeBinsPerTF; ++
i) {
448 std::vector<uint8_t> posStream;
449 std::vector<uint16_t> rawValues;
451 for (
int cru = 0; cru < static_cast<int>(
CRU::MaxCRU); ++cru) {
456 std::vector<Entry> entries;
457 for (uint32_t tb = 0; tb < cmv::NTimeBinsPerTF; ++tb) {
458 const uint16_t
val =
mDataPerTF[cru * cmv::NTimeBinsPerTF + tb];
460 entries.push_back({tb,
val});
464 encodeVarintInto(
static_cast<uint32_t
>(entries.size()), posStream);
467 for (
const auto& e : entries) {
468 encodeVarintInto(
first ? e.tb : (e.tb - prevTB), posStream);
469 rawValues.push_back(e.val);
476 std::vector<uint8_t> valStream;
478 std::vector<uint32_t> zigzags;
479 zigzags.reserve(rawValues.size());
480 for (
const uint16_t
v : rawValues) {
481 zigzags.push_back(zigzagEncode(cmvToSigned(
v)));
484 huffmanEncode(zigzags, valStream);
486 for (
const uint32_t
z : zigzags) {
487 encodeVarintInto(
z, valStream);
492 for (
const uint16_t
v : rawValues) {
493 valStream.push_back(
static_cast<uint8_t
>(
v & 0xFF));
494 valStream.push_back(
static_cast<uint8_t
>(
v >> 8));
499 const uint32_t posStreamSize =
static_cast<uint32_t
>(posStream.size());
500 out.
mData.reserve(4 + posStream.size() + valStream.size());
501 for (
int i = 0;
i < 4; ++
i) {
502 out.
mData.push_back(
static_cast<uint8_t
>((posStreamSize >> (8 *
i)) & 0xFF));
504 out.
mData.insert(out.
mData.end(), posStream.begin(), posStream.end());
505 out.
mData.insert(out.
mData.end(), valStream.begin(), valStream.end());
509 const uint32_t total =
static_cast<uint32_t
>(
CRU::MaxCRU) * cmv::NTimeBinsPerTF;
513 out.
mData.reserve(total * 2);
514 for (uint32_t
i = 0;
i < total; ++
i) {
521 std::vector<uint32_t> zigzags;
522 zigzags.reserve(total);
523 for (
int cru = 0; cru < static_cast<int>(
CRU::MaxCRU); ++cru) {
525 for (uint32_t tb = 0; tb < cmv::NTimeBinsPerTF; ++tb) {
526 const int32_t
val = cmvToSigned(
mDataPerTF[cru * cmv::NTimeBinsPerTF + tb]);
527 const int32_t encoded = useDelta ? (
val - prev) :
val;
531 zigzags.push_back(zigzagEncode(encoded));
536 huffmanEncode(zigzags, out.
mData);
538 for (
const uint32_t
z : zigzags) {
539 encodeVarintInto(
z, out.
mData);
550std::vector<std::pair<int, uint32_t>> CMVPerTFCompressed::decodeSparsePositions(
const uint8_t*&
ptr,
const uint8_t*
end)
554 throw std::runtime_error(
"CMVPerTFCompressed::decompress: truncated position header");
556 const uint32_t posStreamSize =
static_cast<uint32_t
>(
ptr[0]) | (
static_cast<uint32_t
>(
ptr[1]) << 8) |
557 (
static_cast<uint32_t
>(
ptr[2]) << 16) | (
static_cast<uint32_t
>(
ptr[3]) << 24);
560 const uint8_t* posEnd =
ptr + posStreamSize;
562 throw std::runtime_error(
"CMVPerTFCompressed::decompress: posStream overflows payload");
566 std::vector<std::pair<int, uint32_t>> positions;
567 const uint8_t* p =
ptr;
568 for (
int cru = 0; cru < static_cast<int>(
CRU::MaxCRU); ++cru) {
569 const uint32_t
count = decodeVarintLocal(p, posEnd);
572 for (uint32_t
i = 0;
i <
count; ++
i) {
573 const uint32_t delta = decodeVarintLocal(p, posEnd);
574 tb =
first ? delta : (tb + delta);
576 positions.emplace_back(cru, tb);
583std::vector<uint32_t> CMVPerTFCompressed::decodeValueStream(
const uint8_t*&
ptr,
const uint8_t*
end, uint32_t N, uint8_t
flags)
587 return huffmanDecode(
ptr,
end, N);
592 std::vector<uint32_t> out;
594 for (uint32_t
i = 0;
i < N; ++
i) {
595 out.push_back(decodeVarintLocal(
ptr,
end));
601 std::vector<uint32_t> out;
603 for (uint32_t
i = 0;
i < N; ++
i) {
605 throw std::runtime_error(
"CMVPerTFCompressed::decompress: unexpected end in raw value stream");
607 const uint16_t
v =
static_cast<uint16_t
>(
ptr[0]) | (
static_cast<uint16_t
>(
ptr[1]) << 8);
614void CMVPerTFCompressed::decodeSparseValues(
const std::vector<uint32_t>& symbols,
615 const std::vector<std::pair<int, uint32_t>>& positions,
616 uint8_t
flags, CMVPerTF* cmv)
619 for (uint32_t
i = 0; i < static_cast<uint32_t>(positions.size()); ++
i) {
622 raw = signedToCmvLocal(zigzagDecodeLocal(symbols[
i]));
624 raw =
static_cast<uint16_t
>(symbols[
i]);
626 cmv->mDataPerTF[positions[
i].first * cmv::NTimeBinsPerTF + positions[
i].second] = raw;
630void CMVPerTFCompressed::decodeDenseValues(
const std::vector<uint32_t>& symbols, uint8_t
flags, CMVPerTF* cmv)
637 for (uint32_t
i = 0; i < static_cast<uint32_t>(symbols.size()); ++
i) {
638 cmv->mDataPerTF[
i] =
static_cast<uint16_t
>(symbols[
i]);
645 for (
int cru = 0; cru < static_cast<int>(
CRU::MaxCRU); ++cru) {
647 for (uint32_t tb = 0; tb < cmv::NTimeBinsPerTF; ++tb, ++
s) {
648 int32_t
val = zigzagDecodeLocal(symbols[s]);
653 cmv->mDataPerTF[
s] = signedToCmvLocal(
val);
661 throw std::invalid_argument(
"CMVPerTFCompressed::decompress: cmv pointer is null");
672 auto positions = decodeSparsePositions(
ptr,
end);
673 const uint32_t N =
static_cast<uint32_t
>(positions.size());
679 decodeSparseValues(symbols, positions,
mFlags, cmv);
681 const uint32_t N =
static_cast<uint32_t
>(
CRU::MaxCRU) * cmv::NTimeBinsPerTF;
687 decodeDenseValues(symbols,
mFlags, cmv);
693 auto tree = std::make_unique<TTree>(
"ccdb_object",
"ccdb_object");
694 tree->SetAutoSave(0);
695 tree->SetDirectory(
nullptr);
698 tree->Branch(
"CMVPerTF", &
ptr);
701 tree->ResetBranchAddresses();
707 auto tree = std::make_unique<TTree>(
"ccdb_object",
"ccdb_object");
708 tree->SetAutoSave(0);
709 tree->SetDirectory(
nullptr);
712 tree->Branch(
"CMVPerTFCompressed", &
ptr);
715 tree->ResetBranchAddresses();
723 throw std::runtime_error(fmt::format(
"CMVPerTF::writeToFile: cannot open '{}'",
filename));
Structs for storing CMVs to the CCDB.
Common mode values data format definition.
GLboolean GLboolean GLboolean b
GLsizei GLsizei GLfloat distance
GLsizei const GLfloat * value
GLint GLint GLsizei GLsizei GLsizei depth
GLenum GLenum GLsizei len
GLboolean GLboolean GLboolean GLboolean a
GLenum GLuint GLenum GLsizei const GLchar * buf
GLdouble GLdouble GLdouble z
uint8_t itsSharedClusterMap uint8_t
Global TPC definitions and constants.
static constexpr uint8_t kDelta
Delta coding between consecutive values (dense only)
static constexpr uint8_t kVarint
Varint compression of the value stream.
static constexpr uint8_t kHuffman
Canonical Huffman compression of the value stream.
static constexpr uint8_t kSparse
Non-zero positions stored sparsely (varint-encoded deltas)
static constexpr uint8_t kZigzag
Zigzag encoding of deltas or signed values.
std::unique_ptr< TTree > toTTree() const
Serialise into a TTree; each Fill() call appends one entry (one TF)
void decompress(CMVPerTF *cmv) const
Restore a CMVPerTF from this compressed object into *cmv (must not be null)
std::vector< uint8_t > mData
Encoded payload.
uint8_t mFlags
Bitmask of CMVEncoding values.
uint16_t firstBC
First bunch crossing of this TF.
uint32_t firstOrbit
First orbit of this TF.
uint32_t firstOrbit
First orbit of this TF, from heartbeatOrbit of the first CMV packet.
void zeroSmallValues(float threshold=1.0f)
Zero out raw CMV values whose float magnitude is below threshold.
CMVPerTFCompressed compress(uint8_t flags) const
static void writeToFile(const std::string &filename, const std::unique_ptr< TTree > &tree)
Write the TTree to a ROOT file.
uint16_t mDataPerTF[CRU::MaxCRU *cmv::NTimeBinsPerTF]
void roundToIntegers(uint16_t threshold)
Round values to the nearest integer ADC for all values whose rounded magnitude is <= threshold.
void trimGaussianPrecision(float mean, float sigma)
std::unique_ptr< TTree > toTTree() const
Serialise into a TTree; each Fill() call appends one entry (one TF)
uint16_t getCMV(const int cru, const int timeBin) const
Return the raw 16-bit CMV value for a given CRU and timebin within this TF.
float getCMVFloat(const int cru, const int timeBin) const
Return the float CMV value for a given CRU and timebin within this TF.
uint16_t firstBC
First bunch crossing of this TF, from heartbeatBC of the first CMV packet.
std::unique_ptr< TTree > tree((TTree *) flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str()))
char const *restrict const cmp