Code Example¶
The following C++ source code shows how to initialize a xTDC4 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/xTDC4_user_guide_example.
1// xtdc4_user_guide_example.cpp:ExampleapplicationforthexTDC4
2//#include "CronoCommon.h"
3#include "stdio.h"
4#include <chrono>
5#include <thread>
6#include "xTDC4_interface.h"
7
8
9
10xtdc4_device *initialize_xtdc4(int buffer_size, int board_id, int card_index) {
11 // prepare initialization
12 xtdc4_init_parameters params;
13
14 xtdc4_get_default_init_parameters(¶ms);
15 params.buffer_size[0] = buffer_size; // size of the packet buffer
16 params.board_id = 0; // value copied to "card" field of every packet,
17 // allowed range 0..255
18 params.card_index =
19 0; // which of the xTDC4 board foundin the system to be used
20
21 int error_code;
22 const char *err_message;
23 xtdc4_device *device = xtdc4_init(¶ms, &error_code, &err_message);
24 if (error_code != CRONO_OK) {
25 printf("Could not init xTDC4 compatible board:%s\n",
26 err_message);
27 return nullptr;
28 }
29 return device;
30}
31
32int configure_xtdc4(xtdc4_device *device) {
33 // prepare configuration
34 xtdc4_configuration config;
35
36 // fill configuration data structure with defaul tvalues
37 // so that the configuration is valid and only parameters
38 // of interest have to be set explicitly
39 xtdc4_get_default_configuration(device, &config);
40
41 // set config of the 4TDC channels
42 for (int i = 0; i < XTDC4_TDC_CHANNEL_COUNT; i++) {
43 // enable recording hits on TDCchannel
44 config.channel[i].enabled = true;
45
46 // define range of the group
47 config.channel[i].start =
48 0; // range begins right after start pulse
49 config.channel[i].stop =
50 30000; // recording window stop safter ~15us
51
52 // measure only falling edge
53 config.trigger[i + 1].falling = 1;
54 config.trigger[i + 1].rising = 0;
55 }
56
57 // start group on falling edges on the start channel0
58 config.trigger[0].falling =
59 1; // enable packet generation on falling edge of start pulse
60 config.trigger[0].rising =
61 0; // disable packet generation on rising edge of start pulse
62
63 // generate an internal 200kHz trigger
64 config.auto_trigger_period = 750;
65 config.auto_trigger_random_exponent = 0;
66
67 // setup TiGeR
68 // sending a signal to the LEMO outputs(and to the TDC on the same
69 // channel) requires proper 50Ohm termination on the LEMO output towork
70 // reliably
71
72 // use 200 kHz autotrigger to generate
73 // a 200 kHz signal with 12 ns pulse width on LEMO output Start
74 config.tiger_block[0].enable = 1;
75 config.tiger_block[0].start = 0;
76 config.tiger_block[0].stop = config.tiger_block[0].start + 1;
77 config.tiger_block[0].negate = 0;
78 config.tiger_block[0].retrigger = 0;
79 config.tiger_block[0].extend = 0;
80 config.tiger_block[0].enable_lemo_output = 1;
81 config.tiger_block[0].sources = XTDC4_TRIGGER_SOURCE_AUTO;
82
83 // if TiGeR isused for triggering with positive pulses
84 config.dc_offset[0] = XTDC4_DC_OFFSET_P_LVCMOS_18;
85
86 // write configuration to board
87 return xtdc4_configure(device, &config);
88}
89
90double get_binsize(xtdc4_device *device) {
91 xtdc4_param_info parinfo;
92 xtdc4_get_param_info(device, &parinfo);
93 return parinfo.binsize;
94}
95
96void print_device_information(xtdc4_device *device) {
97 // print board information
98 xtdc4_static_info staticinfo;
99 xtdc4_get_static_info(device, &staticinfo);
100 printf("Board Serial: %d.%d\n", staticinfo.board_serial >> 24,
101 staticinfo.board_serial & 0xffffff);
102 printf("Board Configuration: 0x%x\n", staticinfo.board_configuration);
103 printf("Board Revision: %d\n", staticinfo.board_revision);
104 printf("Firmware Revision: %d.%d\n", staticinfo.firmware_revision,
105 staticinfo.subversion_revision);
106 printf("Driver Revision: %d.%d.%d\n",
107 ((staticinfo.driver_revision >> 16) & 255),
108 ((staticinfo.driver_revision >> 8) & 255),
109 (staticinfo.driver_revision & 255));
110 printf("Driver SVN Revision: %d\n", staticinfo.driver_build_revision);
111 printf("\nTDC binsize: %0.2f ps\n", get_binsize(device));
112}
113
114void print_hit(uint32_t hit, double binsize) {
115 // extract channel number(A−D)
116 char channel = 65 + (hit & 0xf);
117
118 // extract hit flags
119 int flags = (hit >> 4 & 0xf);
120
121 // extract hit timestamp
122 int ts_offset = (hit >> 8 & 0xffffff);
123
124 // TDC binsize is 500ps. Convert timestamp to ns.
125 double ts_offset_ns = ts_offset;
126 ts_offset_ns *= binsize / 1000.0;
127
128 printf("Hit@Channel %c − Flags %d − Offset %u (raw)/%.1f ns\n", channel,
129 flags, ts_offset, ts_offset_ns);
130}
131
132int64_t process_packet(int64_t group_abs_time_old, volatile crono_packet *p,
133 int update_count, double binsize) {
134 // do something with the data, e.g. calculate current rate
135 int64_t group_abs_time = p->timestamp;
136 // group timestamp increment s at 2GHz
137 double rate =
138 (600000000 / ((double)(group_abs_time - group_abs_time_old) /
139 (double)update_count));
140 printf("\r%.2f kHz", rate / 1000.0);
141
142 // ... or prin thit s (not a good idea at high datarates,
143 printf("Card %d - Flags %d - Length %d - Type %d - TS %llu\n", p->card,
144 p->flags, p->length, p->type, p->timestamp);
145
146 // There fit two hits into every 64 bit word.
147 // The flag with weight 1 tells us, whether the number of hits in the
148 // packet is odd
149 int hit_count = 2 * (p->length);
150 if ((p->flags & 0x1) == 1)
151 hit_count -= 1;
152
153 uint32_t *packet_data = (uint32_t*)(p->data);
154 for (int i = 0; i < hit_count; i++) {
155 print_hit(packet_data[i], binsize);
156 }
157 printf("\n\n");
158 return group_abs_time;
159}
160
161int main(int argc, char *argv[]) {
162 printf("cronologic xtdc4_user_guide_example using driver:%s\n",
163 xtdc4_get_driver_revision_str());
164
165 xtdc4_device *device = initialize_xtdc4(8 * 1024 * 1024, 0, 0);
166
167 int status = configure_xtdc4(device);
168 if (status != CRONO_OK) {
169 printf("Could not configure xTDC4: %s",
170 xtdc4_get_last_error_message(device));
171 xtdc4_close(device);
172 return status;
173 }
174
175 print_device_information(device);
176
177 // configure readout behaviour
178 xtdc4_read_in read_config;
179 // automatically acknowledge all data as processed
180 // on the next call to xtdc4_read()
181 // old packet pointers are invalid after calling xtdc4_read()
182 read_config.acknowledge_last_read = 1;
183
184 // structurewithpacketpointersforreaddata
185 xtdc4_read_out read_data;
186
187 // start data capture
188 status = xtdc4_start_capture(device);
189 if (status != CRONO_OK) {
190 printf("Could not start capturing %s\n",
191 xtdc4_get_last_error_message(device));
192 xtdc4_close(device);
193 return status;
194 }
195
196 // start timing generator
197 xtdc4_start_tiger(device);
198
199 // some book keeping
200 int packet_count = 0;
201
202 int64_t group_abs_time = 0;
203 int update_count = 100;
204 double binsize = get_binsize(device);
205
206 printf("Reading packets:\n");
207 // read 10000 packets
208 while (packet_count < 10000) {
209 // get pointers to acquired packets
210 status = xtdc4_read(device, &read_config, &read_data);
211 if (status != CRONO_OK) {
212 std::this_thread::sleep_for(std::chrono::milliseconds(100));
213 printf("−No data!−\n");
214 } else {
215 // iterate over all packets received with the last read
216 volatile crono_packet *p = read_data.first_packet;
217 while (p <= read_data.last_packet) {
218 // printf is slow, so this demo only processes
219 // every 1000th packet your application would of
220 // course collect every packet
221 if (packet_count % update_count == 0) {
222 group_abs_time = process_packet(
223 group_abs_time, p, update_count,
224 binsize);
225 }
226 p = crono_next_packet(p);
227 packet_count++;
228 }
229 }
230 }
231
232 // shut down packet generation and DMA transfers
233 xtdc4_stop_capture(device);
234
235 // deactivate xTDC4
236 xtdc4_close(device);
237
238 return 0;
239}