xMap#
Introduction#
Intended Audience#
This document is intended for those users who would like to interface to the XIA xMAP hardware using the Handel driver library. Users of the Handel driver library should be reasonably familiar with the C programming language and this document assumes the same.
Conventions#
Each Handel API that is discussed is linked to function. Follow the link to view the function prototype and documentation of the API’s use.
CHECK_ERROR
is a placeholder for user-defined error handling.
Sample Code#
The guide includes inline code examples to illustrate how specific
features are used. In addition to the inline code examples, three
sample applications are included with Handel: hqsg-xmap.c
,
hqsg-xmap-preset.c
and hqsg-xmap-mapping.c
. Precompiled versions of
the applications (for Windows) are also packaged with Handel.
Each application requires a Handel .ini file to be passed as a command
line argument. A sample file xmap_reset_std.ini
is included in the
distribution; it will need at least the pci_bus and pci_slot modified
to run with your particular xMAP system.
hqsg-xmap#
This application walks through all of the steps required to acquire a single MCA histogram with 5 seconds worth of data. This is the simplest example and shows how to use basic Handel from start to finish.
hqsg-xmap-preset#
Preset runs are an important component to most non-mapping xMAP applications. This example shows how to setup a preset run and poll waiting for the run to complete.
hqsg-xmap-mapping#
Mapping applications introduce new terminology and uses of Handel; this sample shows how to configure an xMAP module and collect a few buffers worth of data. For a real mapping application, the pixel advance would occur using either a GATE or SYNC input. Unfortunately, configuring GATE or SYNC would make the sample code unnecessarily complicated, in addition to requiring a valid GATE/SYNC signal be present simply to run the sample. Instead we use the manual pixel advance feature on the xMAP running in a separate thread to simulate the normal pixel advance. Please keep this caveat in mind when reading the sample code.
Understanding Handel#
Header Files#
Before introducing the details of programming with the Handel API, it is
important to discuss the relevant header files and other external
details related to Handel. All code intending to call a routine in
Handel needs to include the file inc/handel.h as a header. To gain
access to the constants used to define the various logging levels, the
file inc/md_generic.h
must be included; additional constants (preset
run types, mapping mode controls, etc.) are located in
inc/handel_constants.h
. The last header that should be included is
inc/handel_errors.h
, which contains all of the error codes returned by
Handel.
Error Handling#
A good programming practice with Handel is to compare the returned
status value with XIA_SUCCESS – defined in handel_errors.h
– and
then process any returned errors before proceeding. All Handel routines
(except for some of the debugging routines) return an integer value
indicating success or failure. Additional debug information can be
generated by setting up logging in the beginning of the application.
Thread Safety#
Handel is not thread-safe. It is the responsibility of any application
making Handel calls to ensure that only one thread is allowed to access
Handel at a time. For an example of how to protect access to Handel, see
the hqsq-xmap-mapping.c
sample code.
.ini Files#
The last required file external to the actual Handel source code is an
initialization, or “.ini” file. The .ini file defines the setup of
your system by breaking the configuration down into the following
categories: detector ([detector definitions]
), firmware
([firmware definitions]
), hardware ([module definitions]
) and
acquisition values ([default definitions]
[1]). Each category in
the .ini file contains a series of blocks, surrounded by a set of
START / END delimiters. Each block represents one instance of the
logical type associated with the category. For instance, each block in
[detector definitions]
represents a physical detector with some
number of elements. Within each block is a series of key-value pairs
used to define the configuration.
Note
XIA provides a tool as part of the ProSpect application, the Configuration Wizard, to generate an .ini file to initially start your system in ProSpect. XIA’s general recommendation is to use ProSpect to optimize your system and save an .ini file for use in your application. However, by referring to an existing Handel .ini file and the specifications in this section, you can also edit .ini files by hand or generate them in your application.
[detector definitions]#
Each block in this section is used to define one physical detector made up of 1..N channels.
Key |
Description |
---|---|
alias |
Human-readable string naming this detector. Other sections, such as
|
number_of_channels |
An integer describing the number of elements in your detector. If
this value is set to |
type |
The detector type. Handel currently supports the strings |
type_value |
For |
channel{n}_gain |
The preamplifier gain, as a double, for detector element |
channel{n}_polarity |
A string defining the polarity of detector element |
[firmware definitions]#
Each block in this section defines a reference to a single FDD file. FDD files, discussed in more detail below, are a flat file containing logical sets of firmware for the xMAP hardware.
Key |
Description |
---|---|
alias |
Human-readable string naming this FDD file. Other sections, such as
|
filename |
Valid path to the FDD file. |
fdd_tmp_path |
Valid path to a temporary directory that your application has read/write permissions on. Handel will extract individual firmware files to this directory for subsequent download to the xMAP hardware. |
[default definitions]#
All of the acquisition values for each channel are stored in this section. Handel auto-generates this section and the user is not expected to modify it.
[module definitions]#
Each xMAP module is a system gets a separate block in this section. The blocks map each xMAP channel to firmware, acquisition values and a detector channel, and specify the hardware configuration for each module.
Key |
Description |
---|---|
alias |
Human-readable string naming this module. |
module_type |
Always set to xmap. |
number_of_channels |
Always set to 4. |
interface |
Always set to pxi. |
pci_bus / pci_slot |
The PCI bus and slot that this module is connected to. To determine the bus value, you can use the Device Manager on Windows. As mentioned above, the Configuration Wizard in xManager will automatically generate an .ini with the correct bus and slot information for all of the xMAP modules attached to a computer. |
channel{n}_alias |
Specifies a global, unique index for channel |
channel{n}_detector |
Associates channel |
firmware_set_chan{n} |
Assigns an FDD file to module channel |
FDD Files#
The xMAP has 4 specialized processors that require programming after the module has been powered up. The firmware used to program these processors is collectively stored in a Firmware Definition Database (FDD) file. XIA distributes current versions of the xMAP FDD files with both Handel and xManager.
detChans#
Most routines in Handel accept a detChan
integer as the first
argument. As discussed in [module definitions]
, each xMAP channel
in the system must be assigned a unique global ID. Handel .ini files
generated by the Configuration Wizard in xManager follow the
convention of assigning 0 to the first channel in the first module and
N - 1
, where N
is the total number of channels in the system, to
the last channel in the last module. If you know the number of
channels in the system, you can treat the detChan parameter as a
0-based index. For more information on robustly querying the system,
see [Enumerating Modules and Channels] in the Handel API Manual.
Some routines in Handel – mostly routines that set a value – allow the
special detChan -1
to be passed as an argument. This special value
represents all of the detChan``s in the system and is automatically
created by Handel when it loads a new .ini file. When calling routines
like :c:func:`xiaSetAcquisitionValues` the -1 ``detChan
is a convenient
shortcut that eliminates the need to loop over all channels.
Note that not all routines accept the -1 detChan
. The primary
situation where one is required to loop over all necessary channels is
when calling xiaBoardOperation()
, specifically with “apply” and
“mapping_pixel_next”. Note though that both of these operations only
need to be called once per module using code like this [2]:
int i;
int ignored = 0;
for (i = 0; i < TOTAL_CHANNELS_IN_SYSTEM; i += 4) {
CHECK_ERROR(xiaBoardOperation(i, "apply", &ignored));
}
For more information on this please refer to Enumerating Modules and Channels.
MCA Data Acquisition#
Setting up Logging#
Handel provides a comprehensive logging and error reporting mechanism that allows an error to be traced back to a specific line of code in Handel. To utilize the logging system, a log file needs to be designated, preferably at the beginning of the application:
CHECK_ERROR(xiaSetLogLevel(MD_DEBUG));
CHECK_ERROR(xiaSetLogOutput("handel.log"));
See xiaSetLogLevel()
and xiaSetLogOutput()
.
When cleaning up your program resources, you can release the log file
handle and redirect logging to stdout by calling xiaCloseLog()
.
Initializing Handel#
Before acquiring data with Handel it is necessary to initialize the library using an .ini file:
CHECK_ERROR(xiaInit("xmap.ini"));
Once the initialization is complete, the next step is to call
xiaStartSystem()
. This is the first time that Handel attempts to
communicate with the hardware specified in the .ini file. xiaInit()
’s
job is to prepare Handel for data acquisition, while
xiaStartSystem()
’s is to prepare the hardware. xiaStartSystem()
is
one of the longest running functions in Handel, generally speaking,
since it needs to validate the hardware configuration supplied in the
.ini file, test the communication interface (PCI, for the xMAP) and
download all of the required firmware to the module(s). Like
xiaInit()
, xiaStartSystem()
is easy to use:
CHECK_ERROR(xiaStartSystem());
Once xiaStartSystem()
is complete, Handel is ready to perform data
acquisition tasks. xiaStartSystem()
only needs to be called once after
an .ini file is loaded.
Configuring Data Acquisition#
After xiaStartSystem()
has run, the xMAP system is ready to be
configured for data acquisition. If the .ini file that was loaded with
xiaInit()
contains a [default definitions]
section, then the
hardware will be configured using those values. If the
[default definitions]
section is missing, then Handel uses a nominal
set of default values. For most systems, the nominal values are
sufficient to obtain some results from the software, but some of the
settings should be optimized for actual data acquisition.
Handel provides a comprehensive set of acquisition values for
controlling the settings of each xMAP module [3]. Most normal MCA data
acquisition setups will only need to set the handful of values described
below. Once a working set of acquisition values has been created, they
can be saved to an .ini file using the function xiaSaveSystem()
.
The Handel routines to control acquisition values are
xiaSetAcquisitionValues()
and xiaGetAcquisitionValues()
.
The basic setup of an xMAP system involves optimizing the following acquisition values for your system:
peaking_time
dynamic_range
trigger_threshold
calibration_energy
As an example, we will configure the system with a peaking time of 16.0 microseconds, a calibration energy of 5900 eV (an Fe-55 source emitting Mn K-alpha x-rays), a dynamic range of 47200 eV and a trigger threshold of 1000 eV:
double pt = 16.0; /* microseconds */
double thresh = 1000.0; /* eV */
double calib = 5900.0; /* eV */
double range = 47200.0; /* eV */
CHECK_ERROR(xiaSetAcquisitionValues(-1, "peaking_time", &pt));
CHECK_ERROR(xiaSetAcquisitionValues(-1, "trigger_threshold", &thresh));
CHECK_ERROR(xiaSetAcquisitionValues(-1, "calibration_energy", &calib));
CHECK_ERROR(xiaSetAcquisitionValues(-1, "dynamic_range", &range));
Once the acquisition values are set, it is necessary to “apply” them
with xiaBoardOperation()
as discussed in the detChans section.
Run Control#
At this point, the hardware is properly configured and ready to
acquire data. In this section we are interested in starting and
stopping a normal MCA run and reading out the spectrum data. The
routines xiaStartRun()
and xiaStopRun()
control the run. Each routine
need only be called once per module on the XMAP.
The MCA can be read while the run is active and/or after it has been
stopped. The MCA histogram is returned as an array of unsigned long
integers via a call to xiaGetRunData()
[4].
xiaGetRunData()
expects an array equal to (or larger) than the
requested MCA length. The MCA length is set using the
“number_mca_channels” acquisition value and can be read as an
unsigned long
by passing the name “mca_length” to xiaGetRunData()
or as a double
from xiaGetAcquisitionValues()
. With this in mind, a
simple data acquisition session has the following basic footprint:
#include <stdlib.h>
unsigned long *mca = NULL;
unsigned long mca_length = 0;
CHECK_ERROR(xiaGetRunData(0, "mca_length", &mca_length));
mca = malloc(mca_length * sizeof(unsigned long));
if (!mca) {
/* Unable to allocate enough memory. */
}
CHECK_ERROR(xiaStartRun(-1, 0));
/* Collect as much data as you want. */
CHECK_ERROR(xiaStopRun(-1));
CHECK_ERROR(xiaGetRunData(0, "mca", mca));
/* Do something with the MCA histogram. */
free(mca);
Preset Runs#
A common data acquisition technique is to do a “preset” run with a fixed metric of either time or events. A normal MCA run is both started and stopped by the host software; a preset MCA run is started by the host and stopped by the hardware. Allowing the hardware to end the run lets the host application repeatedly acquire data with similar characteristics.
The xMAP supports four distinct preset run types: realtime, energy
livetime, events and triggers. The constants used to define these run
types are in handel_constants.h. The realtime
(XIA_PRESET_FIXED_REAL
) and energy livetime
(XIA_PRESET_FIXED_LIVE
) preset runs instruct the hardware to run
until the specified realtime/energy livetime has elapsed. These times
are specified in seconds with a granularity of 320 ns. Even though the
time granularity is 320 ns, the hardware only checks to see if enough
time has elapsed every 8 ms.
The output events (XIA_PRESET_FIXED_EVENTS
) and input events
(XIA_PRESET_FIXED_TRIGGERS
) preset runs complete when the specified
number of input or output events have been collected.
double preset_realtime = 20.0;
double preset_type = XIA_PRESET_FIXED_REAL;
int ignored = 0;
int n_channels_done;
unsigned long run_active;
CHECK_ERROR(xiaSetAcquisitionValues(-1, "preset_type", &preset_type));
CHECK_ERROR(xiaSetAcquisitionValues(-1, "preset_value", &preset_realtime));
CHECK_ERROR(xiaBoardOperation(-1, "apply", &ignored));
CHECK_ERROR(xiaStartRun(-1, 0));
do {
int i;
n_channels_done = 0;
for (i = 0; i < TOTAL_CHANNELS_IN_SYSTEM; i++) {
CHECK_ERROR(xiaGetRunData(i, "run_active", &run_active));
if ((run_active & 0x1) == 0) {
n_channels_done++;
}
}
Sleep(1);
} while (n_channels_done != TOTAL_CHANNELS_IN_SYSTEM);
CHECK_ERROR(xiaStopRun(-1));
/* Read out data here. */
SCA Settings#
The number of SCA regions can be set via the acquisition values
number_of_scas
, after which each limits can be set with acquisition
values in the format sca{n}_\[lo|hi\]
, e.g. “sca0_lo”, “sca1_hi”.
After the run has stopped, sca values from each region can be read out
from xiaGetRunData()
as run data “sca”, in an array of doubles, with
length equal to the number of predefined SCA regions.
double nSCAs = 2.0;
char scaStr[80];
double scaLowLimits[] = {0.0, 1024.0};
double scaHighLimits[] = {1023.0, 2047.0};
double SCAs[2];
/* Set the number of SCAs */
printf("-- Set SCAs\n");
status = xiaSetAcquisitionValues(-1, "number_of_scas", (void *)&nSCAs);
CHECK_ERROR(status);
/* Set the individual SCA limits */
for (i = 0; i < (int)nSCAs; i++) {
sprintf(scaStr, "sca%d_lo", i);
status = xiaSetAcquisitionValues(-1, scaStr, (void *)&(scaLowLimits[i]));
CHECK_ERROR(status);
sprintf(scaStr, "sca%d_hi", i);
status = xiaSetAcquisitionValues(-1, scaStr, (void *)&(scaHighLimits[i]));
CHECK_ERROR(status);
}
/* Read out the SCAs from the data buffer */
status = xiaGetRunData(0, "sca", (void *)SCAs);
CHECK_ERROR(status);
Cleanup#
The last operation that any xMAP application must do is call
xiaExit()
. Once xiaExit()
is called a new system must be loaded with
xiaInit()
if you want to use Handel again.
Mapping Mode#
The xMAP is designed to support high-speed mapping operations. The current version can store full spectra for each mapping pixel (MCA mode), up to 64 non-overlapping SCAs per pixel (SCA mode) or time-stamped events (List-mode). In general, the controls are the same for each mapping mode variant though different strategies may be required for performance reasons.
To better understand how mapping mode works, it is helpful to briefly describe how the xMAP’s memory is organized. In order to allow for continuous mapping data acquisition, the memory is organized into two independent banks called buffer ‘a’ and buffer ‘b’. A single bank can be read out by the host while the other is filled during data acquisition. Each memory bank is 16 bits wide and 1 Mword (2 ^20^ words) deep. For standard, non-mapping MCA acquisition, these banks are combined to form a single 32-bit x 1 Mword bank.
For continuous mapping, the host computer must be able to read out and clear an entire buffer for all of the modules in the system in less time then it takes to fill one buffer. The minimum pixel dwell time for continuous mapping operation is defined by the readout speed, the buffer clear time, the number of pixels that can be stored in one buffer and the size of the system. As an example, if the system contains 4 xMAP modules so that the total transferred data for a single buffer is 8 MB [5] and the “burst” transfer speed is 25 MB / s [6], it takes 320 ms to read out a single buffer. If that buffer holds data for 64 pixels [7], the minimum pixel dwell time that allows for continuous mapping is 320 ms / 64 pixels = 5 ms / pixel.
Pixel Advance Modes#
The xMAP supports three modes of pixel advance: GATE, SYNC and host control.
GATE#
One of the primary methods for advancing the pixel is to use the GATE input as a pixel clock, where the pixel number advances on a defined edge transition of the input signal. In MCA mode, the GATE signal is used to inhibit data acquisition or to coordinate the acquisition with an external system. Using the default gate polarity setting, if GATE is low then data acquisition is disabled. The polarity is controlled with the “input_logic_polarity” acquisition value; to disable data acquisition when the GATE is high, set it to 1.0.
For mapping mode, transitions on the GATE signal can be used to trigger a pixel advance. Like MCA mode, the default setting is to use the high-to-low transition to trigger the pixel advance and, similarly, “input_logic_polarity” can be set to 1.0 if the low-to-high transition is desired instead. Since the GATE signal requires transitions from high-to-low (or low-to-high), there is necessarily a transition time between states where data acquisition is inhibited. The default setting, controlled with the “gate_ignore” acquisition value, is to ignore the data during the transition period. However if “gate_ignore” is set to 1.0, data acquisition will remain active during the transition.
SYNC#
The other primary method of advancing the pixel is to use the SYNC input
as a pixel clock. Using this method, the pixel will advance for every
N
positive pulses, where N
is set using the “sync_count”
acquisition value. N
can range from 1 to 65535. Finally, the pulses
must be at least 40 ns wide to be recognized by the xMAP.
Host Control#
Lastly, it is possible to advance the pixel directly in Handel using the board operation “mapping_pixel_next”. Manually advancing the pixel is slower and does not provide good real-time control and, as such, is suitable only for debugging and evaluation purposes.
GATE/SYNC Signal Distribution#
A typical PXI crate backplane is broken into one or more “bus segments”. Small crates, 8 slots or less, will contain a single bus segment, while larger crates can contain as many as 3 bus segments. For GATE / SYNC pixel advance, a single module on each bus segment is designated as a “master” module. The master module accepts a GATE / SYNC logic signal via the LEMO connector on the xMAP front panel and routes the signal to the other modules on the bus segment using a line on the PXI backplane. Therefore, each bus segment must have its own master module and the input LEMO for each master module must use the same signal source.
Using Mapping Mode: A Walkthrough#
Each step is labelled with the mapping modes it supports; no label means the step is relevant to all modes.
Enable mapping mode#
double mode = 1.0;
CHECK_ERROR(xiaSetAcquisitionValues(-1, "mapping_mode", &mode));
When “mapping_mode” is set greater then 0.0, Handel downloads the proper firmware to the xMAP modules. Handel also updates the firmware with any mapping-specific acquisition values. To switch back to normal MCA data acquisition, set “mapping_mode” to 0.0. The xMAP currently supports 3 mapping modes: MCA (1.0), SCA (2.0) and List (3.0).
Set the number of bins in the spectrum (MCA mode)#
double nBins = 4096.0;
CHECK_ERROR(xiaSetAcquisitionValues(-1, "number_mca_channels", &nBins));
The number of bins in the spectrum affects the number of pixels that can fit into each buffer.
Set the number of ROIs and their bounds (SCA mode)#
double nSCAs = 2.0;
double sca0Lo = 100.0;
double sca0Hi = 1000.0;
double sca1Lo = 2000.0;
double sca1Hi = 2010.0;
CHECK_ERROR(xiaSetAcquisitionValues(-1, "number_of_scas", &nSCAs));
CHECK_ERROR(xiaSetAcquisitionValues(-1, "sca0_lo", &sca0Lo));
CHECK_ERROR(xiaSetAcquisitionValues(-1, "sca0_hi", &sca0Hi));
CHECK_ERROR(xiaSetAcquisitionValues(-1, "sca1_lo", &sca1Lo));
CHECK_ERROR(xiaSetAcquisitionValues(-1, "sca1_hi", &sca1Hi));
SCA mapping mode supports a maximum of 64, non-overlapping SCAs.
Set the variant (List-mode)#
double variant = 1.0;
CHECK_ERROR(xiaSetAcquisitionValues(-1, "list_mode_variant", &variant));
List-mode supports three variants: energy plus GATE count (0.0), energy plus SYNC count (1.0) and energy plus clock time (2.0). For a detailed description of the variants, please consult the xMAP User’s Manual.
Set the total number of pixels to be acquired in this run (MCA/SCA mode)#
double nMapPixels = 100.0;
CHECK_ERROR(xiaSetAcquisitionValues(-1, "num_map_pixels", &nMapPixels));
“num_map_pixels” can also be set to 0.0 if data acquisition should continue indefinitely.
Set the number of pixels per buffer#
double nMapPixelsPerBuffer = -1.0;
CHECK_ERROR(xiaSetAcquisitionValues(-1, "num_map_pixels_per_buffer", &nMapPixelsPerBuffer));
Setting “num_map_pixels_per_buffer” to -1.0 instructs the DSP to automatically use as many pixels as possible given either the MCA size or the number of SCAs. Of course a specific number can be passed in for “num_map_pixels_per_buffer” as well. Unfortunately getting the actually number of pixels per buffer requires an additional function call:
double actualMapPixelsPerBuffer = 0.0;
CHECK_ERROR(xiaGetAcquisitionValues(0, "num_map_pixels_per_buffer",
&actualMapPixelsPerBuffer));
If the number of mapping pixels per buffer is set larger then the maximum amount the buffer can hold, it will be truncated to the maximum value by the DSP.
Configure pixel control#
At the beginning of the run, the pixel number starts at 0 and is advanced using one of the techniques discussed in the [Pixel Advance Modes] section. The examples below only show the case where the entire system is on a single bus segment. If the xMAP system spans multiple bus segments then additional master modules are required with the correct pixel advance mode setting. Also note that the master setting is only valid for the first channel in a module; settings on all other channels will be ignored.
GATE#
#include "handel_constants.h"
double enabled = 1.0;
double pixelMode = XIA_MAPPING_CTL_GATE;
CHECK_ERROR(xiaSetAcquisitionValues(0, "gate_master", &enabled));
CHECK_ERROR(xiaSetAcquisitionValues(0, "pixel_advance_mode", &pixelMode));
SYNC#
#include "handel_constants.h"
double enabled = 1.0;
double pixelMode = XIA_MAPPING_CTL_SYNC;
double nTicksPerPixel = 100.0;
CHECK_ERROR(xiaSetAcquisitionValues(0, "sync_master", &enabled));
CHECK_ERROR(xiaSetAcquisitionValues(0, "pixel_advance_mode", &pixelMode));
CHECK_ERROR(xiaSetAcquisitionValues(0, "sync_count", &nTicksPerPixel));
HOST#
Manual pixel advance from the host is always available and does not need to be explicitly configured. To advance the pixel, use the following code:
int ignored = 0;
CHECK_ERROR(xiaBoardOperation(0, "mapping_pixel_next", &ignored));
Note that the pixel only needs to be advanced once per module. Advancing it once per channel (per module) will actually advance it 4 times (per module).
Apply the settings#
int ignored = 0;
CHECK_ERROR(xiaBoardOperation(0, "apply", &ignored));
See also the [detChans] section.
Get buffer length (MCA/SCA mode)#
After all of the settings are applied, the xMAP can be queried for the size of a returned buffer. This value can then be used to allocate the appropriate amount of memory.
unsigned long bufferLength = 0;
unsigned long *buffer = NULL;
CHECK_ERROR(xiaGetRunData(0, "buffer_len", &bufferLength));
buffer = malloc(bufferLength * sizeof(unsigned long));
if (!buffer) {
/* Out-of-memory */
}
Start the run#
CHECK_ERROR(xiaStartRun(-1, 0));
Monitor the buffer status#
Once the run is started, pixels are added to the first buffer (‘a’) until it is full. To see if the buffer is full, use the following code:
unsigned short isFull = 0;
while (isFull == 0) {
CHECK_ERROR(xiaGetRunData(0, "buffer_full_a", &isFull));
/* Sleep for a short time here using a routine like Sleep() on win32
* or usleep() on linux.
*/
}
Each module in the system should be polled to determine when all of the modules are ready to be read.
Read full buffer (MCA/SCA mode)#
/* Assumes that buffer was previously allocated. */
CHECK_ERROR(xiaGetRunData(0, "buffer_a", buffer));
Read full list-mode buffer (List-mode)#
unsigned long bufferLength = 0;
unsigned long *buffer = NULL;
CHECK_ERROR(xiaGetRunData(0, "list_buffer_len_a", &bufferLength));
buffer = malloc(bufferLength * sizeof(unsigned long));
if (!buffer) {
/* Out-of-memory */
}
CHECK_ERROR(xiaGetRunData(0, "buffer_a", buffer));
free(buffer);
Unlike MCA and SCA mapping mode, the length of the list-mode buffer varies slightly from read to read.
Signal that the read has completed#
Once a buffer is read, it is important to let the xMAP know that it is available to be filled again. Failure to do this in a timely manner can potentially cause overrun errors.
char currentBuffer = 'a';
CHECK_ERROR(xiaBoardOperation(0, "buffer_done", ¤tBuffer));
Wait for buffer ‘b’ to fill#
With buffer ‘a’ read and signaled as done, the next step is to wait for buffer ‘b’.
unsigned short isFull = 0;
while (isFull == 0) {
CHECK_ERROR(xiaGetRunData(0, "buffer_full_b", &isFull));
/* Sleep for a short time here using a routine like Sleep() on win32
* or usleep() on linux.
*/
}
Then signal “buffer_done” using the same board operation call as for the ‘a’ buffer.
Repeat until all the pixels are collected#
Continue reading, signaling complete and polling while switching between buffers ‘a’ and ‘b’. You may monitor the for run completion by manually checking the pixel number:
unsigned long nMapPixels; /* As set above. */
unsigned long currentPixel;
CHECK_ERROR(xiaGetRunData(0, "current_pixel", ¤tPixel));
if (currentPixel >= nMapPixels) {
/* The run is complete. Break the monitoring loop and continue. */
}
Or simply monitor the run status as in normal MCA data acquisition:
unsigned long runActive;
CHECK_ERROR(xiaGetRunData(0, "run_active", &runActive));
if (!runActive) {
/* The run is complete. Break the monitoring loop and continue. */
}
Stop the run#
Once all of the pixels have been collected, the run must be stopped as usual.
CHECK_ERROR(xiaStopRun(-1));
Mapping Tips#
This section describes (and reiterates) various tips and techniques to make sure that your mapping application runs smoothly.
Enabling mapping mode updates all parameters
When “mapping_mode” is enabled, all of the relevant acquisition values are downloaded to the hardware. There is no need to set these values again.
Designate one detChan per module as the “mapping channel”
Most of the acquisition values related to mapping mode are module-wide settings and do not need to be set on each channel. As an example, the Configuration Wizard in xManager uses the first detChan on each module when generating .ini files for use with Handel.
Cache the mapping buffer length
For all modes except for list-mode, the mapping buffer length, retrieved by passing “buffer_len” to
xiaGetRunData()
, only needs to be read once before the mapping run starts; it will not change once the run is active.Assign a single master module per bus segment
For GATE and SYNC pixel advance modes there needs to be exactly one master module of the appropriate type per bus segment.
Check for buffer overruns
If the per-pixel dwell time is too short for the xMAP to keep up with, it is possible to overrun one of the buffers. When the mapping buffer is overrun, the additional pixels will accumulate in the last pixel of the last active buffer. To signal that the buffer is overrun the value of the run data type “buffer_overrun” will be set to 1.0. Additional information may be retrieved from the DSP parameters
MAPERRORS
andBUFMAPERRORS
.MAPERRORS
is the total number of overruns in the current run andBUFMAPERRORS
is the number of overruns in the current buffer.In general, once a buffer overrun condition has occurred it can be problematic to reconstruct the data even though nothing has been discarded. XIA recommends treating the buffer overrun condition as an indication that the system needs some more tuning to run with the dwell time that caused the overrun.
Parsing List-mode data buffers#
The list-mode data has some additional complications not found when using MCA or SCA mapping mode. After the buffer header the stream of event data packets is found. In addition to the normal event packets, there are two special record types: end of buffer and rollover. The end of buffer record is provided to verify the buffer integrity and is inserted once at the end of the buffer [8]. The rollover record is used to indicate when the 32-bits of time/tick are exhausted in the normal event data and what the new upper words for those values are. The rollover record will appear once per channel per rollover. When parsing the buffers it is critical that the rollover records be tracked so that the event times can be properly reconstructed.
Below is a heavily annotated code sample that shows how to parse a buffer for a single module and print out the channel, time and MCA bin for each event.
/* The returned data from the xMAP is in an array of 16-bit words, but
* we often need to convert two words into a 32-bit value.
*/
#define MAKE_WORD32(x, i) (unsigned long)((x)[(i)] | \
((unsigned long)(x)[(i) + 1] << 16))
int i;
unsigned long upperTimeWords[4];
unsigned long nEventRecords;
unsigned long nSpecialRecords;
unsigned long totalRecords;
unsigned long j;
/* Assume that the variable buffer is already filled in up to bufferLen
* with the header and event data.
*/
/* Load the current upper time words from the buffer header. The
* upper time words for channel 0 begin at offset 72 in the header.
* And each channel has 12 words of data with it, so we need to increment
* by that much for each channel.
*/
for (i = 0; i < 4; i++) {
upperTimeWords[i] = MAKE_WORD32(buffer, 72 + (i * 12));
}
/* Get the number of non-special records from the buffer header. */
nEventRecords = MAKE_WORD32(buffer, 66);
/* Get the number of special records from the buffer header. */
nSpecialRecords = MAKE_WORD32(buffer, 116);
nTotalRecords = nEventRecords + nSpecialRecords;
for (j = 0; j < nTotalRecords; j++) {
unsigned short record[3];
/* Copy each event into its own record for further processing. The
* buffer header is 256 words and each record is 3 words.
*/
memcpy(&record[0], &buffer[256 + (j * 3)], 3 * sizeof(unsigned short));
if (record[0] & 0x8000 > 0) {
/* This is a special record. */
if (record[0] == 0x8000) {
/* We have hit the end of the buffer. */
break;
} else {
/* This is a rollover special record. We need to update the
* upper word for the appropriate channel. The channel that
* this rollover record describes lives in the lower 4 bits of
* the first word of the record. The remaining two words are the
* the new upper time words for that channel.
*/
int chan = record[0] & 0x000F;
upperTimeWords[chan] = MAKE_WORD32(record, 1);
}
} else {
/* Normal event record. The channel is stored in bits 13 and 14 of
* the first word and the energy bin is stored in the lower 13 bits.
*/
unsigned short chan = (record[0] & 0x6000) >> 13;
unsigned short bin = record[0] & 0x1FFF;
/* Use double and ldexp() to create a 64-bit value. Not all platforms
* support 64-bit integral types.
*/
double timestamp = ldexp((double)upperTimeWords[chan], 32) +
(double)record[1] +
ldexp((double)record[2], 16);
printf("Timestamp %0.1f, Channel %hu, Bin %hu\n", timestamp,
chan, bin);
}
}
This code is meant to serve as a rough guideline; it is missing many validation checks based on the values in the buffer header.
Acquisition Values#
This section lists the allowed names for use with
xiaGetAcquisitionValues()
and xiaSetAcquisitionValues()
.
Filter#
- peaking_time
Peaking time of the energy filter, specified in microseconds.
- trigger_threshold
Trigger filter threshold, specified in eV.
- baseline_threshold
Baseline filter threshold, specified in eV.
- energy_threshold
Energy filter threshold, specified in eV.
- gap_time
The gap time of the energy filter, reported in microseconds. Note: This acquisition value is read-only. To set the gap time, please see “minimum_gap_time”.
- trigger_peaking_time
The peaking time of the trigger filter, specified in microseconds.
- trigger_gap_time
The gap time of the trigger filter, specified in microseconds.
- baseline_average
The number of samples averaged together for the baseline.
- peak_sample_offset{N}
Sets the peak sampling time offset constant for decimation
N
, specified in microseconds. This value is optional; setting it will override the defined offset value in the FDD file.
- peak_interval_offset{N}
Sets the peak interval time offset constant for decimation
N
, specified in microseconds. This value is optional; setting it will override the pre-defined value of the offset in the FDD file.
- minimum_gap_time
Sets the minimum gap time for energy filter in microseconds. This value will be used for all decimations.
- maxwidth
Sets the maximum peak width for pile-up inspection in microseconds.
Gain#
- dynamic_range
Energy range corresponding to 40% of the total ADC range, specified in eV.
- calibration_energy
Calibration energy, specified in eV.
- adc_percent_rule
Percent of ADC used for a single step with energy equal to the specified calibration energy. This parameter is provided for backwards compatibility with .ini files generated for previous versions of Handel. XIA recommends using “calibration_energy” and “dynamic_range” to set the gain of your xMAP system.
- preamp_gain
Preamplifier gain specified in mV / keV. This value is synchronized with the gains in the
[detector definitions]
section of the .ini file.
Detector#
- detector_polarity
The input signal polarity, specified as “+”, “-”, “pos” or “neg”. Like “preamp_gain”, this value is synchronized with the appropriate entries in the .ini file.
- reset_delay
The amount of time that the processor should wait after the detector resets before processing the input signal, specified in microseconds.
MCA Data Acquisition#
- number_mca_channels
The number of bins in the MCA spectrum, specified in bins.
- mca_bin_width
Width of an individual bin in the MCA, specified in eV.
- preset_type
Set the preset run type. See handel_constants.h for the constants that can be used.
- preset_value
When a preset run type other then
XIA_PRESET_NONE
is set, this value is either the number of counts or a time (specified in seconds).
SCA Data Acquisition#
- number_of_scas
Sets the number of SCAs.
- sca{N}_{lo|hi}
The SCA limit (low or high) for the requested SCA,
N
, specified as a bin number.N
ranges from 0 to “number_of_scas” - 1.
GATE Control#
The settings in this section are applicable to both MCA and Mapping mode runs, note that gate_master must be set even for single module system for GATE signal to be used.
- gate_master
When 1.0, sets the current module as a GATE master. Only one GATE master is needed per PXI bus segment. To clear this setting, set “gate_master” to 0.0. To switch from “gate_master” to a different master, set the other master to 1.0 and “gate_master” will be automatically cleared. This setting is only valid for the first channel in a module and is ignored for other channels.
- gate_ignore
Determines if data acquisition should continue or be halted during pixel advance while GATE is asserted.
- gate_mode
Determines whether the GATE signal, when asserted, will stop the real time clock (0.0) or not (1.0).
- input_logic_polarity
Sets the polarity of the logic signal connected via the front panel LEMO to either normal (0.0) or inverted (1.0).
Mapping Mode#
- mapping_mode
Toggles between the various mapping modes by switching to the appropriate firmware and downloading the necessary acquisition values. Supported values are: 0.0 = mapping mode disabled, 1.0 = MCA mapping mode, 2.0 = SCA mapping mode, 3.0 = List mode.
- num_map_pixels
Total number of pixels to acquire in the next mapping mode run. If set to 0.0, then the mapping run will continue indefinitely.
- num_map_pixels_per_buffer
The number of pixels stored in each buffer during a mapping mode run. If the value specified is larger then the maximum number of pixels the buffer can hold, it will be rounded down to the maximum. Setting this to -1.0 will automatically set the value to the maximum allowed per buffer. The current, on-board value may be retrieved at any time using
xiaGetAcquisitionValues()
.
- sync_master
When 1.0, sets the current module as a SYNC master. Only one SYNC master is needed per PXI bus segment. To clear this setting, set “sync_master” to 0.0. To switch from “sync_master” to a different master, set the other master to 1.0 and “sync_master” will be automatically cleared. This setting is only valid for the first channel in a module and is ignored for other channels.
- sync_count
Sets the number of SYNC pulses to use for each pixel. Once “sync_count” pulses have been detected, the pixel is advanced.
- lbus_master
When 1.0, sets the current module as an LBUS master. Only one LBUS master is needed per PXI bus segment. To clear this setting, set “lbus_master” to 0.0. To switch from “lbus_master” to a different master, set the other master to 1.0 and “lbus_master” will be automatically cleared. This setting is only valid for the first channel in a module and is ignored for other channels.
- pixel_advance_mode
Sets the pixel advance mode for mapping mode data acquisition. The supported types are listed in handel_constants.h under XIA_MAPPING_CTL_*. Manual pixel advance using
xiaBoardOperation()
is always available and does not need to be set using this acquisition value.
- synchronous_run
This parameter is used in conjunction with “lbus_master” to enable a synchronous data acquisition run.
- buffer_clear_size
Specifies how much of the buffer should be cleared when the “board_done” operation is signalled in mapping mode. The default setting of 0.0 causes the entire buffer to be cleared. Any setting greater than 0.0 causes only that number of words to be cleared. XIA recommends for most applications that the default setting of 0.0 is used as miscalculating the correct length may cause data corruption.
- list_mode_variant
The list mode variant to acquire data with. The allowed variants are energy plus GATE (0.0), energy plus SYNC (1.0) and energy plus clock time (2.0).
Run Data#
This section lists the allowed names for use with xiaGetRunData()
.
The type is the type of the argument passed into the value
parameter of
xiaGetRunData()
. For scalar values, you will need to pass a pointer to the specified
type. For array values, a pointer is specified and the address of the
first element should be passed. See xiaGetRunData()
for usage examples.
Status#
Name |
Type |
Description |
---|---|---|
run_active |
unsigned long |
The current run status for the specified channel. If the value is non-zero then a run is currently active on the channel. |
MCA Data#
Name |
Type |
Description |
---|---|---|
mca_length |
unsigned long |
The current size of the MCA data buffer for the specified channel. |
mca |
unsigned long* |
The MCA data array for the specified channel. The caller is expected
to allocate an array of length “mca_length” and pass that in as the
|
module_mca |
unsigned long* |
Returns the MCA data for all 4 channels flattened into a
single array. The MCA length is required to be the same for all 4
channels in the module. The caller is expected to allocate an array
of |
baseline_length |
unsigned long |
The current size of the baseline data buffer for the specified channel. |
baseline |
unsigned long* |
The baseline data for the specified channel. The caller is expected
to allocate an array of length “baseline_length” and pass that in
as the |
Statistics#
Name |
Type |
Description |
---|---|---|
runtime |
double |
The realtime run statistic, reported in seconds. |
realtime |
double |
Alias for “runtime”. |
trigger_livetime |
double |
The livetime run statistic as measured by the trigger filter, reported in seconds. |
livetime |
double |
The calculated energy filter livetime, reported in seconds. |
input_count_rate |
double |
The measured input count rate, reported as counts / second. |
output_count_rate |
double |
The output count rate, reported as counts / second. |
module_statistics_2 |
double* |
Returns an array containing statistics for the module. The caller is
responsible for allocating enough memory for at least 36 elements
and passing it in as the [channel 0 realtime,
channel 0 trigger livetime,
channel 0 energy livetime,
channel 0 triggers,
channel 0 MCA events,
channel 0 input count rate,
channel 0 output count rate,
channel 0 underflows,
channel 0 overflows,
...
channel 3 realtime,
channel 3 trigger livetime,
...
channel 3 overflows]
|
mca_events |
double |
Returns the number of events in the MCA histogram for the specified channel. |
total_output_events |
unsigned long |
The total number of events in the current run defined as the sum of the events in the MCA, the underflow events and the overflow events. |
events_in_run |
unsigned long |
Deprecated Alias for total_output_events. |
module_statistics |
double* |
Deprecated replaced with Returns an array containing statistics for the module. The caller
is responsible for allocating enough memory for at least 28
elements and passing it in as the [channel 0 realtime,
channel 0 trigger livetime,
channel 0 energy livetime,
channel 0 triggers,
channel 0 MCA events,
channel 0 input count rate,
channel 0 output count rate,
...
channel 3 realtime,
channel 3 trigger livetime,
...
channel 3 output count rate]
|
SCA Data#
Name |
Type |
Description |
---|---|---|
max_sca_length |
unsigned short |
Maximum number of SCA elements supported by the system. |
sca_length |
unsigned short |
The number of elements in the SCA data buffer for the specified channel. |
sca |
double* |
The SCA data buffer for the specified channel. The caller is
expected to allocate an array of length “sca_length” and pass that
in as the |
Mapping Mode#
Note
The following operations require that mapping mode be enabled. If it is not, then the system will return an error.
Name |
Type |
Description |
---|---|---|
buffer_len |
unsigned long |
The size of a mapping buffer. For a given mapping run, this value will remain constant and, therefore, only needs to read once at the start of the run. |
buffer_full_a |
unsigned short |
The current status of buffer ‘a’. If the value is non-zero then the buffer is full. |
buffer_full_b |
unsigned short* |
The current status of buffer ‘b’. If the value is non-zero then the buffer is full. |
buffer_a |
unsigned long* |
The data in buffer ‘a’. The caller is expected to allocate an array
of length “buffer_len” and pass that in as the |
buffer_b |
unsigned long* |
The data in buffer ‘b’. The caller is expected to allocate an array
of length “buffer_len” and pass that in as the |
current_pixel |
unsigned long |
The current pixel number. The number is reset to 0 at the start of each new mapping run. |
buffer_overrun |
unsigned short |
If non-zero, indicates the the current buffer has overflowed. |
list_buffer_len_a |
unsigned long* |
The length of list buffer ‘a’. The length of the list buffer varies between reads. |
list_buffer_len_b |
unsigned long* |
The length of list buffer ‘b’. The length of the list buffer varies between reads. |
Board Operations#
This section lists the allowed names for use with xiaBoardOperation()
.
Data types are specified as in [Run Data]. See xiaBoardOperation()
for
usage examples.
Note
Board operations with “mapping” require that mapping mode be enabled when doing the operation. If mapping mode is not enabled an error is returned.
Name |
Type |
Mode |
Description |
---|---|---|---|
apply |
none [9] |
All |
Apply any recent acquisition value changes to the firmware. |
buffer_done |
char |
Mapping |
Signal that the specified buffer (‘a’ or ‘b’) has been read and may be used for mapping acquisition again. This operation blocks until the buffer is cleared, which takes around 25ms for a 1 Mword buffer. If you need to speed up this operation for small buffer sizes, see acquisition value “buffer_clear_size.” |
mapping_pixel_next |
none |
Mapping |
Advance to the next pixel in a mapping data acquisition run. |
buffer_switch |
none |
Mapping |
Manually force the firmware to switch to filling the other buffer. |
Footnotes