CAEN MCA  0.99.10
SDK for Hexagon
Software development
Precondition
You have to install the library before to start using it. See Installation page for details.

Compile your project

In order to use the library, just include the CAENMCA.h header in your project:

#include <CAENMCA.h>

To link your code with the library, the instructions depends on your operating system

Linux

By default the installer puts libraries and headers into the system folders. To use the library into your project, just add the parameter -lCAENMCA to the linker. For example:

$ gcc mytest.c -lCAENMCA -o mytest

Examples

An example make project is included in the source directory, under <sourcedir>/MCALibTest. To get started, you may try to build it:

$ cd <sourcedir>/MCALibTest
$ make -j3
$ ./bin/MCALibTest eth://127.0.0.1/

Windows

By default the installer puts DLL files into the system folders. You may find a copy also into your install directory, at <installdir>/lib. To use the library into your project, just add the following stuff into your Visual Studio project C/C++ properties:

  • Additional Include Directories: CAENMCA.h path (by default <installdir>/include)

and this into the Linker properties:

  • Additional Library Directories: CAENMCA.lib path (by default <installdir>/lib/<platform>)
  • Additional Dependencies: CAENMCA.lib

Examples

An example Visual Studio project is included in the install directory, under <installdir>\MCALibTest. To get started, you may try to copy it to your user directory, and open it in Visual Studio.

Attention
The path settings of the example project have been set relative to the installer default path C:\Program Files\CAEN\Digitizers\CAENMCA. In case you have chosen a different location, you have to adjust the Visual Studio project accordingly.

Connect a device

The connection with a device in your network is established using CAEN_MCA_OpenDevice(). The function returns an handle that can be used later to interact with the system. For example, this connects to a local instance of the Hexagon Server:

int32_t ret;
CAEN_MCA_HANDLE device = CAEN_MCA_OpenDevice("eth://localhost:56342", &ret, NULL);
// Error
fprintf(stderr, "%s(): failed. Error: '%"PRIi32"'.\n", __func__, ret);
}

Eventually, the connection should be closed by CAEN_MCA_CloseDevice(). This also free all the allocated memory:

You can connect only once per device:

Handles

From Handle article on Wikipedia:

In computer programming, a handle is an abstract reference to a resource. Handles are used when application software references blocks of memory or objects managed by another system [...]. A resource handle [...] can be a pointer that allows access to further information.

In this library, handles are pointers to instances of abstract data types. In a object-oriented programming analogy, they can be seen just as pointers to objects a certain class.

Several types of handle exist, represented by the following class diagram:

Children

The function CAEN_MCA_GetChildHandle() can be used to get handles to child objects, given a handle. The only exception is to get the library handle, when no input handle is needed as first parameter, and the index can be any number:

assert(library != NULL);

The handle of the device is returned by CAEN_MCA_OpenDevice(). Additionally, with the index returned by by the last argument of that function it is possible to get the same handle using CAEN_MCA_GetChildHandle() with the library as first argument. Anyway, the first opened device has always index 0:

assert(device != NULL);

CAEN_MCA_GetChildHandle() can be used only for children, and not for other descendants. For example, it is possible to get the channel handles from the device handle:

assert(channel_0 != NULL);
assert(channel_1 != NULL);

but not from the library handle:

assert(channel_0_library == NULL);

For parameters, a convenient function CAEN_MCA_GetChildHandleByName() is provided, which can be used with a string instead of a numeric index. It is implemented using hash tables, and the performances are almost identical. Numeric indexes of parameters exist but are not published.

For example, we can get a handle to the parameter "PARAM_CH_THRESHOLD" of the Channel 0:

CAEN_MCA_HANDLE parameter = CAEN_MCA_GetChildHandleByName(channel_0, CAEN_MCA_HANDLE_PARAMETER, "PARAM_CH_THRESHOLD");
assert(parameter != NULL);

Note that CAEN_MCA_GetChildHandleByName() can be used also for other handle types (except for CAEN_MCA_HANDLE_COLLECTION). In these cases, the index is just the literal version of the index, except for CAEN_MCA_HANDLE_DEVICE, where it represents the hostname used in CAEN_MCA_OpenDevice():

assert(CAEN_MCA_GetChildHandleByName(device, CAEN_MCA_HANDLE_CHANNEL, "0") == channel_0);
assert(CAEN_MCA_GetChildHandleByName(device, CAEN_MCA_HANDLE_CHANNEL, "1") == channel_1);
//assert(CAEN_MCA_GetChildHandleByName(NULL, CAEN_MCA_HANDLE_DEVICE, "eth://localhost") == device);

Ancestors

The function CAEN_MCA_GetAncestorHandle() can be used to get the the handle of an ancestor of the provided handle. For example, we can get the Channel 0 handle from a parameter of the Channel 0, as well as the device handle:

assert(CAEN_MCA_GetAncestorHandle(parameter, CAEN_MCA_HANDLE_CHANNEL) == channel_0);
assert(CAEN_MCA_GetAncestorHandle(parameter, CAEN_MCA_HANDLE_DEVICE) == device);

In case no ancestor of the requested type are found, the function returns NULL:

Data and Commands

Data retrieve from MCA is managed using the functions CAEN_MCA_SetData() (or its va_list version CAEN_MCA_SetDataV()) and CAEN_MCA_GetData() (or its va_list version CAEN_MCA_GetDataV()). The expected arguments are:

  • first argument is a CAEN_MCA_HANDLE on which to execute the call
  • second argument is an integer representing the requested type (see CAEN_MCA_DataType_t)
  • the third argument is a 64-bit mask, with bit-meaning depending on the selected type (see Data masks)
  • variadic arguments, one for each bit selected in the mask.

Similarly to Data related functions, Commands are managed using the function CAEN_MCA_SendCommand() (or its va_list version CAEN_MCA_SendCommandV()). The expected arguments are:

  • first argument is a CAEN_MCA_HANDLE on which to execute the call
  • second argument is an integer representing the requested type (see CAEN_MCA_CommandType_t)
  • the third argument is a 64-bit mask for input data, with bit-meaning depending on the selected type (see Command masks)
  • the forth argument is a 64-bit mask for output data, with bit-meaning depending on the selected type (see Command masks)
  • variadic arguments, one for each bit selected first in the input data mask, and then in the output data mask.

Variadic argument order

The order of the variadic arguments is specified by the order of the value of its relative mask. For example, if the mask sets the bits with mask = (MASK_A | MASK_B), where MASK_A = 0x1 and MASK_B = 0x4 then you must append the variable relative to MASK_A first, and then the one relative to MASK_B. Note that since the bitwise-OR operator is commutative, the order in which you specify the bits of the mask does not matter at all, like in this example:

int32_t ret = CAEN_MCA_GetData(
channel,
DATAMASK_CHANNELINFO_NMCSSPECTRA | // 0x2 [Mask order does not matter, being | (bitwise or) operator commutative...]
&nenergyspectra, // [Additional variadic argument order **does** matter!]
&nmcsspectra //
);

Variadic argument type

The type of the variadic arguments is specified in the mask documentation: it is the type to be used by CAEN_MCA_SetData(). In CAEN_MCA_GetData() functions, you have to pass a pointer to allocated memory of that type (except for arrays).

In this examples, the same data DATAMASK_VALUE_NUMERIC of CAEN_MCA_DATA_PARAMETER_VALUE is used both with CAEN_MCA_SetData() and CAEN_MCA_GetData(). As reported in the relative documentation, that data requires a double variable as argument. In the first case, pass just the variable as argument:

double pvalue = value;

In the second case, pass a pointer where to store the result:

double pvalue;

Examples

Handle collections

A special case of handles is represented by the collections. Collections are "transparent" elements of the class diagram tree shown in the Handle class diagram. Each object has a collection for every type of child, and every child is contained in the relative collections. For example, the handles of type CAEN_MCA_HANDLE_HVCHANNEL are contained in a special collection belonging to the handle of type CAEN_MCA_HANDLE_DEVICE:

The array containing the handles of the collection, as well as its length, can be obtained in this way:

uint32_t collection_length = 0;
CAEN_MCA_HANDLE *collection_handles = calloc(COLLECTION_MAXLEN, sizeof(*collection_handles));
int32_t ret = CAEN_MCA_GetData(
collection,
&collection_length,
collection_handles
);

Acquisition control

To start acquisition:

// Error
fprintf(stderr, "%s(): failed. Error: '%"PRIi32"'.\n", __func__, ret);
}

To stop acquisition:

// Error
fprintf(stderr, "%s(): failed. Error: '%"PRIi32"'.\n", __func__, ret);
}

where handle can be either a device handle or a channel handle.

To set an automatic stop criteria, set the following four parameters (if the parameter is set to zero, the stop criteria is disabled):

int32_t ret = CAEN_MCA_RetCode_Success;
CAEN_MCA_HANDLE parameter;
parameter = CAEN_MCA_GetChildHandleByName(channel, CAEN_MCA_HANDLE_PARAMETER, "PARAM_CH_STOP_MODE_REALTIME");
ret |= CAEN_MCA_SetData(parameter, CAEN_MCA_DATA_PARAMETER_VALUE, DATAMASK_VALUE_NUMERIC, (double)rt); // nanoseconds
parameter = CAEN_MCA_GetChildHandleByName(channel, CAEN_MCA_HANDLE_PARAMETER, "PARAM_CH_STOP_MODE_LIVETIME");
ret |= CAEN_MCA_SetData(parameter, CAEN_MCA_DATA_PARAMETER_VALUE, DATAMASK_VALUE_NUMERIC, (double)lt); // nanoseconds
parameter = CAEN_MCA_GetChildHandleByName(channel, CAEN_MCA_HANDLE_PARAMETER, "PARAM_CH_STOP_MODE_TOTAL_COUNTS");
ret |= CAEN_MCA_SetData(parameter, CAEN_MCA_DATA_PARAMETER_VALUE, DATAMASK_VALUE_NUMERIC, (double)counts_total);
parameter = CAEN_MCA_GetChildHandleByName(channel, CAEN_MCA_HANDLE_PARAMETER, "PARAM_CH_STOP_MODE_COUNTS_IN_ROI");
// Error
fprintf(stderr, "%s(): failed.\n", __func__);
}

HV control

To set status:

or, alternatively:

uint32_t value = on;

To get status:

uint32_t value;
*on = !!value;

To get extended status bitmask:

CAEN_MCA_HANDLE status_par = CAEN_MCA_GetChildHandleByName(hvchannel, CAEN_MCA_HANDLE_PARAMETER, "PARAM_HVCH_STATUS");
double dstatus;
uint16_t status = (uint16_t)dstatus;

where the meaning of each bit can be printed in this way:

fprintf(stdout, "HV_STATUSBIT_PW: %d\n", !!(status & 0x0001));
fprintf(stdout, "HV_STATUSBIT_RAMPUP: %d\n", !!(status & 0x0002));
fprintf(stdout, "HV_STATUSBIT_RAMPDOWN: %d\n", !!(status & 0x0004));
fprintf(stdout, "HV_STATUSBIT_OVCURR: %d\n", !!(status & 0x0008));
fprintf(stdout, "HV_STATUSBIT_OVVOLT: %d\n", !!(status & 0x0010));
fprintf(stdout, "HV_STATUSBIT_UNDVOLT: %d\n", !!(status & 0x0020));
fprintf(stdout, "HV_STATUSBIT_MAXV: %d\n", !!(status & 0x0040));
fprintf(stdout, "HV_STATUSBIT_MAXI: %d\n", !!(status & 0x0080));
fprintf(stdout, "HV_STATUSBIT_TEMPWARN: %d\n", !!(status & 0x0100));
fprintf(stdout, "HV_STATUSBIT_OVTEMP: %d\n", !!(status & 0x0200));
fprintf(stdout, "HV_STATUSBIT_DISABLE: %d\n", !!(status & 0x0400));
fprintf(stdout, "HV_STATUSBIT_CALIBERR: %d\n", !!(status & 0x0800)); // bit 11 is "WARMUP" on Hexagon / Red Eagle
fprintf(stdout, "HV_STATUSBIT_RESET: %d\n", !!(status & 0x1000));
fprintf(stdout, "HV_STATUSBIT_GOINGOFF: %d\n", !!(status & 0x2000));

To get the active range:

CAEN_MCA_HANDLE active_range_par = CAEN_MCA_GetChildHandleByName(hvchannel, CAEN_MCA_HANDLE_PARAMETER, "PARAM_HVCH_ACTIVE_RANGE");
char* pvalue = malloc(PARAMINFO_NAME_MAXLEN * sizeof(*pvalue));
int32_t ret = CAEN_MCA_GetData(active_range_par, CAEN_MCA_DATA_PARAMETER_VALUE, DATAMASK_VALUE_CODENAME, pvalue);
free(pvalue);

Once we have the active range, monitor parameters like VMon can be read:

CAEN_MCA_HANDLE vmon_par = CAEN_MCA_GetChildHandleByName(active_range, CAEN_MCA_HANDLE_PARAMETER, "PARAM_HVRANGE_VMON");
double vmon;

Note that monitor parameters can be accessed only from the currently active range. To get information about the other ranges, get the handle of a range:

then, use CAEN_MCA_DATA_HVRANGE_INFO:

double vset_min, vset_max, vset_incr, vmax_max;
int32_t ret = CAEN_MCA_GetData(
hvrange,
&vset_min,
&vset_max,
&vset_incr,
&vmax_max);

Parameters

Parameters are values that can be set at runtime to change the behaviour and the state of the device. The full list of the supported parameters, grouped by handle, can be found at Supported parameters.

The list of supported parameter of a given handle can be retrieved also at runtime:

uint32_t collection_length = 0;
CAEN_MCA_HANDLE *collection_handles = calloc(COLLECTION_MAXLEN, sizeof(*collection_handles));
int32_t ret = CAEN_MCA_GetData(
collection,
&collection_length,
collection_handles
);
// Error
fprintf(stderr, "%s(): failed. Error: '%"PRIi32"'.\n", __func__, ret);
}
else {
// Print results
fprintf(stdout, "Number of parameters found: %"PRIu32"\n", collection_length);
for (uint32_t i = 0; i < collection_length; i++) {
int32_t type;
int32_t index;
char *name = malloc(HANDLE_NAME_MAXLEN * sizeof(*name));
collection_handles[i],
&type,
&index,
name
);
// Error
fprintf(stderr, "%s(): failed. Error: '%"PRIi32"'.\n", __func__, ret);
}
else {
fprintf(stdout, "%"PRIu32": %s (type: %"PRIi32", index: %"PRIi32")\n", i, name, type, index);
}
free(name);
}
}

To get a parameter handle given its codename:

To get extended parameter information:

char *parameter_name = calloc(PARAMINFO_NAME_MAXLEN, sizeof(*parameter_name));
char *parameter_codename = calloc(PARAMINFO_NAME_MAXLEN, sizeof(*parameter_codename));
uint32_t infobits;
char *uom_name = calloc(PARAMINFO_NAME_MAXLEN, sizeof(*uom_name));
char *uom_codename = calloc(PARAMINFO_NAME_MAXLEN, sizeof(*uom_codename));
int32_t uom_power;
double min, max, incr;
uint32_t nallowed_values;
double *allowed_values = calloc(PARAMINFO_LIST_MAXLEN, sizeof(*allowed_values));
char **allowed_value_codenames = calloc(PARAMINFO_LIST_MAXLEN, sizeof(*allowed_values));
char **allowed_value_names = calloc(PARAMINFO_LIST_MAXLEN, sizeof(*allowed_values));
for (int32_t i = 0; i < PARAMINFO_LIST_MAXLEN; i++) {
if (allowed_value_codenames != NULL) allowed_value_codenames[i] = calloc(PARAMINFO_NAME_MAXLEN, sizeof(char));
if (allowed_value_names != NULL) allowed_value_names[i] = calloc(PARAMINFO_NAME_MAXLEN, sizeof(char));
}
int32_t ret = CAEN_MCA_GetData(
parameter,
parameter_name,
parameter_codename,
&infobits,
uom_name,
uom_codename,
&uom_power,
&type,
&min,
&max,
&incr,
&nallowed_values,
allowed_values,
allowed_value_codenames,
allowed_value_names
);
// Error
fprintf(stderr, "%s(): failed. Error: '%"PRIi32"'.\n", __func__, ret);
}
else {
fprintf(stdout, "Parameter: %s (%s)\n", parameter_codename, parameter_name);
switch (type) {
fprintf(stdout, "\tMin: %f max: %f incr: %f\n", min, max, incr);
break;
for (uint32_t i = 0; i < nallowed_values; i++)
fprintf(stdout, "\tAllowed value: %s [%s] (%f)\n", allowed_value_codenames[i], allowed_value_names[i], allowed_values[i]);
break;
default:
fprintf(stderr, "\tUnknown parameter type.\n");
}
fprintf(stdout, "\tUnit of measurement: %s (power: %"PRIi32")\n", uom_name, uom_power);
}
for (int32_t i = 0; i < PARAMINFO_LIST_MAXLEN; i++) {
if (allowed_value_names != NULL) free(allowed_value_names[i]);
if (allowed_value_codenames != NULL) free(allowed_value_codenames[i]);
}
free(allowed_value_names);
free(allowed_value_codenames);
free(allowed_values);
free(uom_codename);
free(uom_name);
free(parameter_codename);
free(parameter_name);

To get the value of parameters with type CAEN_MCA_PARAMETER_TYPE_RANGE:

double pvalue;

To set the value of parameters with type CAEN_MCA_PARAMETER_TYPE_RANGE:

double pvalue = value;

To get the value of parameters with type CAEN_MCA_PARAMETER_TYPE_LIST:

char *pvalue = malloc(PARAMINFO_NAME_MAXLEN * sizeof(*pvalue));
int32_t ret = CAEN_MCA_GetData(parameter, CAEN_MCA_DATA_PARAMETER_VALUE, DATAMASK_VALUE_CODENAME, pvalue); // No reference here!
Note
You do not have to pass a pointer to an array, but just the array!

To set the value of parameters with type CAEN_MCA_PARAMETER_TYPE_LIST:

const char *pvalue = value;

Save, load and delete configurations

Save the current configuration:

const char *name = savename;
int32_t ret;
if (name == NULL)
// If no name is provided, the default name is a SQL CURRENT_TIMESTAMP with format "YYYY-MM-DD HH:MM:SS"
// Note: no more than a save per second allowed!
else
// Error
fprintf(stderr, "%s(): failed. Error: '%"PRIi32"'.\n", __func__, ret);
}

Load a saved configuration:

const char *name = savename;
int32_t ret;
if (name == NULL)
// If no name is provided, the most recent configuration is loaded
else
// Error
fprintf(stderr, "%s(): failed. Error: '%"PRIi32"'.\n", __func__, ret);
}

Delete a saved configuration:

const char *name = savename;
int32_t ret;
if (name == NULL)
// If no name is provided, all the saved configurations will be deleted
else
// Error
fprintf(stderr, "%s(): failed. Error: '%"PRIi32"'.\n", __func__, ret);
}

Change configurations database path:

char *path = pathname;
int32_t ret;
if (path == NULL) // null path not allowed
else {
// Error
fprintf(stderr, "%s(): failed setting database path. Error: '%"PRIi32"'.\n", __func__, ret);
}
else {
// Get path back to check successful set
strcpy(path, "");
// Error
fprintf(stderr, "%s(): failed getting database path. Error: '%"PRIi32"'.\n", __func__, ret);
}
}
}

NOTE: the change is not persistent, you need to set path every time the library is reloaded. The previous database is left unchanged and not copied to the new location.

Registers

Read the value of a register:

uint32_t data;
int32_t ret = CAEN_MCA_SendCommand(
device,
address,
&data);

Energy Spectra and ROIs

Get a spectrum handle:

Get an energy spectrum:

int32_t ret = CAEN_MCA_RetCode_Success;
// Initialize variables
double numofbins;
uint64_t realtime;
uint64_t livetime;
uint64_t deadtime;
uint32_t overflows, underflows;
uint64_t nentries;
uint32_t nrois;
uint32_t autosaveperiod;
uint32_t stabilizer_status;
char *filename = malloc(ENERGYSPECTRUM_FULLPATH_MAXLEN * sizeof(*filename));
uint32_t *dataarray = malloc(ENERGYSPECTRUM_MAXLEN * sizeof(*dataarray));
char *sample_id = malloc(SAMPLEID_MAXLEN * sizeof(*sample_id));
char *Sdatetime = malloc(DATETIME_MAXLEN * sizeof(*Sdatetime));
char* sdatetime = malloc(DATETIME_MAXLEN * sizeof(*sdatetime));
// Get data
spectrum,
dataarray,
&realtime,
&livetime,
&deadtime,
&overflows,
&underflows,
&nentries,
&nrois,
filename,
&autosaveperiod,
&stabilizer_status,
sample_id,
Sdatetime,
sdatetime
);
// Get number of bins as parameter
CAEN_MCA_HANDLE *parameter = CAEN_MCA_GetChildHandleByName(spectrum, CAEN_MCA_HANDLE_PARAMETER, "PARAM_ENERGY_SPECTRUM_NBINS");
// Error
fprintf(stderr, "%s(): failed. Error: '%"PRIi32"'.\n", __func__, ret);
}

Set the number of bins:

CAEN_MCA_HANDLE parameter = CAEN_MCA_GetChildHandleByName(spectrum, CAEN_MCA_HANDLE_PARAMETER, "PARAM_ENERGY_SPECTRUM_NBINS");
int32_t ret = CAEN_MCA_SetData(parameter, CAEN_MCA_DATA_PARAMETER_VALUE, DATAMASK_VALUE_NUMERIC, (double)nbins);

Spectra are saved at stop and periodically during the run. Set the auto-save period (the default is 5 seconds):

Set the remote filename where to save che spectrum:

Get a ROI handle:

Get the entries in a ROI:

uint64_t roi_nentries;
int32_t ret = CAEN_MCA_GetData(roi, CAEN_MCA_DATA_ROI, DATAMASK_ROI_NENTRIES, &roi_nentries);

Set range of a ROI:

Clear a spectrum and its ROIs:

The gain stabilization can be enabled for the energy spectrum in order to compensate for possible temperature variation effects.

Enable/Disable the gain stabilizer:

Set the ROI used by the gain stabilizer (this ROI defines the stabilization peak):

CAEN_MCA_HANDLE parameter = CAEN_MCA_GetChildHandleByName(spectrum, CAEN_MCA_HANDLE_PARAMETER, "PARAM_GAIN_STABILIZER_ROIL");
int32_t ret = CAEN_MCA_SetData(parameter, CAEN_MCA_DATA_PARAMETER_VALUE, DATAMASK_VALUE_NUMERIC, (double)roil);
CAEN_MCA_HANDLE parameter = CAEN_MCA_GetChildHandleByName(spectrum, CAEN_MCA_HANDLE_PARAMETER, "PARAM_GAIN_STABILIZER_ROIH");
int32_t ret = CAEN_MCA_SetData(parameter, CAEN_MCA_DATA_PARAMETER_VALUE, DATAMASK_VALUE_NUMERIC, (double)roih);

Set the update time for the gain stabilizer:

CAEN_MCA_HANDLE parameter = CAEN_MCA_GetChildHandleByName(spectrum, CAEN_MCA_HANDLE_PARAMETER, "PARAM_GAIN_STABILIZER_TIME");
int32_t ret = CAEN_MCA_SetData(parameter, CAEN_MCA_DATA_PARAMETER_VALUE, DATAMASK_VALUE_NUMERIC, (double)time);

Reset the gain stabilizer:

Enable and get waveforms

Enable waveforms:

char *value = calloc(PARAMINFO_NAME_MAXLEN, sizeof(*value));
if (enabled)
strncpy(value, "TRUE", PARAMINFO_NAME_MAXLEN - 1);
else
strncpy(value, "FALSE", PARAMINFO_NAME_MAXLEN - 1);
// Error
fprintf(stderr, "%s(): failed. Error: '%"PRIi32"'.\n", __func__, ret);
}
free(value);

Get waveforms:

int32_t ret = CAEN_MCA_RetCode_Success;
// To get the number of sample, we need the record length and the sample period
uint32_t length = 0;
// Get record length (in ns)
double rl;
// Get sample period (in ps)
uint32_t tsample_ps;
// Get waveforms
channel,
atrace1,
atrace2,
dtrace1,
dtrace2
);
// Error
fprintf(stderr, "%s(): failed. Error: '%"PRIi32"'.\n", __func__, ret);
}
else {
length = (uint32_t)rl * 1000 / tsample_ps;
}

MCS Spectra

Get a MCS spectrum handle:

Get a MCS spectrum:

int32_t ret = CAEN_MCA_RetCode_Success;
// Initialize variables
double nchannels;
uint32_t *dataarray = malloc(MCSSPECTRUM_MAXLEN * sizeof(*dataarray));
uint8_t spe_status;
uint32_t pass_number;
// Get number of bins as parameter
CAEN_MCA_HANDLE *parameter = CAEN_MCA_GetChildHandleByName(mcs_spectrum, CAEN_MCA_HANDLE_PARAMETER, "PARAM_MCS_CHANNELS");
// Get mcs data
ret |= CAEN_MCA_GetData(mcs_spectrum,
dataarray,
&spe_status,
&pass_number
);
// Error
fprintf(stderr, "%s(): failed. Error: '%"PRIi32"'.\n", __func__, ret);
}
else {
if (data != NULL && nbins != NULL) {
*nbins = (uint32_t)nchannels;
memcpy(data, dataarray, *nbins * sizeof(*data));
*status = (CAEN_MCA_MCS_Spectrum_Status_t)spe_status;
*pass = pass_number;
}
fprintf(stdout, "Number of bins: %"PRIu32"\n", (uint32_t)nchannels);
}
free(dataarray);

Set the number of bins:

CAEN_MCA_HANDLE parameter = CAEN_MCA_GetChildHandleByName(mcs_spectrum, CAEN_MCA_HANDLE_PARAMETER, "PARAM_MCS_CHANNELS");
int32_t ret = CAEN_MCA_SetData(parameter, CAEN_MCA_DATA_PARAMETER_VALUE, DATAMASK_VALUE_NUMERIC, (double)nbins);

Set the dwell time:

CAEN_MCA_HANDLE parameter = CAEN_MCA_GetChildHandleByName(mcs_spectrum, CAEN_MCA_HANDLE_PARAMETER, "PARAM_MCS_DWELL_TIME");
int32_t ret = CAEN_MCA_SetData(parameter, CAEN_MCA_DATA_PARAMETER_VALUE, DATAMASK_VALUE_NUMERIC, (double)value);

Set the MCS counting mode (MCS on ICR, MCS on Ext.Signal, MCS on SCA):

CAEN_MCA_HANDLE parameter = CAEN_MCA_GetChildHandleByName(mcs_spectrum, CAEN_MCA_HANDLE_PARAMETER, "PARAM_MCS_MODE");
int32_t ret = CAEN_MCA_SetData(parameter, CAEN_MCA_DATA_PARAMETER_VALUE, DATAMASK_VALUE_NUMERIC, (double)mode);

Set the MCS acquisition mode: MCS REPLACE (replace bin contents after a sweep), MCS SUM (sum bin contents after a sweep), MCS REPLACE AND SUM (replace bin contents after first sweep, then sum)

CAEN_MCA_HANDLE parameter = CAEN_MCA_GetChildHandleByName(mcs_spectrum, CAEN_MCA_HANDLE_PARAMETER, "PARAM_MCS_ACQ_MODE");
int32_t ret = CAEN_MCA_SetData(parameter, CAEN_MCA_DATA_PARAMETER_VALUE, DATAMASK_VALUE_NUMERIC, (double)mode);

Enable/Disable hardware and software Dwell

CAEN_MCA_HANDLE parameter = CAEN_MCA_GetChildHandleByName(mcs_spectrum, CAEN_MCA_HANDLE_PARAMETER, "PARAM_MCS_SW_DWELL_ENABLE");
CAEN_MCA_HANDLE parameter = CAEN_MCA_GetChildHandleByName(mcs_spectrum, CAEN_MCA_HANDLE_PARAMETER, "PARAM_MCS_HW_DWELL_ENABLE");

Enable/Disable hardware and software Sweep

CAEN_MCA_HANDLE parameter = CAEN_MCA_GetChildHandleByName(mcs_spectrum, CAEN_MCA_HANDLE_PARAMETER, "PARAM_MCS_SW_SWEEP_ENABLE");
CAEN_MCA_HANDLE parameter = CAEN_MCA_GetChildHandleByName(mcs_spectrum, CAEN_MCA_HANDLE_PARAMETER, "PARAM_MCS_HW_SWEEP_ENABLE");

Clear a MCS spectrum:

Send a Sweep command to a MCS spectrum:

DT Spectra

Introduction

This tool can be used to find the best values for the coincidence parameters (coincidence window length and coincidence delay), in order to configure the board channels to work in coincidence/anticoincidence mode. When the DT spectrum is enabled, it is filled with the time difference DT between the timestamps of the events collected by the board channels. The plot is always centered in DT = 0 and its range is given by [-(Nbins/2 * DTstep), Nbins/2 * DTstep], where Nbins is the number of bins and DTstep is the time value corresponding to a single bin.

Set the number of DT spectrum bins:

CAEN_MCA_HANDLE parameter = CAEN_MCA_GetChildHandleByName(dt_spectrum, CAEN_MCA_HANDLE_PARAMETER, "PARAM_DT_SPECTRUM_NBINS");
int32_t ret = CAEN_MCA_SetData(parameter, CAEN_MCA_DATA_PARAMETER_VALUE, DATAMASK_VALUE_NUMERIC, (double)nbins);

Set the time interval for the DT spectrum:

CAEN_MCA_HANDLE parameter = CAEN_MCA_GetChildHandleByName(dt_spectrum, CAEN_MCA_HANDLE_PARAMETER, "PARAM_DT_SPECTRUM_STEP");
int32_t ret = CAEN_MCA_SetData(parameter, CAEN_MCA_DATA_PARAMETER_VALUE, DATAMASK_VALUE_NUMERIC, (double)step);

Before enabling the DT spectrum, the user should set the reference channel for DT calculations:

Enable the coincidence mode for both channels and set a big value for the coincidence window length:

uint32_t nchannels;
CAEN_MCA_HANDLE channel, coinc_win, neigh_coinc, coinc_mode;
fprintf(stderr, "%s(): failed. Error: '%"PRIi32"'.\n", __func__, ret);
return ret;
}
for (uint32_t ch = 0; ch < nchannels; ch++) {
assert(channel != NULL);
neigh_coinc = CAEN_MCA_GetChildHandleByName(channel, CAEN_MCA_HANDLE_PARAMETER, "PARAM_CH_COINC_NEIGHTRG_ENABLE");
assert(neigh_coinc != NULL);
fprintf(stderr, "%s(): failed. Error: '%"PRIi32"'.\n", __func__, ret);
return ret;
}
coinc_mode = CAEN_MCA_GetChildHandleByName(channel, CAEN_MCA_HANDLE_PARAMETER, "PARAM_CH_COINC_NEIGHTRG_MODE");
assert(coinc_mode != NULL);
fprintf(stderr, "%s(): failed. Error: '%"PRIi32"'.\n", __func__, ret);
return ret;
}
coinc_win = CAEN_MCA_GetChildHandleByName(channel, CAEN_MCA_HANDLE_PARAMETER, "PARAM_CH_COINCWIN_LEN");
assert(coinc_win != NULL);
double length = 165000.;
ret = CAEN_MCA_SetData(coinc_win, CAEN_MCA_DATA_PARAMETER_VALUE, DATAMASK_VALUE_NUMERIC, length);//window length = 165 ms
fprintf(stderr, "%s(): failed. Error: '%"PRIi32"'.\n", __func__, ret);
return ret;
}
}
}
uint32_t enable_dt = (uint32_t)enable;

How to read the DT spectrum from the board:

uint64_t *dataarray = calloc(1, DTSPECTRUM_MAXLEN * sizeof(*dataarray));
uint64_t ndata;
int32_t ret = CAEN_MCA_GetData(
dt_spectrum,
dataarray,
&ndata);

Clear the DT spectrum:

List mode

Introduction

This feature can be used to get a list of the events, or save it to a remote file. An event is made of:

Three buffers (one per each type of data) are allocated on acquisition start with size DATAMASK_LIST_MAXNEVTS. When the buffers have been filled, behaviour depends on the selected mode:

A reset occurs as soon as you call CAEN_MCA_DATA_LIST_MODE on CAEN_MCA_GetData() to get any of:

At stop, events in the buffer are written to file (if in file mode) and then freed.

A new acquisition will overwrite the existing file if the filename is not changed.

Control

Enable or disable list mode:

int32_t ret = CAEN_MCA_SetData(channel, CAEN_MCA_DATA_LIST_MODE, DATAMASK_LIST_ENABLE, (uint32_t)enabled);

Set the save mode:

CAEN_MCA_ListSaveMode_t savemode = mode;

In case of save mode set to CAEN_MCA_SAVEMODE_FILE_ASCII or CAEN_MCA_SAVEMODE_FILE_BINARY, this set the data to save into the remote file:

uint32_t mask = 0;
if (timetag) mask |= LIST_FILE_DATAMASK_TIMETAG;
if (energy) mask |= LIST_FILE_DATAMASK_ENERGY;
if (extras) mask |= LIST_FILE_DATAMASK_FLAGS;

Set the maximum number of events to store in the server internal buffers:

This is an example to get and parse lists:

uint32_t maxnevts;
uint32_t nevts;
uint32_t getfake, getsat, getskim;
uint32_t enabled;
uint32_t datamask;
char *filename = malloc(LISTS_FULLPATH_MAXLEN * sizeof(*filename));
uint64_t *datatimetag = malloc(LISTS_DATA_MAXLEN * sizeof(*datatimetag));
uint16_t *dataenergy = malloc(LISTS_DATA_MAXLEN * sizeof(*dataenergy));
uint32_t *dataflags = malloc(LISTS_DATA_MAXLEN * sizeof(*dataflags));
int32_t ret = CAEN_MCA_GetData(
channel,
&enabled,
&savemode,
filename,
&datamask,
&getfake,
&maxnevts,
&nevts,
datatimetag,
dataenergy,
dataflags,
&getsat,
&getskim
);
// Error
fprintf(stderr, "%s(): failed. Error: '%"PRIi32"'.\n", __func__, ret);
}
else {
fprintf(stdout, "Enabled: %"PRIu32"\n", enabled);
fprintf(stdout, "Save mode: ");
switch (savemode) {
case CAEN_MCA_SAVEMODE_FILE_ASCII: fprintf(stdout, "File (ASCII)\n"); break;
case CAEN_MCA_SAVEMODE_FILE_BINARY: fprintf(stdout, "File (binary)\n"); break;
case CAEN_MCA_SAVEMODE_MEMORY: fprintf(stdout, "Memory\n"); break;
}
fprintf(stdout, "Get fake events: %s\n", getfake ? "true" : "false");
bool timetag = datamask & LIST_FILE_DATAMASK_TIMETAG;
bool energy = datamask & LIST_FILE_DATAMASK_ENERGY;
bool extras = datamask & LIST_FILE_DATAMASK_FLAGS;
fprintf(stdout, "Data mask (for file modes): ");
fprintf(stdout, "Timetag (%s)\t", timetag ? "true" : "false");
fprintf(stdout, "Energy (%s)\t", energy ? "true" : "false");
fprintf(stdout, "Extras (%s)\t", extras ? "true" : "false");
fprintf(stdout, "\n");
fprintf(stdout, "Filename: %s\n", filename);
fprintf(stdout, "Events: %"PRIu32" (max: %"PRIu32")\n", nevts, maxnevts);
fprintf(stdout, "First 10 events received:\n");
for (uint32_t i = 0; i < nevts && i < 10; i++) {
fprintf(stdout, "\t#%"PRIu32":", i);
fprintf(stdout, "\tTimetag: %"PRIu64"", datatimetag[i]);
fprintf(stdout, "\tEnergy: %"PRIu32"", dataenergy[i]);
fprintf(stdout, "\tFlags: 0x%08"PRIx16"", dataflags[i]);
fprintf(stdout, "\n");
}
}
free(datatimetag);
free(dataenergy);
free(dataflags);
free(filename);

Discover devices on the newtork

Find Hexagon devices on your network, a special command::CAEN_MCA_DATA_DISCOVEREDDEVICES on CAEN_MCA_GetData() is provided:

int32_t ret = CAEN_MCA_RetCode_Success;
// Get the handle to the Library.
// Set the timeout to 3000 ms
uint64_t timeout_ms = 3000;
library,
timeout_ms
);
// Search and get the discovered devices. The function will return after 3000 ms
uint32_t cnt_found = 0;
char **names = malloc(DISCOVERY_LIST_MAXLEN * sizeof(*names));
uint32_t *serial_numbers = malloc(DISCOVERY_LIST_MAXLEN * sizeof(*serial_numbers));
char **ip_addresses = malloc(DISCOVERY_LIST_MAXLEN * sizeof(*ip_addresses));
uint32_t *input_channels = malloc(DISCOVERY_LIST_MAXLEN * sizeof(*input_channels));
char **model_names = malloc(DISCOVERY_LIST_MAXLEN * sizeof(*model_names));
char **paths = malloc(DISCOVERY_LIST_MAXLEN * sizeof(*paths));
for (int32_t i = 0; i < DISCOVERY_LIST_MAXLEN; i++) {
if (names != NULL) names[i] = calloc(DISCOVERY_NAME_MAXLEN, sizeof(*names[i]));
if (ip_addresses != NULL) ip_addresses[i] = calloc(DISCOVERY_IP_MAXLEN, sizeof(*ip_addresses[i]));
if (model_names != NULL) model_names[i] = calloc(DISCOVERY_IP_MAXLEN, sizeof(*ip_addresses[i]));
if (paths != NULL) paths[i] = calloc(DISCOVERY_IP_MAXLEN, sizeof(*ip_addresses[i]));
}
library,
&cnt_found,
names,
serial_numbers,
ip_addresses,
input_channels,
model_names,
paths
);
// Error
fprintf(stderr, "%s(): failed. Error: '%"PRIi32"'.\n", __func__, ret);
}
else {
fprintf(stdout, "Number of devices found: %"PRIu32"\n", cnt_found);
for (uint32_t i = 0; i < cnt_found; i++) {
// Use your results here
fprintf(stdout, "%"PRIu32": ", i);
fprintf(stdout, "Name: %s\t", names[i]);
fprintf(stdout, "Model name: %s\t", model_names[i]);
fprintf(stdout, "Serial number: %"PRIu32"\t", serial_numbers[i]);
fprintf(stdout, "Input channels: %"PRIu32"\t", input_channels[i]);
fprintf(stdout, "IP address: %s\t", ip_addresses[i]);
fprintf(stdout, "Path: %s\t", paths[i]);
fprintf(stdout, "\n");
}
}
// Free data
for (int32_t i = 0; i < DISCOVERY_LIST_MAXLEN; i++) {
if (names != NULL) free(names[i]);
if (ip_addresses != NULL) free(ip_addresses[i]);
if (model_names != NULL) free(model_names[i]);
if (paths != NULL) free(paths[i]);
}
free(names);
free(ip_addresses);
free(model_names);
free(paths);