This is a driver for the Pilatus pixel array detectors Dectris. It inherits from ADDriver and implements many of the parameters in ADStdDriverParams.h. It also implements a number of parameters that are specific to the Pilatus detectors.
The interface to the detector is via a TCP/IP socket interface to the camserver server that Dectris provides. The camserver program must be started before the areaDetector software is started, typically by running the camonly script provided by Dectris.
The camserver program saves the data to disk as TIFF files. The areaDetector software reads these disk files in order to read the data, because camserver does not provide another mechanism to access the data.
The following table describes how the Pilatus driver implements some of the standard driver parameters.
Parameter Definitions in pilatusDetector.cpp and EPICS Record Definitions in pilatus.template | ||
Enum name | EPICS record name | Description |
---|---|---|
ADTriggerMode | $(P$(R)TriggerMode |
The driver redefines the choices for the ADTriggerMode parameter (record $(P)$(R)TriggerMode)
from ADStdDriverParams.h. The choices for the Pilatus are:
Exposure ,
ExtEnable , ExtTrigger , and ExtMTrigger respectively.
Alignment mode uses the Exposure command as well, but continuously
takes images into the same temporary file (alignment.tif ).
|
ADExposureTime | $(P$(R)ExposureTime | In External Enable mode this value is not used by camserver. However, it should be set larger than the maximum time exposure time from the external source, so that pilatusROI.st can estimate how long to wait for the data files to be created before timing out. |
ADNumImages | $(P$(R)NumImages | Controls the number of images to acquire. It applies in all trigger modes except Alignment. |
ADExposurePeriod | $(P$(R)ExposurePeriod | Controls the exposure period in seconds. It applies only in Internal or External Trigger modes when NumImages > 1. |
ADNumExposures | $(P$(R)NumExposures | Controls the number of exposures per image. It is only used in External Enable mode. |
ADAquire | $(P$(R)Acquire | Controls the acquisition. Setting this to 1 starts image acquisition. The driver sets the record to 0 when acquisition is complete. This means an entire acquisition series if NImages > 1. Setting this to 0 aborts an acquisition. If the driver was currently acquiring imges then this record will cause the "Stop" and "K" (Kill) commands to be sent to camserver. |
ADFilePath | $(P$(R)FilePath | Controls the path for saving images. It must be a valid path for camserver and for the areaDetector driver, which is normally running in an EPICS IOC. If camserver and the EPICS IOC are not running on the same machine then soft links will typically be used to make the paths look identical. |
ADFileFormat | $(P)$(R)FileFormat |
camserver uses the file extension to determine what format to save the files in.
The areaDetector Pilatus driver only supports TIFF files, so the extension should
be .tif. When saving multiple images (NImages>1) camserver has its own rules for
creating the names of the individual files. The rules are as follows. The name constructed
using the algorithm described for ADFileTemplate under
File Saving Parameters in ADStdDriverParams is used as a basename. The following
examples show the interpretation of the basename.
Basename Files produced test6.tif test6_00000.tif, test6_00001.tif, ... test6_.tif test6_00000.tif, test6_00001.tif, ... test6_000.tif test6_000.tif, test6_001.tif, ... test6_014.tif test6_014.tif, test6_015.tif, ... test6_0008.tif test6_0008.tif, test6_0009.tif, ... test6_2_0035.tif test6_2_0035.tif, test6_2_0036.tif, ...The numbers following the last '_' are taken as a format template, and as a start value. The minimum format is 3; there is no maximum; the default is 5. The format is also constrained by the requested number of images. |
It is useful to use NDPluginROI to define an ROI containing the entire Pilatus detector. The MaxValue_RBV PV in this ROI can be monitored to make sure that the 20-bit limit of 1,048,575 is not being approached in any pixel.
The Pilatus driver implements the following parameters in addition to those in ADStdDriverParams.h:.
Note that to reduce the width of this table the enum names have been split into
2 lines, but these are just a single name, for example PilatusDelayTime
.
Parameter Definitions in pilatusDetector.cpp and EPICS Record Definitions in pilatus.template | ||||||
Enum name | asyn interface | Access | Description | drvUser string | EPICS record name | EPICS record type |
---|---|---|---|---|---|---|
Pilatus DelayTime |
asynFloat64 | r/w | Delay in seconds between the external trigger and the start of image acquisition. It only applies in External Trigger mode | DELAY_TIME | $(P)$(R)DelayTime | ao |
Pilatus Threshold |
asynFloat64 | r/w | Threshold energy in keV | THRESHOLD | $(P)$(R)ThresholdEnergy | ao |
N/A | N/A | r/w |
Gain menu. Controls the value of Vrf, which determines the shaping time and gain
of the input amplifiers. The allowed values are:
|
N/A | $(P)$(R)GainMenu | mbbo |
Pilatus Armed |
asynInt32 | r/o | Flag to indicate when the Pilatus is ready to accept external trigger signals (0=not ready, 1=ready). This should be used by clients to indicate when it is OK to start sending trigger pulses to the Pilatus. If pulses are send before Armed=1 then the Pilatus may miss them, leading to DMA timeout errors from camserver | ARMED | $(P)$(R)Armed | bi |
Pilatus TiffTimeout |
asynFloat64 | r/w | Timeout in seconds when reading a TIFF file. It should be set to several seconds, because there can be delays for various reasons. One reason is that there is sometimes a delay between when an External Enable acquisition is started and when the first external pulse occurs. Another is that it can take some time for camserver processes to finish writing the files. | TIFF_TIMEOUT | $(P)$(R)ReadTiffTimeout | ao |
Pilatus BadPixelFile |
asynOctet | r/w |
Name of a file to be used to replace bad pixels. If this record does not point to
a valid bad pixel file then no bad pixel mapping is performed. The bad pixel map
is used before making the NDArray callbacks. It does not modify the data in the
files that camserver writes. This is a simple ASCII file with the following format:
badX1,badY1 replacementX1,replacementY1 badX2,badY2 replacementX2,replacementY2 ...The X and Y coordinates range from 0 to NXPixels-1 and NYPixels-1. Up to 100 bad pixels can be defined. The bad pixel mapping simply replaces the bad pixels with another pixel's value. It does not do any averaging. It is felt that this is sufficient for the purpose for which this driver was written, namely fast on-line viewing of ROIs and image data. More sophisticated algorithms can be used for offline analysis of the image files themselves. The following is an example bad pixel file for a GSECARS detector: 263,3 262,3 264,3 266,3 263,3 266,3 300,85 299,85 300,86 299,86 471,129 472,129 |
BAD_PIXEL_FILE | $(P)$(R)BadPixelFile | waveform |
Pilatus NumBadPixels |
asynInt32 | r/o | The number of bad pixels defined in the bad pixel file. Useful for seeing if the bad pixel file was read correctly. | NUM_BAD_PIXELS | $(P)$(R)NumBadPixels | longin |
Pilatus FlatFieldFile |
asynOctet | r/w |
Name of a file to be used to correct for the flat field. If this record does not
point to a valid flat field file then no flat field correction is performed. The
flat field file is simply a TIFF file collected by the Pilatus that is used to correct
for spatial non-uniformity in the response of the detector. It should be collected
with a spatially uniform intensity on the detector at roughly the same energy as
the measurements being corrected. When the flat field file is read, the average
pixel value (averageFlatField) is computed using all pixels with intensities > PilatusMinFlatField.
All pixels with intensity < PilatusMinFlatField in the flat field are replaced with
averageFlatField. When images are collected before the NDArray callbacks are performed
the following per-pixel correction is applied:
ImageData[i] = (averageFlatField * ImageData[i])/flatField[i]; |
FLAT_FIELD_FILE | $(P)$(R)FlatFieldFile | waveform |
Pilatus MinFlatField |
asynInt32 | r/w | The mimimum valid intensity in the flat field. This value must be set > 0 to prevent divide by 0 errors. If the flat field was collected with some pixels having very low intensity then this value can be used to replace those pixels with the average response. | MIN_FLAT_FIELD | $(P)$(R)MinFlatField | longout |
Pilatus FlatFieldValid |
asynInt32 | r/o | This record indicates if a valid flat field file has been read. 0=No, 1=Yes. | FLAT_FIELD_VALID | $(P)$(R)FlatFieldValid | bi |
N/A | N/A | N/A |
asyn record to control debugging communication with camserver. Setting the CNCT
field in this record to Disconnect causes the drvAsynIPPort server
to disconnect from camserver. This can be used to allow another program, such as
TVX, to temporarily take control of camserver, without restarting the EPICS IOC.
Set CNCT to Connect to reconnect the IOC to camserver, or simply process
any record which communicates with camserver, because the driver will automatically
reconnect. |
N/A | $(P)$(R)CamserverAsyn | asyn |
The Pilatus driver is created with the following command, either from C/C++ or from the EPICS IOC shell.
pilatusDetectorConfig(const char *portName, const char *camserverPort, int maxSizeX, int maxSizeY, int maxBuffers, size_t maxMemory);
Argument | Description |
---|---|
portName |
The name of the asyn port for this detector. |
camserverPort |
The name of the asyn TCP/IP port to communicate with camserver. This must have been
previously created with drvAsynIPPortConfig() ,
|
maxSizeX |
The number of pixels in the X direction on the detector. This is 487 for the Pilatus 100K. |
maxSizeY |
The number of pixels in the Y direction on the detector. This is 195 for the Pilatus 100K. |
maxBuffers |
Maximum number of buffers to be created for plugin callbacks. Passed to the constructor for the ADDriver base class. |
maxMemory |
Maximum number of bytes of memory to be allocated for plugin callbacks. Passed to the constructor for the ADDriver base class. |
The following is an example st.cmd startup script:
< envPaths errlogInit(20000) dbLoadDatabase("$(AREA_DETECTOR)/dbd/pilatusDetectorApp.dbd") pilatusDetectorApp_registerRecordDeviceDriver(pdbbase) ### # Create the asyn port to talk to the Pilatus on port 41234. drvAsynIPPortConfigure("camserver","gse-pilatus2:41234") # Set the input and output terminators. asynOctetSetInputEos("camserver", 0, "\030") asynOctetSetOutputEos("camserver", 0, "\n") pilatusDetectorConfig("Pil", "camserver", 487, 195, 50, 200000000) dbLoadRecords("$(AREA_DETECTOR)/ADApp/Db/ADBase.template", "P=13PIL1:,R=cam1:,PORT=Pil,ADDR=0,TIMEOUT=1") dbLoadRecords("$(AREA_DETECTOR)/ADApp/Db/pilatus.template","P=13PIL1:,R=cam1:,PORT=Pil,ADDR=0,TIMEOUT=1,CAMSERVER_PORT=camserver") # Create a standard arrays plugin drvNDStdArraysConfigure("PilImage", 5, 0, "Pil", 0, -1) dbLoadRecords("$(AREA_DETECTOR)/ADApp/Db/NDPluginBase.template","P=13PIL1:,R=image1:,PORT=PilImage,ADDR=0,TIMEOUT=1,NDARRAY_PORT=Pil,NDARRAY_ADDR=0") dbLoadRecords("$(AREA_DETECTOR)/ADApp/Db/NDStdArrays.template", "P=13PIL1:,R=image1:,PORT=PilImage,ADDR=0,TIMEOUT=1,SIZE=32,FTVL=LONG,NELEMENTS=94965") # Create an ROI plugin with 8 ROIs drvNDROIConfigure("PilROI", 5, 0, "Pil", 0, 8, -1) dbLoadRecords("$(AREA_DETECTOR)/ADApp/Db/NDPluginBase.template","P=13PIL1:,R=ROI1:, PORT=PilROI,ADDR=0,TIMEOUT=1,NDARRAY_PORT=Pil,NDARRAY_ADDR=0") dbLoadRecords("$(AREA_DETECTOR)/ADApp/Db/NDROI.template", "P=13PIL1:,R=ROI1:, PORT=PilROI,ADDR=0,TIMEOUT=1") dbLoadRecords("$(AREA_DETECTOR)/ADApp/Db/NDROIN.template", "P=13PIL1:,R=ROI1:0:,PORT=PilROI,ADDR=0,TIMEOUT=1,HIST_SIZE=256") dbLoadRecords("$(AREA_DETECTOR)/ADApp/Db/NDROIN.template", "P=13PIL1:,R=ROI1:1:,PORT=PilROI,ADDR=1,TIMEOUT=1,HIST_SIZE=256") dbLoadRecords("$(AREA_DETECTOR)/ADApp/Db/NDROIN.template", "P=13PIL1:,R=ROI1:2:,PORT=PilROI,ADDR=2,TIMEOUT=1,HIST_SIZE=256") dbLoadRecords("$(AREA_DETECTOR)/ADApp/Db/NDROIN.template", "P=13PIL1:,R=ROI1:3:,PORT=PilROI,ADDR=3,TIMEOUT=1,HIST_SIZE=256") dbLoadRecords("$(AREA_DETECTOR)/ADApp/Db/NDROIN.template", "P=13PIL1:,R=ROI1:4:,PORT=PilROI,ADDR=3,TIMEOUT=1,HIST_SIZE=256") dbLoadRecords("$(AREA_DETECTOR)/ADApp/Db/NDROIN.template", "P=13PIL1:,R=ROI1:5:,PORT=PilROI,ADDR=3,TIMEOUT=1,HIST_SIZE=256") dbLoadRecords("$(AREA_DETECTOR)/ADApp/Db/NDROIN.template", "P=13PIL1:,R=ROI1:6:,PORT=PilROI,ADDR=3,TIMEOUT=1,HIST_SIZE=256") dbLoadRecords("$(AREA_DETECTOR)/ADApp/Db/NDROIN.template", "P=13PIL1:,R=ROI1:7:,PORT=PilROI,ADDR=3,TIMEOUT=1,HIST_SIZE=256") # Create "fastSweep" drivers for the MCA record to do on-the-fly scanning of ROI data initFastSweep("PilSweepTotal", "PilROI", 8, 2048, "TOTAL_ARRAY", "CALLBACK_PERIOD") initFastSweep("PilSweepNet", "PilROI", 8, 2048, "NET_ARRAY", "CALLBACK_PERIOD") # Load MCA records for the fast sweep drivers dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=13PIL1:,M=ROI1:0:TotalArray,DTYP=asynMCA,NCHAN=2048,INP=@asyn(PilSweepTotal 0)") dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=13PIL1:,M=ROI1:1:TotalArray,DTYP=asynMCA,NCHAN=2048,INP=@asyn(PilSweepTotal 1)") dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=13PIL1:,M=ROI1:2:TotalArray,DTYP=asynMCA,NCHAN=2048,INP=@asyn(PilSweepTotal 2)") dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=13PIL1:,M=ROI1:3:TotalArray,DTYP=asynMCA,NCHAN=2048,INP=@asyn(PilSweepTotal 3)") dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=13PIL1:,M=ROI1:4:TotalArray,DTYP=asynMCA,NCHAN=2048,INP=@asyn(PilSweepTotal 4)") dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=13PIL1:,M=ROI1:5:TotalArray,DTYP=asynMCA,NCHAN=2048,INP=@asyn(PilSweepTotal 5)") dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=13PIL1:,M=ROI1:6:TotalArray,DTYP=asynMCA,NCHAN=2048,INP=@asyn(PilSweepTotal 6)") dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=13PIL1:,M=ROI1:7:TotalArray,DTYP=asynMCA,NCHAN=2048,INP=@asyn(PilSweepTotal 7)") dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=13PIL1:,M=ROI1:0:NetArray,DTYP=asynMCA,NCHAN=2048,INP=@asyn(PilSweepNet 0)") dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=13PIL1:,M=ROI1:1:NetArray,DTYP=asynMCA,NCHAN=2048,INP=@asyn(PilSweepNet 1)") dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=13PIL1:,M=ROI1:2:NetArray,DTYP=asynMCA,NCHAN=2048,INP=@asyn(PilSweepNet 2)") dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=13PIL1:,M=ROI1:3:NetArray,DTYP=asynMCA,NCHAN=2048,INP=@asyn(PilSweepNet 3)") dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=13PIL1:,M=ROI1:4:NetArray,DTYP=asynMCA,NCHAN=2048,INP=@asyn(PilSweepNet 4)") dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=13PIL1:,M=ROI1:5:NetArray,DTYP=asynMCA,NCHAN=2048,INP=@asyn(PilSweepNet 5)") dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=13PIL1:,M=ROI1:6:NetArray,DTYP=asynMCA,NCHAN=2048,INP=@asyn(PilSweepNet 6)") dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=13PIL1:,M=ROI1:7:NetArray,DTYP=asynMCA,NCHAN=2048,INP=@asyn(PilSweepNet 7)") #asynSetTraceMask("Pil",0,255) #asynSetTraceMask("PilROI",0,3) #asynSetTraceIOMask("PilROI",0,4) # Load scan records for scanning energy threshold dbLoadRecords("$(SSCAN)/sscanApp/Db/scan.db", "P=13PIL1:cam1:,MAXPTS1=2000,MAXPTS2=200,MAXPTS3=20,MAXPTS4=10,MAXPTSH=10") set_requestfile_path("./") set_savefile_path("./autosave") set_requestfile_path("$(AREA_DETECTOR)/ADApp/Db") set_requestfile_path("$(SSCAN)/sscanApp/Db") set_pass0_restoreFile("auto_settings.sav") set_pass1_restoreFile("auto_settings.sav") save_restoreSet_status_prefix("13PIL1:") dbLoadRecords("$(AUTOSAVE)/asApp/Db/save_restoreStatus.db", "P=13PIL1:") iocInit() # save things every thirty seconds create_monitor_set("auto_settings.req", 30,"P=13PIL1:,D=cam1:")
The following show the MEDM screens that are used to control the Pilatus debtector. Note that the general purpose screen ADBase.adl can be used, but it exposes many controls that are not applicable to the Pilatus.
pilatusDetector.adl
is the main screen used to control the Pilatus
driver. All records except those for ROIs are accessed through this screen.
NDROI8.adl
is used to define the ROIs, and to display the statistics
for each ROI. In this example there are 3 valid ROIs defined. ROI 0 is the entire
detector, ROI 1 is a 100x50 rectangle starting at [300,60], and ROI 2 is a 50x30
rectangle starting at [320,70].
mca.adl
can be used to plot the net or total counts in an ROI when
NImages>1. In this example the plot is the net counts in ROI 1 as the diffractometer
chi was scanned +- 1 degree with 1000 points at .02 seconds/point. This was done
with the SPEC command
lup chi -1 1 1000 .02
using trajectory scanning on a Newport kappa diffractometer. This was a compound motor scan with the Newport XPS putting out pulses every .02 seconds. These pulses triggered the Pilatus in External Enable mode. The Pilatus driver read each TIFF file as it was created and updated this plot every 0.2 seconds. The total time to collect this scan with 1000 images was 20 seconds.
scan_more.adl
is used to define a scan. In this example the sscan record
is set up to scan the ThresholdEnergy PV and to collect the total counts in ROI2,
which was defined to include the entire detector.
scanDetPlot.adl
is used to plot the results of a scan after it is complete.
In this example the total counts in ROI 2 are plotted as a function of the ThresholdEnergy
as it was scanned from 3000 to 10000 eV in 250 eV steps. The source was Fe55, and
the cut-off is at 6 keV, as expected for the Mn Ka and Mn Kb x-rays that this source
produces.
asynRecord.adl
is used to control the debugging information printed
by the asyn TCP/IP driver (asynTraceIODriver) and the SNL program (asynTraceIODevice).
asynOctet.adl
can be used to send any command to camserver and display
the response. It can be loaded from the More menu in asynRecord.adl above.
At the GSECARS beamlines (13-ID-C and 13-BM-C) at the APS we use SPEC to control our Newport diffractometers. We have added and modified SPEC macros to use the pilatusDetector areaDetector driver to treat the Pilatus detector as a SPEC counter. This works in both traditional step-scanning mode, as well as in trajectory scanning mode. Here are some snippets from the SPEC macros for the Pilatus. We can supply the source files on request.
# need some more globals (kludge) global PILATUS_ROI_PV global PILATUS_imgPATH_PV global PILATUS_FNAME_PV global PILATUS_FILENUMBER_PV global PILATUS_FILEFORMAT_PV global PILATUS_EXPSRTM_PV global PILATUS_NFRAME_PV global PILATUS_EXPPRD_PV global PILATUS_NEXPFRM_PV global PILATUS_ACQ_PV global PILATUS_ACQMODE_PV ############################################################### def _setup_img '{ local j, str # PILATUS_PREFIX should be detector aquisition pv (GSE-PILATUS1:) if ( PILATUS_PREFIX == "") PILATUS_PREFIX = "GSE-PILATUS1:" PILATUS_PREFIX = getval("Enter PILATUS pv prefix",PILATUS_PREFIX) # rois pvs PILATUS_ROI_PV = PILATUS_PREFIX "ROI1NetCounts" PILATUS_imgPATH_PV = PILATUS_PREFIX "FilePath" PILATUS_FNAME_PV = PILATUS_PREFIX "Filename" PILATUS_FILENUMBER_PV = PILATUS_PREFIX "FileNumber" PILATUS_FILEFORMAT_PV = PILATUS_PREFIX "FileFormat" PILATUS_EXPSRTM_PV = PILATUS_PREFIX "ExposureTime" PILATUS_NFRAME_PV = PILATUS_PREFIX "NImages" PILATUS_EXPPRD_PV = PILATUS_PREFIX "ExposurePeriod" PILATUS_NEXPFRM_PV = PILATUS_PREFIX "NExposures" PILATUS_ACQ_PV = PILATUS_PREFIX "Acquire" PILATUS_ACQMODE_PV = PILATUS_PREFIX "AcquireMode" ... def epics_pilatus_count '{ ... # write to data base fields # Need to convert path from string to byte array # Note: we use the "wait" parameter in epics_put here (new to spec5.7.02) so that # it uses ca_put_callback, to know that all PVs have been processed # before we start counting. Use 1 second timeout, will actually be # much faster than this unless something is wrong. array _temp[256] _temp = PILATUS_IMAGE_DIR # Do not change path for now #epics_put(PILATUS_imgPATH_PV,_temp, 1) epics_put(PILATUS_FNAME_PV,img_fname, 1) epics_put(PILATUS_FILENUMBER_PV,NPTS, 1) epics_put(PILATUS_FILEFORMAT_PV,_fileformat, 1) epics_put(sc_prtm_pv,cnt_time_val, 1) epics_put(PILATUS_EXPSRTM_PV,cnt_time_val, 1) epics_put(PILATUS_ACQMODE_PV,0, 1) # Internal trigger epics_put(PILATUS_NFRAME_PV, 1, 1) epics_put(PILATUS_NEXPFRM_PV, 1, 1) def user_getcounts '{ local pv_roi, j, pv ... # using image_count routine } else if ( EPICS_COUNT == 4 ) { S[iroi] = 0 S[iroi] = epics_get(PILATUS_ROI_PV)
The following measurements were done to demonstrate the performance that can be obtained with the areaDetector Pilatus driver.
lup chi -2 2 1000 .015This tells SPEC to do a relative scan of the chi axis from -2 degrees to +2 degrees with 1000 points at .015 seconds/point. On our kappa diffractometer this entails a coordinated motion of the phi, kappa and omega axes. The EPICS trajectory scanning software downloads the non-linear trajectory that SPEC computes into the XPS controller, which executes it. As the motors are moving the XPS outputs synchronization pulses at the period of the collection time, .015 seconds in this case. These pulses are stretched (see Hardware notes below) and used as the external input to the Pilatus. The time to execute this scan should be 15.0 seconds. The actual time was 16.3 seconds, measured using camonitor on the Acquire PV. Again, this includes the time for camserver to save all 1000 images to disk (366 MB), and for pilatusROI to read each file, correct the bad pixels and flat field, compute the ROIs, and post the ROIs to EPICS. It also posted the images to EPICS at 1Hz (15 images total). The total additional time was less than 1.3 seconds for all 1000 images. As soon as the acquisition was complete SPEC plotted the net counts in the first ROI (containing the Bragg peak) as follows:
For comparison this identical scan was executed in traditional step-scanning mode, where the motors stopped at each point in the scan. The Pilatus was run in Internal mode with NImages=1. The total time for the scan was 870 seconds (more than 14 minutes), compared to 16.3 seconds in trajectory mode. Most of this overhead is the settling time for the motors, with only a small fraction due to the Pilatus single-exposure mode. The trajectory scanning mode is thus more than 50 times faster to execute the identical SPEC scan.
The Pilatus supports 3 types of external triggering. In External Trigger mode (the camserver ExtTrigger command) the Pilatus uses the programmed values of ExposureTime, ExposurePeriod, NImages and NExposures. It waits for a single external trigger, then waits for Delay seconds and then collects the entire sequence. It is very similar to Internal mode with NImages>1, except that it waits for a trigger to begin collecting the sequence.
In External Enable mode (the camserver ExtEnable command) the Pilatus uses the external signal to control acquisition. Only NImages and NExposures are used, ExposureTime and ExposurePeriod are not used. When the signal is high the detector counts, and on the transition to low it begins its readout.
In External MultiTrigger Mode (the camserver ExtMTrigger command) the Pilatus uses the programmed ExposureTime, in addition to NImages and NExposures. Each external trigger pulse causes the Pilatus to collect one image at the programmed exposure time. This mode works well with a trigger source like the Newport motor controllers or the SIS380x multichannel scaler, that put out a short trigger pulse for each image. One only needs to take care that the time between external trigger pulses is at least 4msec longer than the programmed exposure time, to allow time for the detector to read out before the next trigger pulse arrives.
When using the External Enable mode, we use an inexpensive analog pulse generator to convert the trigger pulses from the MM4005 and XPS to a form suitable for External Enable mode with the Pilatus. This is the solution we have developed that seems to be reliable:
The Tenma TGP110 seems to be currently called a Tenma 72-6860, and lists for about $350 new at Newark.
When we were initially testing the Pilatus in the lab, we had many errors in External Enable mode, where it did not seem to be seeing the external pulses. camserver would get DMA timeouts, and need to be restarted. Dectris said these were happening because the cables on our detector are longer than normal, and the voltage drop from the power supply to the detector was leading to marginal voltage values. They suggested shortening the cables or increasing the supply voltage slightly. When moving the detector to the hutch these problems initially went away. However, they then recurred, and we fixed the problem by increasing the power supply voltage from 4.4 to 4.7 volts at the detector.
Dectris has since informed me that they have increased the power supply voltage on all new Pilatus systems, so this should no longer be an issue.
The following are some current restrictions of the pilatusROI SNL program: