-
Notifications
You must be signed in to change notification settings - Fork 0
Scan Configuration Files
-
Scan configuration files describe a scan configuration, they are Igor
.ipf
files and located in\WaveMetrics\Igor Pro 6 User Files\ScanM\ScM_Configs
. The variables used to describe a scan configuration are explained below. Standard xy scans require no additional files. -
Scan path function file. Scans that require more complicated scan paths (e.g. spiral scans) in the following referred to as "arbitrary scans" require an additional
.ipf
file that contains a defined set of standardized functions. For details, see section Arbitrary Scans below. Scan path function files live in\WaveMetrics\Igor Pro 6 User Files\User Procedures\ScanM
.
Where applicable, the relevant corresponding variables in scan configuration files (here, e.g. for a 64x64 image scan) are given for the different definitions.
-
AI channel := analogue input channel (#0 .. #3). Typically, AI #0 and #1 are used for fluorescence (e.g. green OGB-1 and red SR101), while AI #2 records trigger signals for offline synchronization with light stimulus. AI #3 is currently unused.
-
AO channel := analogue output channel (#0 .. #3). Currently, AO #0 and #1 are used for the x and y galvo scanners, respectively, AO #2 is providing a blanking signal (to turn down the laser via the Pockels Cell during the retrace and simultaneously allow for stimulus LEDs to be on). AO 3 will be used to drive an electrically tunable lens (ETL) and, thus, the z axis (to be implemented).
-
sample := an single voltage measurement of an AI channel.
-
pixel := voltage (intensity) value for a given scan position as defined by the x-y(-z) scanner paths. A pixel represents a single sample or the average of multiple samples (if oversampling is activated). The time bin (=pixel) for intensity "collection" along the scan path is given by the targeted pixel duration (in µs), the number of measurements (=samples) that are averaged for the pixel value is given by the oversampling factor:
cp.targetedPixelDur_us = 25.0 // total pixel duration cp.nSubPixOversample = 25 // oversampling factor
Note that the smallest time unit allowed is 1 µs.
-
scan line := a line of pixels, e.g. a row in an x-y image scan. Usually defined by the path of the fastest scan axis. For "historical" reasons, we typically use lines of 2 ms duration. Each scan line consists of 3 sections (all measured in number of pixels): offset, pixel data, and retrace. They correspond to the following variables in scan configuration files (here, e.g. for a 64x64 image scan):
cp.nXPixLineOffs = 6 // # of no-data pixels at line start cp.dXDataPixels = 64 // # of data pixels per line cp.nPixRetrace = 10 // # of no-data pixels for line retrace
Only the pixel data contains image information; offset and retrace are used to move the scanner to the next scan line. Offset pixels should also be used to move the scanners from their parking position to the beginning of the first scan line, whereas retrace pixels should be used at the end of a frame to move the scanners into parking position.
-
frame := multiple scan lines, e.g. an individual "image" of an xy image scan. On the scan path side, it corresponds to a full scan pattern that is repeated in case of time-lapsed recordings.
cp.dYPixels = 64 // # of lines per frame
In principle, a frame can also contain a 3D volume (to be implemented).
-
pixel buffer := the buffer that ScanM uses to fetch pixel data during a scan for display purpose. The pixel buffer can be the size of a frame, then display is updated once per recorded frame. A frame can also be divided into smaller partitions (e.g. 1 frame = 4 pixel buffers), i.e. to display already parts of the image before the scanning of the full frame is finished. The following variable defines the number of pixel buffers per frame:
cp.nDivFrameBuf = 4 // # of pixel buffers used per frame
-
display wave := the Igor wave that represents the current frame recorded from an AI channel in the ScanM GUI. Its aspect ratio is defined by:
cp.aspectRatioFrame = 1.0 // Aspect ratio display wave
-
stimulation buffer := the buffer that contains the AO voltages traces send to the x-y scanners (AO #0,1), serves as blanking signal (AO #2), and drives, for instance, an ETL (AO #3). Usually, the stimulation buffer contains the scan path for a full frame. However, if the frames are rather small (e.g. 64x16 image scans), then writing these buffers to the NI cards can generate too much communication overhead between ScanM, the drivers and the NI hardware. To ameliorate this, the scan buffer can also contain multiple copies of the scan path for a single frame. This behavior is controlled by the following parameter:
cp.stimBufPerFr = 1 // # of stimulus buffers per frame
-
Description text for GUI and scan mode (currently:
ScM_scanMode_XYImage
orScM_scanMode_TrajectArb
):cp.sConfigDesc = "64x64 image, 2ms/ln, os25" cp.ScanMode = ScM_scanMode_XYImage
-
For scan mode =
ScM_scanMode_XYImage
, an internal scan path function can be used - it generates simple rectangular image scans. In this case, no scan path function name must be given:cp.sExtScanPathFuncName = ""
For scan mode =
ScM_scanMode_TrajectArb
the name of a scan path function must be given. For details, see section Arbitrary Scans below. -
Parameters defining the AO and AI voltage range, the preselected AI channels and the number of available AI channels (up to 4). For AO channel definitions, see
ScM_USER_DEFINITIONS.ipf
.cp.minAI_V = -1.0 // AI voltage range minimum cp.maxAI_V = +5.0 // AI voltage range maximum cp.AIChannelSelect = 0x01 // AI channel pre-selection cp.nAIChansPossible = 4 // # of AI channels that can be recorded cp.minAO_V = -5.0 // AO voltage range minimum cp.maxAO_V = +5.0 // AO voltage range maximum
-
Park positions for the galvo scanners (AO #0,1), default level for the blanking signal (AO #2) and ...
cp.parkScannerX_V = 0.0 // Park positions cp.parkScannerY_V = 0.0 cp.parkBlankLaser_V = 4.0
-
All four AO channels are enabled by default. To change this, the user can selectively disable AO #2 and/or #3 using the following parameters. This can be useful to safe memory, i.e. in the case of high-resolution anatomy scans that do not require a z axis.
cp.noAOCh2_Blanking = 0 // 0=enabled (default), 1=disabled cp.noAOCh3_Z = 1
EDIT: AO channel #3 needs to be activated in order to use the ETL, even when only using it to change the focal plane with the GUI during a focus scan.
-
Currently, there is no dedicated line scan mode. As a work-around, one can "switch-off" the y scanner during normal x-y scans (mode
ScM_scanMode_XYImage
) using the following parameter:cp.noYScan = 1 // 0=ignored, 1=switch off y scanner
-
Note: There are a couple of z scan-related parameters defined; these may still be objected to changes and therefore should not be used for now.
cp.nZPixLineOffs = 0 // # of no-data pixels at line start cp.nZPixRetrace = 0 // # of no-data pixels for line retrace cp.dZDataPixels = 0 // # of data pixels per line cp.usesZForFastScan = 0
To define an new arbitrary scan pattern, two files must be created: a scan configuration file and a scan path function file.
It that describes the scan configuration and refers to a user-defined, external
scan path function. The name of this Igor .ipf
must be unique and reside in
and located in \WaveMetrics\Igor Pro 6 User Files\ScanM\ScM_Configs
.
Its layout must follow that of "standard" scan configuration files.
For an example, see stim_r8_2msLn_1os.ipf
, which defines a spiral scan
(by Luke Rogerson) and is listed in the following. For simplicity, only the
relevant sections are commented. For further explanations on the parameters,
see above.
#pragma rtGlobals = 1
// ---> START OF USER SECTION
The module name must be unique and the same as the .ipf
file name.
#pragma ModuleName = stim_r8_2msLn_1os
// <--- END OF USER SECTION
// ------------------------------------------------------
static function ScM_defineStim (sName)
string sName
struct ScM_ConfigParams cp
ScM_initConfigParamsStruct(cp, sName)
Do not change the code outside the sections marked USER SECTION
.
// ---> START OF USER SECTION
//
cp.sConfigDesc = "Radial Scan"
Set scan mode to ScM_scanMode_TrajectArb
and give the name of the scan
path function (here radialScan
) defined in the respective
scan path function file (see below).
cp.ScanMode = ScM_scanMode_TrajectArb
cp.sExtScanPathFuncName = "radialScan"
cp.aspectRatioFrame = 1.0 // Aspect ratio display wave
The following parameters define a scan line with 1664 data pixels, followed by a 336 pixel retrace period. A frame consists of 8 scan lines and the scan path for a full frame is written in one piece to the NI card.
cp.nXPixLineOffs = 0 // # of no-data pixels at line start
cp.nPixRetrace = 336 // # of no-data pixels for line retrace
cp.dXDataPixels = 1664 // # of data pixels per line
cp.dYPixels = 8 // # of lines per frame
cp.stimBufPerFr = 1 // # of stimulus buffers per frame
The duration of each scan line is 2 ms, with a pixel duration of 1 µs and no over-sampling.
cp.targetedPixelDur_us = 1 // total pixel duration
cp.nSubPixOversample = 1 // oversampling factor
For displaying purposes, ScanM requests full frames.
cp.nDivFrameBuf = 1 // # of pixel buffers used per frame
Set the voltage range for the AI channels, pre-select AI channel #0 as a minimum requirement but allow the user to select all 4 AI channels, if needed.
cp.minAI_V = -1.0 // AI voltage range minimum
cp.maxAI_V = +5.0 // AI voltage range maximum
cp.AIChannelSelect = 0x01 // AI channel pre-selection
cp.nAIChansPossible = 4 // # of AI channels that can be recorded
Set the allowed voltage range of the AO channels.
cp.minAO_V = -4.0*sqrt(pi) // AO voltage range minimum
cp.maxAO_V = +4.0*sqrt(pi) // AO voltage range maximum
Set park positions of the scanners, the state of the blanking signal between scans, and disable AO #3 (no z axis).
cp.parkScannerX_V = 0.0 // Park positions
cp.parkScannerY_V = 0.0
cp.parkBlankLaser_V = 0.0
cp.noAOCh3_Z = 1
Here come the parameters specific for scan mode ScM_scanMode_TrajectArb
.
Since individual pixels may not be easily assigned to distinct positions
and/or certain positions may be scanned a different number of times during a
frame, a virtual frame size for decoding/display purposes is defined:
cp.dxFrDecoded = 32 // Frame size for decoded / reconstructed image
cp.dyFrDecoded = 32
Default voltage range for the scan path of a frame:
cp.trajDefVRange_V = 1 // default voltage range in V
Since different scan paths may need different parameters, the user can here define an arbitrary set of (max. 100) additional parameters to be used in the scan path function. As an example, here is the set of parameters for Luke's spiral scans:
cp.nTrajParams = 5 // 0="standard" spiral, following parameters ignored
cp.trajParams[0] = 8 // Number of rotated offsets
cp.trajParams[1] = 0.015 // Step size
cp.trajParams[2] = 1/7.5*pi // Tightness of coils
cp.trajParams[3] = 64 // nPixels before end of spiral to start blanking
cp.trajParams[4] = 128 // nPixels pause in the centre before spiral start
// <--- END OF USER SECTION
ScM_createStim(cp)
end
// ------------------------------------------------------
It contains a defined set of standardized functions: (a) to generate the scan path, (b) to prepare a decoder, and (c) to decode/reconstruct a frame for display.
Scan path function files are .ipf
files, live in
\WaveMetrics\Igor Pro 6 User Files\User Procedures\ScanM
and their name must start with spf_
.
For an example, see spf_Spirals.ipf
, which defines the scan path and decoder
functions for a spiral scan (by Luke Rogerson) and is listed in the following.
For simplicity, only the relevant sections are commented in detail. Critical sections
are marked. For the full example, see file.
CRITICAL - Define module name:
// ---> START OF USER SECTION
#pragma ModuleName = spf_Spirals
// <--- END OF USER SECTION
This function is called once when the configuration file is loaded and the scan
path is generated. Its name must be unique in ScanM and identical to the string
assigned to cp.sExtScanPathFuncName
in the scan configuration file.
As input, it receives ...
-
wScanPathFuncParams[]
with the main scan path parameters from the scan configuration file. For details, see below.
All input parameters are read-only - they must not be changed!!
As output, it must generate in the current data folder 4 temporary waves (float) containing the scan path voltages for all 4 AO channels for the duration of one frame. Note that even for disabled AO channels, a wave each must be generated (and set to zero). The waves need to be named as follows:
-
StimX[]
, AO channel 0, x scanner -
StimY[]
, AO channel 1, y scanner -
StimPC[]
, AO channel 2, laser blanking signal (TTL levels) -
StimZ[]
, AO channel 3, z axis (i.e. ETL)
CRITICAL - The head of the first function has to look as follows:
function radialScan(wScanPathFuncParams)
wave wScanPathFuncParams
From here on, it is up to the user how to structure the function. In this example,
first some variables are defined and parameters are retrieved from wScanPathFuncParams
.
variable numberOfPoints, flyback, MaxAO_V, dxFrDecoded, dyFrDecoded
variable nOffsets, stepSize, coilFactor, earlyRetrace, pauseRetrace
variable totalSize, normFactor, angle, away, itx
variable awayStep, theta, delta
variable x_max, y_max
// Retrieve general parameters about the scan configuration
//
numberOfPoints = wScanPathFuncParams[0] // cp.dXDataPixels, number of pixels per line (w/o retrace)
flyback = wScanPathFuncParams[1] // cp.nPixRetrace, number of pixels for retrace
totalsize = wScanPathFuncParams[2] // cp.trajDefaultVRange, default total voltage range
MaxAO_V = wScanPathFuncParams[3] // cp.maxAO_V, maximal AO voltage
dxFrDecoded = wScanPathFuncParams[4] // cp.dxFrDecoded, frame width for reconstructed/decoded frame
dyFrDecoded = wScanPathFuncParams[5] // cp.dyFrDecoded, frame height for reconstructed/decoded frame
// Retrieve scan function-specific, user-defined parameters
// with the number of parameters defined in cp.nTrajParams and
// the parameters in cp.trajParams[]
//
nOffsets = wScanPathFuncParams[6] // Number of rotated offsets
stepSize = wScanPathFuncParams[7] // Step size
coilFactor = wScanPathFuncParams[8] // Tightness of coils
earlyRetrace = wScanPathFuncParams[9] // nPixels before end of spiral to start blanking
pauseRetrace = wScanPathFuncParams[10] // nPixels pause in the centre before spiral start
In the example file, a number variables are initialized:
numberOfPoints += flyback
awayStep = 1 /(coilFactor *2 *pi)
theta = stepSize /coilFactor
CRITICAL - Generate the four output waves (as decribed above). In this example,
NumberofPoints
containes the number of pixels in a scan line and nOffsets
the
number of scan lines per frame.
make/O/N=(NumberofPoints*nOffsets) StimX = 0
make/O/N=(NumberofPoints*nOffsets) StimY = 0
make/O/N=(NumberofPoints*nOffsets) StimZ = 0
make/O/N=(NumberofPoints*nOffsets) StimPC = ScM_TTLhigh
In the rest of the function, these waves are filled with the scan path voltages.
For details, see spf_Spirals.ipf
.
CRITICAL - Make sure that you clean up temporary waves at the end of the function to reduce the risk that Igor runs out of memory.
killWaves/z StimX1Offs, StimY1Offs, StimPC1Offs
killWaves/z pix_x, pix_y, pix_r, pix_theta
end
This function prepares the decoding of the pixel data generated with the respective
scan path function. This will be called once when the stimulus configuration is loaded,
right after calling the scan path function and after copying StimX
, StimY
,
StimZ
and StimPC
into the scan stimulus buffer of the scan configuration.
This function can be empty if not needed; it is meant to be used to create waves
and global variables (in the datafolder of that particular scan configuration) that
are needed for decoding or to accelerate decoding during the scan.
This function's name must be <scan path function>_prepareDecode
, in this example
radialScan_prepareDecode
.
As input, it receives ...
- the scan stimulus buffer
wStimBufData[nCh][]
- the scan parameters
wScanPathFuncParams
(for details on content, see scan path function above).
All input parameters are read-only - they must not be changed!!
As output, the function must return one of two constants:
-
SCM_PixDataResorted
if pixel data is just resorted (w/o loss of information) by the scan decoder or -
SCM_PixDataDecoded
if the reconstruction/decoding of the pixel data involves some kind of information loss.
The return value determines if ScM_FileIO.ipf
retains two sets of data waves (SCM_PixDataDecoded
),
one with the raw and one with the decoded pixe data, or just one set (SCM_PixDataResorted
).
CRITICAL - The head of this function has to look as follows:
function radialScan_prepareDecode(wStimBufData, wScanPathFuncParams)
wave wStimBufData, wScanPathFuncParams
From here on, it is up to the user how to structure the function. In this example,
first some variables are defined and parameters are retrieved from wScanPathFuncParams
.
variable dxFrDecoded, dyFrDecoded
// Retrieve parameters about the scan configuration
//
dxFrDecoded = wScanPathFuncParams[4] // cp.dxFrDecoded, frame width for reconstructed/decoded frame
dyFrDecoded = wScanPathFuncParams[5] // cp.dyFrDecoded, frame height for reconstructed/decoded frame
In the example, waves are generated that later help/accelerate decoding/reconstructing the frames for display, i.e. projecting the spirals onto a 32x32 pixel frame.
CRITICAL - Again make sure that you clean up temporary waves at the end of the function to reduce the risk that Igor runs out of memory.
This function is called frequently (for each pixel buffer and AI channel retrieved) during the scanning. Its job is to decode the pixel data on the fly and it is responsible for populating the display wave.
Note that the function should be very fast; it should not take more than a few milliseconds per call, otherwise the display will more and more lag behind the recoding. If nescessary, an external XOP function can be implemented to speed up decoding - this is, in fact, done in the example.
This function's name must be <scan path function>_decode
, that is in the example
radialScan_decode
.
As input, it receives ...
- the linearized display wave
wImgFrame[dx*dy]
for the current AI channel - a copy of the display wave,
wImgFrameAv[dx*dy]
, that can be used e.g. for averaging - the new pixel data block
wPixelDataBlock[]
(contains the data for all recorded AI channels) - a string
sCurrConfPath
with the path to the current scan configuration folder in case waves need to be accessed there. - a numeric wave
wParams
, which currently contains:-
wParams[0]
- the number of AI channels recorded (1..4) -
wParams[1]
- the index of the AI channel to be decoded (0..3) -
wParams[2]
- the line offset (in pixels) -
wParams[3]
- the length of a frame (in pixels) -
wParams[4]
- the length of a pixel data block per channel (in pixels) -
wParams[5]
- the number of frames per step (only used for z-stack averaging) -
wParams[6]
- 1=only complete frames are shown, 0=pixel blocks are shown as they arrive
-
As output, the display wave wImgFrame[]
(see input parameters) must be populated.
CRITICAL - The head of this function has to look as follows:
function radialScan_decode(wImgFrame, wImgFrameAv, wPixelDataBlock, sCurrConfPath, wParams)
wave wImgFrame, wImgFrameAv, wPixelDataBlock
string sCurrConfPath
wave wParams
In the example, a number of local variables are defined:
variable dxFrDecoded, dyFrDecoded, nAICh, iAICh
variable dataLength, start, stop
variable nPoints, nOffsets, offset_pixel, data_pixels
variable flyback
sCurrConfPath
can be employed to get access to waves and globale variables
in the current scan configuration data folder, if needed.
wave pCountMatrix = $(sCurrConfPath +"countMatrix")
wave pwStimBufData = $(sCurrConfPath +"wStimBufData")
wave pwScanPathFuncParams = $(sCurrConfPath +"wScanPathFuncParams")
Retrieve the required parameters about the scan configuration etc.
data_pixels = pwScanPathFuncParams[0] // cp.dXDataPixels, number of pixels per line (w/o retrace)
flyback = pwScanPathFuncParams[1] // cp.nPixRetrace, number of pixels for retrace
dxFrDecoded = pwScanPathFuncParams[4] // cp.dxFrDecoded, frame width for reconstructed/decoded frame
dyFrDecoded = pwScanPathFuncParams[5] // cp.dyFrDecoded, frame height for reconstructed/decoded frame
nOffsets = pwScanPathFuncParams[6] // Number of rotated offsets
offset_pixel = pwScanPathFuncParams[9] // nPixels before end of spiral to start blanking
To interpret the pixel buffer, the number of recorded AI channels and the index of the AI channel to reconstruct/decode is needed:
nAICh = wParams[0] // number of AI channels recorded (1..4)
iAICh = wParams[1] // index of AI channel to reconstruct (0..3)
From here on, the script starts decoding the pixel buffer to reconstruct the
current frame for display. For details, see spf_Spirals.ipf
.
Note that spf_Spirals.ipf
uses an external function ( ScanDecoder(...)
)
from an XOP. The function was written in C++ (by Luke Rogerson) and compiled into
an XOP.
-
Getting started ...
1.1 Installation
1.2 Release notes
1.3 Issues -
User manual (preliminary)
2.1 Scan configurations
2.2 Specific features