Belle II Software development
Belle2::ECL::RawDataHadron Namespace Reference

This is a set of function for packing/unpacking the data produced by the new ShaperDSP firmware that does hadron component fit. More...

Functions

unsigned long long packAmplitude (long long peak_amp)
 Amplitude packing.
 
unsigned long long unpackAmplitude (unsigned long long amp_packed)
 Amplitude unpacking (from 14 bits float to 18 bit int)
 
int packTime (int peak_time)
 Time packing.
 
int unpackTime (int time_packed)
 Time unpacking (from 11 bits float to 12 bits int)
 
int packHadronFraction (int A_hadron, int A_total)
 Hadron fraction packing.
 
double unpackHadronFraction (int fraction_packed)
 Hadron fraction unpacking.
 
int integer_division_32 (int dividend, int divisor)
 Basically the fastest algorithm for approximate integer division (with the precision (1/32)).
 

Detailed Description

This is a set of function for packing/unpacking the data produced by the new ShaperDSP firmware that does hadron component fit.

See ecl/utility/include/ECLRawDataHadron.h for details.

See 51st B2GM slides from ECL session, these docs and eclUnpackerModule.cc for more details.

Data format has changed to make space for the hadron fraction value. Unlike the original ShaperDSP which sent fit amplitude and time as integer values, new firmware sends amplitude and time as IEEE 754 floating point values.

Function Documentation

◆ integer_division_32()

int integer_division_32 ( int dividend,
int divisor )

Basically the fastest algorithm for approximate integer division (with the precision (1/32)).

Algorithm description

Calculating (n / m)

  1. Determine p \in [0, 18], such that:
     ⎛      p ⎞
    
    32 <= ⎝ m / 2 ⎠ < 64
  2. Define shifted (divided) values n_S, m_S:
        p
    
    n = n / 2 ∈ [0, 63] S p m = m / 2 ∈ [32, 63] S
  3. Calculate (n / m) as follows:

\frac{n}{m} = \frac{n_S}{m_S} = n_S \cdot \left( \frac{2^{16}}{m_S} \right) \cdot \frac{1}{2^{16}}

  n          ⎛  16 ⎞

n S ⎜ 2 ⎟ 1 ─ = ── = n ∙ ⎜ ─── ⎟ ∙ ─── m m S ⎜ m ⎟ 16 S ⎝ S ⎠ 2

┌─────────────└───────┘────────────────────────────────┐ Since m_S can take only 32 different values, we pre-calculate 32 different constants for (2^{16} / m_S)

  1. Correction to improve division accuracy:

    Instead of n_S, use (n_S + m_S / 2), this will result in correct rounding for the division.

Definition at line 216 of file ECLRawDataHadron.cc.

217 {
218 if (divisor < 32)
219 // Division can be done for divisor < 32 but it is meaningless:
220 // hadron component fit is meaningless for amp < 32
221 return 0;
222
223 if (dividend >= (1 << 18) || dividend < 0) {
224 fprintf(stderr, "\n\033[31m");
225 fprintf(stderr, "%s:%d: Error! Dividend outside of expected range: %d\n", __FILE__, __LINE__, dividend);
226 fprintf(stderr, "\033[0m\n");
227 return 0; // exit(1);
228 }
229 if (divisor >= (1 << 19)) {
230 fprintf(stderr, "\n\033[31m");
231 fprintf(stderr, "%s:%d: Error! Divisor outside of expected range: %d\n", __FILE__, __LINE__, divisor);
232 fprintf(stderr, "\033[0m\n");
233 return 0; // exit(1);
234 }
235
236 // while divisor < 32:
237 // dividend *= 2
238 // divisor *= 2
239 //###############################################
240 // GET CORRECT BITSHIFT
241 //###############################################
242 int i = 18;
243 int p = i - 5;
244 while (i >= 5) {
245 if ((divisor & (1 << i)) != 0) {
246 p = (i - 5);
247 break;
248 }
249 i = i - 1;
250 }
251 //#######################
252 // Equivalent math expression:
253 //
254 // i = ⎣ log2(divisor) ⎦
255 // p = i - 5
256 //
257 //#######################
258 // Equivalent code (same as a while loop above)
259 // if (divisor & 0x40000) != 0:
260 // p = 13
261 // elif (divisor & 0x20000) != 0:
262 // p = 12
263 // elif (divisor & 0x10000) != 0:
264 // p = 11
265 // elif (divisor & 0x08000) != 0:
266 // p = 10
267 // ...
268 // elif (divisor & 0x00020) != 0:
269 // p = 0
270 //#######################
271
272 // NOTE: Because of this multiplication, dividend requires 23 bits.
273 // The code can be rewritten to manage with 18 bits.
274 dividend = dividend << 5;
275
276 int n_S = dividend >> p;
277 int m_S = divisor >> p;
278
279 // The values of round((2^16) / m_S), where m_S is in 32..63 range
280 const int precalculated_constants[] = {
281 // round(2**16 / (x+0.5)) for x in range(32, 64)
282 // ===>
283 2016, 1956, 1900, 1846, 1796, 1748, 1702, 1659,
284 1618, 1579, 1542, 1507, 1473, 1440, 1409, 1380,
285 1351, 1324, 1298, 1273, 1248, 1225, 1202, 1181,
286 1160, 1140, 1120, 1101, 1083, 1066, 1049, 1032
287 };
288
289 // n ⎛ 16 ⎞
290 // n S ⎜ 2 ⎟ 1
291 // ─ = ── = n ∙ ⎜ ─── ⎟ ∙ ───
292 // m m S ⎜ m ⎟ 16
293 // S ⎝ S ⎠ 2
294
295 return ((n_S + m_S / 2) * precalculated_constants[m_S - 32]) >> 16;
296 }

◆ packAmplitude()

unsigned long long packAmplitude ( long long peak_amp)

Amplitude packing.

See format description in unpackAmplitude docs.

Definition at line 27 of file ECLRawDataHadron.cc.

28 {
29 // amplitude packing (from 18 bits int to 14 bits float)
30 // sign -- not used, 0 bits
31 // exponent -- 3 bits
32 // fraction -- 11 bits
33 if (peak_amp < 0) {
34#ifdef ECL_RAW_DATA_HADRON_STANDALONE_BUILD
35 printf("\n\033[31m");
36 printf("%s:%d: Error! Amplitude can never be negative, you have to call this function as packAmplitude(peak_amp + 128).\n",
37 __FILE__, __LINE__);
38 printf("\033[0m\n");
39 exit(1);
40#else
41 B2FATAL("Amplitude can never be negative, you have to call this function as packAmplitude(peak_amp + 128).");
42#endif
43 }
44 unsigned long long exponent;
45 unsigned long long fraction;
46 unsigned long long amp_packed = 0;
47
48 exponent = 0;
49 fraction = peak_amp;
50
51 if ((fraction & 0x20000) != 0)
52 exponent = 7;
53 else if ((fraction & 0x10000) != 0)
54 exponent = 6;
55 else if ((fraction & 0x08000) != 0)
56 exponent = 5;
57 else if ((fraction & 0x04000) != 0)
58 exponent = 4;
59 else if ((fraction & 0x02000) != 0)
60 exponent = 3;
61 else if ((fraction & 0x01000) != 0)
62 exponent = 2;
63 else if ((fraction & 0x00800) != 0)
64 exponent = 1;
65 else
66 exponent = 0;
67
68 if (exponent > 0) {
69 if (exponent > 1) {
70 fraction += 1 << (exponent - 2);
71 fraction = (fraction >> (exponent - 1)) - (1 << 11);
72 }
73 }
74 //###############################################
75 // ACCOUNTING FOR OVERFLOW
76 //###############################################
77 // See the comments in packTime function
78 // for the explanation
79 //###############################################
80 if ((fraction & 0x00800) != 0)
81 fraction -= 1;
82
83 amp_packed = (exponent << 11) | fraction;
84 return amp_packed;
85 }

◆ packHadronFraction()

int packHadronFraction ( int A_hadron,
int A_total )

Hadron fraction packing.

Simply shifting (A_hadron / A_total) from [-0.25, 0.5) into [0, 31]

Definition at line 298 of file ECLRawDataHadron.cc.

299 {
300 // The actual expression is ( (A_hadron / A_total + 0.25) / 0.75 ) * 32
301 // (for the firmware, dividend and divisor are multiplied by 4 to simplify calculations)
302 int dividend = 4 * A_hadron + A_total;
303 if (dividend < 0) return 0;
304 int packed_fraction = integer_division_32(dividend, 3 * A_total);
305 return packed_fraction;
306 }
int integer_division_32(int dividend, int divisor)
Basically the fastest algorithm for approximate integer division (with the precision (1/32)).

◆ packTime()

int packTime ( int peak_time)

Time packing.

See format description in unpackTime docs.

Definition at line 101 of file ECLRawDataHadron.cc.

102 {
103 // time packing (from 12 bits int to 11 bits float) as per IEEE 754
104 // https://en.wikipedia.org/wiki/Double-precision_floating-point_format#IEEE_754_double-precision_binary_floating-point_format:_binary64
105 // sign -- 1 bit
106 // exponent -- 2 bits
107 // fraction -- 8 bits
108 unsigned int exponent = 0;
109 unsigned int sign = 0;
110 int fraction = peak_time; // time from -2048 to 2047
111 if ((fraction & 0x800) != 0) {
112 sign = 1;
113 fraction = -fraction;
114 }
115
116 if ((fraction & 0x400) != 0)
117 exponent = 3;
118 else if ((fraction & 0x200) != 0)
119 exponent = 2;
120 else if ((fraction & 0x100) != 0)
121 exponent = 1;
122 else
123 exponent = 0;
124
125
126 if (exponent > 0) {
127 if (exponent > 1) {
128 // Adjust initial value so that the value is rounded to
129 // closest possible number.
130 fraction += 1 << (exponent - 2);
131 }
132 fraction = (fraction >> (exponent - 1)) - (1 << 8);
133 //###############################################
134 // ACCOUNTING FOR OVERFLOW
135 //###############################################
136 // In several specific cases, rounding can cause
137 // the value to overflow, this has to be handled.
138 // (for example:
139 // We would like to compress 63 from 5 bits to 3 bits.
140 // Thus, we divide 63 by 2^(5-3) == 4
141 // 63 + (4 / 2) == 65 # apply rounding
142 // 65 / 4 == 16 # divide by 2^(5-3)
143 // And 16 will exceed the bit width of 3!
144 // So we need to account for such cases
145 //###############################################
146 if ((fraction & 0x100) != 0)
147 fraction -= 1;
148 }
149
150 int peak_time_packed = (sign << 10) | (exponent << 8) | fraction;
151 return peak_time_packed;
152 }

◆ unpackAmplitude()

unsigned long long unpackAmplitude ( unsigned long long amp_packed)

Amplitude unpacking (from 14 bits float to 18 bit int)

sign – not used, 0 bits exponent – 3 bits fraction – 11 bits

Definition at line 87 of file ECLRawDataHadron.cc.

88 {
89 unsigned int exponent = (amp_packed >> 11) & 0b111;
90 unsigned int fraction = (amp_packed) & 0b11111111111;
91 if (exponent == 0) {
92 return fraction - 128;
93 } else {
94 return (1 << (10 + exponent)) + fraction * (1 << (exponent - 1)) - 128;
95 }
96 }

◆ unpackHadronFraction()

double unpackHadronFraction ( int fraction_packed)

Hadron fraction unpacking.

Simply shifting packed value from [0, 31] into [-0.25, 0.5)

Definition at line 308 of file ECLRawDataHadron.cc.

309 {
310 return fraction_packed / 32.0 * 0.75 - 0.25;
311 }

◆ unpackTime()

int unpackTime ( int time_packed)

Time unpacking (from 11 bits float to 12 bits int)

sign – 1 bit exponent – 2 bits fraction – 8 bits

Definition at line 154 of file ECLRawDataHadron.cc.

155 {
156 unsigned int sign = (time_packed >> 10) & 0b1;
157 unsigned int exponent = (time_packed >> 8) & 0b11;
158 unsigned int fraction = (time_packed) & 0b11111111;
159
160 int result;
161
162 if (exponent == 0)
163 result = fraction;
164 else
165 result = ((1 << (7 + exponent)) + fraction * (1 << (exponent - 1)));
166 if (sign == 1)
167 result = -result;
168 return result;
169 }