#!/bin/sh  
# \
  exec oagwish "$0" "$@"

# $Log: not supported by cvs2svn $
# Revision 1.20  2008/02/06 16:34:30  soliday
# Removed calls to setfacl because it is no longer needed.
#
# Revision 1.19  2008/01/17 16:45:28  soliday
# Updated by putting setfacl command inside of catch statement.
#
# Revision 1.18  2007/10/12 14:09:02  shang
# modified the setfacl command to work correctly in both solaris and linux system.
#
# Revision 1.17  2006/03/28 23:17:57  emery
# Changed finer range to +/- 30. Added forward and backward 70 buttons.
#
# Revision 1.16  2006/03/02 23:03:40  emery
# In Review/Install panel, changed the large increment and decrement
# button fron 9 to 7, and changed the OK button name to "OK to Install".
#
# Revision 1.15  2006/03/02 16:53:08  emery
# Replaces orbit correction handling procedure with one
# that simply aborts all possible orbit control:
# worksation based correction, datapool correction.
# Changed the overview message about the instructing
# the user in what bunch pattern to injection.
#
# Revision 1.14  2006/02/21 22:59:48  emery
# Force the user to enter a description. Make plots of bpm
# go to next page for a new sector.
#
# Revision 1.13  2003/10/14 22:39:49  borland
# Removed -echo option from sddsexperiment do avoid error.
#
# Revision 1.12  2001/09/11 07:46:58  emery
# Added orbit correction tests from H. Shang.
# Fixed saved description field.
#
# Revision 1.11  2001/09/03 14:55:05  emery
# Added another setfacl -m mask:rwx command for the descriptions
# file after the append operation just to make sure of the
# access.
#
# Revision 1.10  2001/09/03 14:46:44  emery
# Fixed print statement for the appended info of the description.sdds file.
#
# Revision 1.9  2001/09/03 14:41:07  emery
# Fixed access mode for opening descriptions.sdds to append.
#
# Revision 1.8  2001/08/23 21:33:01  emery
# Added entry box for description so that files can be recalled
# for viewing or reprocessing based on description.
#
# Revision 1.7  2001/05/04 01:11:03  emery
# Added smoothing option for noise analysis.
#
# Revision 1.6  2001/05/03 18:19:04  borland
# Changed -reuse to -reuse=page,row to prevent an error in sddsxref.
#
# Revision 1.`5  2001/04/30 15:46:44  emery
# Fixed some sector looops that were including a sector 0 variable
# by mistake. Fixed GetSectorTiming which wasn't handling sector > 35.
# Added exec setfacl -m mask:rwx $outputDir as a stop gap for the mkdir
# tcl command which ignores the acl data.
#
# Revision 1.4  2001/04/25 20:13:32  emery
# Corrected the -force option in file delete.
# Removed puts stderr statement in install timing procedure.
#
# Revision 1.3  2001/04/16 19:57:27  emery
# Made extensive modifications to add processing of the data.
# (Previous version only collected data).
#

set auto_path [linsert $auto_path 0  /usr/local/oag/apps/lib/$env(HOST_ARCH)]
set auto_path [linsert $auto_path 0 /usr/local/oag/lib_patch/$env(HOST_ARCH)]
APSDebugPath

set TimingScanArchivalDir /home/helios/oagData/sr/bpmTimingScans

proc RunTimingScanExperiment {args} {
    set outputDir ""
    set outputRoot ""
    set description ""
    set runIndex -1
    set points 37
    set pause 1
    set samples 5
    set initial -153
    set final 153
    set relative 1
    set statusCallback APSNoOp
    set scanCallback APSNoOp
    set analysisMode ""
    set smoothingMode ""
    set sectorList ""
    global TimingScanArchivalDir
    APSStrictParseArguments {outputDir outputRoot description runIndex points pause \
                               statusCallback initial final relative analysisMode \
                               smoothingMode sectorList scanCallback}
    
    if [catch {turnOffOrbitCorrection} results] {
        return -code error "RunTimingScanExperiment: $results"
    }
    
    if $points<=2 {
        eval $statusCallback {"points must be >2"}
        return -code error "RunTimingScanExperiment: points must be >2"
    }
    if {$initial>=$final} {
        eval $statusCallback {"initial must be < final"}
        return -code error "RunTimingScanExperiment: initial must be < final"
    }
    if $pause<0 {
        eval $statusCallback {"pause must be >=0"}
        return -code error "RunTimingScanExperiment: pause must be >=0"
    }

    if ![string length $description] {
        eval $statusCallback {"A description must be given."}
        return
    }

    # Check the output directory and rootname 
    if ![string length $outputDir] {
        set outputDir .
    }
    if ![file exists $outputDir] {
        if {[catch {file mkdir $outputDir} result]} {
            eval $statusCallback {"$result"}
            return -code error "RunTimingScanExperiment: $result"
        }
        #catch {exec setfacl -m mask:rwx $outputDir}
    }
    if ![string length $outputRoot] {
        eval $statusCallback {"output root is blank"}
        return -code error "RunTimingScanExperiment: output root is blank"
    }
    if {$runIndex<0} {
        # search for the next available "slot" for an output file.
        set existingFiles [glob -nocomplain $outputDir/$outputRoot\[0-9\]\[0-9\]*]
        set runIndex 0
        if [llength $existingFiles] {
            while 1 {
                set outputFile [format $outputDir/$outputRoot%02ld $runIndex]
                if [lsearch -glob $existingFiles ${outputFile}*]==-1 break
                incr runIndex
            }
        }
    }
    set outputFile [format $outputDir/$outputRoot%02ld.newTiming.raw $runIndex]
    eval $statusCallback {"Main output file is $outputFile"}

    if [file exists $outputFile] {
        # Make sure the user wants to overwrite the existing file.
        set response [APSMultipleChoice [APSUniqueName .] -question "$outputFile exists.  Overwrite it?" \
                        -labelList {Yes No} -returnList {Yes No} -name "File overwrite choice" ]
        switch $response {
            Yes { 
                if [catch {file delete -force [glob -nocomplain ${outputFile}*]} result] {
                    eval $statusCallback {"Unable to remove files!"}
                    return
                }
                eval $statusCallback {"$outputFile will be overwritten."}
                
            }
            No { 
                eval $statusCallback {"Give a new output file name."}
                return -code error "RunTimingScanExperiment: Duplicate filename"
            }
        }
    }

    # generate template in /home/helios/oagData/sr/bpmTimingScans/inputFiles directory
    eval $statusCallback {"Updating template file..."}
#    if [catch {exec $TimingScanArchivalDir/inputFiles/makeTimingScanTemplate \
      -selectNew $sectorList} result] {
#        return -code error "RunTimingScanExperiment: $result"
# }

    set inputDir $TimingScanArchivalDir/inputFiles
    set template $inputDir/newTimingScanTemplate.exp
    set tmpfile /tmp/[APSTmpString]
    set inputFile $tmpfile.input
    APSAddToTempFileList $inputFile 
    # do replacements on the template file to make the sddsexperiment input file
    if [catch \
          {exec replaceText $template $inputFile \
             -original=<points>,<initial>,<final>,<pause>,<inputDir>,<samples>,<relative> \
             -replacement=$points,[expr $initial * 0.001],[expr $final * 0.001],$pause,$inputDir,$samples,$relative} result] {
        eval $statusCallback {"$result"}
        return -code error "RunTimingScanExperiment: $result"
    }

    # make a snapshot of timing data for individual BPMs
    eval $statusCallback {"Taking snapshot of timing data..."}
    if [catch {exec sddssnapshot $inputDir/individualNewTiming.req -nameOfData=OriginalDelay \
                 [file root $outputFile].indivDelays} result] {
        eval $statusCallback {"$result"}
        return -code error "RunTimingScanExperiment: $result"
    }

# set memory scanner averaging.
    switch $analysisMode {
        noise {
            if [catch {exec cavput -list=S:bpm:memscan_wt_ao=2 \
                      } result ] {
                return -code  error "RunTimingScanExperiment: $result"
            }
        }
        intensity {
            if [catch {exec cavput -list=S:bpm:memscan_wt_ao=11 \
                      } result ] {
                return -code  error "RunTimingScanExperiment: $result"
            }
        }
        default {
            return -code error "RunTimingScanExperiment: analysisMode $analysisMode unknown"
        }
    } 
    eval $statusCallback {"Starting sddsexperiment..."}
    set execID [APSExecLog [APSUniqueName .] -width 120 -contextHelp \
                  "Execution window for sddsexperiment scan of BPM timing" \
                  -unixCommand "sddsexperiment -verbose $inputFile $outputFile -summarize" \
                  -abortCallback "$scanCallback -code abort" \
                  -cancelCallback "$scanCallback -code cancel" \
                  -callback "$scanCallback -code ok -fileRoot $outputFile -description \"$description\""]

    return
}

proc ScanDoneCallback {args} {
    global TimingScanArchivalDir env
    set code ""
    set fileRoot ""
    set description ""
    APSStrictParseArguments {code fileRoot description}

    APSEnableButton .userFrame.run.button
    bell
    # restore memory scanner averaging.
    if [ catch {exec cavput -list=S:bpm:memscan_wt_ao=11 \
              } result ] {
        return -code  error "RunTimingScanExperiment: $result"
    }
    switch $code {
        ok {
            set message "Scan completed successfully. Memory scanner weight returned to 11."
            APSSetVarAndUpdate status $message
            APSInfoWindow [APSUniqueName .] -name "sddsexperiment report." \
              -infoMessage $message
            bell
        }
        - {
            set message "Scan aborted or cancelled. Memory scanner weight returned to 11.\n\nCheck for proper condition of timing."
            APSSetVarAndUpdate status $message
            APSAlertBox [APSUniqueName .] -name "sddsexperiment report." \
              -errorMessage $message
            bell
            return
        }
    }
    if ![string length $fileRoot] {
        APSSetVarAndUpdate status "Scan aborted or cancelled---check for proper condition of timing."
        return
    }
    set files [glob -nocomplain ${fileRoot}*]
    # I assumed that there would only one file
    # I put in a loop in case that somehow more than one
    # file appears with glob command.
    if [llength $files] {
        foreach file $files {
            exec sddsprocess $file -noWarning \
              "-print=para,Description,[APSMakeSafeQualifierString $description]"
            file delete ${file}~
          }
        APSSetVarAndUpdate status "Compressing file..."
        if [catch {eval exec gzip $files} result] {
            APSSetVarAndUpdate status "$result"
        }
        APSSetVarAndUpdate status "Done."
    } else {
        APSSetVarAndUpdate status "No data was collected!"
        return
    }
    # take the first file, and check that it ends with *newTiming.raw.gz
    set file [lindex $files 0].gz
    if ![regexp {newTiming.raw.gz} $file] {
        APSSetVarAndUpdate status "File $file is of unknown type."
        return
    }
    if [file exists $TimingScanArchivalDir/data/descriptions.sdds] {
        set dirOld [pwd]
        cd $TimingScanArchivalDir/data
        set fid [open descriptions.sdds a]
        regexp {.*/data/(.*)/scan(.*).newTiming.raw.gz$} $file {} subDir index
        puts $fid "$file $subDir.$index \"$subDir.$index -> $description\""
        close $fid
#         if [regexp {Linux} $env(HOST_ARCH)] {
#             catch {exec setfacl -M $TimingScanArchivalDir/files.acl $TimingScanArchivalDir/data/descriptions.sdds}
#         } else {
#             catch {exec setfacl -f $TimingScanArchivalDir/files.acl $TimingScanArchivalDir/data/descriptions.sdds}
#         }
        cd $dirOld
    }
}

proc ProcessTimingScanChoice {args} {
    set script ""
    set statusCallback APSNoOp
    set analysisMode ""
    set smoothingMode ""
    APSStrictParseArguments {script analysisMode smoothingMode statusCallback}

    if ![string length $analysisMode] {
        return -code error "ProcessTimingScanChoice: No value given for argument analysisMode"
    }
    if ![string length $smoothingMode] {
        return -code error "ProcessTimingScanChoice: No value given for argument smoothingMode"
    }

    set selectionList [FindDataSetsAndDescriptions]
    set completeDataSetList [lindex $selectionList 0]
    set completeDescriptionList [lindex $selectionList 1]
    set dataToProcess 2001-0414.01
    set dataToProcess [APSChooseItemFromList \
                   -name "Data Set Selection" \
                   -itemList $completeDescriptionList \
                   -returnList $completeDataSetList \
                   -returnIndices 0 \
                   -multiItem 0 \
                   -contextHelp "Select a data set for data reprocessing or review."]
    
    if ![string length $dataToProcess] {
        APSSetVarAndUpdate status "No data set selected.!"
        return
    }
    if [catch {eval $script -runID $dataToProcess -analysisMode $analysisMode \
                 -smoothingMode $smoothingMode \
                 -statusCallback $statusCallback} result] {
        eval $statusCallback {"$result"}
        return -code error "ProcessTimingScanChoice: $result"
    }
}

proc GetTimingSector {args} {
    APSParseArguments {sector}
    if {$sector<35} {
        # sectors below 35 share the timing PV
        if {1==[expr $sector%2]} {
            return $sector
        } else {
            return [expr $sector - 1]
        }
    }
    return $sector
}

proc ReadTimingScan {args} {
    global TimingScanArchivalDir 
    set inputDir $TimingScanArchivalDir/inputFiles

    set runID ""
    set statusCallback APSNoOp
    APSParseArguments {runID statusCallback}

    # construct name of main data file 
    # e.g. 2001-0112.00 converted to 2001-0112/scan00
    regsub \\.  $runID /scan runID
    set runFile $TimingScanArchivalDir/data/$runID.newTiming.raw.gz
    if ![file exists $runFile] {
        eval $statusCallback {"$runFile not found"}
        return -code error "ReadTimingScan: $runFile not found"
    }
    eval $statusCallback {"Reading file $runID"}
    regsub {.raw.gz} $runFile {} fileRoot
    if [catch {exec sddsquery -col $runFile -sddsOutput=$fileRoot.query \
             } columnList] {
        return -code error "ReadTimingScan: Problem with getting columns form file $runFile: $columnList"
    }
    if [catch {exec sddsprocess $fileRoot.query -pipe=out \
                 -match=col,Name=S*:P*,Name=StDev*,!,&,Name=Sigma*,!,& \
                 -scan=col,Sector,Name,S%ld,type=long \
                 | sddssort -pipe -col=Sector -unique \
                 | sdds2stream -pipe -col=Sector \
             } sectorList] {
        return -code error "ReadTimingScan: $sectorList"
    }
    APSSetSRSectorButtons -mode all-off -rootname sectors -itemList Sector
    foreach sector $sectorList {
        global sectorsS${sector}Sector
        set sectorsS${sector}Sector 1
    }
    eval $statusCallback {"Done."}
    return
}

proc AnalyzeTimingScan {args} {
    global TimingScanArchivalDir 
    set inputDir $TimingScanArchivalDir/inputFiles

    set runID ""
    set statusCallback APSNoOp
    set analysisMode ""
    set smoothingMode ""
    APSParseArguments {runID statusCallback analysisMode smoothingMode}

    # construct name of main data file 
    # e.g. 2001-0112.00 converted to 2001-0112/scan00
    regsub \\.  $runID /scan runID
    set runFile $TimingScanArchivalDir/data/$runID.newTiming.raw.gz
    if ![file exists $runFile] {
        eval $statusCallback {"$runFile not found"}
        return -code error "AnalyzeTimingScan: $runFile not found"
    }
    eval $statusCallback {"Working on analysis of $runID in mode $analysisMode"}

    regsub {.raw.gz} $runFile {} fileRoot
    set procFile $fileRoot.proc

    # remove old file if there is one
    catch {file delete -force $procFile} 
    set newTimingSectors [GetSectorList]

    # combines the noise of the x and y readings to make a single
    # quantity for each bpm.
    set procCommands "\"-def=col,%s,%s:ms:x sqr %s:ms:y sqr + sqrt,select=StDevS*:P?:ms:x,edit=%/:ms:x//\" -pipe=out | sddsprocess -pipe=in"

    switch $smoothingMode {
        none {
            set procCommands "\"-def=col,%s,%s:ms:x sqr %s:ms:y sqr + sqrt,select=StDevS*:P?:ms:x,edit=%/:ms:x//\" -pipe=out | sddsprocess -pipe=in"
            foreach sector $newTimingSectors {
                set sectorTiming [GetTimingSector -sector $sector]
                lappend procCommands -process=StDevS${sector}?:P?,minimum,Optimum%s,position,functionOf=S${sectorTiming}BpmTimeP
                lappend procCommands -process=S${sector}?:P?:sum.RVAL,maximum,Optimum%s,position,functionOf=S${sectorTiming}BpmTimeP
            }
        }
        fit {
            eval $statusCallback {"Fit mode not implemented yet."}
            return -code error "AnalyzeTimingScan: Fit mode not implemented yet."
        }
        smooth {
            set procCommands "\"-def=col,%s,%s:ms:x sqr %s:ms:y sqr + sqrt,select=StDevS*:P?:ms:x,edit=%/:ms:x//\" -pipe=out | sddssmooth -pipe -points=7 -passes=5 -col=StDevS*:P? | sddsprocess -pipe=in"
            foreach sector $newTimingSectors {
                set sectorTiming [GetTimingSector -sector $sector]
                lappend procCommands "-process=StDevS${sector}?:P?,minimum,Optimum%s,position,functionOf=S${sectorTiming}BpmTimeP"
                lappend procCommands -process=S${sector}?:P?:sum.RVAL,maximum,Optimum%s,position,functionOf=S${sectorTiming}BpmTimeP
            }
#            lappend procCommands " | sddsprocess -pipe=in \"-redef=col,%s,%s rec,select=StDevS*:P?\" "
        }
        default {
            eval $statusCallback {"Unknown smoothing mode."}
            return -code error "AnalyzeTimingScan: Unknown smoothing mode."
        }
    }
    # put the processing options in between the two files in case
    # pipes are required, as is the case for noise analysis.
    if [catch {eval exec sddsprocess $runFile $procCommands $procFile \
             } result] {
        eval $statusCallback {"$result:\nPossible explanation: missing odd sector from scan?"}
        return -code error "AnalyzeTimingScan: $result.\n\nPossible explanation: missing odd sector from scan?"
    }

    set outputFile $fileRoot.newDelays
    catch {file delete -force $outputFile}
    # Steps in processing:
    # coarse scan: seek maximum sum signal for each bpm
    #        determine new values of sector delays
    # fine scan: seek minimum noise for each bpm
    #        determine new values of sector and bpm delays.
    # finer scan: seek minimum noise for each bpm within the
    #             flat area.
    #        determine new values of sector and bpm delays.

    set tmpRoot /tmp/[APSTmpString]
    APSAddToTempFileList $tmpRoot.0 $tmpRoot.1 $tmpRoot.2 $tmpRoot.3 $tmpRoot.4
    switch $analysisMode {
        noise {
            if [catch \
                  {exec sddsconvert $procFile -pipe=out \
                     -retain=para,OptimumStDevS*:P? \
                     -delete=col,* \
                     -edit=para,Optimum*,%/Optimum// \
                     | sddscollapse -pipe \
                     | sddscollect -pipe \
                     -collect=prefix=StDev,column=${analysisMode}Timing \
                     | sddsprocess -pipe \
                     -convertunits=col,${analysisMode}Timing,nsecs,usecs,1000 \
                     -scan=col,Sector,Rootname,S%ld,type=long \
                     "-def=col,TimingPVSector,Sector 34 > pop ? : = 2 mod 1 == pop pop ? : 1 - \$ \$,type=long" \
                     | sddssort -pipe -col=Sector \
                     | sddsbreak -pipe \
                     -increaseof=TimingPVSector \
                     | sddsprocess -pipe \
                     -proc=TimingPVSector,first,TimingPVSector \
                     -proc=${analysisMode}Timing,minimum,%sMin \
                     | sddsprocess -pipe=in $tmpRoot.0 \
                     -redef=para,TimingPVSector,TimingPVSector,type=long \
                  } result] {
                return -code error "AnalyzeTimingScan(1): $result"
            }
            # File is now split into sectors, with TimingPVSector
            # may have the same value for two consecutive pages.
            # tricky editing command to remove the S<sector> part of the
            # the bpm name.
            if [catch \
                  {exec sddsxref $tmpRoot.0 $fileRoot.indivDelays -pipe=out \
                     -take=OriginalDelay -match=Rootname=BPMName -noWarning \
                     | sddsprocess -pipe \
                     "-def=col,TotalDelay,${analysisMode}Timing OriginalDelay +,units=nsecs" \
                     -proc=TotalDelay,minimum,%sMin \
                     -edit=col,BPMType,Rootname,ebbbbi/X/aZX \
                     | tee $tmpRoot.1 \
                     | sddsconvert -pipe -delete=para,TimingPVSector \
                     | sddsxref -pipe $TimingScanArchivalDir/inputFiles/bpmOrder.sdds \
                     -match=BPMType -take=Index -reuse=page,row \
                     | sddssort -pipe -col=Sector -col=Index \
                     | sddsprocess -pipe=in $tmpRoot.2 -noWarning \
                     -print=para,AnalysisMode,noise \
                     "-def=col,NewDelay,TotalDelay TotalDelayMin -,units=nsecs" \
                     "-def=col,ChannelIndex,Index 1 -,type=long" \
                     -print=column,ControlName,S%ldBpmTime:Ch%ldDelayP,Sector,ChannelIndex \
                     -print=column,ValueString,%21.15e,NewDelay \
                     -print=column,ControlType,pv \
                     -print=column,Lineage,- \
                     -define=column,Count,1,type=long \
                     -print=parameter,SnapType,Absolute
                  } result] {
                return -code error "AnalyzeTimingScan(2): $result"
            }

            if [catch \
                  {exec sddscollapse $tmpRoot.1 -pipe=out \
                     | sddsprocess -pipe=in $tmpRoot.3 \
                     -print=para,AnalysisMode,Noise \
                     -convertunits=col,TotalDelayMin,usecs,nsecs,0.001 \
                     -print=column,ControlName,S%ldBpmTimeP,TimingPVSector \
                     -print=column,ValueString,%21.15e,TotalDelayMin \
                     -print=column,ControlType,pv \
                     -print=column,Lineage,- \
                     -define=column,Count,1,type=long \
                     -print=parameter,SnapType,Absolute 
                  } result] {
                return -code error "AnalyzeTimingScan(3): $result"
            }

            if [catch \
                  {exec sddscombine $tmpRoot.2 $tmpRoot.3 $outputFile \
                     -merge -overwrite \
                 } result] {
                return -code error "AnalyzeTimingScan(4): $result"
            }
        }
        intensity {
            if [catch \
                  {exec sddsconvert $procFile -pipe=out \
                     -retain=para,OptimumS*P?:sum.RVAL \
                     -delete=para,OptimumStDev* \
                     -delete=col,* \
                     -edit=para,Optimum*,%/Optimum// \
                     | sddscollapse -pipe \
                     | sddscollect -pipe \
                     -collect=suffix=:sum.RVAL,column=${analysisMode}Timing \
                     | sddsprocess -pipe \
                     -convertunits=col,${analysisMode}Timing,nsecs,usecs,1000 \
                     -scan=col,Sector,Rootname,S%ld,type=long \
                     "-def=col,TimingPVSector,Sector 34 > pop ? : = 2 mod 1 == pop pop ? : 1 - \$ \$,type=long" \
                     | sddssort -pipe -col=Sector \
                     | sddsbreak -pipe \
                     -increaseof=TimingPVSector \
                     | tee $tmpRoot.0 \
                     | sddsprocess -pipe \
                     -proc=TimingPVSector,first,TimingPVSector \
                     -proc=${analysisMode}Timing,average,%sAve \
                     | sddsprocess -pipe \
                     -redef=para,TimingPVSector,TimingPVSector,type=long \
                     | sddscollapse -pipe \
                     | sddsprocess -pipe=in $outputFile \
                     -print=para,AnalysisMode,intensity \
                     -print=col,Rootname,S%ld,TimingPVSector \
                     -convertunits=col,${analysisMode}TimingAve,usecs,nsecs,0.001 \
                     -print=column,ControlName,S%ldBpmTimeP,TimingPVSector \
                     -print=column,ValueString,%21.15e,${analysisMode}TimingAve \
                     -print=column,ControlType,pv \
                     -print=column,Lineage,- \
                     -define=column,Count,1,type=long \
                     -print=parameter,SnapType,Absolute \
                 } result] {
                return -code error "AnalyzeTimingScan(5): $result"
            }
        }
        default {
            return -code error "AnalyzeTimingScan: analysis mode $analysisMode unknown"
        }
    }
    eval $statusCallback {"Good results: [exec sdds2stream -rows $tmpRoot.0]"}

    if [catch {exec sddsprintout $outputFile $tmpRoot.4 \
                 "-title=Timing scan results for scan $fileRoot" \
                 -col=Rootname,format=%8s \
                 -column=ControlName,format=%32s \
                 -column=ValueString,format=%20s \
             } result ] {
        return -code error "AnalyzeTimingScan(6): $result"
    }
    
    # display as a printout
    APSFileDisplayWindow [APSUniqueName .] -fileName $tmpRoot.4 \
      -deleteOnClose 1 -width 80

}

proc InstallTimingScanResults {args} {
    global TimingScanArchivalDir 
    global bpmTimingUserAcceptance bpmTimingInstallStatus
    global bpmTimingBPMName bpmTimingSector bpmTimingDelay bpmTimingUserAction

    set runID ""
    set statusCallback APSNoOp
    APSParseArguments {runID analysisMode statusCallback}
    # analysisMode passed in argument not used. Rather the 
    # value saved in the newDelays file is used.
    set tmpfile /tmp/[APSTmpString]

    # construct name of main data file 
    regsub \\.  $runID /scan runID
    # set runFile $TimingScanArchivalDir/data/$runID.newTiming.raw.gz
    # the proc file has additional data which is useful.
    set runFile $TimingScanArchivalDir/data/$runID.newTiming.proc
    set snapFile $TimingScanArchivalDir/data/$runID.newTiming.newDelays
    if {![file exists $runFile] } {
        eval $statusCallback {"$runFile not found"}
        return -code error "InstallTimingScanResults(1): $runFile not found"
    }
    if {![file exists $snapFile] } {
        eval $statusCallback {"$snapFile not found"}
        return -code error "InstallTimingScanResults(2): $snapFile not found"
    }
    eval $statusCallback {"Working on installation of $runID"}

    if [catch {exec sdds2stream -para=AnalysisMode $snapFile} analysisMode] {
        eval $statusCallback {"$analysisMode"}
        return -code error "InstallTimingScanResults(3): $analysisMode"
    }

    set measurementList [split [exec sddsquery -col $runFile] \n]
    # delay file will contain data only when individual bpm timing is
    # changed, i.e. analysis mode "noise".
    set delayFile $tmpfile.delay
    APSAddToTempFileList $delayFile
    switch $analysisMode {
        noise {
            if [catch {exec sddsprocess $snapFile $delayFile \
                         -match=column,ControlName=*DelayP} result] {
                eval $statusCallback {"$result"}
                return -code error "InstallTimingScanResults(4): $result"
            }
            if {[catch {sdds open $delayFile r} fid] || \
                  [catch {APSGetSDDSColumn -sddsFD $fid -column Rootname} BPMNameList] || \
                  [catch {APSGetSDDSColumn -sddsFD $fid -column noiseTiming} optimumTimingList] || \
                  [catch {APSGetSDDSColumn -sddsFD $fid -column ControlName} controlNameList] || \
                  [catch {APSGetSDDSColumn -sddsFD $fid -column ValueString} valueStringList] } {
                eval $statusCallback {"Problem reading control names"}
                return -code error "InstallTimingScanResults(5): Problem reading control names."
            }
        }
        intensity {
            # data is sector-wide timing PVs only.
            if {[catch {sdds open $snapFile r} fid] || \
                  [catch {APSGetSDDSColumn -sddsFD $fid -column ControlName} controlNameList] || \
                  [catch {APSGetSDDSColumn -sddsFD $fid -column ValueString} valueStringList] } {
                eval $statusCallback {"Problem reading control names"}
                return -code error "InstallTimingScanResults(6): Problem reading control names."
            }
        }
        default {
            return -code error "InstallTimingScanResults(7): Unknown analysis mode $analysisMode."
        }
    }
    set valueStringList0 $valueStringList

    set sddsplotList ""
    set acceptanceList ""
    set index 0
    set bpmTypeList {A:P1 A:P2 A:P3 A:P4 B:P5 B:P4 B:P3 B:P2 B:P1}
    switch $analysisMode {
        noise {
            set bpmIndex -1
            foreach controlName $controlNameList valueString $valueStringList BPMName $BPMNameList optTiming $optimumTimingList {
                scan $BPMName S%ld sector
                regexp {S.*([AB]:P.)}  $BPMName {} bpmType
                incr bpmIndex
                if {$bpmIndex==9} {
                    lappend sddsplotList -nextPage
                    set bpmIndex 0
                }
                # this will leave a blank area for bpms not in the controlName list
                while {[lindex $bpmTypeList $bpmIndex] != $bpmType} {
                    incr bpmIndex
                    if {$bpmIndex==9} {
                        lappend sddsplotList -nextPage
                        set bpmIndex 0
                    }
                }
                set timingSector [GetTimingSector -sector $sector]
                # using usec units bites us again.
                set optTiming [expr $optTiming * 0.001]
                lappend sddsplotList -column=S${timingSector}BpmTimeP,${BPMName}:ms:?,StDev%s -graph=error,type=0,vary=subtype -ylabel=$BPMName -leg=edit=%/$BPMName:ms:// -drawline=x0v=$optTiming,x1v=$optTiming,q0v=0,q1v=1,line=2 -column=S${timingSector}BpmTimeP,StDev${BPMName} -grap=line,type=3 -ylabel=StDev -leg=spec=StDev -yscale=id=noise -mode=y=log -tick=ylog -end
                incr index
                lappend acceptanceList 1
            }
        }
        intensity {
            # plot each bpm as a function of delay. plot the 
            # optimum delay as a vertical line. 
            # Loop over sector-wide PV
            foreach controlName $controlNameList valueString $valueStringList {
                scan $controlName S%ld sector
                set bpmTimingDelay $valueString
                lappend sddsplotList -column=$controlName,S${sector}?:P?:sum.RVAL -graph=sym,connect,sca=4 -sep=nameindex -drawline=x0v=$bpmTimingDelay,x1v=$bpmTimingDelay,q0v=0,q1v=1,line=1 -end
                if {$sector < 34} {
                    set nextSector [expr $sector + 1]
                    # check presence of at least the first bpm intensity
                    if {-1 < [lsearch $measurementList S${nextSector}A:P1:sum.RVAL]} {
                        lappend sddsplotList -column=$controlName,S${nextSector}?:P?:sum.RVAL -graph=sym,connect,sca=4 -sep=nameindex -drawline=x0v=$bpmTimingDelay,x1v=$bpmTimingDelay,q0v=0,q1v=1,line=1 -end
                    }
                }
                lappend acceptanceList 1
            }
        }
    }

    eval exec sddsplot -topline=$runID -title= -layout=3,3 \
      $runFile $sddsplotList  &

    set w [APSUniqueName .]
    APSDialogBox $w -name "Data evaluation dialog" \
      -contextHelp "Dialog for data evaluation for bpm timng scan installation." \
      -okCommand "set bpmTimingUserAction ok" \
      -cancelCommand "set bpmTimingUserAction cancel"
    APSEnableButton $w.buttonRow.ok.button
    $w.buttonRow.ok.button configure -text "OK to Install"
    APSSetContextHelp $w.buttonRow.ok.button -contextHelp \
      "Sends the timing values for the accepted BPMs to the IOCs."
    APSSetContextHelp $w.buttonRow.cancel.button -contextHelp \
      "Cancels the operation without changing any timing in the IOCs."
    set bpmTimingInstallStatus "Plotting data.  Please wait..."
    APSScrolledStatus .status -parent $w.userFrame -width 60 -height 10 \
      -textVariable bpmTimingInstallStatus

    # Evaluate either the individual bpm timings (noise analysis mode) or the
    # sector-wide timing (intensity analysis mode)
    switch $analysisMode {
        noise {
            APSLabeledOutput .bpm -parent $w -label "BPM: " \
              -textVariable bpmTimingBPMName -width 26
            APSLabeledEntry .delay -parent $w -label "Delay (ns): " \
              -textVariable bpmTimingDelay -width 26
            APSRadioButtonFrame .rbf -parent $w -label "Accept: " \
              -variable bpmTimingUserAcceptance -orientation horizontal \
              -valueList {1 0} -buttonList {Yes No}
            APSDialogBoxAddButton .fwd -parent $w -text "Forward"  \
              -command "set bpmTimingUserAction forward" \
              -contextHelp "Moves editing point forward one BPM"
            APSDialogBoxAddButton .bwd -parent $w -text "Backward" \
              -command "set bpmTimingUserAction backward" \
              -contextHelp "Move editing point backward one BPM"
            APSDialogBoxAddButton .fwd7 -parent $w -text "Forward 7"  \
              -command "set bpmTimingUserAction forward7" \
              -contextHelp "Moves editing point forward one sector"
            APSDialogBoxAddButton .bwd7 -parent $w -text "Backward 7" \
              -command "set bpmTimingUserAction backward7" \
              -contextHelp "Moves editing point backward one sector"
            APSDialogBoxAddButton .fwd70 -parent $w -text "Forward 70"  \
              -command "set bpmTimingUserAction forward70" \
              -contextHelp "Moves editing point forward one sector"
            APSDialogBoxAddButton .bwd70 -parent $w -text "Backward 70" \
              -command "set bpmTimingUserAction backward70" \
              -contextHelp "Moves editing point backward one sector"
        }
        intensity {
            APSLabeledOutput .bpm -parent $w -label "Sector: " \
              -textVariable bpmTimingSector -width 26
            APSLabeledEntry .delay -parent $w -label "Delay (ns): " \
              -textVariable bpmTimingDelay -width 26
            APSRadioButtonFrame .rbf -parent $w -label "Accept: " \
              -variable bpmTimingUserAcceptance -orientation horizontal \
              -valueList {1 0} -buttonList {Yes No}
            APSDialogBoxAddButton .fwd -parent $w -text "Forward"  \
              -command "set bpmTimingUserAction forward" \
              -contextHelp "Moves editing point forward one timing sector"
            APSDialogBoxAddButton .bwd -parent $w -text "Backward" \
              -command "set bpmTimingUserAction backward" \
              -contextHelp "Move editing point backward one timing sector"
        }
    }
    APSSetVarAndUpdate bpmTimingInstallStatus "Please review and evaluate plots."

    set index 0
    set maxIndex [expr [llength $controlNameList]-1]
    while 1 {
        if $index<0 {
            set index 0
        }
        if $index>$maxIndex {
            set index $maxIndex
        }
        # for noise analysis mode, controlName are S<sector>BpmTime:Ch<num>DelayP
        # for intensity analysis mode, controlName are S<sector>BpmTimeP
        set controlName [lindex $controlNameList $index]
        set bpmTimingDelay [lindex $valueStringList $index]
        switch $analysisMode {
            noise {
                set bpmTimingBPMName [lindex $BPMNameList $index]
            }
            intensity {
                scan $controlName S%ld bpmTimingSector
            }
        }
        set bpmTimingUserAcceptance [lindex $acceptanceList $index]
        tkwait variable bpmTimingUserAction
        set acceptanceList [lreplace $acceptanceList $index $index $bpmTimingUserAcceptance]
        set valueStringList [lreplace $valueStringList $index $index $bpmTimingDelay]
        switch $bpmTimingUserAction {
            ok {
                break
            }
            forward {
                incr index
            }
            forward7 {
                incr index 7
            }
            forward70 {
                incr index 70
            }
            backward {
                incr index -1
            }
            backward70 {
                incr index -70
            }
            backward7 {
                incr index -7
            }
            cancel {
                catch {destroy $w}
                eval $statusCallback {"No installation."}
                return
            }
        }
    }
    catch {destroy $w}

    set outputControlName ""
    set outputDelay ""
    set outputCount 0
    for {set index 0} {$index<=$maxIndex} {incr index} {
        if [lindex $acceptanceList $index] {
            # form lists of output control names and delta values from user
            # (user is allowed to change delays throught the dialog boxes)
            lappend outputControlName [lindex $controlNameList $index]
            lappend outputDelayDelta [expr [lindex $valueStringList $index]-[lindex $valueStringList0 $index]]
            incr outputCount
        }
    }
    if $outputCount {
        eval $statusCallback {"$outputCount delays accepted.  Installing..."}
        set deltaFile $tmpfile.delta
        APSAddToTempFileList deltaFile
        if {[catch {sdds open $deltaFile w} fid] || \
              [catch {sdds defineColumn $fid ControlName -type SDDS_STRING 
                  sdds defineColumn $fid DelayDelta -type SDDS_DOUBLE
                  sdds writeLayout $fid
                  sdds startPage $fid $outputCount
                  eval sdds setColumn $fid ControlName $outputControlName
                  eval sdds setColumn $fid DelayDelta $outputDelayDelta
                  sdds writePage $fid 
                  sdds close $fid} result]} {
            eval $statusCallback {"$fid $result"}
            return -code error "InstallTimingScanResults(8): $fid $result"
        }
    } else {
        eval $statusCallback {"Zero delays accepted. No installation."}
        return
    }    

    # pull delta delays into snapshot file
    # make altered ValueString column
    set delaySnap $tmpfile.delaySnap
    set SectorTimingSnap $tmpfile.sectorTimingSnap
    set fullSnap $tmpfile.fullSnap
    APSAddToTempFileList $SectorTimingSnap $delaySnap $fullSnap
    switch $analysisMode {
        noise {
            if [catch {exec sddsxref $delayFile $deltaFile -pipe=out \
                         -match=ControlName -take=* -nowarning \
                         | sddsprocess -pipe=in $delaySnap \
                         "-redefine=column,NewDelay,NewDelay DelayDelta +" \
                         "-reprint=column,ValueString,%21.15e,NewDelay" 
                exec sddsprocess $snapFile $SectorTimingSnap \
                         -match=column,ControlName=S*BpmTimeP
                exec sddscombine $SectorTimingSnap $delaySnap -merge $fullSnap \
                     } result] {
                eval $statusCallback {"$fid $result"}
                return -code error "InstallTimingScanResults(9): $fid $result"
            }
        }
        intensity {
            if [catch {exec sddsxref $snapFile $deltaFile -pipe=out \
                         -match=ControlName -take=* -nowarning \
                         | sddsprocess -pipe=in $fullSnap \
                         "-redefine=column,intensityTimingAve,intensityTimingAve DelayDelta +" \
                         "-reprint=column,ValueString,%21.15e,intensityTimingAve" \
                     } result] {
                eval $statusCallback {"$fid $result"}
                return -code error "InstallTimingScanResults(10): $fid $result"
            }
        }
    }

    set logFile $tmpfile.burtlog
    APSAddToTempFileList $logFile
    if {1} {
        if {[catch {exec sddscasr -restore $fullSnap -l} result]} {
            eval $statusCallback {"Error restoring snapshot: $result"}
            return -code error "InstallTimingScanResults(10): Errors in restore of $fullSnap: $result"
        }
    }
    # make list of BPMs that were not installed, either because the user
    # rejected them or because they didn't have good scans.
    set badList $tmpfile.badList
    APSAddToTempFileList $badList
    if [catch {exec sddsselect $TimingScanArchivalDir/inputFiles/individualNewTiming.req \
                $fullSnap -match=ControlName -pipe=out -invert \
                | sddsprintout -pipe=in $badList -column=ControlName \
                "-title=BPMs for which delay data was not changed." } result] {
        return -code error "InstallTimingScanResults(11): Error making list of bad BPMs: $result"
    }
    APSFileDisplayWindow [APSUniqueName .] -fileName $badList \
      -comment "BPMs for which delay data was not changed." \
      -width 40 

    eval $statusCallback {"Installation of timing data completed."}
}

proc SwitchArchivalMode {args} {
    set archival 0
    set nonArchivalWidgets ""
    APSStrictParseArguments {archival nonArchivalWidgets}
    global outputDir outputRoot runIndex 
    global TimingScanArchivalDir 
    if $archival {
        foreach elem $nonArchivalWidgets {
            $elem configure -state disabled
        }
        set outputDir $TimingScanArchivalDir/data/[exec date +%Y-%m%d]
        set outputRoot scan
        set runIndex -1
    } else {
        foreach elem $nonArchivalWidgets {
            $elem configure -state normal
        }
        set outputDir [pwd]
        set outputRoot ""
        set runIndex 0
    }
}

set nonArchivalWidgets ""
set archivalWidgets ""
proc MakeNonArchivalFrame {widget args} {
    set parent ""
    APSStrictParseArguments {parent}

    global nonArchivalWidgets archivalWidgets
    APSFrame $widget -parent $parent -label "Nonarchival output specification"
    set w $parent$widget.frame
    
    APSLabeledEntry .outputDir -parent $w -label "Directory: " \
      -width 60 -textVariable outputDir -contextHelp \
      "The directory for the files to which data will be saved."
    lappend nonArchivalWidgets $w.outputDir.entry
    APSLabeledEntry .output -parent $w    -label "Rootname:  " \
      -width 60 -textVariable outputRoot -contextHelp \
      "The root name for the files to which data will be saved.  The output file is <rootname>-<runIndex>.sdds"
    lappend nonArchivalWidgets $w.output.entry
    APSLabeledEntry .index -parent $w     -label "Run index: " \
      -width 60 -textVariable runIndex -contextHelp \
      "The run index for the next experiment.  Automatically incremented after each experiment."
    lappend nonArchivalWidgets $w.index.entry

    APSLabeledEntry .desc -parent $w     -label "Description: " \
      -width 60 -textVariable description -contextHelp \
      "The description for the next experiment. Useful for recording bunch pattern."

    APSRadioButtonFrame .archival -parent $w -label "Archival: " \
      -variable archivalData -buttonList {Yes No} -valueList {1 0} \
      -orientation horizontal -commandList \
      {"SwitchArchivalMode -archival 1 -nonArchivalWidgets $nonArchivalWidgets" \
         "SwitchArchivalMode -archival 0 -nonArchivalWidgets $nonArchivalWidgets"} \
      -contextHelp "Selects whether to collect and review archival data.  \
 The data is placed in an archival area for long-term use." 

    SwitchArchivalMode -archival 1 -nonArchivalWidgets $nonArchivalWidgets 
}

proc MakeAnalysisParametersFrame {widget args} {
    set parent ""
    APSStrictParseArguments {parent}
    APSFrame $widget -parent $parent -label "Analysis parameters"
    APSRadioButtonFrame .mode -parent $parent$widget.frame -label "Mode: " \
      -variable analysisMode -buttonList {noise intensity} \
      -valueList {noise intensity} -orientation horizontal \
      -contextHelp "Selects the mode of collecting data. In noise mode the data is collected to enhance the noise of the readback and deemphasize orbit drift by setting the bpm averager wieght to 2. In intensity mode, the averager weigth is set to 11 to get accurate averaging and intensity data.\n\nIn \"noise\" analysis mode, the data will be procesed to seek the minimum noise levels on individual bpms. The main sector timing will be set to make the individual bpm timing PVs as small as possible but greater than zero.\n\nIn \"intensity\" mode, the data will be procesed to seek the maximum sum signals of the bpms. Since we expect to use the \"intensity\" mode in the coarse scan, only the main sector timing PV is adjusted (individual bpm timing not changed)." 
      APSRadioButtonFrame .smoooth -parent $parent$widget.frame -label "Smoothing: " \
      -variable smoothingMode -buttonList {none smooth fit} \
      -valueList {none smooth fit} -orientation horizontal \
      -contextHelp "Smoothing parameter on noise before finding the minimum."
}

proc MakeSetScanParametersFrame {widget args} {
    set parent ""
    APSStrictParseArguments {parent}

    APSFrame $widget -parent $parent -label "Pre-set scan parameters"
    set w $parent$widget.frame
    APSButton .coarse -parent $w -text "Coarse" \
      -command {set points 72; set pause 0.5; set samples 1
          set initial 0; set final 3600; set relative 0} \
      -contextHelp "Sets up the scan parameters for a coarse scan which will find the best trigger position inside a turn of the storage ring."
    APSButton .fine -parent $w -text "Fine" \
      -command {set points 41; set pause 0.5; set samples 5
          set initial -200; set final 200; set relative 1} \
      -contextHelp "Sets up the scan parameters for a fine scan which will find the best trigger position with a 10 ns resolution."
    APSButton .finer -parent $w -text "Finer" \
      -command {set points 31; set pause 0.5; set samples 15
          set initial -30; set final 30; set relative 1} \
      -contextHelp "Sets up the scan parameters for a finer scan which will find the best trigger position at a 1 ns resolution."
}

proc MakeScanParametersFrame {widget args} {
    set parent ""
    APSStrictParseArguments {parent}

    APSFrame $widget -parent $parent -label "Scan parameters"
    set w $parent$widget.frame
    APSLabeledEntry .points -parent $w -label "Points: " \
      -width 10 -textVariable points -contextHelp \
      "The number of points at which to take data."
    APSLabeledEntry .samples -parent $w -label "Samples: " \
      -width 10 -textVariable samples -contextHelp \
      "The number of samples to take per point."
    APSLabeledEntry .pause -parent $w -label "Pause (s): " \
      -width 10 -textVariable pause -contextHelp \
      "The amount of time to pause between samples."
    APSLabeledEntry .initial -parent $w -label "Initial (ns): " \
      -width 10 -textVariable initial -contextHelp \
      "The initial delay offset for the experiment."
    APSLabeledEntry .final -parent $w -label "Final (ns): " \
      -width 10 -textVariable final -contextHelp \
      "The final delay offset for the experiment."
    APSRadioButtonFrame .relative -parent $w -label "Relative: " \
      -variable relative -buttonList {Yes No} -valueList {1 0} \
      -orientation horizontal -contextHelp \
      "Does scan relative to present value of sector delay."

}

proc MakeSectorSelectionFrame {widget args} {
    set parent ""
    APSStrictParseArguments {parent}

    APSFrame $widget -parent $parent -label "Select sectors"
    $parent$widget.frame configure -relief flat
    set newTimingSectors [exec sdds2stream -col=Sector \
                            /home/helios/oagData/sr/NewTiming/sectors.sdds]
    set missingList ""
    for {set i 1} {$i<41} {incr i} {
        global sectorsS${i}Sector
        if {-1 == [lsearch $newTimingSectors $i]} {
            lappend missingList S${i}Sector
        }
    }
    APSSRSectorButtons .sectorButtons -parent $parent$widget.frame -rootname sectors \
      -orientation horizontal \
      -label "" -description "Sector selections" \
      -itemList Sector -packOption "-side top" \
      -itemLabelList Sector \
      -missingList $missingList \
      -command "APSSRSectorUnsetMissing -rootname sectors -missingList $missingList"
    return
}

proc setStatusText {text} {
    global status
    set status $text
    update
}

proc GetSectorList {} {
    set sectorList ""
    for {set i 1} {$i<41} {incr i} {
        global sectorsS${i}Sector
        if [set sectorsS${i}Sector] {
            lappend sectorList $i
        }
    }
    return $sectorList
}
proc FindDataSetsAndDescriptions {} {
    global TimingScanArchivalDir env

    if {![file exists $TimingScanArchivalDir/data/descriptions.sdds]} {
        set dirOld [pwd]
        cd $TimingScanArchivalDir/data
        # create file of data sets and descriptions
        set fid [open descriptions.sdds w]
        puts $fid "SDDS1"
        puts $fid "&column name=File type=string &end"
        puts $fid "&column name=DataSet type=string &end"
        puts $fid "&column name=Description type=string &end"
        puts $fid "&data mode=ascii no_row_counts=1 &end"
        set files [exec find $TimingScanArchivalDir/data -name "*newTiming.raw.gz" ]
        foreach file [lsort -decreasing $files] {
            if [catch {exec sdds2stream -page=1 -para=Description $file \
                     } description] {
                set description ""
            }
            regexp {.*/data/(.*)/scan(.*).newTiming.raw.gz$} $file {} subDir index
            # removes double quotes
            regexp {^"(.*)"$} $description {} description
            puts $fid "$file $subDir.$index \"$subDir.$index -> $description\""
        }
        close $fid
 #       if [regexp {Linux} $env(HOST_ARCH)] {
 #           catch {exec setfacl -M $TimingScanArchivalDir/files.acl $TimingScanArchivalDir/data/descriptions.sdds}
 #       } else {
 #           catch {exec setfacl -f $TimingScanArchivalDir/files.acl $TimingScanArchivalDir/data/descriptions.sdds}
 #       }
        cd $dirOld
    }
    sdds load $TimingScanArchivalDir/data/descriptions.sdds descriptions
    set dataSetList [lindex $descriptions(Column.DataSet) 0]
    set descriptionList [lindex $descriptions(Column.Description) 0]
    return [list $dataSetList $descriptionList]
}

proc turnOffOrbitCorrection {args} {
    global orbitCorrectionRunning dpOrbitCorrectionRunning controllawMode dpControllawMode
    set orbitCorrectionRunning 0
    set dpOrbitCorrectionRunning 0
    foreach coord {X Y} {
        if [catch {exec cavget -list=S:RC:OrbitControlLaw${coord}C.RUN -pend=10} running1] {
            return -code error $running1
        }
        if {$running1=="?"} {
            return -code error "Unable to get value of S:RC:OrbitControlLaw${coord}C.RUN!"
        }
        if $running1 {
            set orbitCorrectionRunning 1
        }
        if [catch {exec cavget -list=DP:S:OrbitControlLaw${coord}SDDS.RUN -pend=10} running2] {
            return -code error $running2
        }
        if {$running2=="?"} {
            return -code error "Unable to get value of DP:S:OrbitControlLaw${coord}SDDS.RUN!"
        }
        if $running2 {
            set dpOrbitCorrectionRunning 1
        }
    }
    if $orbitCorrectionRunning {
        if [catch {exec cavget -list=S:RC:OrbitControlLawXC.SUSP,S:RC:OrbitControlLawYC.SUSP,S:rfFreqControlLawRC.SUSP -cavputForm} controllawMode] {
            return -code error "Problem getting orbit controllaw status: $controllawMode"
        }
        if [catch {exec cavput -list=S:RC:OrbitControlLawXC,S:RC:OrbitControlLawYC,S:rfFreqControlLawRC -list=.SUSP=1} result] {
            return -code error "Problem suspending controllaw: $result"
        }
    }
    if $dpOrbitCorrectionRunning {
        #turn off DP orbit correction
        if [catch {exec cavget -list=DP:S: \
                     -list=OrbitControlLawX,OrbitControlLawY \
                     -list=SDDS.CMND -cavputForm \
                 } dpControllawMode] {
            return -code error "Problem getting DP orbit controllaw status: $dpControllawMode"
        }
        if [catch {exec cavput -list=DP:S: \
                     -list=OrbitControlLawX,OrbitControlLawY \
                     -list=SDDS.ABRT=1 \
                 } result] {
            return -code error "Problem aborting DP controllaw: $result"
        }
    }
    #set corrector mode to scalar
    foreach corrplane {h v} {
        if [catch {APSSetCorrMode -corrMode scalar -plane $corrplane} result] {
            return -code error "Problen setting corrector mode to scalar: $result"
        }
    }
}

proc RestoreControllaw {args} {
    global orbitCorrectionRunning dpOrbitCorrectionRunning controllawMode dpControllawMode
    if {$orbitCorrectionRunning && [string length $controllawMode]} {
        if [catch {exec cavput -list=$controllawMode} result] {
            return -code error "Can not restore orbit correction: $result"
        }
    }
    if {$dpOrbitCorrectionRunning && [string length $dpControllawMode] } {
        #set corrector mode to vector
        foreach corrplane {h v} {
            if [catch {APSSetCorrMode -corrMode vector -plane $corrplane} result] {
                return -code error "Problen setting corrector mode to scalar: $result"
            }
        }
        if [catch {exec cavput -list=$dpControllawMode} result] {
            return -code error "Can not restore dp orbit correction: $result"
        }
    }
}

set outputDir [pwd]
set outputRoot ""
set runIndex 0
set points 37
set pause 1
set samples 5
set initial -153
set final 153
set archivalData 1
set analysisMode noise
set smoothingMode smooth
set relative 1

set CVSRevisionAuthor "\$Revision: 1.21 $ \$Author: shang $"
APSApplication . -name SRBPMNewTimingScan -version $CVSRevisionAuthor \
  -overview "Does SR BPM timing scans to determine best timing for BPM operation for the new timing system. It's best to run the scan with the bunch train to be used in User runs, i.e. a single bunch. The optimum timing is obtained by minimizing the noise of the readback. The boxcar averager is set to 2, and theuser should inject a low charge af about 1 mA."

set status Ready.
APSScrolledStatus .status -parent .userFrame -textVariable status -width 60 -height 8
MakeNonArchivalFrame .nonarchival -parent .userFrame 
MakeSectorSelectionFrame .sector -parent .userFrame
.userFrame.sector configure
APSFrameGrid .grid -parent .userFrame -xList {x1 x2 x3}

MakeSetScanParametersFrame .setscan -parent .userFrame.grid.x1
MakeScanParametersFrame .scan -parent .userFrame.grid.x2
MakeAnalysisParametersFrame .analysis -parent .userFrame.grid.x3

APSButton .run -parent .userFrame -text Run -command \
  {catch {
      RunTimingScanExperiment -outputRoot $outputRoot -runIndex $runIndex \
        -outputDir $outputDir \
        -description $description \
        -analysisMode $analysisMode -sectorList [GetSectorList] \
        -points $points -pause $pause -initial $initial -final $final \
        -relative $relative \
        -statusCallback setStatusText -scanCallback ScanDoneCallback} status} \
  -contextHelp "Does a BPM timing scan."

APSButton .readSectors -parent .userFrame -text "Read sectors..." \
  -command {ProcessTimingScanChoice -script ReadTimingScan  -smoothingMode $smoothingMode \
              -analysisMode $analysisMode -statusCallback setStatusText} \
  -contextHelp "Reads contents of a data file and depresses the sector buttons."

APSButton .review -parent .userFrame -text Process/Review... \
  -command {ProcessTimingScanChoice -script AnalyzeTimingScan \
              -analysisMode $analysisMode -smoothingMode $smoothingMode \
              -statusCallback setStatusText} \
  -contextHelp "Processes and reviews data. This will create a file which can be loaded with the Install button.\n\nIn the \"noise\" analysis mode, the individual bpm timing PVs will be set to give a  minimum noise reading.\n\nIn the \"intensity\" analysis mode, the sector bpm timing PVs will be set for the maximum sum signal. The individual bpm timing PVs will not be changed."

APSButton .install -parent .userFrame -text Review/Install... \
  -command {ProcessTimingScanChoice -script InstallTimingScanResults \
              -analysisMode $analysisMode  -smoothingMode $smoothingMode \
              -statusCallback setStatusText} \
  -contextHelp \
  "Reviews and allows selective installation of processed results."

