.. _CodeExamplesRef: Code Examples ============= .. _ExampleCodeTdcHistoRef: Time Histogram Pipe (stand-alone TDC) ------------------------------------- The following example demonstrates usage of a 1D time histogram for the TDC channel input **0** of a stand-alone TDC:: // requires C99 standard or later #include #include int main() { int dd = sc_tdc_init_inifile("tdc_gpx3.ini"); if (dd < 0) { // unable to initialize hardware. dd contains error code char error_description[ERRSTRLEN]; sc_get_err_msg(dd, error_description); puts(error_description); return dd; } else { // dd is device descriptor which is used in all other functions double tdc_binsize; int ret = sc_tdc_get_binsize2(dd, &tdc_binsize); if (ret < 0) { //if there is error happened. puts("could not get binsize"); return ret; } printf("tdc binsize is %lf\n", tdc_binsize); // now open a tdc_histo pipe struct sc_pipe_tdc_histo_params_t params; params.depth = BS32; // 32 bit per time channel (point) in the histogram params.channel = 0; // pipe for channel #0 is requested params.modulo = 0; // modulo is off params.binning = 4; // histogram binning is set to 4 params.offset = 1000; // histogram starts from the 1000 time bins (see sc_tdc_get_binsize2()). params.size = 2000; // histogram size is 2000 time bins (but note binning)! params.accumulation_ms = 0; // accumulation is off params.allocator_owner = NULL; // parameter for allocator cbf params.allocator_cb = NULL; // internal allocator is used int pipe_id = sc_pipe_open2(dd, TDC_HISTO, (void *)¶ms); if (pipe_id < 0) { //pipe_id contains error code char error_description[ERRSTRLEN]; sc_get_err_msg(pipe_id, error_description); puts(error_description); return pipe_id; } sc_tdc_start_measure2(dd, 1000); // start a measurement for 1000 milliseconds unsigned *tdc_histo; ret = sc_pipe_read2(dd, pipe_id, (void *)&tdc_histo, -1); //after the call // tdc_histo pointer will point to the histogram data. The buffer for the // histogram data will be allocated by an internal allocator and will be // destroyed during the next call to sc_pipe_read2. // -1 in the last argument means to wait infinitely (2^32 milliseconds). if (ret < 0) { // note that this could be timeout as well char error_description[ERRSTRLEN]; sc_get_err_msg(pipe_id, error_description); puts(error_description); return ret; } // Now, we have the data and may process it or visualize it. // For example, tdc_histo[0] is the first histogram element, // that contains the summed number of detected pulses for the // basic time bins '4000', '4001', '4002', '4003' --- since // the pipe was configured with binning 4 and offset 1000. sc_pipe_close2(dd, pipe_id); // Here, the data pipe is closed. NOTE: tdc_histo now points to an // invalid memory region, that must not be accessed anymore. Any access // to the tdc_data has to be performed before calling sc_pipe_close2(). // If necessary, make a copy before calling sc_pipe_close2(). sc_tdc_deinit2(dd); // Release hardware and resources. return 0; } } .. _ExampleCodeImagePipeRef: Image Pipe ---------- The following example demonstrates usage of a data pipe for images ( images are targeted towards delay-line detector and camera applications and do not receive data, if a *stand-alone* TDC is used):: int image2d_ex() // 2d image example. { const uint32_t size_x = 512; const uint32_t size_y = 512; // initialize the hardware and get a device descriptor int dd = sc_tdc_init_inifile("tdc_gpx3.ini"); if (dd < 0) { char error_description[ERRSTRLEN]; sc_get_err_msg(dd, error_description); printf("error! code: %d, message: %s\n", dd, error_description); return dd; } struct sc_pipe_dld_image_xy_params_t prms; memset(&prms, 0, sizeof(prms)); prms.depth = BS32; //4 bytes per pixel in the image prms.channel = -1; //all channels together prms.binning = {1, 1, 1}; prms.roi = {{0,0,0}, {size_x, size_y, -1}}; // configure an "image pipe" and store the pipe id in "pd" int pd = sc_pipe_open2(dd, DLD_IMAGE_XY, (void *)&prms); if (pd < 0) { // check for an error char error_description[ERRSTRLEN]; sc_get_err_msg(pd, error_description); printf("error! code: %d, message: %s\n", pd, error_description); sc_tdc_deinit2(dd); return pd; } int ret = sc_tdc_start_measure2(dd, 200); //start 200 ms measurement if (ret < 0) { char error_description[ERRSTRLEN]; sc_get_err_msg(ret, error_description); printf("error! code: %d, message: %s\n", ret, error_description); sc_pipe_close2(dd, pd); sc_tdc_deinit2(dd); return ret; } uint32_t *image; // The following sc_pipe_read2(...) call blocks (waits) until the measurement // is finished. The image variable is set to the image data buffer, allocated // by the library ("internal memory allocation"). The size of the image buffer // in bytes is roi_x * roi_y * 4, in this example 512 * 512 * 4 (see prms.roi // and prms.depth settings). // Deallocation happens when the next call to sc_pipe_read2(), sc_pipe_close2() // or sc_tdc_deinit2() is made. ret = sc_pipe_read2(dd, pd, (void **) &image, UINT32_MAX); if (ret < 0) { char error_description[ERRSTRLEN]; sc_get_err_msg(ret, error_description); printf("error! code: %d, message: %s\n", ret, error_description); sc_pipe_close2(dd, pd); sc_tdc_deinit2(dd); return ret; } // Here, the application can process the image data. for (size_t i=0; i > mem_chunks_; const size_t chunk_size_; public: Allocator (size_t s) :chunk_size_(s) {} static int pre_alloc(void *p, void **u) { return (static_cast (p))->alloc(u); } int alloc(void **u) { std::unique_ptr chunk(new unsigned char [chunk_size_]); memset(&(chunk[0]), 0, chunk_size_); // initialize memory to all zeros *u = &(chunk[0]); mem_chunks_.push_back(std::move(chunk)); return 0; } }; int statistics_pipe_ex() { // initialize the hardware and get device descriptor int dd = sc_tdc_init_inifile("tdc_gpx3.ini"); if (dd < 0) { char error_description[ERRSTRLEN]; sc_get_err_msg(dd, error_description); printf("error! code: %d, message: %s\n", dd, error_description); return dd; } sc_pipe_statistics_params_t prms; memset(&prms, 0, sizeof(prms)); Allocator mem(sizeof(statistics_t)); prms.allocator_owner = static_cast (&mem); prms.allocator_cb = &(mem.pre_alloc); int pd = sc_pipe_open2(dd, STATISTICS, (void *)&prms); if (pd < 0) { char error_description[ERRSTRLEN]; sc_get_err_msg(pd, error_description); printf("error! code: %d, message: %s\n", pd, error_description); sc_tdc_deinit(dd); return pd; } int ret = sc_tdc_start_measure2(dd, 200); // start 200 ms measure if (ret < 0) { char error_description[ERRSTRLEN]; sc_get_err_msg(ret, error_description); printf("error! code: %d, message: %s\n", ret, error_description); sc_pipe_close2(dd, pd); sc_tdc_deinit(dd); return ret; } statistics_t *stat; // During measurement, the scTDC library calls the Allocator::pre_alloc // function, which allocates memory to hold one instance of a statistics_t // record, saves the memory block in the list and returns it to the library. // The next function blocks until the measurement is finished. The application // gets the pointer to the statistics data from the sc_pipe_read2() function. // The memory is deallocated when the 'mem' variable is destroyed (which // happens automatically at the end of main()). If the application calls // sc_tdc_start_measure2() and sc_pipe_read2() several times, here the 'mem' // object accumulates memory chunks in the list. // An alternative implementation of the Allocator class could also choose // to just allocate a single memory buffer and return this buffer from the // allocator callback multiple times. The library modifies the buffer by // adding values to its elements, rather than overwriting elements with new // values. This results in an accumulating effect across multiple measurements // (the library does not reset the buffer to zero, so the zeroing remains // under the control of the application developer). // This works for images and histograms as well. int ret = sc_pipe_read2(dd, pd, (void *)&stat, UINT32_MAX); if (ret < 0) { char error_description[ERRSTRLEN]; sc_get_err_msg(ret, error_description); printf("error! code: %d, message: %s\n", ret, error_description); sc_pipe_close2(dd, pd); sc_tdc_deinit(dd); return ret; } // Here we can do something with the statistics data printf("counts_read[0][0] = %d\n", stat->counts_read[0][0]); sc_pipe_close2(dd, pd); sc_tdc_deinit2(dd); // Here, the statistics data is still accessible because the 'mem' object is // still on the stack. // In case of 'internal' memory allocation, the pointer to the statistics data // would be invalid at this point. return 0; } .. _ExampleCodeUserCallbacksRef: User Callbacks Pipe ------------------- The following example shows how to set up the user callbacks pipe to process TDC or DLD data in a sequence-of-events form:: #include #include #include struct sc_DeviceProperties3 sizes; /* Include an actual Semaphore implementation, such as in * https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h */ class Semaphore; Semaphore sem; void cb_start(void *p) { /* this function gets called every time a measurement starts */ } void cb_end(void *p) { /* this function gets called every time a measurement finishes */ sem.signal(); } void cb_millis(void *p) { /* this function gets called every time a millisecond has ellapsed as * tracked by the hardware */ } void cb_stat(void *p, const struct statistics_t *stat) { /* this function gets called every time statistics data is received, * usually at the end of every measurement, but before the end-of-measurement * callback */ } void cb_tdc_event (void *priv, const struct sc_TdcEvent *const event_array, size_t event_array_len) { const char *buffer = (const char *) event_array; size_t j; for (j=0; jchannel, obj->time_data ... contain information about * the j-th TDC event provided during this call. */ } } void cb_dld_event (void *priv, const struct sc_DldEvent *const event_array, size_t event_array_len) { const char *buffer = (const char *) event_array; size_t j; for (j=0; jdif1, obj->dif2, obj->sum ... contain information about * the j-th DLD event provided during this call. */ } } int main() { int dd; int ret; struct PrivData priv_data; char *buffer; struct sc_pipe_callbacks *cbs; struct sc_pipe_callback_params_t params; int pd; dd = sc_tdc_init_inifile("tdc_gpx3.ini"); if (dd < 0) { char error_description[ERRSTRLEN]; sc_get_err_msg(dd, error_description); printf("error! code: %d, message: %s\n", dd, error_description); return dd; } ret = sc_tdc_get_device_properties(dd, 3, &sizes); if (ret < 0) { char error_description[ERRSTRLEN]; sc_get_err_msg(ret, error_description); printf("error! code: %d, message: %s\n", ret, error_description); return ret; } buffer = calloc(1, sizes.user_callback_size); cbs = (struct sc_pipe_callbacks *)buffer; cbs->priv = &priv_data; cbs->start_of_measure = cb_start; cbs->end_of_measure = cb_end; cbs->millisecond_countup = cb_millis; cbs->statistics = cb_stat; cbs->tdc_event = cb_tdc_event; cbs->dld_event = cb_dld_event; params.callbacks = cbs; pd = sc_pipe_open2(dd, USER_CALLBACKS, ¶ms); if (pd < 0) { char error_description[ERRSTRLEN]; sc_get_err_msg(pd, error_description); printf("error! code: %d, message: %s\n", pd, error_description); return pd; } free(buffer); ret = sc_tdc_start_measure2(dd, 1000); if (ret < 0) { char error_description[ERRSTRLEN]; sc_get_err_msg(ret, error_description); printf("error! code: %d, message: %s\n", ret, error_description); return dd; } /* Wait until the semaphore is signalled, which happens in our callback for * the end-of-measurement event */ sem.wait(); sc_pipe_close2(dd, pd); sc_tdc_deinit2(dd); return 0; } .. include:: 06b_code_examples_cam_pipes.rst.inc