areaDetector MarCCD driver

November 12, 2008

Mark Rivers

University of Chicago

Table of Contents

Introduction

This is a driver for the MarCCD detectors from Rayonix/MarUSA. 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 MarCCD detectors.

The interface to the detector is via a TCP/IP socket interface to the marccd_server_socket server that MarUSA provides. The marccd_server_socket program must be started before the areaDetector software is started, by running the marccd program and executing Acquire/Remote Control/Start.

The marccd program saves the data to disk as TIFF files. The areaDetector software reads these disk files in order to read the data, because marccd does not provide another mechanism to access the data.

Implementation of standard driver parameters

The following table describes how the MarCCD driver implements some of the standard driver parameters.

Parameter Definitions in marccd.cpp and EPICS Record Definitions in marccd.template
Enum name EPICS record name Description
ADFrameType $(P$(R)FrameType The driver redefines the choices for the ADFrameType parameter (record $(P)$(R)FrameType) from ADStdDriverParams.h. The choices for the MarCCD are:
  • Normal (corrected data frame without double correlatino)
  • Background (background frame with 0 exposure time, double correlation to remove zingers)
  • Raw (data frame without correction for background or spatial distortion)
  • Multiple External Trigger (high to low transition on external signal triggers a single acquisition for the programmed exposure time)
  • DblCorrelation (two images each collected for half the nominal acquisition time, zingers removed by double correlation)
ADNumImages $(P$(R)NumImages Controls the number of images to acquire when ADImageMode is ADImageMultiple.
ADAcquirePeriod $(P$(R)AcquirePeriod Controls the period between images when ADImageMode is ADImageMultiple or ADImageContinuous. If this is greater than the acquisition time plus readout overhead then the driver will wait until the period has elapsed before starting the next acquisition.
ADFilePath $(P$(R)FilePath Controls the path for saving images. It must be a valid path for marccd and for the areaDetector driver, which is normally running in an EPICS IOC. If marccd 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 marccd only supports TIFF files.

It is useful to use NDPluginROI to define an ROI containing the entire marccd detector. The MaxValue_RBV PV in this ROI can be monitored to make sure that the 16-bit limit of 65,535 is not being approached in any pixel.

MarCCD specific parameters

The MarCCD 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 marccd.cpp and EPICS Record Definitions in marccd.template
Enum name asyn interface Access Description drvUser string EPICS record name EPICS record type
Status parameters
marCCD
State
asynInt32 r/o State word returned by marccd server. The low-order 4-bits of this word are the state of the marccd server, and will be Idle (0x0), Error (0x7), or Busy (0x8). The next 20 bits encode the state of the 5 server tasks (Acquire, Readout, Correct, Save, Dezinger) with 4-bits per task. Each task can be in the state Idle (0x0), Queued (0x1), Executing (0x2), Error (0x4), or Reserved (0x8). MAR_STATE $(P)$(R)MarState_RBV longin
marCCD
Status
asynInt32 r/o Status of the marccd server task (Idle, Error, or Busy) MAR_STATUS $(P)$(R)MarStatus_RBV mbbi
marCCDTask
AcquireStatus
asynInt32 r/o Status of the marccd server acquire task (Idle, Queued, Executing, Error, or Reserved) MAR_ACQUIRE_STATUS $(P)$(R)MarAcquireStatus_RBV mbbi
marCCDTask
ReadoutStatus
asynInt32 r/o Status of the marccd server readout task (Idle, Queued, Executing, Error, or Reserved) MAR_READOUT_STATUS $(P)$(R)MarReadoutStatus_RBV mbbi
marCCDTask
CorrectStatus
asynInt32 r/o Status of the marccd server correct task (Idle, Queued, Executing, Error, or Reserved) MAR_CORRECT_STATUS $(P)$(R)MarCorrectStatus_RBV mbbi
marCCDTask
WritingStatus
asynInt32 r/o Status of the marccd server file writing task (Idle, Queued, Executing, Error, or Reserved) MAR_WRITING_STATUS $(P)$(R)MarWritingStatus_RBV mbbi
marCCDTask
DezingerStatus
asynInt32 r/o Status of the marccd server dezinger task (Idle, Queued, Executing, Error, or Reserved) MAR_DEZINGER_STATUS $(P)$(R)MarDezingerStatus_RBV mbbi
marCCD
ReadStatus
asynInt32 r/w Writing 1 to this parameter causes the status to be read from the marccd server. By processing or periodically scanning this record the status information can be refreshed. This is normally not necessary, but if ADArrayCallbacks is 0 and marCCDOverlap is 1 then the status will not indicate that the system is idle when acquisition is complete, because the driver polling stops before the file is written. This record can be used to eliminate the confusion that might cause. MAR_READ_STATUS $(P)$(R)MarReadStatus bo
Optimization parameters
marCCD
Overlap
asynInt32 r/w The marccd server has 5 tasks (Acquire, Readout, Correct, Write, Dezinger) that can overlap their operation. The areaDetector driver can exploit this to improve performance in some circumstances. If this parameter is set to 1 (Overlap) then the ADAcquire parameter will go to 0 (Done) when the Readout task is done executing, but before the Correct and Write tasks have finished correcting and saving the file to disk. This improves performance because the next image can begin as soon as Acquire goes to done, and hence before the previous image is written to disk. Note, however that this parameter must be set to 0 (Sequential) if callbacks are being used to compute ROIs that are being used in data collection, e.g. in a scan. If this is not done then the ROI information will be read before it is updated and incorrect scan data will result. MAR_OVERLAP $(P)$(R)OverlapMode
$(P)$(R)OverlapMode_RBV
bo
bi
Frameshift parameters
marCCD
Frameshift
asynInt32 r/w marccd can be used for time-resolved studies by collecting multiple data sets before reading out the detector. This is done by placing a mask in front of the detector that restricts the x-rays to horizontal stripe. An exposure is made, and then an external signal causes the detector to shift the image by the number of lines given by this parameter. A number of images separated by times of a few milliseconds can be collected, and then the detector is read out. MAR_FRAME_SHIFT $(P)$(R)FrameShift
$(P)$(R)FrameShift_RBV
longout
longin
Timeout parameters
marCCD
TiffTimeout
asynFloat64 r/w Timeout in seconds when reading a TIFF file. It should be set to several seconds, because there it can take some time for the marccd server to write the file. MAR_TIFF_TIMEOUT $(P)$(R)ReadTiffTimeout ao
Ancillary parameters. These parameters are written to the header of the marccd TIFF file.
marCCD
DetectorDistance
asynFloat64 r/w Distance from the sample to the detector (mm) MAR_DETECTOR_DISTANCE $(P)$(R)DetectorDistance ao
marCCD
BeamX
asynFloat64 r/w X position of the direct beam on the detector (mm) MAR_BEAM_X $(P)$(R)BeamX ao
marCCD
BeamY
asynFloat64 r/w Y position of the direct beam on the detector (mm) MAR_BEAM_Y $(P)$(R)BeamY ao
marCCD
StartPhi
asynFloat64 r/w Starting value of phi rotation (deg) MAR_START_PHI $(P)$(R)StartPhi ao
marCCD
RotationAxis
asynOctet r/w Rotation axis being used (phi, omega, etc.) MAR_ROTATION_AXIS $(P)$(R)RotationAxis ao
marCCD
RotationRange
asynFloat64 r/w Rotation range of the rotation axis. MAR_ROTATION_RANGE $(P)$(R)RotationRange ao
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

Configuration

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:")

MEDM screens

The following show the MEDM screens that are used to control the MarCCD debtector. Note that the general purpose screen ADBase.adl can be used, but it exposes many controls that are not applicable to the MarCCD, and lacks some fields that are important for the MarCCD.

marccd.adl is the main screen used to control the MarCCD driver.

marccd.adl

marccd.png

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].

NDROI8.adl

NDROI8.png

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.

mca.adl

pilatusMCA.png

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.

scan_more.adl

pilatusROI_scan_more.png

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.

scanDetPlot.adl

pilatus_scan_plot.png

asynRecord.adl is used to control the debugging information printed by the asyn TCP/IP driver (asynTraceIODriver) and the SNL program (asynTraceIODevice).

asynRecord.adl

pilatusAsynRecord.png

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.

asynOctet.adl

pilatusAsynOctet.png

SPEC interface

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)

Performance measurements

The following measurements were done to demonstrate the performance that can be obtained with the areaDetector Pilatus driver.

  1. AcquireMode=Internal, NumImages=1000, AcquireTime=.005, AcquirePeriod=.01, NumExposures=1. The time to collect this series should be exactly 10.0 seconds. The actual time was measured using the EPICS camonitor program. It printed the time when acquisition was started (Acquire changed to Acquire=1) and when acquisition was complete (Acquire changed to Done=0). The time was 10.022 seconds. 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 all of the images to EPICS. The total additional time was less than 0.03 seconds for all 1000 images.
  2. AcquireMode=Internal, NImages=1, ExposureTime=.01, NExposures=1. An EPICS sscan record was used to collect 1000 points. There were no positioner PVs (to eliminate motor overhead). The only detector trigger was the Pilatus Acquire PV. The only detector PV was ROI1:0:Total_RBV. In this mode camserver is being told to individually collect each file. If there were no overhead then time to collect this series should be exactly 10.0 seconds. The actual time measured using the EPICS camonitor program was 45.514 seconds. The overhead is thus 35.514 seconds, or 35 ms per point. In this single-frame mode pilatusROI is thus able to collect >20 images/second. For comparison, another measurement was done using the same EPICS sscan record, but using a Joerger VSC16 scaler as the detector trigger and detector. The preset time was also .01 seconds. The elapsed time for a 1000 point scan was 16.068 seconds, so the overhead was 6.068 seconds, or 6 ms per point.
  3. AcquireMode=Ext. Enable, NImages=1000, NExposures=1. SPEC was used to collect 1000 points using trajectory scanning mode with the Newport XPS motor controller. The following SPEC command was used:
          lup chi -2 2 1000 .015
          
    This 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:

    1000 point SPEC scan with 15 ms per point collected in 16.3 seconds

    pilatusROI_spec.png

    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.

 

Hardware notes

Trigger pulses

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.

Detector Voltage

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.

Restrictions

The following are some current restrictions of the pilatusROI SNL program: