Code Example

The following C++ source code shows how to initialize a TimeTagger4 board, configure it, and loop over incoming packets.

The source code can also be found on our GitHub repository, located at https://github.com/cronologic-de/xtdc_babel/tree/main/timetagger4_user_guide_example.

  1// timetagger4_user_guide_example.cpp : Example application for the TimeTagger4
  2#include <stdio.h>
  3#include <stdlib.h>
  4#include <chrono>
  5#include <thread>
  6#include "TimeTagger4_interface.h"
  7
  8// If true the time tagger triggers a start periodically
  9// The time difference of signals on channel A are measured
 10// else start signal either from input or tiger is used (see below)
 11// frequency of  start signal is printed and the hits are sampled
 12const bool USE_CONTINUOUS_MODE = false;
 13const bool USE_TIGER_START = true;	// if false, external signal must be provided on start; not applicable if continuous mode is enabled
 14const bool USE_TIGER_STOPS = true; 	// if false please connect signals to some of channels A-D
 15
 16
 17timetagger4_device * initialize_timetagger(int buffer_size, int board_id, int card_index) {
 18    // prepare initialization
 19    timetagger4_init_parameters params;
 20
 21    timetagger4_get_default_init_parameters(&params);
 22    params.buffer_size[0] = buffer_size;				// size of the packet buffer
 23    params.board_id = board_id;							// value copied to "card" field of every packet, allowed range 0..255
 24    params.card_index = card_index;						// which of the TimeTagger4 board found in the system to be used
 25
 26    int error_code;
 27    const char * err_message;
 28    timetagger4_device * device = timetagger4_init(&params, &error_code, &err_message);
 29    if (error_code != CRONO_OK) {
 30        printf("Could not init TimeTagger4 compatible board: %s\n", err_message);
 31        return nullptr;
 32    }
 33    timetagger4_static_info static_info;
 34    timetagger4_get_static_info(device, &static_info);
 35    bool timeTaggerNG = static_info.board_revision >= 7;
 36    if (USE_CONTINUOUS_MODE && !timeTaggerNG) {
 37        printf("Cannot use continuous mode with TimeTagger 1G/2G: %s\n", err_message);
 38        timetagger4_close(device);
 39        return nullptr;
 40    }
 41
 42    return device;
 43}
 44
 45int configure_timetagger(timetagger4_device * device) {
 46    // prepare configuration
 47    timetagger4_static_info static_info;
 48    timetagger4_get_static_info(device, &static_info);
 49    timetagger4_configuration config;
 50    // fill configuration data structure with default values
 51    // so that the configuration is valid and only parameters
 52    // of interest have to be set explicitly
 53    timetagger4_get_default_configuration(device, &config);
 54
 55    // set config of the 4 TDC channels
 56    for (int i = 0; i < TIMETAGGER4_TDC_CHANNEL_COUNT; i++)
 57    {
 58        // enable recording hits on TDC channel
 59        config.channel[i].enabled = true;
 60
 61        // define range of the group
 62        config.channel[i].start = 0;	// range begins right after start pulse
 63        if (!USE_CONTINUOUS_MODE) {
 64            config.channel[i].stop = 30000;	// recording window stops after ~15 us
 65        }
 66        else {
 67            config.channel[i].stop = 0x7fffffff; // trigger is independent of stops
 68            // set to maximal value
 69        }
 70
 71        // measure only rising edge for tiger (positive) pulse or falling for user (negative) pulse
 72        config.trigger[TIMETAGGER4_TRIGGER_A + i].falling = USE_TIGER_STOPS ? 0 : 1;
 73        config.trigger[TIMETAGGER4_TRIGGER_A + i].rising = USE_TIGER_STOPS ? 1 : 0;
 74    }
 75
 76    // generate an internal 25 kHz trigger, used for tiger and continuous mode
 77    config.auto_trigger_period = (int)(static_info.auto_trigger_ref_clock / 25000);
 78    config.auto_trigger_random_exponent = 0;
 79
 80    // setup TiGeR
 81    // sending a signal to the LEMO outputs (and to the TDC on the same channel)
 82    // requires proper 50 Ohm termination on the LEMO output to work reliably
 83
 84    // width of the 12ns pulse in the auto_trigger clock periods
 85    int pulse_width = (int) (12e-9 * static_info.auto_trigger_ref_clock);
 86
 87    if (!USE_CONTINUOUS_MODE) {
 88        // use 200 kHz auto trigger to generate
 89
 90        // generate above configured auto trigger to generate a 
 91        // signal with 12 ns pulse width on LEMO output Start
 92        config.tiger_block[0].enable = USE_TIGER_START ? 1 : 0;
 93        config.tiger_block[0].start = 0;
 94        config.tiger_block[0].stop = config.tiger_block[0].start + pulse_width;
 95        config.tiger_block[0].negate = 0;
 96        config.tiger_block[0].retrigger = 0;
 97        config.tiger_block[0].extend = 0;
 98        config.tiger_block[0].enable_lemo_output = 1;
 99        config.tiger_block[0].sources = TIMETAGGER4_TRIGGER_SOURCE_AUTO;
100        // if TiGeR is used for triggering with positive pulses
101        if (USE_TIGER_START)
102            config.dc_offset[0] = TIMETAGGER4_DC_OFFSET_P_LVCMOS_18;
103        else // user input expect NIM signal
104            config.dc_offset[0] = TIMETAGGER4_DC_OFFSET_N_NIM;
105
106        // start group on falling edges on the start channel 0
107        config.trigger[TIMETAGGER4_TRIGGER_S].falling = USE_TIGER_START ? 0 : 1;
108        config.trigger[TIMETAGGER4_TRIGGER_S].rising = USE_TIGER_START ? 1 : 0;
109    } else {
110        // Auto trigger is used as a start signal
111        config.tdc_mode = TIMETAGGER4_TDC_MODE_CONTINUOUS;
112    }
113
114    for (int i = 1; i < TDC4_TIGER_COUNT; i++) {
115        config.tiger_block[i].enable = USE_TIGER_STOPS ? 1 : 0;
116        config.tiger_block[i].start = i * 100;
117        config.tiger_block[i].stop = config.tiger_block[i].start + pulse_width;
118        config.tiger_block[i].negate = 0;
119        config.tiger_block[i].retrigger = 0;
120        config.tiger_block[i].extend = 0;
121        config.tiger_block[i].enable_lemo_output = USE_TIGER_STOPS ? 1 : 0;
122        config.tiger_block[i].sources = TIMETAGGER4_TRIGGER_SOURCE_AUTO;
123
124
125        if (USE_TIGER_STOPS)
126            config.dc_offset[i] = TIMETAGGER4_DC_OFFSET_P_LVCMOS_18;
127        else // user input expect NIM signal
128            config.dc_offset[i] = TIMETAGGER4_DC_OFFSET_N_NIM;		
129
130        // this is not related to the tigers, but uses the same indexing (0 is start)
131        // optionally increase input delay by 10 * 200 ps for each channel on new TT		
132        // config.delay_config[i].delay = i * 10;
133    }
134
135    // write configuration to board
136    return timetagger4_configure(device, &config);
137}
138
139
140void print_device_information(timetagger4_device * device, timetagger4_static_info * si, timetagger4_param_info * pi) {
141    // print board information
142    printf("Board Serial        : %d.%d\n", si->board_serial >> 24, si->board_serial & 0xffffff);
143    printf("Board Configuration : %s\n", timetagger4_get_device_name(device));
144    printf("Board Revision      : %d\n", si->board_revision);
145    printf("Firmware Revision   : %d.%d\n", si->firmware_revision, si->subversion_revision);
146    printf("Driver Revision     : %d.%d.%d\n", ((si->driver_revision >> 16) & 255), ((si->driver_revision >> 8) & 255), (si->driver_revision & 255));
147    printf("Driver SVN Revision : %d\n", si->driver_build_revision);
148    printf("\nTDC binsize         : %0.2f ps\n", pi->binsize);
149}
150
151double last_abs_ts_on_a = 0;
152int64_t last_group_abs_time = 0;
153
154int64_t processPacket(volatile crono_packet *p, bool print, timetagger4_static_info *si, timetagger4_param_info *pi) {
155    // do something with the data, e.g. calculate current rate
156    int64_t group_abs_time = p->timestamp;
157    if (!USE_CONTINUOUS_MODE) {
158        // group timestamp increments at binsize, but we see only a fraction of the packets (every update_count)
159        double rate = 1e12 / ((double)(group_abs_time - last_group_abs_time) * pi->packet_binsize);
160        if (print && last_group_abs_time > 0) {
161            printf("\r%.6f kHz", rate / 1000.0);
162            // ...or print hits (not a good idea at high data rates,
163            printf("Card %d - flags %d - length %d - type %d - TS %llu\n", p->card, p->flags, p->length, p->type, p->timestamp);
164        }
165        last_group_abs_time = group_abs_time;
166    }
167
168    int hit_count = 2 * (p->length);
169    // Two hits fit into every 64 bit word. The second in the last word might be empty
170    // This flag  tells us, whether the number of hits in the packet is odd
171    if ((p->flags & TIMETAGGER4_PACKET_FLAG_ODD_HITS) != 0)
172        hit_count -= 1;
173
174    uint32_t* packet_data = (uint32_t*)(p->data);
175    uint32_t rollover_count = 0;
176    // 
177    uint64_t rollover_period_bins = si->rollover_period;
178    for (int i = 0; i < hit_count; i++)
179    {
180        uint32_t hit = packet_data[i];
181        uint32_t channel = hit & 0xf;
182        // extract hit flags
183        uint32_t flags = hit >> 4 & 0xf;
184
185
186        if ((flags & TIMETAGGER4_HIT_FLAG_TIME_OVERFLOW) != 0) {
187            // this is a overflow of the 23/24 bit counter)
188            rollover_count++;
189        }
190        else {
191            // extract channel number (A-D)
192            char channel_letter = 65 + channel;
193
194            // extract hit timestamp
195            uint32_t ts_offset = hit >> 8 & 0xffffff;
196
197            // Convert timestamp to ns, this is relative to the start of the group
198            double ts_offset_ns = (ts_offset + rollover_count * rollover_period_bins) * pi->binsize / 1000.0;
199
200            if (USE_CONTINUOUS_MODE) {
201                if (channel == 0)
202                {
203                    // compute the absolute time by adding the group time in ns
204                    double abs_ts_on_a = (group_abs_time * pi->packet_binsize) / 1000 + ts_offset_ns;
205                    double diff = abs_ts_on_a - last_abs_ts_on_a;
206                    if (last_abs_ts_on_a > 0 && print) {
207                        printf("Time difference between hits on A  %.1f ns\n", diff);
208                    }
209
210                    last_abs_ts_on_a = abs_ts_on_a;
211                }
212            }
213            else {
214                if (print)
215                    printf("Hit  on channel %c - flags %d - offset %u (raw) / %.1f ns\n", channel_letter, flags, ts_offset, ts_offset_ns);
216            }
217        }
218    }
219    return group_abs_time;
220}
221
222int main(int argc, char* argv[]) {
223    printf("cronologic timetagger4_user_guide_example using driver: %s\n", timetagger4_get_driver_revision_str());
224    timetagger4_device* device = initialize_timetagger(8 * 1024 * 1024, 0, 0);
225    if (device == nullptr) {
226        exit(1);
227    }
228    int status = configure_timetagger(device);
229    if (status != CRONO_OK) {
230        printf("Could not configure TimeTagger4: %s", timetagger4_get_last_error_message(device));
231        timetagger4_close(device);
232        return status;
233    }
234    timetagger4_static_info static_info;
235    timetagger4_get_static_info(device, &static_info);
236
237    timetagger4_param_info parinfo;
238    timetagger4_get_param_info(device, &parinfo);
239
240    print_device_information(device, &static_info, &parinfo);
241
242    // configure readout behaviour
243    timetagger4_read_in read_config;
244    // automatically acknowledge all data as processed
245    // on the next call to timetagger4_read()
246    // old packet pointers are invalid after calling timetagger4_read()
247    read_config.acknowledge_last_read = 1;
248
249    // structure with packet pointers for read data
250    timetagger4_read_out read_data;
251
252    // start data capture
253    status = timetagger4_start_capture(device);
254    if (status != CRONO_OK) {
255        printf("Could not start capturing %s", timetagger4_get_last_error_message(device));
256        timetagger4_close(device);
257        return status;
258    }
259
260    // start timing generator
261    timetagger4_start_tiger(device);
262
263    // some book keeping
264    int packet_count = 0;
265    int empty_packets = 0;
266    int packets_with_errors = 0;
267    bool last_read_no_data = false;
268
269    int64_t group_abs_time = 0;
270    int64_t group_abs_time_old = 0;
271    int update_count = 100;
272
273    printf("Reading packets:\n");
274    bool no_data_printed = false;
275    // read 10000 packets
276    while (packet_count < 10000)
277    {
278        // get pointers to acquired packets
279        status = timetagger4_read(device, &read_config, &read_data);
280        if (status != CRONO_OK) {
281            std::this_thread::sleep_for(std::chrono::milliseconds(10));
282            // to avoid a lot of lines with no data
283            if (!no_data_printed) {
284                printf(" - No data! -\n");
285                no_data_printed = true;
286            }
287        }
288        else
289        {
290            // iterate over all packets received with the last read
291            volatile crono_packet* p = read_data.first_packet;
292            while (p <= read_data.last_packet)
293            {
294                // printf is slow, so this demo only processes every nth packet 
295                // your application would of course collect every packet
296                bool print = packet_count % update_count == 0;
297
298                processPacket( p, print, &static_info, &parinfo);
299                no_data_printed = false;
300                p = crono_next_packet(p);
301                packet_count++;
302            }
303        }
304    }
305
306    // shut down packet generation and DMA transfers
307    timetagger4_stop_capture(device);
308
309    // deactivate timetagger4
310    timetagger4_close(device);
311    return 0;
312}