igor-public/pearl/pearl-anglescan-tracker.ipf
matthias muntwiler 80a01f2bdb updates: pshell import, angle-scans, elog
- pshell import: fix units and data scaling.
- pshell import: support new multi-region scans.
- angle scans: add trim function.
- angle scans: update import_tpi_scan function.
- angle scans: fix scales of check waves in normalization.
- area display: new cursor mode for background selection.
- elog: bugfixes (attachment list, check existing logbook).
2017-07-04 11:06:49 +02:00

1259 lines
33 KiB
Igor

#pragma rtGlobals=3
#pragma version = 1.4
#pragma IgorVersion = 6.2
#pragma ModuleName = PearlAnglescanTracker
#include "pearl-area-profiles", version > 1.04
#include "pearl-area-import", version > 1.05
#include "pearl-scienta-preprocess", version > 1.00
#include "pearl-anglescan-process", version >= 1.6
#include <New Polar Graphs>
// $Id$
//
// author: matthias.muntwiler@psi.ch
// Copyright (c) 2014-15 Paul Scherrer Institut
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
/// @file
/// @brief instant visualization of angle scan and manipulator position.
/// @ingroup ArpesPackage
///
/// the angle scan tracker is an instant, progressive display of an angle scan during acquisition.
/// the detector window indicator provides an intuitive way to understand the manipulator motion and angle mapping of the instrument.
/// this can be useful to align the detector with a sample direction in a reference scan.
///
/// the tracker will run in offline mode, if it is started on a computer not connected to the beamline network.
/// in offline mode, all features except data exchange with the instrument are still available.
/// this is useful to simulate the measurement settings of an experiment while planning or during data analysis.
///
/// the following public functions can be called by the user.
///
/// * ast_setup() - set up data structures and display graph
/// * ast_prepare() - prepare for new measurement, clear graph
/// * ast_add_image() - add a detector image to the graph
/// * ast_update_detector() - set the current position indicator
/// * ast_import() - load an angle scan as used by pearl-anglescan-process.ipf
/// * ast_export() - export accumulated data to a separate data folder as used by pearl-anglescan-process.ipf
/// * ast_close() - stop tracker, close graph, release data structures
///
/// @pre
/// run-time requirements for online mode at the beamline
/// * EPICS.XOP of Paul Scherrer Institut, version 0.3.0 (March 2015) or later, must be loaded.
/// * caRepeater.exe program (from EPICS distribution) must be running.
/// * EPICS_CA_MAX_ARRAY_BYTES environment variable of Igor and caRepeater must be set to >= 50000000.
///
/// @see pearl-anglescan-process.ipf
///
/// @author matthias muntwiler, matthias.muntwiler@psi.ch
///
/// @copyright 2013-15 Paul Scherrer Institut @n
/// Licensed under the Apache License, Version 2.0 (the "License"); @n
/// you may not use this file except in compliance with the License. @n
/// You may obtain a copy of the License at
/// http://www.apache.org/licenses/LICENSE-2.0
/// @namespace PearlAnglescanTracker
/// @brief instant visualization of angle scan and manipulator position.
///
/// PearlAnglescanTracker is declared in @ref pearl-anglescan-tracker.ipf.
///
/// package name is used as data folder name
static strconstant package_name = "pearl_anglescan_tracker"
/// data folder path
static strconstant package_path = "root:packages:pearl_anglescan_tracker:"
/// semicolon-separated list of persistent variable, string, and wave names
static strconstant prefs_objects = "projection;theta_offset;tilt_offset;phi_offset;reduction_func;reduction_params"
/// initialize package data once when the procedure is first loaded
static function AfterCompiledHook()
dfref savedf = GetDataFolderDFR()
variable do_init = 1
if (DataFolderExists(package_path))
setdatafolder $(package_path)
nvar /z init_done
if (nvar_exists(init_done))
if (init_done)
do_init = 0
endif
endif
endif
if (do_init)
init_package()
load_prefs()
setdatafolder $(package_path)
variable /g init_done = 1
endif
setdatafolder savedf
return 0
end
static function init_package()
dfref savedf = getdatafolderdfr()
setdatafolder root:
newdatafolder /o/s packages
newdatafolder /o/s $package_name
// configuration (persistent)
string /g graphname = "graph_anglescan_tracker"
string /g dataname = "tracker"
string /g projection = "stereographic"
// recently used (persistent)
variable /g theta_offset = 0
variable /g tilt_offset = 0
variable /g phi_offset = 0
string /g reduction_func = "int_linbg_reduction"
string /g reduction_params = "Lcrop=0.1;Hcrop=0.1;Lsize=0.2;Hsize=0.2;Cpos=0.5;Csize=0.4"
// recently used (volatile)
string /g export_folderpath = "root:"
variable /g export_format = 1
// run-time variables (volatile)
string /g detector_tracename
variable /g capturing = 0
variable /g buf_size = 0 // number of measurements that fit into the data buffer
variable /g buf_count = 0 // number of measurements contained in the data buffer
variable /g buf_width = 0 // number of slices that fit into the data buffer
// load icon of sample holder
string path = FunctionPath("")
path = ParseFilePath(1, path, ":", 1, 0) + "tracker-sample-picture.png"
LoadPict /O/Q path, pict_tracker_sample
setdatafolder savedf
end
/// save persistent package data to the preferences file.
///
/// this function is called when the user clicks the corresponding button.
///
static function save_prefs()
dfref saveDF = GetDataFolderDFR()
string fullPath = SpecialDirPath("Packages", 0, 0, 0)
fullPath += package_name
NewPath/O/C/Q tempPackagePrefsPath, fullPath
fullPath += ":preferences.pxp"
SetDataFolder root:packages
SetDataFolder $package_name
SaveData /O /Q /J=prefs_objects fullPath
KillPath/Z tempPackagePrefsPath
SetDataFolder saveDF
end
/// load persistent package data from the preferences file.
///
/// the preferences file is an Igor packed experiment file in a special preferences folder.
///
/// this function is called automatically when the procedure is first compiled,
/// or whenever the user clicks the corresponding button.
///
static function load_prefs()
dfref saveDF = GetDataFolderDFR()
variable result = -1
setdatafolder root:
NewDataFolder /O/S packages
NewDataFolder /O/S $package_name
string fullPath = SpecialDirPath("Packages", 0, 0, 0)
fullPath += package_name
GetFileFolderInfo /Q /Z fullPath
if (V_Flag == 0) // Disk directory exists?
fullPath += ":preferences.pxp"
GetFileFolderInfo /Q /Z fullPath
if (V_Flag == 0) // Preference file exist?
LoadData /O /R /Q fullPath
result = 0
endif
endif
SetDataFolder saveDF
return result
end
/// disconnect EPICS channels before Igor quits.
static function IgorQuitHook(app)
string app
epics_disconnect()
end
/// set up data structures, display graph, and try to connect to analyser.
function ast_setup()
setup_data()
setup_detector()
setup_graph()
epics_connect()
end
/// prepare for new measurement and clear the data buffer.
///
/// optionally, set new manipulator offsets.
/// the offsets are the manipulator readback coordinates
/// where the sample surface is oriented in normal emission,
/// and the handle of the sample plate points horizontally to the left (9 o'clock).
///
/// @param theta_offset set new theta offset. default: no change.
/// @param tilt_offset set new tilt offset. default: no change.
/// @param phi_offset set new phi offset. default: no change.
function ast_prepare([theta_offset, tilt_offset, phi_offset])
variable theta_offset
variable tilt_offset
variable phi_offset
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
if (!ParamIsDefault(theta_offset))
nvar v_theta_offset = theta_offset
v_theta_offset = theta_offset
endif
if (!ParamIsDefault(tilt_offset))
nvar v_tilt_offset = tilt_offset
v_tilt_offset = tilt_offset
endif
if (!ParamIsDefault(phi_offset))
nvar v_phi_offset = phi_offset
v_phi_offset = phi_offset
endif
nvar buf_count
nvar buf_size
nvar buf_width
buf_count = 0
buf_size = 0
buf_width = 0
svar dataname
clear_hemi_grid(dataname)
// work-around: set one point to a real number to make the rest of the trace in the graph transparent
wave values = $(dataname + "_i")
values[numpnts(values) - 1] = 0
update_data_graph()
setdatafolder saveDF
end
/// set the data processing parameters
///
/// the parameters will be effective for subsequent measurements only.
/// previously acquired data is not affected.
/// the processing parameters are saved with the preferences.
///
/// @param reduction_func name of custom reduction function, e.g. "int_linbg_reduction".
/// any user-defined function with the same signature as adh5_default_reduction() is allowed.
/// @param reduction_params parameter string for the reduction function.
/// the format depends on the actual function.
/// for int_linbg_reduction, e.g., "Lcrop=0.1;Hcrop=0.1;Lsize=0.2;Hsize=0.2;Cpos=0.5;Csize=0.4".
function ast_set_processing(reduction_func, reduction_params)
string reduction_func
string reduction_params
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
svar red_func = reduction_func
svar red_params = reduction_params
red_func = reduction_func
red_params = reduction_params
setdatafolder saveDF
end
/// process and add a detector image to the tracker scan.
///
/// @param image detector image with correct X (energy) and Y (angle) scaling
/// @param theta polar angle of manipulator
/// @param tilt tilt angle of manipulator
/// @param phi azimuthal angle of manipulator
///
/// the manipulator angles are corrected by the preset offsets internally.
function ast_add_image(image, theta, tilt, phi)
wave image
variable theta
variable tilt
variable phi
add_image_data(image, theta, tilt, phi)
process_image_data()
update_data_graph()
end
/// export tracker data to a separate, independent data set.
///
/// the exported data can then be used for further processing.
/// the data is exported to the current data folder, or root if XPDplot compatibility is requested.
///
/// @param folder destination folder path
/// @param nickname name prefix for waves
/// @param xpdplot xpdplot compatibility, see make_hemi_grid() for details
/// @arg 0 (default)
/// @arg 1 create additional waves and notebook required by XPDplot
///
function ast_export(folder, nickname, [xpdplot])
dfref folder
string nickname
variable xpdplot
if (ParamIsDefault(xpdplot))
xpdplot = 0
endif
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
svar dataname
duplicate_hemi_scan(dataname, folder, nickname, xpdplot=xpdplot)
setdatafolder saveDF
end
/// import tracker data from an existing angle scan dataset.
///
/// @param nickname name prefix for waves. data must be in current data folder.
///
function ast_import(nickname)
string nickname
dfref savedf = getdatafolderdfr()
svar /sdfr=$(package_path) dataname
duplicate_hemi_scan(nickname, $(package_path), dataname)
setdatafolder saveDF
end
/// update the current position indicator.
///
/// @param theta polar angle of manipulator
/// @param tilt tilt angle of manipulator
/// @param phi azimuthal angle of manipulator
/// @param range angle range (60 or 45)
///
/// the manipulator angles are corrected by the preset offsets internally.
///
function ast_update_detector(theta, tilt, phi, range)
variable theta
variable tilt
variable phi
variable range
update_detector(theta, tilt, phi, range)
update_detector_graph()
end
/// stop tracker, close graph, release data structures.
function ast_close()
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
epics_disconnect()
svar graphname
KillWindow $graphname
setdatafolder saveDF
end
static function setup_data()
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
nvar buf_size
nvar buf_count
nvar buf_width
buf_size = 0
buf_count = 0
buf_width = 0
make /n=(1,1) /o buf_i
make /n=(1) /o buf_th, buf_ph, buf_ti
svar dataname
variable npolar = 91
make_hemi_grid(npolar, dataname)
// work-around: set one point to a real number to make the rest of the trace in the graph transparent
wave values = $(dataname + "_i")
values[numpnts(values) - 1] = 0
setdatafolder saveDF
end
/// extend the data buffer for the next polar scan
///
/// call this function if the buffer is full.
///
/// @param num_slices number of slices that the measurement contains
///
static function extend_data(num_slices)
variable num_slices
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
nvar buf_size
nvar buf_count
nvar buf_width
variable new_size = buf_size + 91
buf_width = num_slices
wave buf_i
wave buf_th
wave buf_ti
wave buf_ph
redimension /n=(buf_width,new_size) buf_i
redimension /n=(new_size) buf_th, buf_ph, buf_ti
buf_i[][buf_size, new_size-1] = nan
buf_th[buf_size, new_size-1] = nan
buf_ph[buf_size, new_size-1] = nan
buf_ti[buf_size, new_size-1] = nan
buf_size = new_size
setdatafolder saveDF
return buf_count
end
static function setup_detector()
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
make /n=31 /o detector_angle, detector_pol, detector_az, detector_rad
setscale /i x -30, 30, "°", detector_angle, detector_pol, detector_az, detector_rad
detector_angle = x
setdatafolder saveDF
end
/// reduce a detector image and add the result to the data buffer.
///
/// @param image detector image with correct X (energy) and Y (angle) scaling
/// @param theta polar angle of manipulator
/// @param tilt tilt angle of manipulator
/// @param phi azimuthal angle of manipulator
///
/// the manipulator angles are corrected by the preset offsets internally.
static function add_image_data(image, theta, tilt, phi)
wave image
variable theta
variable tilt
variable phi
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
svar dataname
nvar theta_offset
nvar tilt_offset
nvar phi_offset
// extract angle distribution from image using reduction function mechanism from area-import
svar red_func_name = reduction_func
svar red_params = reduction_params
funcref adh5_default_reduction red_func = $red_func_name
variable nx = dimsize(image, 0)
make /n=(nx) /free profile1, profile2
string loc_params = red_params
red_func(image, profile1, profile2, loc_params)
nx = numpnts(profile1)
// write the result to the buffer
nvar buf_count
nvar buf_size
nvar buf_width
wave buf_i
wave buf_th
wave buf_ph
wave buf_ti
if ((buf_count >= buf_size) || (nx > buf_width))
extend_data(nx)
setscale /p x dimoffset(profile1,0), dimdelta(profile1,0), waveunits(profile1,0), buf_i
endif
buf_i[][buf_count] = profile1[p]
buf_th[buf_count] = theta - theta_offset
buf_ti[buf_count] = -(tilt - tilt_offset)
buf_ph[buf_count] = phi - phi_offset
buf_count += 1
setdatafolder saveDF
end
/// process the data buffer to generate the tracker dataset.
///
static function process_image_data()
wave image
variable theta
variable tilt
variable phi
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
svar dataname
nvar buf_count
nvar buf_size
nvar buf_width
wave buf_i
wave buf_th
wave buf_ph
wave buf_ti
duplicate /free /R=[0,buf_width-1][0,buf_count-1] buf_i, buf_n
duplicate /free /R=[0,buf_count-1] buf_th, w_th
duplicate /free /R=[0,buf_count-1] buf_ti, w_ti
duplicate /free /R=[0,buf_count-1] buf_ph, w_ph
normalize_strip_x(buf_n, smooth_method=4)
if (buf_count >= 10)
normalize_strip_theta(buf_n, w_th, smooth_method=4)
endif
if (dimoffset(buf_i,0) < -20)
crop_strip(buf_n, -25, 25)
else
crop_strip(buf_n, -15, 15)
endif
make /n=1 /free d_polar, d_azi
convert_angles_ttpd2polar(w_th, w_ti, w_ph, buf_n, d_polar, d_azi)
d_azi += 180 // changed 151030 (v1.4)
d_azi = d_azi >= 360 ? d_azi - 360 : d_azi
hemi_add_anglescan(dataname, buf_n, d_polar, d_azi)
setdatafolder saveDF
end
/// update the current position indicator.
///
/// @param theta polar angle of manipulator
/// @param tilt tilt angle of manipulator
/// @param phi azimuthal angle of manipulator
/// @param range angle range (60 or 45)
///
/// the manipulator angles are corrected by the preset offsets internally.
///
static function update_detector(theta, tilt, phi, range)
variable theta
variable tilt
variable phi
variable range
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
nvar theta_offset
nvar tilt_offset
nvar phi_offset
make /n=1 /free m_theta
make /n=1 /free m_tilt
make /n=1 /free m_phi
m_theta = theta - theta_offset
m_tilt = tilt - tilt_offset
m_tilt *= -1 // checked 140702
m_phi = phi - phi_offset
//m_phi *= -1 // checked 140702
wave detector_angle, detector_pol, detector_az, detector_rad
setscale /i x -range/2, +range/2, "°", detector_angle
detector_angle = x
convert_angles_ttpa2polar(m_theta, m_tilt, m_phi, detector_angle, detector_pol, detector_az)
redimension /n=(numpnts(detector_pol)) detector_rad
detector_rad = 2 * tan(detector_pol / 2 * pi / 180)
detector_az += 180 // changed 151030 (v1.4)
detector_az = detector_az >= 360 ? detector_az - 360 : detector_az
setdatafolder saveDF
end
/// create the graph window.
static function setup_graph()
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
svar dataname
svar graphname
wave detector_az
wave detector_rad
wave detector_angle
svar tracename = detector_tracename
graphname = display_hemi_scan(dataname, graphname=graphname)
tracename = WMPolarAppendTrace(graphname, detector_rad, detector_az, 360)
ModifyGraph /w=$graphname lstyle($tracename)=0
ModifyGraph /w=$graphname lsize($tracename)=1.5
ModifyGraph /w=$graphname zColor($tracename)={detector_angle,*,*,RedWhiteBlue,0}
ColorScale /w=$graphname /C /N=text1 trace=$tracename
ColorScale /w=$graphname /C /N=text1 /F=0 /B=1 /A=LB /X=0.00 /Y=0.00
ColorScale /w=$graphname /C /N=text1 width=1.5, heightPct=20, frame=0.00
ColorScale /w=$graphname /C /N=text1 lblMargin=0
ColorScale /w=$graphname /C /N=text1 nticks=2, tickLen=2.00, tickThick=0.50
TextBox /w=$graphname /C /N=tb_manip /F=0 /B=1 /X=0.00 /Y=0.00 /E=2 "\\{\"manip = (%.1f, %.1f, %.1f)\", "
AppendText /w=$graphname /N=tb_manip /NOCR "root:packages:pearl_anglescan_tracker:curTheta, "
AppendText /w=$graphname /N=tb_manip /NOCR "root:packages:pearl_anglescan_tracker:curTilt, "
AppendText /w=$graphname /N=tb_manip /NOCR "root:packages:pearl_anglescan_tracker:curPhi}"
// the window hook releases the EPICS variables when the window is killed
DoWindow /T $graphname, "Angle Scan Tracker"
SetWindow $graphname, hook(ast_hook) = ast_window_hook
ControlBar /w=$graphname 21
Button b_capture win=$graphname, title="start", pos={0,0}, size={40,21}, proc=PearlAnglescanTracker#bp_capture
Button b_capture win=$graphname, fColor=(65535,65535,65535), fSize=10
Button b_capture win=$graphname, help={"Start/stop capturing."}
PopupMenu pm_params win=$graphname, mode=0, value="load preferences;save preferences;reduction parameters;manipulator offsets", title="parameters"
PopupMenu pm_params win=$graphname, pos={70,0}, bodyWidth=80, proc=PearlAnglescanTracker#pmp_parameters
PopupMenu pm_params win=$graphname, help={"Load/save/edit data processing parameters"}
PopupMenu pm_data win=$graphname, mode=0, value="import;export;load file;save file", title="data"
PopupMenu pm_data win=$graphname, pos={120,0}, proc=PearlAnglescanTracker#pmp_data
PopupMenu pm_data win=$graphname, help={"Load/save data from/to independent dataset or file"}
SetDrawLayer /w=$graphname ProgFront
SetDrawEnv /w=$graphname xcoord=rel, ycoord=rel
DrawPict /w=$graphname 0, 0, 1, 1, pict_tracker_sample
update_capture()
setdatafolder saveDF
end
static function update_data_graph()
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
svar dataname
svar graphname
// nothing to do - trace is updated automatically
setdatafolder saveDF
end
static function update_detector_graph()
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
svar dataname
svar graphname
// nothing to do - trace is updated automatically
setdatafolder saveDF
end
/// connect the angle scan tracker to EPICS
///
/// the tracker uses channels of the analyser and the manipulator.
///
/// if the EPICS XOP is not loaded, the function does nothing.
/// if channels are not available, the function exits with an error code after a timeout of 5 seconds.
/// the Igor run-time error status is reset to suppress the error dialog.
///
/// @returns zero if successful, non-zero if an error occurred
///
/// @todo the X03DA channel names are hard-coded.
static function epics_connect()
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
// close PVs which may be open from a previous call
epics_disconnect()
// create variables and waves
make /n=(1)/o arraydata, xscale, yscale
make /n=(1,1)/o image
variable /g ndimensions
variable /g arraysize0, arraysize1
variable /g datatype
variable /g colormode
string /g controls, monitors
string /g xunits, yunits
// channel ID variables
variable /g chidDetectorState = 0
variable /g chidArrayData = 0
variable /g chidXScale = 0
variable /g chidYScale = 0
variable /g chidNDimensions = 0
variable /g chidArraySize0 = 0
variable /g chidArraySize1 = 0
variable /g chidDataType = 0
variable /g chidColorMode = 0
variable /g chidLensMode = 0
variable /g chidTheta = 0
variable /g chidTilt = 0
variable /g chidPhi = 0
variable /g curDetectorState = 0
variable /g curLensMode = 0
variable /g curTheta = 0
variable /g curTilt = 0
variable /g curPhi = 0
variable /g acqTheta = 0
variable /g acqTilt = 0
variable /g acqPhi = 0
variable /g connected = 0
string epicsname = "X03DA-SCIENTA:"
string imagename = epicsname + "image1:"
string camname = epicsname + "cam1:"
string manipname = "X03DA-ES2-MA:"
variable timeout = 5 // seconds
#if exists("pvWait")
// EPICS.XOP version 0.3.0 or later
pvOpen /Q chidDetectorState, camname + "DetectorState_RBV" // 0 = idle
pvOpen /Q chidLensMode, camname + "LENS_MODE_RBV"
pvOpen /Q chidXScale, camname + "CHANNEL_SCALE_RBV"
pvOpen /Q chidYScale, camname + "SLICE_SCALE_RBV"
pvOpen /Q chidArrayData, imagename + "ArrayData"
pvOpen /Q chidNDimensions, imagename + "NDimensions_RBV"
pvOpen /Q chidArraySize0, imagename + "ArraySize0_RBV"
pvOpen /Q chidArraySize1, imagename + "ArraySize1_RBV"
pvOpen /Q chidDataType, imagename + "DataType_RBV"
pvOpen /Q chidColorMode, imagename + "ColorMode_RBV"
pvOpen /Q chidTheta, manipname + "THT.RBV"
pvOpen /Q chidTilt, manipname + "TLT.RBV"
pvOpen /Q chidPhi, manipname + "PHI.RBV"
pvWait timeout
if (!GetRTError(1))
connected = 1
endif
#elif exists("pvOpen")
// EPICS.XOP version < 0.3.0
pvOpen /T=(timeout) chidDetectorState, camname + "DetectorState_RBV" // 0 = idle
pvOpen /T=(timeout) chidLensMode, camname + "LENS_MODE_RBV"
pvOpen /T=(timeout) chidXScale, camname + "CHANNEL_SCALE_RBV"
pvOpen /T=(timeout) chidYScale, camname + "SLICE_SCALE_RBV"
pvOpen /T=(timeout) chidArrayData, imagename + "ArrayData"
pvOpen /T=(timeout) chidNDimensions, imagename + "NDimensions_RBV"
pvOpen /T=(timeout) chidArraySize0, imagename + "ArraySize0_RBV"
pvOpen /T=(timeout) chidArraySize1, imagename + "ArraySize1_RBV"
pvOpen /T=(timeout) chidDataType, imagename + "DataType_RBV"
pvOpen /T=(timeout) chidColorMode, imagename + "ColorMode_RBV"
pvOpen /T=(timeout) chidTheta, manipname + "THT.RBV"
pvOpen /T=(timeout) chidTilt, manipname + "TLT.RBV"
pvOpen /T=(timeout) chidPhi, manipname + "PHI.RBV"
if (!GetRTError(1))
connected = 1
endif
#endif
#if exists("pvMonitor")
if (connected)
pvMonitor /F=ast_callback_detector chidDetectorState, curDetectorState
pvMonitor /F=ast_callback_manip chidTheta, curTheta
pvMonitor /F=ast_callback_manip chidTilt, curTilt
pvMonitor /F=ast_callback_manip chidPhi, curPhi
pvMonitor /F=ast_callback_manip chidLensMode, curLensMode
pvMonitor /F=ast_callback_data chidArrayData
endif
#endif
if (connected)
print "angle scan tracker: online"
else
print "angle scan tracker: offline"
endif
setdatafolder saveDF
return !connected
end
static function epics_disconnect_chid(chid_var_name)
string chid_var_name
#if exists("pvClose")
nvar /z chid = $chid_var_name
if (nvar_exists(chid))
if (chid != 0)
pvClose chid
endif
chid = 0
endif
#endif
end
static function epics_disconnect()
dfref savedf = GetDataFolderDFR()
setdatafolder $(package_path)
nvar connected
if (connected)
connected = 0
epics_disconnect_chid("chidDetectorState")
epics_disconnect_chid("chidArrayData")
epics_disconnect_chid("chidXScale")
epics_disconnect_chid("chidYScale")
epics_disconnect_chid("chidNDimensions")
epics_disconnect_chid("chidArraySize0")
epics_disconnect_chid("chidArraySize1")
epics_disconnect_chid("chidDataType")
epics_disconnect_chid("chidColorMode")
epics_disconnect_chid("chidLensMode")
epics_disconnect_chid("chidTheta")
epics_disconnect_chid("chidTilt")
epics_disconnect_chid("chidPhi")
print "angle scan tracker: offline"
endif
setdatafolder savedf
end
/// window hook
///
/// disconnects from EPICS when the window is closed.
///
static function ast_window_hook(s)
STRUCT WMWinHookStruct &s
Variable hookResult = 0
switch(s.eventCode)
case 2: // kill
epics_disconnect()
break
endswitch
return hookResult
End
/// callback function for new analyser data from EPICS.
function ast_callback_data(chan)
variable chan
nvar capturing = $(package_path + "capturing")
if (!capturing)
return 0
endif
dfref savedf = GetDataFolderDFR()
setdatafolder $(package_path)
#if exists("pvGetWave")
// retrieve data
nvar chidArrayData
nvar chidXScale
nvar chidYScale
nvar chidNDimensions
nvar chidArraySize0
nvar chidArraySize1
nvar chidDataType
nvar chidColorMode
nvar chidTheta
nvar chidTilt
nvar chidPhi
nvar acqTheta
nvar acqTilt
nvar acqPhi
wave arraydata
wave image
wave xscale
wave yscale
variable ndimensions
variable arraysize0
variable arraysize1
variable datatype
variable colormode
//printf "array callback: acqtheta = %.1f, acqtilt = %.1f, acqphi = %.1f\r", acqTheta, acqTilt, acqPhi
pvGet chidNDimensions, ndimensions
pvGet chidArraySize0, arraysize0
pvGet chidArraySize1, arraysize1
pvGet chidDataType, datatype
pvGet chidColorMode, colormode
// sanity checks
if (ndimensions != 2)
return -2
endif
if (colormode != 0)
return -3
endif
redimension /n=(arraysize0 * arraysize1) arraydata
redimension /n=(arraysize0, arraysize1) image
redimension /n=(arraysize0) xscale
redimension /n=(arraysize1) yscale
switch(datatype)
case 0: // int8
redimension /b arraydata, image
break
case 1: // uint8
redimension /b/u arraydata, image
break
case 2: // int16
redimension /w arraydata, image
break
case 3: // uint16
redimension /w/u arraydata, image
break
case 4: // int32
redimension /i arraydata, image
break
case 5: // uint32
redimension /i/u arraydata, image
break
case 6: // float32
redimension /s arraydata, image
break
case 7: // float64
redimension /d arraydata, image
break
endswitch
pvGetWave chidArrayData, arraydata
pvGetWave chidXScale, xscale
pvGetWave chidYScale, yscale
image = arraydata[p + q * arraysize0]
setscale /i x xscale[0], xscale[numpnts(xscale)-1], image
setscale /i y yscale[0], yscale[numpnts(yscale)-1], image
ast_add_image(image, acqTheta, acqTilt, acqPhi)
#endif
setdatafolder savedf
return 0
end
/// callback function for new detector state from EPICS.
///
/// save the manipulator position at the beginning of image acquisition.
/// it is used by ast_callback_data().
function ast_callback_detector(chan)
variable chan
dfref savedf = GetDataFolderDFR()
setdatafolder $(package_path)
// retrieve data
nvar curDetectorState
nvar curTheta
nvar curTilt
nvar curPhi
nvar acqTheta
nvar acqTilt
nvar acqPhi
if (curDetectorState == 1)
acqTheta = curTheta
acqTilt = curTilt
acqPhi = curPhi
endif
//printf "detector callback: acqtheta = %.1f, acqtilt = %.1f, acqphi = %.1f, detstate = %d\r", acqTheta, acqTilt, acqPhi, curDetectorState
setdatafolder savedf
return 0
end
/// callback function for new manipulator position from EPICS.
function ast_callback_manip(chan)
variable chan
dfref savedf = GetDataFolderDFR()
setdatafolder $(package_path)
// retrieve data
nvar lensmode = curLensMode
nvar theta = curTheta
nvar tilt = curTilt
nvar phi = curPhi
//printf "manipulator callback: curtheta = %.1f, curtilt = %.1f, curphi = %.1f, lensmode = %d\r", theta, tilt, phi, lensmode
variable range
switch(lensmode)
case 1:
range = 45 // angular 45
break
case 2:
range = 60 // angular 60
break
default:
range = 2 // transmission or error
endswitch
ast_update_detector(theta, tilt, phi, range)
setdatafolder savedf
return 0
end
// GUI functions
static function bp_capture(ba) : ButtonControl
STRUCT WMButtonAction &ba
switch( ba.eventCode )
case 2: // mouse up
toggle_capture()
break
case -1: // control being killed
break
endswitch
return 0
end
static function toggle_capture()
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
nvar capturing
svar graphname
capturing = !capturing
if (capturing)
ast_prepare()
Button b_capture win=$graphname, title="stop"
else
Button b_capture win=$graphname, title="start"
endif
setdatafolder saveDF
end
static function update_capture()
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
nvar capturing
svar graphname
if (capturing)
Button b_capture win=$graphname, title="stop"
else
Button b_capture win=$graphname, title="start"
endif
setdatafolder saveDF
end
static function pmp_data(pa) : PopupMenuControl
STRUCT WMPopupAction &pa
switch( pa.eventCode )
case 2: // mouse up
pmp_data_mouseup(pa)
break
case -1: // control being killed
break
endswitch
return 0
end
static function pmp_data_mouseup(pa)
STRUCT WMPopupAction &pa
switch(pa.popNum)
case 1:
import_tracker_data()
break
case 2:
export_tracker_data()
break
case 3:
load_tracker_data()
break
case 4:
save_tracker_data()
break
endswitch
end
/// export tracker data (with prompt)
static function export_tracker_data()
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
svar export_folderpath
nvar export_format
string folderpath = export_folderpath
string nickname = ""
variable format = export_format
prompt folderpath, "Folder Path"
prompt nickname, "Nick Name"
prompt format, "Format", popup, "PEARL;XPDplot"
doprompt "Export Parameters", folderpath, nickname, format
if (v_flag == 0)
export_folderpath = folderpath
export_format = format
// note: if a full or partial path is used, all data folders except for the last in the path must already exist.
newdatafolder /o $folderpath
variable xpdplot = format == 2
ast_export($folderpath, nickname, xpdplot=xpdplot)
endif
setdatafolder saveDF
end
/// import tracker data (with prompt)
static function import_tracker_data()
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
svar export_folderpath
string folderpath = export_folderpath
string nickname = ""
dfref dfBefore = GetDataFolderDFR()
Execute /q/z "CreateBrowser prompt=\"Select wave from dataset\", showWaves=1, showVars=0, showStrs=0"
dfref dfAfter = GetDataFolderDFR()
SetDataFolder dfBefore
SVAR list = S_BrowserList
NVAR flag = V_Flag
if ((flag != 0) && (ItemsInList(list) >= 1))
string wname = StringFromList(0, list)
wave w = $wname
string prefix = get_hemi_prefix(w)
dfref df = GetWavesDataFolderDFR(w)
setdatafolder df
ast_import(prefix)
endif
setdatafolder saveDF
end
/// save tracker data to file (with prompt)
static function save_tracker_data()
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
svar dataname
save_hemi_scan(dataname, "", "")
setdatafolder saveDF
end
/// import tracker data from file (with prompt)
static function load_tracker_data()
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
NewDataFolder /O/S load_data
LoadWave /t /q
if (v_flag > 0)
string wname = StringFromList(0, s_wavenames, ";")
wave w = $wname
string prefix = get_hemi_prefix(w)
ast_import(prefix)
endif
setdatafolder $(package_path)
KillDataFolder /Z load_data
setdatafolder saveDF
end
static function pmp_parameters(pa) : PopupMenuControl
STRUCT WMPopupAction &pa
switch( pa.eventCode )
case 2: // mouse up
pmp_parameters_mouseup(pa)
break
case -1: // control being killed
break
endswitch
return 0
end
static function pmp_parameters_mouseup(pa)
STRUCT WMPopupAction &pa
switch(pa.popNum)
case 1:
load_prefs()
break
case 2:
save_prefs()
break
case 3:
edit_reduction_params()
break
case 4:
edit_offsets()
break
endswitch
end
static function edit_reduction_params()
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
svar pref_func = reduction_func
svar pref_params = reduction_params
string loc_func = pref_func
string loc_params = pref_params
if (prompt_func_params(loc_func, loc_params) == 0)
ast_set_processing(loc_func, loc_params)
endif
setdatafolder saveDF
end
static function edit_offsets()
dfref savedf = getdatafolderdfr()
setdatafolder $(package_path)
nvar theta_offset
nvar tilt_offset
nvar phi_offset
variable loc_theta = theta_offset
variable loc_tilt = tilt_offset
variable loc_phi = phi_offset
prompt loc_theta, "theta offset"
prompt loc_tilt, "tilt offset"
prompt loc_phi, "phi offset"
doprompt "manipulator offsets", loc_theta, loc_tilt, loc_phi
if (v_flag == 0)
theta_offset = loc_theta
tilt_offset = loc_tilt
phi_offset = loc_phi
endif
setdatafolder saveDF
end