NOTE: This module is now obsolete and has been replaced by the areaDetector module.
The Pilatus
detector is controlled at the lower level by a program called camserver
that runs on the Linux machine to which the Pilatus detector is connected. camserver
listens for socket connections from clients, and accepts ASCII commands to collect images
and perform other actions.
Dectris supplies a basic EPICS interface with the Pilatus that uses the "stream" device support to communicate with camserver. While this interface is useable, it lacks many of the features that are desireable for use on a beamline.
pilatusROI is designed to be a replacement for the EPICS device support provided from Dectris. It includes the following features that the Dectris support lacks:
The EPICS implementation consists of the following:
pilatusROI.st
.
This program implements all of the logic for acquiring images, reading
the TIFF files, computing ROIs, and making the data available to EPICS.
pilatusROI.template, pilatusROI_N.template
.
These databases
contain almost no "logic" with no links between records in the database.
Some of the records use "streamDevice" for communication with camserver. Other
records are simply variables which channel access clients and the State
Notation Language (SNL) program use.
pilatusROI.protocol
.
This file defines the protocol used for
communicating with camserver.
pilatusROI_settings.req, pilatusROI_N_settings.req
.
These files define the EPICS PVs
that will be automatically saved and restored when the EPICS IOC is restarted, so that
state information is preserved.
pilatusROI.adl,
pilatus8ROIs.adl, pilatusROI_waveform.adl
. These screens are
used to control image acquisition, and definition and display of ROI data.
epics_image_display.pro
for displaying the images
as they are sent to EPICS. This program is also available as a pre-built IDL ".sav" file
that can be run for free under the IDL Virtual Machine.
The following EPICS records are used by pilatusROI. All records are prefixed by the macro $(DET) which must be passed to the template file when the records are loaded.
AcquireMode
(mbbo)MinImageUpdateTime
PV is ignorred in
this mode, so that all images are sent to EPICS for display.
It is similar to the exposem
command in TVX.
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
).
ExposureTime
(ao)NImages
(longout)ExposurePeriod
(ao)NExposures (longout)
DelayTime
(ao)Acquire
(busy)Armed
(bo)Abort
(bo)StatusMessage
(stringout)
ThresholdEnergy
(ao)Gain
(mbbo)
FilePath
(waveform, FTVL=UCHAR, NELM=256)This record is an EPICS waveform record with FTVL=UCHAR and NELM=256. This removes the 40 character restriction on path name lengths that arise if an EPICS "string" PV is used. medm allows one to edit and display such records correctly. EPICS clients will typically need to convert the path name from a string to an integer or byte array before sending the path name to EPICS. This is easy to do in clients like SPEC, Matlab, and IDL.
Filename
(stringout)FileNumber
(longout)FileFormat
(stringout)epicsSnprintf(FullFilename, sizeof(FullFilename), FileFormat, FilePath, Filename, FileNumber);FilePath, Filename, FileNumber are converted in that order with FileFormat. The default file format is
"%s%s%4.4d.tif"
. The first %s converts the FilePath,
followed immediately by another %s for Filename.
FileNumber is formatted with %4.4d, which results in a
fixed field with of 4 digits, with leading zeros as required. Finally, the .tif extension
is added to the file name. This will make camserver save files in TIFF format with that extension.
This mechanism for creating file names is very flexible. Other characters, such as _ can be put in Filename or FileFormat as desired. If one does not want to have FileNumber in the file name at all, then just omit the %d format specifier from FileFormat.
Note that 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 above algorithm 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.
AutoIncrement
(bo)FullFilename
(waveform, FTVL=UCHAR, NELM=256)ROI$(N)XMin
(longout)ROI$(N)XMax
(longout)ROI$(N)YMin
(longout)ROI$(N)YMax
(longout)ROI$(N)BgdWidth
(longout)ROI$(N)TotalCounts
(ao)ROI$(N)NetCounts
(ao)ROI$(N)MinCounts
(longout)ROI$(N)MaxCounts
(longout)ROI$(N)Label
(stringout)ROI$(N)WFTotalCounts
(waveform, FTVL=DOUBLE, NELM=$(NCHANS))ROI$(N)WFNetCounts
(waveform, FTVL=DOUBLE, NELM=$(NCHANS))MinWFUpdateTime
(ao)ImageData
(waveform, FTVL=LONG, NELM=$(NPIXELS))NXPixels
(longout)NYPixels
(longout)HighlightROIs
(bo)PostImages
(bo)MinImageUpdateTime
(ao)BadPixelFile
(waveform, FTVL=UCHAR, NELM=256)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 pilatusROI was written, namely fast on-line viewing of ROIs and ImageData. More sophisticated algorithms can be used for offline analysis of the image files themselves. The following is an example bad pixel file for the 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
NBadPixels
(longout)FlatFieldFile
(waveform, FTVL=UCHAR, NELM=256)ImageData[i] = (averageFlatField * ImageData[i])/flatField[i];
MinFlatField
(ao)FlatFieldValid
(bo)ReadTiffTimeout
(ao)SendMessage
(waveform, FTVL=UCHAR, NELM=256)ReplyMessage
(waveform, FTVL=UCHAR, NELM=256)Connect
(bo)Disconnect
(bo)scan1, scan2, scan3, scan4, scanH
(sscan)
pilatusROI.template is loaded with the following macro parameters:
Macro parameter | Description |
---|---|
$(DET) |
PV name prefix. This identifies this Pilatus detector from others that may be running on the same subnet. |
$(NXPIXELS) |
The number of pixels in the X (fast index) direction on the detector. This is 487 for the Pilatus 100K. |
$(NYPIXELS) |
The number of pixels in the Y (slow index) direction on the detector. This is 195 for the Pilatus 100K. |
$(NPIXELS) |
The total number of pixels on the detector. This is NXPIXELS*NYPIXELS=94965 for the Pilatus 100K. |
$(PORT) |
The name of the asyn port connected to the Pilatus via a TCP/IP socket. |
pilatusROI_N.template is loaded for each ROI with the following macro parameters:
Macro parameter | Description |
---|---|
$(DET) |
PV name prefix. This identifies this Pilatus detector from others that may be running on the same subnet. |
$(N) |
The number of this ROI. Starts with 1. |
$(XMIN) |
The minimum value of X for this ROI. Starts with 0. |
$(XMAX) |
The maximum value of X for this ROI. Starts with 0. |
$(YMIN) |
The minimum value of Y for this ROI. Starts with 0. |
$(YMAX) |
The maximum value of Y for this ROI. Starts with 0. |
$(BGD_WIDTH) |
The background width for this ROI. If BGD_WIDTH <=0 then no background subtraction is done, and NetCounts=TotalCounts. |
$(NCHANS) |
The maximum number of elements in the WFTotalCounts and WFNetCounts waveform arrays. This sets the maximum value of NImages for collecting ROI arrays. |
The pilatusROIs SNL program is started with the following macro parameters:
Macro parameter | Description |
---|---|
DET |
PV name prefix. This identifies this Pilatus detector from others that may be running on the same subnet. |
PORT |
The name of the asyn port connected to the Pilatus via a TCP/IP socket. |
NROIS |
The number of ROIs loaded in the substitutions file with pilatusROI_N.template. Maximum=32. |
The following is an example st.cmd startup script:
< envPaths ### # Load the EPICS database file dbLoadDatabase("$(PILATUS)/dbd/pilatus.dbd") pilatus_registerRecordDeviceDriver(pdbbase) ### # Create the asyn port to talk to the Pilatus on port 41234. drvAsynIPPortConfigure("pilatus","gse-pilatus1:41234") # Set the input and output terminators. asynOctetSetInputEos("pilatus", 0, "\030") asynOctetSetOutputEos("pilatus", 0, "\n") # Define the environment variable pointing to stream protocol files. epicsEnvSet("STREAM_PROTOCOL_PATH", "$(PILATUS)/pilatusApp/Db") ### # Specify where save files should be set_savefile_path(".", "autosave") ### # Specify what save files should be restored. Note these files must be # in the directory specified in set_savefile_path(), or, if that function # has not been called, from the directory current when iocInit is invoked set_pass0_restoreFile("auto_settings.sav") set_pass1_restoreFile("auto_settings.sav") ### # Specify directories in which to to search for included request files set_requestfile_path("./") set_requestfile_path("$(AUTOSAVE)", "asApp/Db") set_requestfile_path("$(CALC)", "calcApp/Db") set_requestfile_path("$(SSCAN)", "sscanApp/Db") set_requestfile_path("$(PILATUS)", "pilatusApp/Db") ### # Load the save/restore status PVs dbLoadRecords("$(AUTOSAVE)/asApp/Db/save_restoreStatus.db", "P=PILATUS:") ### # Load the substitutions for for this IOC dbLoadTemplate("PILATUS_all.subs") # Load an asyn record for debugging dbLoadRecords("$(ASYN)/db/asynRecord.db", "P=PILATUS:,R=asyn1,PORT=pilatus,ADDR=0,IMAX=80,OMAX=80") # Load sscan records for scanning dbLoadRecords("$(SSCAN)/sscanApp/Db/scan.db", "P=PILATUS:,MAXPTS1=2000,MAXPTS2=200,MAXPTS3=20,MAXPTS4=10,MAXPTSH=2048") ### # Set debugging flags if desired #asynSetTraceIOMask("pilatus",0,2) #asynSetTraceMask("pilatus",0,3) ### # Start the IOC iocInit ### # Save settings every thirty seconds create_monitor_set("auto_settings.req", 30, "P=PILATUS:") ### # Start the SNL program seq(pilatusROIs, "DET=PILATUS:, PORT=pilatus, NROIS=16")The following is the substutitions file PILATUS_all.subs referenced above. It creates 16 ROIS, and defines 5 valid ones: the entire chip, and the 4 quadrants of the chip:
ffile $(PILATUS)/db/pilatusROI.template { pattern {DET, NXPIXELS, NYPIXELS, NPIXELS, PORT} {PILATUS:, 487, 195, 94965, pilatus} } file $(PILATUS)/db/pilatusROI_N.template { pattern {DET, N, XMIN, XMAX, YMIN, YMAX, BGD_WIDTH, NCHANS} {PILATUS:, 1, 0, 486, 0, 194, 1, 2000} {PILATUS:, 2, 0, 243, 0, 97, 1, 2000} {PILATUS:, 3, 0, 243, 98, 194, 1, 2000} {PILATUS:, 4, 244, 486, 0, 97, 1, 2000} {PILATUS:, 5, 244, 486, 98, 194, 1, 2000} {PILATUS:, 6, -1, -1, -1, -1, 1, 2000} {PILATUS:, 7, -1, -1, -1, -1, 1, 2000} {PILATUS:, 8, -1, -1, -1, -1, 1, 2000} {PILATUS:, 9, -1, -1, -1, -1, 1, 2000} {PILATUS:, 10, -1, -1, -1, -1, 1, 2000} {PILATUS:, 11, -1, -1, -1, -1, 1, 2000} {PILATUS:, 12, -1, -1, -1, -1, 1, 2000} {PILATUS:, 13, -1, -1, -1, -1, 1, 2000} {PILATUS:, 14, -1, -1, -1, -1, 1, 2000} {PILATUS:, 15, -1, -1, -1, -1, 1, 2000} {PILATUS:, 16, -1, -1, -1, -1, 1, 2000} }
The following show the MEDM screens that are used to control the pilatusROI software.
pilatusROI.adl
is the main screen used to control the pilatusROI SNL
program. All records except those that are specific to each ROI are accessed through this
screen.
pilatus8ROIs.adl
is used to define the ROIs, and to display the statistics for
each ROI. In this example there are 2 valid ROIs defined. ROI 1 is a small rectangle near the center
containing the Bragg diffraction peak from a crystal. ROI 2 is the entire chip.
pilatusROI_waveform.adl
is 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 .02using 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 pilatusROI program 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.
epics_image_display
that can be used to display the ImageData PV that pilatusROI sends over EPICS. This IDL
client is available as source code (which requires an IDL license), and also as a pre-built IDL .sav
file that can be run for free under the IDL Virtual Machine. This IDL program can run on any machine that IDL
runs on, and that has the ezcaIDL shareable library built for it. This includes Windows, Linux, Solaris, and Mac.
epics_image_display
is included in the
CARS IDL imaging software.
The control window for epics_image_display
is shown below. It has fields to input the name of
the EPICS PV with the image data, which is $(DET)ImageData in the case of pilatusROI. It also has fields
for the number of pixels in the X and Y directions. This is needed because EPICS waveform records are
1-dimensional only, and so do not contain the information on the number of rows and columns in the image.
epics_image_display
uses the routine
image_display.pro
to display the images. This routine displays row and column profiles as the cursor is moved. It allows
changing the color lookup tables, and zooming in and out with the left and right mouse buttons. The following
is an example of image_display
displaying a Pilatus image with an Fe55 source in front of the detector.
# 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)
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.
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:
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.