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(¶ms);
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(¶ms, &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}