#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 // $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