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)
127 if (symbols.empty()) {
130 for (
int i = 0;
i < 12; ++
i) {
137 std::map<uint32_t, uint64_t> freq;
138 for (
const uint32_t
z : symbols) {
149 std::vector<HNode>
nodes;
150 nodes.reserve(freq.size() * 2);
151 for (
const auto& [sym,
f] : freq) {
152 nodes.push_back({
f, sym, -1, -1,
true});
155 auto cmp = [&](
int a,
int b) {
158 std::vector<int> heap;
159 heap.reserve(
nodes.size());
160 for (
int i = 0; i < static_cast<int>(
nodes.size()); ++
i) {
163 std::make_heap(heap.begin(), heap.end(),
cmp);
165 while (heap.size() > 1) {
166 std::pop_heap(heap.begin(), heap.end(),
cmp);
167 const int a = heap.back();
169 std::pop_heap(heap.begin(), heap.end(),
cmp);
170 const int b = heap.back();
173 heap.push_back(
static_cast<int>(
nodes.size()) - 1);
174 std::push_heap(heap.begin(), heap.end(),
cmp);
178 std::map<uint32_t, uint8_t> codeLens;
180 const int root = heap[0];
181 std::vector<std::pair<int, int>>
stack;
182 stack.push_back({root, 0});
183 while (!
stack.empty()) {
186 if (
nodes[idx].isLeaf) {
200 std::vector<SymLen> symLens;
201 symLens.reserve(codeLens.size());
202 for (
const auto& [sym,
len] : codeLens) {
203 symLens.push_back({sym,
len});
205 std::sort(symLens.begin(), symLens.end(), [](
const SymLen&
a,
const SymLen&
b) {
206 return a.len != b.len ? a.len < b.len : a.sym < b.sym;
210 std::map<uint32_t, std::pair<uint32_t, uint8_t>> codeTable;
214 for (
const auto& sl : symLens) {
216 code = (code + 1) << (sl.len - prevLen);
218 codeTable[sl.sym] = {code, sl.len};
224 buf.reserve(
buf.size() + 4 + symLens.size() * 5 + 8 + (symbols.size() / 8 + 1));
225 const uint32_t numSym =
static_cast<uint32_t
>(symLens.size());
226 for (
int i = 0;
i < 4; ++
i) {
227 buf.push_back(
static_cast<uint8_t>((numSym >> (8 *
i)) & 0xFF));
229 for (
const auto& sl : symLens) {
230 for (
int i = 0;
i < 4; ++
i) {
231 buf.push_back(
static_cast<uint8_t>((sl.sym >> (8 *
i)) & 0xFF));
233 buf.push_back(sl.len);
237 const size_t totalBitsOffset =
buf.size();
238 for (
int i = 0;
i < 8; ++
i) {
243 uint64_t totalBits = 0;
246 for (
const uint32_t
z : symbols) {
247 const auto& [code,
len] = codeTable.at(
z);
248 for (
int b =
static_cast<int>(
len) - 1;
b >= 0; --
b) {
249 curByte =
static_cast<uint8_t>(curByte | (((code >>
b) & 1u) << (7 - bitsInByte)));
252 if (bitsInByte == 8) {
253 buf.push_back(curByte);
259 if (bitsInByte > 0) {
260 buf.push_back(curByte);
264 for (
int i = 0;
i < 8; ++
i) {
265 buf[totalBitsOffset +
i] =
static_cast<uint8_t>((totalBits >> (8 *
i)) & 0xFF);
272std::vector<uint32_t> huffmanDecode(
const uint8_t*&
ptr,
const uint8_t*
end, uint32_t N)
274 auto readU32 = [&]() -> uint32_t {
276 throw std::runtime_error(
"huffmanDecode: unexpected end reading uint32");
278 const uint32_t
v =
static_cast<uint32_t
>(
ptr[0]) | (
static_cast<uint32_t
>(
ptr[1]) << 8) |
279 (
static_cast<uint32_t
>(
ptr[2]) << 16) | (
static_cast<uint32_t
>(
ptr[3]) << 24);
284 const uint32_t numSym = readU32();
289 std::vector<SymLen> symLens(numSym);
290 for (uint32_t
i = 0;
i < numSym; ++
i) {
291 symLens[
i].sym = readU32();
293 throw std::runtime_error(
"huffmanDecode: unexpected end reading code length");
295 symLens[
i].len = *
ptr++;
298 std::map<uint8_t, uint32_t> firstCode;
299 std::map<uint8_t, std::vector<uint32_t>> symsByLen;
303 for (
const auto& sl : symLens) {
305 code = (code + 1) << (sl.len - prevLen);
307 if (!firstCode.count(sl.len)) {
308 firstCode[sl.len] = code;
310 symsByLen[sl.len].push_back(sl.sym);
316 throw std::runtime_error(
"huffmanDecode: unexpected end reading totalBits");
318 uint64_t totalBits = 0;
319 for (
int i = 0;
i < 8; ++
i) {
320 totalBits |=
static_cast<uint64_t
>(
ptr[
i]) << (8 *
i);
324 const uint8_t minLen = symLens.empty() ? 1 : symLens.front().len;
325 const uint8_t maxLen = symLens.empty() ? 1 : symLens.back().len;
326 uint64_t bitsRead = 0;
330 auto nextBit = [&]() ->
int {
333 throw std::runtime_error(
"huffmanDecode: unexpected end of bitstream");
338 const int bit = (curByte >> bitPos) & 1;
343 std::vector<uint32_t> out;
345 while (out.size() < N) {
348 for (uint8_t curLen = 1; curLen <= maxLen; ++curLen) {
349 if (bitsRead >= totalBits) {
350 throw std::runtime_error(
"huffmanDecode: bitstream exhausted before all symbols decoded");
352 accum = (accum << 1) | static_cast<uint32_t>(nextBit());
354 if (curLen < minLen) {
357 const auto fcIt = firstCode.find(curLen);
358 if (fcIt == firstCode.end()) {
361 if (accum >= fcIt->second) {
362 const uint32_t
idx = accum - fcIt->second;
363 const auto& sv = symsByLen.at(curLen);
364 if (idx < sv.size()) {
365 out.push_back(sv[idx]);
372 throw std::runtime_error(
"huffmanDecode: invalid Huffman code in bitstream");
384 if (cru < 0 || cru >=
static_cast<int>(
CRU::MaxCRU)) {
385 throw std::out_of_range(fmt::format(
"CMVPerTF::getCMV: cru {} out of range [0, {})", cru,
static_cast<int>(
CRU::MaxCRU)));
387 if (timeBin < 0 ||
static_cast<uint32_t
>(timeBin) >= cmv::NTimeBinsPerTF) {
388 throw std::out_of_range(fmt::format(
"CMVPerTF::getCMV: timeBin {} out of range [0, {})", timeBin,
static_cast<int>(cmv::NTimeBinsPerTF)));
390 return mDataPerTF[cru * cmv::NTimeBinsPerTF + timeBin];
395 const uint16_t
raw =
getCMV(cru, timeBin);
396 const uint16_t mag =
raw & 0x7FFF;
400 const bool positive = (
raw >> 15) & 1;
401 return positive ? mag / 128.f : -mag / 128.f;
406 if (threshold <= 0.f) {
409 for (uint32_t
i = 0; i < static_cast<uint32_t>(
CRU::MaxCRU) * cmv::NTimeBinsPerTF; ++
i) {
410 const float mag = (
mDataPerTF[
i] & 0x7FFF) / 128.f;
411 if (mag < threshold) {
419 if (threshold == 0) {
422 for (uint32_t
i = 0; i < static_cast<uint32_t>(
CRU::MaxCRU) * cmv::NTimeBinsPerTF; ++
i) {
427 const uint16_t rounded =
static_cast<uint16_t
>(((
raw & 0x7FFFu) + 64u) >> 7);
428 if (rounded > threshold) {
431 mDataPerTF[
i] = (rounded == 0) ? 0 :
static_cast<uint16_t
>((
raw & 0x8000u) | (rounded << 7));
441 for (uint32_t
i = 0; i < static_cast<uint32_t>(
CRU::MaxCRU) * cmv::NTimeBinsPerTF; ++
i) {
457 std::vector<uint8_t> posStream;
458 std::vector<uint16_t> rawValues;
460 for (
int cru = 0; cru < static_cast<int>(
CRU::MaxCRU); ++cru) {
465 std::vector<Entry> entries;
466 for (uint32_t tb = 0; tb < cmv::NTimeBinsPerTF; ++tb) {
467 const uint16_t
val =
mDataPerTF[cru * cmv::NTimeBinsPerTF + tb];
469 entries.push_back({tb,
val});
473 encodeVarintInto(
static_cast<uint32_t
>(entries.size()), posStream);
476 for (
const auto& e : entries) {
477 encodeVarintInto(
first ? e.tb : (e.tb - prevTB), posStream);
478 rawValues.push_back(e.val);
485 std::vector<uint8_t> valStream;
487 std::vector<uint32_t> zigzags;
488 zigzags.reserve(rawValues.size());
489 for (
const uint16_t
v : rawValues) {
490 zigzags.push_back(zigzagEncode(cmvToSigned(
v)));
493 huffmanEncode(zigzags, valStream);
495 for (
const uint32_t
z : zigzags) {
496 encodeVarintInto(
z, valStream);
501 for (
const uint16_t
v : rawValues) {
502 valStream.push_back(
static_cast<uint8_t
>(
v & 0xFF));
503 valStream.push_back(
static_cast<uint8_t
>(
v >> 8));
508 const uint32_t posStreamSize =
static_cast<uint32_t
>(posStream.size());
509 out.
mData.reserve(4 + posStream.size() + valStream.size());
510 for (
int i = 0;
i < 4; ++
i) {
511 out.
mData.push_back(
static_cast<uint8_t
>((posStreamSize >> (8 *
i)) & 0xFF));
513 out.
mData.insert(out.
mData.end(), posStream.begin(), posStream.end());
514 out.
mData.insert(out.
mData.end(), valStream.begin(), valStream.end());
518 const uint32_t total =
static_cast<uint32_t
>(
CRU::MaxCRU) * cmv::NTimeBinsPerTF;
522 out.
mData.reserve(total * 2);
523 for (uint32_t
i = 0;
i < total; ++
i) {
530 std::vector<uint32_t> zigzags;
531 zigzags.reserve(total);
532 for (
int cru = 0; cru < static_cast<int>(
CRU::MaxCRU); ++cru) {
534 for (uint32_t tb = 0; tb < cmv::NTimeBinsPerTF; ++tb) {
535 const int32_t
val = cmvToSigned(
mDataPerTF[cru * cmv::NTimeBinsPerTF + tb]);
536 const int32_t encoded = useDelta ? (
val - prev) :
val;
540 zigzags.push_back(zigzagEncode(encoded));
545 huffmanEncode(zigzags, out.
mData);
547 for (
const uint32_t
z : zigzags) {
548 encodeVarintInto(
z, out.
mData);
559std::vector<std::pair<int, uint32_t>> CMVPerTFCompressed::decodeSparsePositions(
const uint8_t*&
ptr,
const uint8_t*
end)
563 throw std::runtime_error(
"CMVPerTFCompressed::decompress: truncated position header");
565 const uint32_t posStreamSize =
static_cast<uint32_t
>(
ptr[0]) | (
static_cast<uint32_t
>(
ptr[1]) << 8) |
566 (
static_cast<uint32_t
>(
ptr[2]) << 16) | (
static_cast<uint32_t
>(
ptr[3]) << 24);
569 const uint8_t* posEnd =
ptr + posStreamSize;
571 throw std::runtime_error(
"CMVPerTFCompressed::decompress: posStream overflows payload");
575 std::vector<std::pair<int, uint32_t>> positions;
576 const uint8_t* p =
ptr;
577 for (
int cru = 0; cru < static_cast<int>(
CRU::MaxCRU); ++cru) {
578 const uint32_t
count = decodeVarintLocal(p, posEnd);
581 for (uint32_t
i = 0;
i <
count; ++
i) {
582 const uint32_t delta = decodeVarintLocal(p, posEnd);
583 tb =
first ? delta : (tb + delta);
585 positions.emplace_back(cru, tb);
592std::vector<uint32_t> CMVPerTFCompressed::decodeValueStream(
const uint8_t*&
ptr,
const uint8_t*
end, uint32_t N, uint8_t
flags)
596 return huffmanDecode(
ptr,
end, N);
601 std::vector<uint32_t> out;
603 for (uint32_t
i = 0;
i < N; ++
i) {
604 out.push_back(decodeVarintLocal(
ptr,
end));
610 std::vector<uint32_t> out;
612 for (uint32_t
i = 0;
i < N; ++
i) {
614 throw std::runtime_error(
"CMVPerTFCompressed::decompress: unexpected end in raw value stream");
616 const uint16_t
v =
static_cast<uint16_t
>(
ptr[0]) | (
static_cast<uint16_t
>(
ptr[1]) << 8);
623void CMVPerTFCompressed::decodeSparseValues(
const std::vector<uint32_t>& symbols,
624 const std::vector<std::pair<int, uint32_t>>& positions,
625 uint8_t
flags, CMVPerTF* cmv)
628 for (uint32_t
i = 0; i < static_cast<uint32_t>(positions.size()); ++
i) {
631 raw = signedToCmvLocal(zigzagDecodeLocal(symbols[
i]));
633 raw =
static_cast<uint16_t
>(symbols[
i]);
635 cmv->mDataPerTF[positions[
i].first * cmv::NTimeBinsPerTF + positions[
i].second] =
raw;
639void CMVPerTFCompressed::decodeDenseValues(
const std::vector<uint32_t>& symbols, uint8_t
flags, CMVPerTF* cmv)
646 for (uint32_t
i = 0; i < static_cast<uint32_t>(symbols.size()); ++
i) {
647 cmv->mDataPerTF[
i] =
static_cast<uint16_t
>(symbols[
i]);
654 for (
int cru = 0; cru < static_cast<int>(
CRU::MaxCRU); ++cru) {
656 for (uint32_t tb = 0; tb < cmv::NTimeBinsPerTF; ++tb, ++
s) {
657 int32_t
val = zigzagDecodeLocal(symbols[s]);
662 cmv->mDataPerTF[
s] = signedToCmvLocal(
val);
670 throw std::invalid_argument(
"CMVPerTFCompressed::decompress: cmv pointer is null");
681 auto positions = decodeSparsePositions(
ptr,
end);
682 const uint32_t N =
static_cast<uint32_t
>(positions.size());
688 decodeSparseValues(symbols, positions,
mFlags, cmv);
690 const uint32_t N =
static_cast<uint32_t
>(
CRU::MaxCRU) * cmv::NTimeBinsPerTF;
696 decodeDenseValues(symbols,
mFlags, cmv);
702 auto tree = std::make_unique<TTree>(
"ccdb_object",
"ccdb_object");
703 tree->SetAutoSave(0);
704 tree->SetDirectory(
nullptr);
707 tree->Branch(
"CMVPerTF", &
ptr);
710 tree->ResetBranchAddresses();
716 auto tree = std::make_unique<TTree>(
"ccdb_object",
"ccdb_object");
717 tree->SetAutoSave(0);
718 tree->SetDirectory(
nullptr);
721 tree->Branch(
"CMVPerTFCompressed", &
ptr);
724 tree->ResetBranchAddresses();
732 throw std::runtime_error(fmt::format(
"CMVPerTF::writeToFile: cannot open '{}'",
filename));
Structs for storing CMVs to the CCDB.
Common mode values data format definition.
o2::raw::RawFileWriter * raw
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.
uint32_t firstOrbitDPL
First orbit of this TF.
uint8_t mFlags
Bitmask of CMVEncoding values.
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)
uint32_t firstOrbitDPL
First orbit of this TF, from DPL.
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.
std::unique_ptr< TTree > tree((TTree *) flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str()))
char const *restrict const cmp