Files
ADAndor/documentation/pilatusDoc.html

1008 lines
45 KiB
HTML
Executable File

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>areaDetector Pilatus driver</title>
</head>
<body>
<div style="text-align: center">
<h1>
areaDetector Pilatus driver</h1>
<h2>
December 11, 2008</h2>
<h2>
Mark Rivers</h2>
<h2>
University of Chicago</h2>
</div>
<h2>
Table of Contents</h2>
<ul>
<li><a href="#Introduction">Introduction</a></li>
<li><a href="#StandardNotes">Implementation of standard driver parameters</a></li>
<li><a href="#Driver_parameters">Pilatus specific parameters</a></li>
<li><a href="#Unsupported">Unsupported standard driver parameters</a></li>
<li><a href="#Screenshots">Screenshots</a></li>
<li><a href="#Configuring">Configuring</a></li>
<li><a href="#Performance measurements">Performance measurements</a> </li>
<li><a href="#Hardware notes">Hardware notes</a> </li>
<li><a href="#Restrictions">Restrictions</a> </li>
</ul>
<h2 id="Introduction" style="text-align: left">
Introduction</h2>
<p>
This is a driver for the Pilatus pixel array detectors <a href="http://www.dectris.com">
Dectris</a>. 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.</p>
<p>
The interface to the detector is via a TCP/IP socket interface to the <b>camserver</b>
server that Dectris provides. The camserver program must be started before the areaDetector
software is started, typically by running the <b>camonly</b> script provided by
Dectris.
</p>
<p>
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.
</p>
<h2 id="StandardNotes" style="text-align: left">
Implementation of standard driver parameters</h2>
<p>
The following table describes how the Pilatus driver implements some of the standard
driver parameters.
</p>
<table border="1" cellpadding="2" cellspacing="2" style="text-align: left">
<tbody>
<tr>
<td align="center" colspan="3">
<b>Parameter Definitions in pilatusDetector.cpp and EPICS Record Definitions in pilatus.template</b></td>
</tr>
<tr>
<th>
Enum name</th>
<th>
EPICS record name</th>
<th>
Description</th>
</tr>
<tr>
<td>
ADTriggerMode</td>
<td>
$(P$(R)TriggerMode</td>
<td>
The driver redefines the choices for the ADTriggerMode parameter (record $(P)$(R)TriggerMode)
from ADStdDriverParams.h. The choices for the Pilatus are:
<ul>
<li>Internal (external signal not used)</li>
<li>External Enable (count while external trigger line is high, readout on high to
low transition)</li>
<li>External Trigger (begin acquisition sequence on high to low transition of external
trigger line)</li>
<li>Multiple External Trigger (high to low transition on external signal triggers
a single acquisition for the programmed exposure time)</li>
<li>Alignment (collect images as fast as exposure time and readout permit, images
written to a temporary file)</li>
</ul>
The first 4 modes correspond directly to the camserver commands <code>Exposure</code>,
<code>ExtEnable</code>, <code>ExtTrigger</code>, and <code>ExtMTrigger</code> respectively.
Alignment mode uses the <code>Exposure</code> command as well, but continuously
takes images into the same temporary file (<code>alignment.tif</code>).
</td>
</tr>
<tr>
<td>
ADExposureTime</td>
<td>
$(P$(R)ExposureTime</td>
<td>
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
the driver can estimate how long to wait for the data files to be created before
timing out.</td>
</tr>
<tr>
<td>
ADNumImages</td>
<td>
$(P$(R)NumImages</td>
<td>
Controls the number of images to acquire. It applies in all trigger modes except
Alignment.</td>
</tr>
<tr>
<td>
ADExposurePeriod</td>
<td>
$(P$(R)ExposurePeriod</td>
<td>
Controls the exposure period in seconds. It applies only in Internal or External
Trigger modes when NumImages &gt 1.</td>
</tr>
<tr>
<td>
ADNumExposures</td>
<td>
$(P$(R)NumExposures</td>
<td>
Controls the number of exposures per image. It is only used in External Enable mode.</td>
</tr>
<tr>
<td>
ADAquire</td>
<td>
$(P$(R)Acquire</td>
<td>
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 &gt 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.</td>
</tr>
<tr>
<td>
ADFilePath</td>
<td>
$(P$(R)FilePath</td>
<td>
Controls the path for saving images. It must be a valid path for camserver <i>and</i>
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.</td>
</tr>
<tr>
<td>
ADFileFormat</td>
<td>
$(P)$(R)FileFormat</td>
<td>
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 <a href="areaDetectorDoc.html#ADStdDriverParams">
File Saving Parameters in ADStdDriverParams</a> is used as a basename. The following
examples show the interpretation of the basename.
<pre>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, ...
</pre>
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.</td>
</tr>
</tbody>
</table>
<p>
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.
</p>
<h2 id="Driver_parameters" style="text-align: left">
Pilatus specific parameters</h2>
<p>
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 <code>PilatusDelayTime</code>.
</p>
<table border="1" cellpadding="2" cellspacing="2" style="text-align: left">
<tbody>
<tr>
<td align="center" colspan="7">
<b>Parameter Definitions in pilatusDetector.cpp and EPICS Record Definitions in pilatus.template</b></td>
</tr>
<tr>
<th>
Enum name</th>
<th>
asyn interface</th>
<th>
Access</th>
<th>
Description</th>
<th>
drvUser string</th>
<th>
EPICS record name</th>
<th>
EPICS record type</th>
</tr>
<tr>
<td>
Pilatus<br />
DelayTime</td>
<td>
asynFloat64</td>
<td>
r/w</td>
<td>
Delay in seconds between the external trigger and the start of image acquisition.
It only applies in External Trigger mode</td>
<td>
DELAY_TIME</td>
<td>
$(P)$(R)DelayTime</td>
<td>
ao</td>
</tr>
<tr>
<td>
Pilatus<br />
Threshold</td>
<td>
asynFloat64</td>
<td>
r/w</td>
<td>
Threshold energy in keV</td>
<td>
THRESHOLD</td>
<td>
$(P)$(R)ThresholdEnergy</td>
<td>
ao</td>
</tr>
<tr>
<td>
N/A</td>
<td>
N/A</td>
<td>
r/w</td>
<td>
Gain menu. Controls the value of Vrf, which determines the shaping time and gain
of the input amplifiers. The allowed values are:
<ul>
<li>0 ("Fast/Low") Fastest shaping time (~125ns) and lowest gain. </li>
<li>1 ("Medium/Medium") Medium shaping time (~200 ns) and medium gain. </li>
<li>2 ("Slow/High") Slow shaping time (~400 ns) and high gain. </li>
<li>3 ("Slow/Ultrahigh") Slowest peaking time (? ns) and highest gain. </li>
</ul>
</td>
<td>
N/A</td>
<td>
$(P)$(R)GainMenu</td>
<td>
mbbo</td>
</tr>
<tr>
<td>
Pilatus<br />
Armed</td>
<td>
asynInt32</td>
<td>
r/o</td>
<td>
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</td>
<td>
ARMED</td>
<td>
$(P)$(R)Armed</td>
<td>
bi</td>
</tr>
<tr>
<td>
Pilatus<br />
TiffTimeout</td>
<td>
asynFloat64</td>
<td>
r/w</td>
<td>
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.</td>
<td>
TIFF_TIMEOUT</td>
<td>
$(P)$(R)ReadTiffTimeout</td>
<td>
ao</td>
</tr>
<tr>
<td>
Pilatus<br />
BadPixelFile</td>
<td>
asynOctet</td>
<td>
r/w</td>
<td>
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:
<pre>badX1,badY1 replacementX1,replacementY1
badX2,badY2 replacementX2,replacementY2
...
</pre>
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:
<pre>
263,3 262,3
264,3 266,3
263,3 266,3
300,85 299,85
300,86 299,86
471,129 472,129
</pre>
</td>
<td>
BAD_PIXEL_FILE</td>
<td>
$(P)$(R)BadPixelFile</td>
<td>
waveform</td>
</tr>
<tr>
<td>
Pilatus<br />
NumBadPixels</td>
<td>
asynInt32</td>
<td>
r/o</td>
<td>
The number of bad pixels defined in the bad pixel file. Useful for seeing if the
bad pixel file was read correctly.</td>
<td>
NUM_BAD_PIXELS</td>
<td>
$(P)$(R)NumBadPixels</td>
<td>
longin</td>
</tr>
<tr>
<td>
Pilatus<br />
FlatFieldFile</td>
<td>
asynOctet</td>
<td>
r/w</td>
<td>
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 &gt
PilatusMinFlatField. All pixels with intensity &lt 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:
<pre>ImageData[i] =
(averageFlatField *
ImageData[i])/flatField[i];
</pre>
</td>
<td>
FLAT_FIELD_FILE</td>
<td>
$(P)$(R)FlatFieldFile</td>
<td>
waveform</td>
</tr>
<tr>
<td>
Pilatus<br />
MinFlatField</td>
<td>
asynInt32</td>
<td>
r/w</td>
<td>
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.</td>
<td>
MIN_FLAT_FIELD</td>
<td>
$(P)$(R)MinFlatField</td>
<td>
longout</td>
</tr>
<tr>
<td>
Pilatus<br />
FlatFieldValid</td>
<td>
asynInt32</td>
<td>
r/o</td>
<td>
This record indicates if a valid flat field file has been read. 0=No, 1=Yes.</td>
<td>
FLAT_FIELD_VALID</td>
<td>
$(P)$(R)FlatFieldValid</td>
<td>
bi</td>
</tr>
<tr>
<td>
N/A</td>
<td>
N/A</td>
<td>
N/A</td>
<td>
asyn record to control debugging communication with camserver. Setting the CNCT
field in this record to <code>Disconnect</code> 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 <code>Connect</code> to reconnect the IOC to camserver, or simply process
any record which communicates with camserver, because the driver will automatically
reconnect.</td>
<td>
N/A</td>
<td>
$(P)$(R)CamserverAsyn</td>
<td>
asyn</td>
</tr>
</tbody>
</table>
<h2 id="Configuration" style="text-align: left">
Configuration</h2>
<p>
The Pilatus driver is created with the following command, either from C/C++ or from
the EPICS IOC shell.
</p>
<pre>
pilatusDetectorConfig(const char *portName, const char *camserverPort,
int maxSizeX, int maxSizeY, int maxBuffers, size_t maxMemory);
</pre>
<table border="1" cellpadding="2" cellspacing="2" style="text-align: left">
<tbody>
<tr>
<th>
Argument</th>
<th>
Description</th>
</tr>
<tr>
<td>
<code>portName</code></td>
<td>
The name of the asyn port for this detector.
</td>
</tr>
<tr>
<td>
<code>camserverPort</code></td>
<td>
The name of the asyn TCP/IP port to communicate with camserver. This must have been
previously created with <code>drvAsynIPPortConfig()</code>,
</td>
</tr>
<tr>
<td>
<code>maxSizeX</code></td>
<td>
The number of pixels in the X direction on the detector. This is 487 for the Pilatus
100K.</td>
</tr>
<tr>
<td>
<code>maxSizeY</code></td>
<td>
The number of pixels in the Y direction on the detector. This is 195 for the Pilatus
100K.</td>
</tr>
<tr>
<td>
<code>maxBuffers</code></td>
<td>
Maximum number of buffers to be created for plugin callbacks. Passed to the constructor
for the ADDriver base class.</td>
</tr>
<tr>
<td>
<code>maxMemory</code></td>
<td>
Maximum number of bytes of memory to be allocated for plugin callbacks. Passed to
the constructor for the ADDriver base class.</td>
</tr>
</tbody>
</table>
<p>
The following is an example st.cmd startup script:
</p>
<pre>< 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:")
</pre>
<h2 id="MEDM screens" style="text-align: left">
MEDM screens</h2>
<p>
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.</p>
<p>
<code>pilatusDetector.adl</code> is the main screen used to control the Pilatus
driver. All records except those for ROIs are accessed through this screen.
</p>
<div style="text-align: center">
<h3 style="text-align: center">
pilatusDetector.adl</h3>
<img alt="pilatusDetector.png" src="pilatusDetector.png" /></div>
<p>
<code>NDROI8.adl</code> 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].</p>
<div style="text-align: center">
<h3 style="text-align: center">
NDROI8.adl</h3>
<img alt="NDROI8.png" src="NDROI8.png" /></div>
<p>
<code>mca.adl or mca_small.adl</code> 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
</p>
<pre>lup chi -1 1 1000 .02
</pre>
<p>
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.8 seconds.</p>
<div style="text-align: center">
<h3 style="text-align: center">
mca_small.adl</h3>
<img alt="pilatusMCA.png" src="pilatusMCA.png" style="text-align: left" /></div>
<p>
<code>scan_more.adl</code> 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.</p>
<div style="text-align: center">
<h3>
scan_more.adl</h3>
<img alt="pilatusThresholdScanSetup.png" src="pilatusThresholdScanSetup.png" /></div>
<p>
<code>scanDetPlot.adl</code> is used to plot the results of a scan after it is complete.
In this example the total counts in ROI 1 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.</p>
<div style="text-align: center">
<h3>
scanDetPlot.adl</h3>
<img alt="pilatusThresholdScanPlot.png" src="pilatusThresholdScanPlot.png" /></div>
<p>
<code>asynRecord.adl</code> is used to control the debugging information printed
by the asyn TCP/IP driver for camserver (asynTraceIODriver).</p>
<div style="text-align: center">
<h3>
asynRecord.adl</h3>
<img alt="pilatusAsyn.png" src="pilatusAsyn.png" /></div>
<p>
<code>asynOctet.adl</code> 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.</p>
<div style="text-align: center">
<h3>
asynOctet.adl</h3>
<img alt="pilatusAsynOctet.png" src="pilatusAsynOctet.png" /></div>
<h2 id="SPEC interface" style="text-align: left">
SPEC interface</h2>
<p>
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 <a href="http://cars.uchicago.edu/software/epics/trajectoryScan.html">
trajectory scanning</a> mode. Here are some snippets from the SPEC macros for
the Pilatus. We can supply the source files on request.</p>
<pre> # need some more globals (kludge)
global PILATUS_ROI_PV
global PILATUS_ROI_ARRAY_PV
global PILATUS_ROI_ARRAY_START_PV
global PILATUS_ROI_ARRAY_NUSE_PV
global PILATUS_ROI_ARRAY_ACQ_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_ARMED_PV
global PILATUS_ABORT_PV
global PILATUS_ACQMODE_PV
global PILATUS_READOUT_TIME
global PILATUS_ROI_0_MinX_PV
global PILATUS_ROI_0_SizeX_PV
global PILATUS_ROI_0_MinY_PV
global PILATUS_ROI_0_SizeY_PV
###############################################################
def _setup_img '{
...
# PILATUS_PREFIX detector name i.e. (GSE-PILATUS1:)
if ( PILATUS_PREFIX == "") PILATUS_PREFIX = "GSE-PILATUS1:"
PILATUS_PREFIX = getsval("Enter PILATUS detector name i.e. GSE-PILATUS1:",PILATUS_PREFIX)
# PILATUS_DET_PREFIX is the pv used by areaDetector to identify a specific detector.
# When only one detector is used it is usally (cam1:)
if ( PILATUS_DET_PREFIX == "") PILATUS_DET_PREFIX = "cam1:"
PILATUS_DET_PREFIX = getsval("Enter PILATUS specific detector name i.e. cam1:",PILATUS_DET_PREFIX)
# PILATUS_ROI_PREFIX is the pv used by areaDetector to identify a specific a ROI plugin.
# When only one ROI plugin is used it is usally (ROI1:)
if ( PILATUS_ROI_PREFIX == "") PILATUS_DET_PREFIX = "ROI1:"
PILATUS_ROI_PREFIX = getsval("Enter PILATUS ROI plugin name i.e. ROI1:",PILATUS_ROI_PREFIX)
if (PILATUS_MOUNT == "") PILATUS_MOUNT = "cars5/Data"
PILATUS_MOUNT = getsval("Enter mount point relative to camserver home directory",PILATUS_MOUNT)
if (PILATUS_SPEC_MOUNT == "") PILATUS_SPEC_MOUNT = "cars5/Data"
PILATUS_SPEC_MOUNT = getsval("Enter mount point relative to spec home directory",PILATUS_SPEC_MOUNT)
...
PILATUS_ROI_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:Net_RBV"
PILATUS_ROI_ARRAY_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:NetArray"
PILATUS_ROI_ARRAY_START_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:NetArrayEraseStart"
PILATUS_ROI_ARRAY_NUSE_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:NetArray.NUSE"
PILATUS_ROI_ARRAY_ACQ_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:NetArray.ACQG"
PILATUS_IMGPATH_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "FilePath"
PILATUS_FNAME_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "FileName"
PILATUS_FILENUMBER_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "FileNumber"
PILATUS_FILEFORMAT_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "FileTemplate"
PILATUS_EXPSRTM_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "AcquireTime"
PILATUS_NFRAME_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "NumImages"
PILATUS_EXPPRD_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "AcquirePeriod"
PILATUS_NEXPFRM_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "NumExposures"
PILATUS_ACQ_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "Acquire"
PILATUS_ARMED_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "Armed"
PILATUS_ABORT_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "Acquire"
PILATUS_ACQMODE_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "TriggerMode"
PILATUS_THRESHOLD_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "ThresholdEnergy"
PILATUS_ROI_0_MinX_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:MinX"
PILATUS_ROI_0_SizeX_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:SizeX"
PILATUS_ROI_0_MinY_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:MinY"
PILATUS_ROI_0_SizeY_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:SizeY"
PILATUS_ROI_0_BgdWidth_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:BgdWidth"
...
def epics_pilatus_count '{
...
# Call macro that creates and set the Pilatus path and filename
img_full_filename
# Setup exposure time, collection mode and number of frames
epics_put(PILATUS_FILENUMBER_PV,NPTS, 1)
epics_put(PILATUS_NFRAME_PV, 1, 1)
epics_put(PILATUS_ACQMODE_PV,0, 1) # Internal trigger
epics_put(PILATUS_EXPSRTM_PV,cnt_time_val, 1)
epics_put(PILATUS_NEXPFRM_PV, 1, 1)
...
# hit the triggers
epics_put(PILATUS_ACQ_PV,1)
epics_put(sc_cnt_pv,1)
# wait for scaler and Pilatus AQG to finish
status = 1
sc_done = FALSE
img_done = FALSE
data_done = FALSE
while(status){
# is the scalar done
if (epics_get(sc_cnt_pv)=="Done"){
sc_done = TRUE;
#p "scaler done"
}
# is the pilatus done
if (epics_get(PILATUS_ACQ_PV) == "Done"){
img_done = TRUE;
#p "image collection done"
}
if( (sc_done==TRUE) && (img_done==TRUE)) break;
sleep(0.01)
}
# use the get_counts routine to read the scalers
# note get_counts also calls user_getcounts
# thats where the rois get read.
get_counts
}'
def user_getcounts '{
...
# using image_count routine
} else if ( EPICS_COUNT == 4 ) {
S[iroi] = 0
S[iroi] = epics_get(PILATUS_ROI_PV)
</pre>
<h2 id="Performance measurements">
Performance measurements</h2>
<p>
The following measurements were done to demonstrate the performance that can be
obtained with the areaDetector Pilatus driver.</p>
<ol>
<li>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 the driver 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.</li>
<li>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 the driver 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.</li>
<li>AcquireMode=Ext. Enable, NImages=1000, NExposures=1. SPEC was used to collect
1000 points using <a href="http://cars.uchicago.edu/software/epics/trajectoryScan.html">
trajectory scanning</a> mode with the Newport XPS motor controller. The following
SPEC command was used:
<pre> lup chi -1 1 1000 .02
</pre>
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, .020 seconds in this case. These pulses are
used as the external trigger to the Pilatus. The time to execute this scan should
be 20.0 seconds. The actual time was 20.8 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 the Pilatus driver 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.8 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:
<div style="text-align: center">
<h3>
1000 point SPEC scan with 20 ms per point collected in 20.8 seconds</h3>
<img alt="pilatusSPEC.png" src="pilatusSPEC.png" /></div>
<p>
</p>
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 NumImages=1. The total time for the scan was 870 seconds (more than 14
minutes), compared to 20.8 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.</li>
</ol>
<h2 id="Hardware notes" style="text-align: left">
</h2>
<h3>
Trigger pulses</h3>
<p>
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.</p>
<p>
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.</p>
<p>
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.</p>
<p>
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:</p>
<ul>
<li>The synchonization pulses from the Newport MM4005 or XPS controller are input
into the external next pulse (channel advance, control signal 1) input of the SIS3801
multiscaler. This is the normal configuration used for MCS counting without the
Pilatus in trajectory scanning mode.</li>
<li>The Copy In Progress (CIP) output of the SIS3801 (control signal 5) is connected
to the Trigger Input of a Tenma TGP110 10 MHz Pulse Generator. CIP will output a
pulse whenever the SIS3801 does a channel advance, either in external mode with
the motor controller pulse input, or in internal timed channel advance mode. The
TGP100 Pulse Generator is configured as follows:
<ul>
<li>Trigger Input connected to CIP output of SIS3801.</li>
<li>Triggered mode.</li>
<li>Complement output.</li>
<li>Pulse duration set with knobs to 3msec.</li>
<li>TTL Output connected to the External Input of the Pilatus.</li>
</ul>
</li>
<li>With this configuration the SIS3801 CIP output is normally at 5V, and outputs
a 0V pulse 1 microsecond long. The trailing (rising) edge of that pulse triggers
the TGP110. The TGP110 TTL output is also normally at 5V, and outputs a 0V pulse
3 milliseconds long each time the SIS3801 pulses. That output is connected to the
Pilatus External Input. In External Enable mode when Pilatus External Input is high
the Pilatus is counting. When the External Input is low the Pilatus reads out. The
readout time is set via the knobs on the pulse generator to be 3 ms, which is close
to the minimum time allowed on the Pilatus.</li>
</ul>
<p>
The Tenma TGP110 seems to be currently called a Tenma 72-6860, and lists for about
$350 new at <a href="http://www.newark.com">Newark</a>.
</p>
<h3>
Detector Voltage</h3>
<p>
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.</p>
<p>
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.</p>
<h2 id="Restrictions">
Restrictions</h2>
<p>
The following are some current restrictions of the areaDetector Pilatus driver:</p>
<ul>
<li>Limited to TIFF file format. camserver can save files in other formats, but the
driver can currently only read TIFF files. It uses the standard libtiff library
to read the files, so it should work on big or little endian machines, and should
work with uncompressed or compressed files. It has only been tested with uncompressed
files on a little-endian machine.</li>
<li>The EPICS IOC should be run on the same computer as camserver. This is not strictly
necessary, and places a small additional load on the CPU and network on that computer.
However, we have found that TIFF files are available to be read within 10ms after
camserver says they have been written if the IOC is running on the same machine
as camserver. This is true even if the files are being saved on a remote NFS or
SMB file system. On the other hand, if the IOC and camserver are running on separate
machines, then the filesystem can wait up to 1 second after camserver says the TIFF
file has been written before the IOC can read it. This is true even if the files
are being written to the computer that the IOC is running on! This 1 second delay
is often unacceptable for fast single-exposure scans, i.e. with NImages=1.</li>
<li>The Pilatus driver keeps retrying to read each TIFF file until the modification
date of the TIFF file is <i>after</i> the time that the acquisition was started.
If it did not do this check then it could be reading and displaying old files that
happen to have the same name as the current files being collected. This check requires
that the computer that is running the soft IOC must have its clock well synchronized
with the clock on the computer on which the files are being written (i.e. the computer
generating the file modification time). If the clocks are not synchronized then
the files may appear to be stale when they are not, and the driver will time out.
The driver actually tolerates up to 10 second clock skew betweeen the computers
but any more than this may lead to problems.</li>
<li>Setting Acquire to 0 does not always stop acquisition immediately because camserver
does not reliably implement the "K" command to stop an exposure sequence. In particular
with NumImages>1 camserver seems to often ignore the K command completely, even
with exposure times/periods as long as 10 seconds. With NumImages=1 it does kill
the exposure after a few seconds.</li>
<li>The following items are hardcoded in the driver. They can be changed by compiling
if necessary.
<ul>
<li>MAX_MESSAGE_SIZE=256 The maximum size of message to/from camserver.</li>
<li>MAX_FILENAME_LEN=256 The maximum size of a complete file name including path and
extension.</li>
<li>FILE_READ_DELAY=.01 seconds. The time between polling to see if the TIFF file
exists or if it is the expected size.</li>
<li>MAX_BAD_PIXELS=100 The maximum number of bad pixels.</li>
</ul>
</li>
</ul>
</body>
</html>