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