EventLoaderATLASpix.cpp 22.3 KB
Newer Older
1
#include "EventLoaderATLASpix.h"
2
3
4
5
6
#include <regex>

using namespace corryvreckan;
using namespace std;

7
8
EventLoaderATLASpix::EventLoaderATLASpix(Configuration config, std::shared_ptr<Detector> detector)
    : Module(std::move(config), detector), m_detector(detector) {
9

10
11
    m_inputDirectory = m_config.getPath("input_directory");
    m_clockCycle = m_config.get<double>("clock_cycle", static_cast<double>(Units::convert(6.25, "ns")));
12
13

    // Allow reading of legacy data format using the Karlsruhe readout system:
14
    m_legacyFormat = m_config.get<bool>("legacy_format", false);
15

16
17
18
    // m_clkdivendM = m_config.get<int>("clkdivend", 0.) + 1;
    m_clkdivend2M = m_config.get<int>("clkdivend2", 0.) + 1;

19
20
21
22
    if(m_config.has("calibration_file")) {
        m_calibrationFile = m_config.getPath("calibration_file");
    }

23
24
    // ts1Range = 0x800 * m_clkdivendM;
    ts2Range = 0x40 * m_clkdivend2M;
25
}
26

27
uint32_t EventLoaderATLASpix::gray_decode(uint32_t gray) {
28
29
30
31
32
    uint32_t bin = gray;
    while(gray >>= 1) {
        bin ^= gray;
    }
    return bin;
33
}
34

35
void EventLoaderATLASpix::initialise() {
36

37
    uint32_t datain;
38

39
40
    m_detectorBusy = false;

Tomas Vanat's avatar
Tomas Vanat committed
41
42
    // File structure is RunX/data.bin
    // Assume that the ATLASpix is the DUT (if running this algorithm)
43

44
    // Open the root directory
45
    DIR* directory = opendir(m_inputDirectory.c_str());
46
    if(directory == nullptr) {
47
        LOG(ERROR) << "Directory " << m_inputDirectory << " does not exist";
48
49
50
51
52
        return;
    }
    dirent* entry;

    // Read the entries in the folder
53
    while((entry = readdir(directory))) {
54
        // Check for the data file
55
        string filename = m_inputDirectory + "/" + entry->d_name;
56
        if(filename.find("data.bin") != string::npos) {
57
58
59
60
61
            m_filename = filename;
        }
    }

    // If no data was loaded, give a warning
62
    if(m_filename.length() == 0) {
63
        LOG(WARNING) << "No data file was found for ATLASpix in " << m_inputDirectory;
64
    } else {
65
        LOG(STATUS) << "Opened data file for ATLASpix: (dbg)" << m_filename;
66
    }
67

68
69
70
71
72
    // Open the binary data file for later
    m_file.open(m_filename.c_str(), ios::in | ios::binary);
    LOG(DEBUG) << "Opening file " << m_filename;

    // fast forward file to T0 event
73
    old_fpga_ts = 0;
74
    while(1) {
75
        m_file.read(reinterpret_cast<char*>(&datain), 4);
76
77
78
79
        if(m_file.eof()) {
            m_file.clear();
            m_file.seekg(ios::beg);
            LOG(WARNING) << "No T0 event was found in file " << m_filename
80
                         << ". Rewinding the file to the beginning and loading all events.";
81
82
83
84
85
            break;
        } else if((datain & 0xFF000000) == 0x70000000) {
            LOG(STATUS) << "Found T0 event at position " << m_file.tellg() << ". Skipping all data before this event.";
            oldpos = m_file.tellg();
            unsigned long ts3 = datain & 0x00FFFFFF;
86
87
            old_fpga_ts = (static_cast<unsigned long long>(ts3));
            int checkpos = static_cast<int>(oldpos) - 8;
88
            while(checkpos >= 0) {
89
90
                std::streampos tmppos = checkpos;
                m_file.seekg(tmppos);
91
                m_file.read(reinterpret_cast<char*>(&datain), 4);
92
93
94
                unsigned int message_type = (datain >> 24);
                // TS2
                if(message_type == 0b00100000) {
95
                    old_fpga_ts = ((static_cast<unsigned long long>(datain & 0x00FFFFFF)) << 24) | ts3;
96
                    LOG(DEBUG) << "Set old_fpga_ts to " << old_fpga_ts;
97
98
99
100
101
102
103
104
105
                    break;
                }
                // TS3
                else if(message_type == 0b01100000) {
                    if(ts3 != (datain & 0x00FFFFFF)) {
                        LOG(WARNING) << "Last FPGA timestamp " << (datain & 0x00FFFFFF) << " does not match to T0 event "
                                     << ts3 << ". Some timestamps at the begining might be corrupted.";
                    }
                }
106
                checkpos = static_cast<int>(tmppos) - 4;
107
108
109
110
111
            }
            m_file.seekg(oldpos);
            break;
        }
    }
112

113
    // Make histograms for debugging
114
115
    hHitMap = new TH2F("hitMap",
                       "hitMap",
116
                       m_detector->nPixels().X(),
117
                       0,
118
119
                       m_detector->nPixels().X(),
                       m_detector->nPixels().Y(),
120
                       0,
121
                       m_detector->nPixels().Y());
Jens Kroeger's avatar
Jens Kroeger committed
122

123
124
    hPixelToT = new TH1F("pixelToT", "pixelToT", 64, 0, 64);
    hPixelToT->GetXaxis()->SetTitle("ToT in TS2 clock cycles.");
125
126
    hPixelToTCal = new TH1F("pixelToTCal", "pixelToT", 100, 0, 100);
    hPixelToA = new TH1F("pixelToA", "pixelToA", 100, 0, 100);
127
    hPixelsPerFrame = new TH1F("pixelsPerFrame", "pixelsPerFrame", 200, 0, 200);
128
    hPixelsOverTime = new TH1F("pixelsOverTime", "pixelsOverTime", 2e6, 0, 2e6);
129

130
    // Read calibration:
131
    m_calibrationFactors.resize(static_cast<size_t>(m_detector->nPixels().X() * m_detector->nPixels().Y()), 1.0);
132
133
134
135
136
137
138
139
140
    if(!m_calibrationFile.empty()) {
        std::ifstream calibration(m_calibrationFile);
        std::string line;
        std::getline(calibration, line);

        int col, row;
        double calibfactor;
        while(getline(calibration, line)) {
            std::istringstream(line) >> col >> row >> calibfactor;
141
            m_calibrationFactors.at(static_cast<size_t>(row * 25 + col)) = calibfactor;
142
143
        }
        calibration.close();
144
145
    }

Jens Kroeger's avatar
Jens Kroeger committed
146
    LOG(INFO) << "Using clock cycle length of " << m_clockCycle << " ns." << std::endl;
147

148
149
    m_oldtoa = 0;
    m_overflowcounter = 0;
150
151
}

152
StatusCode EventLoaderATLASpix::run(std::shared_ptr<Clipboard> clipboard) {
153

154
155
156
157
158
    // Check if event frame is defined:
    if(!clipboard->has_persistent("eventStart") || !clipboard->has_persistent("eventEnd")) {
        throw ModuleError("Event not defined. Add Metronome module or Event reader defining the event.");
    }

159
160
161
    // If have reached the end of file, close it and exit program running
    if(m_file.eof()) {
        m_file.close();
162
        LOG(DEBUG) << "Returning <Failure> status, ATLASPix data file reached the end.";
163
        return StatusCode::Failure;
164
165
    }

166
167
    double start_time = clipboard->get_persistent("eventStart");
    double end_time = clipboard->get_persistent("eventEnd");
168
    bool busy_at_start = m_detectorBusy;
169
170

    // Read pixel data
171
    Pixels* pixels = (m_legacyFormat ? read_legacy_data(start_time, end_time) : read_caribou_data(start_time, end_time));
172

173
174
175
176
177
    if(busy_at_start || m_detectorBusy) {
        LOG(DEBUG) << "Returning <DeadTime> status, ATLASPix is BUSY.";
        return StatusCode::DeadTime;
    }

178
179
180
181
182
    for(auto px : (*pixels)) {
        hHitMap->Fill(px->column(), px->row());
        hPixelToT->Fill(px->tot());
        hPixelToTCal->Fill(px->charge());
        hPixelToA->Fill(px->timestamp());
183
184

        // Pixels per 100us:
185
        hPixelsOverTime->Fill(static_cast<double>(Units::convert(px->timestamp(), "ms")));
186
187
    }

188
    // Fill histograms
189
    hPixelsPerFrame->Fill(static_cast<double>(pixels->size()));
190

191
192
    // Put the data on the clipboard
    if(!pixels->empty()) {
193
        clipboard->put(m_detector->name(), "pixels", reinterpret_cast<Objects*>(pixels));
194
    } else {
Jens Kroeger's avatar
Jens Kroeger committed
195
        delete pixels;
196
        LOG(DEBUG) << "Returning <NoData> status, no hits found.";
197
        return StatusCode::NoData;
198
199
200
    }

    // Return value telling analysis to keep running
201
    LOG(DEBUG) << "Returning <Success> status, hits found in this event window.";
202
    return StatusCode::Success;
203
204
}

205
Pixels* EventLoaderATLASpix::read_caribou_data(double start_time, double end_time) {
206
207
    LOG(DEBUG) << "Searching for events in interval from " << Units::display(start_time, {"s", "us", "ns"}) << " to "
               << Units::display(end_time, {"s", "us", "ns"}) << ", file read position " << m_file.tellg()
208
               << ", old_fpga_ts = " << old_fpga_ts << ".";
209

210
211
212
213
    // Pixel container
    Pixels* pixels = new Pixels();

    // Read file and load data
214
    uint32_t datain;
215
    long long ts_diff; // tmp
216
217

    // Initialize all to 0 for a case that hit data come before timestamp/trigger data
218
219
220
    long long hit_ts = 0;            // 64bit value of a hit timestamp combined from readout and pixel hit timestamp
    unsigned long long atp_ts = 0;   // 16bit value of ATLASpix readout timestamp
    unsigned long long trig_cnt = 0; // 32bit value of trigger counter (in FPGA)
221
222
223
224
225
226
227
    unsigned long long readout_ts =
        old_readout_ts;                       // 64bit value of a readout timestamp combined from FPGA and ATp timestamp
    unsigned long long fpga_ts = old_fpga_ts; // 64bit value of FPGA readout timestamp
    unsigned long long fpga_ts1 = 0;          // tmp [63:48] of FPGA readout timestamp
    unsigned long long fpga_ts2 = 0;          // tmp [47:24] of FPGA readout timestamp
    unsigned long long fpga_ts3 = 0;          // tmp [23:0] of FPGA readout timestamp
    unsigned long long fpga_tsx = 0;          // tmp for FPGA readout timestamp
228
229
    bool new_ts1 = true;
    bool new_ts2 = true;
230
231
232
    bool window_end = false;
    bool keep_pointer_stored = false;
    bool keep_reading = true;
233
234

    // Repeat until input EOF:
235
    while(keep_reading) {
236
        // Read next 4-byte data from file
237
        m_file.read(reinterpret_cast<char*>(&datain), 4);
238
239
240
241
        if(m_file.eof()) {
            LOG(DEBUG) << "EOF...";
            break;
        }
242

243
244
245
246
247
248
        // LOG(DEBUG) << "Found " << (datain & 0x80000000 ? "pixel data" : "header information");

        // Check if current word is a pixel data:
        if(datain & 0x80000000) {
            // Structure: {1'b1, column_addr[5:0], row_addr[8:0], rise_timestamp[9:0], fall_timestamp[5:0]}
            // Extract pixel data
249
            long ts2 = gray_decode((datain)&0x003F);
250
            // long ts2 = gray_decode((datain>>6)&0x003F);
251
            // TS1 counter is by default half speed of TS2. By multiplying with 2 we make it equal.
252
253
            long ts1 = (gray_decode((datain >> 6) & 0x03FF)) << 1;
            // long ts1 = (gray_decode(((datain << 4) & 0x3F0) | ((datain >> 12) & 0xF)))<<1;
254
255
            int row = ((datain >> (6 + 10)) & 0x01FF);
            int col = ((datain >> (6 + 10 + 9)) & 0x003F);
256
257
            // long tot = 0;

258
            ts_diff = ts1 - static_cast<long long>(readout_ts & 0x07FF);
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278

            if(ts_diff > 0) {
                // Hit probably came before readout started and meanwhile an OVF of TS1 happened
                if(ts_diff > 0x01FF) {
                    ts_diff -= 0x0800;
                }
                // Hit probably came after readout started and is within range.
                // else {
                //    // OK...
                //}
            } else {
                // Hit probably came after readout started and after OVF of TS1.
                if(ts_diff < (0x01FF - 0x0800)) {
                    ts_diff += 0x0800;
                }
                // Hit probably came before readout started and is within range.
                // else {
                //    // OK...
                //}
            }
279

280
            hit_ts = static_cast<long long>(readout_ts) + ts_diff;
281

282
            // Convert the timestamp to nanoseconds:
283
            double timestamp = m_clockCycle * static_cast<double>(hit_ts);
284

285
            if(timestamp > end_time) {
286
287
                keep_pointer_stored = true;
                LOG(DEBUG) << "Skipping processing event, pixel is after event window ("
288
                           << Units::display(timestamp, {"s", "us", "ns"}) << " > "
289
                           << Units::display(end_time, {"s", "us", "ns"}) << ")";
290
                continue;
291
292
            }

293
294
295
296
297
298
            if(timestamp < start_time) {
                LOG(DEBUG) << "Skipping pixel hit, pixel is before event window ("
                           << Units::display(timestamp, {"s", "us", "ns"}) << " < "
                           << Units::display(start_time, {"s", "us", "ns"}) << ")";
                continue;
            }
299
300
            // this window still contains data in the event window, do not stop processing
            window_end = false;
301
302
303
304
305
306
307
            if(m_detectorBusy && (busy_readout_ts < readout_ts)) {
                LOG(WARNING) << "ATLASPix went BUSY between "
                             << Units::display((m_clockCycle * static_cast<double>(busy_readout_ts)), {"s", "us", "ns"})
                             << " and "
                             << Units::display((m_clockCycle * static_cast<double>(readout_ts)), {"s", "us", "ns"}) << ".";
                m_detectorBusy = false;
            }
308
            data_pixel_++;
309
            // If this pixel is masked, do not save it
310
            if(m_detector->masked(col, row)) {
311
312
313
                continue;
            }

314
            // calculate ToT only when pixel is good for storing (division is time consuming)
315
            int tot = static_cast<int>(ts2 - ((hit_ts % static_cast<long long>(64 * m_clkdivend2M)) / m_clkdivend2M));
316
            if(tot < 0) {
317
                tot += 64;
318
            }
Tomas Vanat's avatar
Tomas Vanat committed
319
            // convert ToT to nanoseconds
320
            // double tot_ns = tot * m_clockCycle;
321
322
323
324
325

            LOG(TRACE) << "HIT: TS1: " << ts1 << "\t0x" << std::hex << ts1 << "\tTS2: " << ts2 << "\t0x" << std::hex << ts2
                       << "\tTS_FULL: " << hit_ts << "\t" << Units::display(timestamp, {"s", "us", "ns"})
                       << "\tTOT: " << tot; // << "\t" << Units::display(tot_ns, {"s", "us", "ns"});

326
            Pixel* pixel = new Pixel(m_detector->name(), row, col, tot, timestamp);
327
            LOG(DEBUG) << "PIXEL:\t" << *pixel;
328
            pixels->push_back(pixel);
329

330
        } else {
331
332
333
334
335
            // data is not hit information
            // if (keep_pointer_stored) then we will go through the data one more time and wee will count that next time.
            if(!keep_pointer_stored) {
                data_header_++;
            }
336

337
338
339
340
341
            // Decode the message content according to 8 MSBits
            unsigned int message_type = (datain >> 24);
            switch(message_type) {
            // Timestamp from ATLASpix [23:0]
            case 0b01000000:
342
343
344
345
346
347
348
349
350
                // the whole previous readout was behind the time window, we can stop processing and return
                if(window_end) {
                    LOG(TRACE) << "Rewinding to file pointer : " << oldpos;
                    m_file.seekg(oldpos);
                    // exit the while loop
                    keep_reading = false;
                    // exit case
                    break;
                }
351
352

                atp_ts = (datain >> 7) & 0x1FFFE;
353
                ts_diff = static_cast<long long>(atp_ts) - static_cast<long long>(fpga_ts & 0x1FFFF);
354
355
356
357
358
359
360
361
362
363

                if(ts_diff > 0) {
                    if(ts_diff > 0x10000) {
                        ts_diff -= 0x20000;
                    }
                } else {
                    if(ts_diff < -0x1000) {
                        ts_diff += 0x20000;
                    }
                }
364
                readout_ts = static_cast<unsigned long long>(static_cast<long long>(fpga_ts) + ts_diff);
365

366
367
368
369
370
371
372
373
374
375
376
                if(!keep_pointer_stored) {
                    // Store this position in the file in case we need to rewind:
                    LOG(TRACE) << "Storing file pointer position: " << m_file.tellg() << " and readout TS: " << readout_ts;
                    oldpos = m_file.tellg();
                    old_readout_ts = readout_ts;
                } else {
                    LOG(TRACE) << "File pointer position already stored for the first event out of the window. Current "
                                  "readout_ts = "
                               << readout_ts;
                }
                // If the readout time is after the window, mark it as a candidate for last readout in the window
377
                if((static_cast<double>(readout_ts) * m_clockCycle) > end_time) {
378
379
                    window_end = true;
                }
380
                break;
381

382
383
384
385
386
387
388
389
            // Trigger counter from FPGA [23:0] (1/4)
            case 0b00010000:
                trig_cnt = datain & 0x00FFFFFF;
                break;

            // Trigger counter from FPGA [31:24] and timestamp from FPGA [63:48] (2/4)
            case 0b00110000:
                trig_cnt |= (datain << 8) & 0xFF000000;
390
                fpga_ts1 = ((static_cast<unsigned long long>(datain) << 48) & 0xFFFF000000000000);
391
392
393
394
395
                new_ts1 = true;
                break;

            // Timestamp from FPGA [47:24] (3/4)
            case 0b00100000:
396
                fpga_tsx = ((static_cast<unsigned long long>(datain) << 24) & 0x0000FFFFFF000000);
397
398
                if((!new_ts1) && (fpga_tsx < fpga_ts2)) {
                    fpga_ts1 += 0x0001000000000000;
399
                    LOG(WARNING) << "Missing TS_FPGA_1, adding one";
400
401
402
403
404
405
406
407
408
409
410
411
                }
                new_ts1 = false;
                new_ts2 = true;
                fpga_ts2 = fpga_tsx;
                break;

            // Timestamp from FPGA [23:0] (4/4)
            case 0b01100000:
                m_identifiers["FPGA_TS"]++;
                fpga_tsx = ((datain)&0xFFFFFF);
                if((!new_ts2) && (fpga_tsx < fpga_ts3)) {
                    fpga_ts2 += 0x0000000001000000;
412
                    LOG(WARNING) << "Missing TS_FPGA_2, adding one";
413
414
415
416
417
                }
                new_ts2 = false;
                fpga_ts3 = fpga_tsx;
                fpga_ts = fpga_ts1 | fpga_ts2 | fpga_ts3;
                break;
418

419
420
421
            // BUSY was asserted due to FIFO_FULL + 24 LSBs of FPGA timestamp when it happened
            case 0b00000010:
                m_identifiers["BUSY_ASSERT"]++;
422
423
                busy_readout_ts = readout_ts;
                m_detectorBusy = true;
424
425
426
427
428
429
430
431
432
433
                break;

            // T0 received
            case 0b01110000:
                LOG(WARNING) << "Another T0 event was found in the data at position " << m_file.tellg();
                break;

            // Empty data - should not happen
            case 0b00000000:
                m_identifiers["EMPTY_DATA"]++;
Tomas Vanat's avatar
Tomas Vanat committed
434
                LOG(DEBUG) << "EMPTY_DATA";
435
436
437
438
439
440
441
442
                break;

            // Other options...
            default:
                // LOG(DEBUG) << "...Other";
                // Unknown message identifier
                if(message_type & 0b11110010) {
                    m_identifiers["UNKNOWN_MESSAGE"]++;
Tomas Vanat's avatar
Tomas Vanat committed
443
                    LOG(DEBUG) << "UNKNOWN_MESSAGE";
444
445
446
447
                } else {
                    // Buffer for chip data overflow (data that came after this word were lost)
                    if((message_type & 0b11110011) == 0b00000001) {
                        m_identifiers["BUFFER_OVERFLOW"]++;
Tomas Vanat's avatar
Tomas Vanat committed
448
                        LOG(DEBUG) << "BUFFER_OVERFLOW";
449
450
451
452
                    }
                    // SERDES lock established (after reset or after lock lost)
                    if((message_type & 0b11111110) == 0b00001000) {
                        m_identifiers["SERDES_LOCK_ESTABLISHED"]++;
Tomas Vanat's avatar
Tomas Vanat committed
453
                        LOG(DEBUG) << "SERDES_LOCK_ESTABLISHED";
454
455
456
457
                    }
                    // SERDES lock lost (data might be nonsense, including up to 2 previous messages)
                    else if((message_type & 0b11111110) == 0b00001100) {
                        m_identifiers["SERDES_LOCK_LOST"]++;
Tomas Vanat's avatar
Tomas Vanat committed
458
                        LOG(DEBUG) << "SERDES_LOCK_LOST";
459
460
461
462
                    }
                    // Unexpected data came from the chip or there was a checksum error.
                    else if((message_type & 0b11111110) == 0b00000100) {
                        m_identifiers["WEIRD_DATA"]++;
Tomas Vanat's avatar
Tomas Vanat committed
463
                        LOG(DEBUG) << "WEIRD_DATA";
464
465
466
467
468
469
470
471
472
473
474
475
476
                    }
                    // Unknown message identifier
                    else {
                        m_identifiers["UNKNOWN_MESSAGE"]++;
                        LOG(WARNING) << "UNKNOWN_MESSAGE";
                    }
                }
                break;
                // End case
            }
        }
    }
    LOG(DEBUG) << "Returning " << pixels->size() << " pixels";
477
478
479
    return pixels;
}

480
Pixels* EventLoaderATLASpix::read_legacy_data(double, double) {
481

482
    // Pixel container
483
484
485
    Pixels* pixels = new Pixels();

    // Read file and load data
486
487
    while(!m_file.eof()) {

488
        int col, row, tot, ts;
489
490
491
492
493
        unsigned long long int toa, TriggerDebugTS, dummy, bincounter;

        m_file >> col >> row >> ts >> tot >> dummy >> dummy >> bincounter >> TriggerDebugTS;

        // If this pixel is masked, do not save it
494
        if(m_detector->masked(col, row)) {
495
496
497
498
499
500
            continue;
        }

        // TOT
        if(tot <= (ts * 2 & 0x3F)) {
            tot = 64 + tot - (ts * 2 & 0x3F);
501
        } else {
502
503
504
505
            tot = tot - (ts * 2 & 0x3F);
        }

        // Apply calibration:
506
507
508
        double cal_tot = tot * m_calibrationFactors.at(static_cast<size_t>(row * 25 + col));
        LOG(TRACE) << "Hit " << row << "\t" << col << ": " << m_calibrationFactors.at(static_cast<size_t>(row * 25 + col))
                   << " * " << tot << " = " << cal_tot;
509
510
511
512

        ts &= 0xFF;
        ts *= 2; // atlaspix timestamp runs at 10MHz, multiply by to to get 20.

513
        if((bincounter & 0x1FF) < static_cast<unsigned long long>(ts)) {
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
            toa = ((bincounter & 0xFFFFFFFFFFFFFE00) - (1 << 9)) | (ts & 0x1FF);
        } else {
            toa = (bincounter & 0xFFFFFFFFFFFFFE00) | (ts & 0x1FF);
        }

        if(((toa + 10000) & 0xFFFFF000) < (m_oldtoa & 0xFFFFF000)) {
            m_overflowcounter++;
            LOG(DEBUG) << "Overflow detected " << m_overflowcounter << " " << (toa & 0xFFFFF000) << " "
                       << (m_oldtoa & 0xFFFFF000);
        } // Atlaspix only! Toa has overflow at 32 bits.

        toa += (0x100000000 * m_overflowcounter);
        m_oldtoa = toa & 0xFFFFFFFF;
        LOG(DEBUG) << "    " << row << "\t" << col << ": " << tot << " " << ts << " " << bincounter << " " << toa << " "
                   << (TriggerDebugTS - toa);

530
        // TriggerDebugTS *= 4096. / 5;              // runs with 200MHz, divide by 5 to scale counter value to 40MHz
531
532
        double toa_timestamp =
            4096. * 2 * static_cast<double>(toa); // runs with 20MHz, multiply by 2 to scale counter value to 40MHz
533

534
        // Convert TOA to nanoseconds:
535
        toa_timestamp /= (4096. * 0.04);
536

537
        Pixel* pixel = new Pixel(m_detector->name(), row, col, tot, toa_timestamp);
538
        pixel->setCharge(cal_tot);
539
        pixels->push_back(pixel);
540
541
    }

542
    return pixels;
543
544
}

545
void EventLoaderATLASpix::finalise() {
546

547
548
549
550
    LOG(INFO) << "Identifier distribution:";
    for(auto id : m_identifiers) {
        LOG(INFO) << "\t" << id.first << ": " << id.second;
    }
551
552

    LOG(INFO) << "Found " << data_pixel_ << " pixel data blocks and " << data_header_ << " header words";
553
}