
#
#
# There are two types of flag images processed by these routines:
# 1. sddscontour-style flag images, as produced by IDL or image2sdds.
#  The routines for this type have "SddscontourFlagImage" in the
#  name.
# 2. Horizontal-line flag images, as produced by sddswmonitor using
#  the new horizontal line PVs. The routines for this type have 
#  "HLImage" in the name.
#

proc APSVertSplitSddscontourFlagImage {args} {
    # Script to process flag image files such as those used by sddscontour.
    # The file must have the following organization:
    # * Parameters xDimension and yDimension give the number of pixels per
    #   dimension.
    # * Data is in column Intensity.
    # * The data is a series of vertical lines.  I.e., the vertical index
    #   is the row index modulo the xDimension.
    # The output is two files, one containing the odd lines and one containing
    # the even lines.

    set input ""
    set oddOutput ""
    set evenOutput ""
    APSStrictParseArguments {input oddOutput evenOutput}
    if {![string length $input] || \
          (![string length $oddOutput] && ![string length $evenOutput])} {
        return -code error "APSVertSplitSddscontourFlagImage: missing values"
    }

    if ![file exists $input] {
        return -code error "APSVertSplitSddscontourFlagImage: Not found: $input"
    }
    if {[string length $oddOutput] && [file exists $oddOutput]} {
        return -code error "APSVertSplitSddscontourFlagImage: Already exists: $oddOutput"
    }
    if {[string length $evenOutput] && [file exists $evenOutput]} {
        return -code error "APSVertSplitSddscontourFlagImage: Already exists: $evenOutput"
    }

    set tmpFile /tmp/[APSTmpString]
    if [catch {exec sddsprocess $input $tmpFile \
                 "-define=column,yIndex,i_row xDimension / int,type=short" \
                 "-define=column,yIsOdd,yIndex = 2 / int 2 * -,type=short"} result] {
        return -code error "APSVertSplitSddscontourFlagImage: $result"
    }
    if {[string length $evenOutput] && \
          [catch {exec sddsprocess $tmpFile -pipe=out \
                    -filter=column,yIsOdd,0,0 \
                    "-redefine=parameter,yDimension,n_rows xDimension / int,type=long" \
                    "-redefine=parameter,yInterval,yInterval 2 *" \
                    "-redefine=parameter,NumberOfRows,n_rows NumberOfColumns / int,type=long" \
                    | sddsconvert -pipe=in $evenOutput \
                    -delete=column,yIsOdd,yIndex} result]} {
        return -code error "APSVertSplitSddscontourFlagImage: $result"
    }
    if {[string length $oddOutput] && \
          [catch {exec sddsprocess $tmpFile -pipe=out \
                    -filter=column,yIsOdd,1,1 \
                    "-redefine=parameter,yDimension,n_rows xDimension / int,type=long" \
                    "-redefine=parameter,yInterval,yInterval 2 *" \
                    "-redefine=parameter,NumberOfRows,n_rows NumberOfColumns / int,type=long" \
                    "-redefine=parameter,yMinimum,1" \
                    | sddsconvert -pipe=in $oddOutput \
                    -delete=column,yIsOdd,yIndex} result]} {
        return -code error "APSVertSplitSddscontourFlagImage: $result"
    }
}

proc APSNormalizeSddscontourFlagImage {args} {
    # Script to process flag image files such as those used by sddscontour.
    # The file must have the following organization:
    # * Parameters xDimension and yDimension give the number of pixels per
    #   dimension.
    # * Data is in column Intensity.
    # * The data is a series of vertical lines.  I.e., the vertical index
    #   is the row index modulo the xDimension.
    # The script independently normalizes the even and odd vertical lines.

    set input ""
    set output ""
    APSStrictParseArguments {input output}

    if ![file exists $input] {
        return -code error "APSNormalizeSddscontourFlagImage: Not found: $input"
    }
    if [file exists $output] {
        return -code error "APSNormalizeSddscontourFlagImage: Already exists: $output"
    }
    if [string compare $input $output]==0 {
        return -code error "APSNormalizeSddscontourFlagImage: input and output are identical"
    }

    if [catch {exec sddsprocess $input -pipe=out \
                 "-define=column,yIndex,i_row xDimension / int,type=short" \
                 "-define=column,xIndex,i_row xDimension yIndex * -,type=short" \
                 "-define=column,yIsOdd,yIndex = 2 / int 2 * -,type=short" \
                 -process=Intensity,max,%sMaxEven,function=yIsOdd,lower=-0.5,upper=0.5 \
                 -process=Intensity,max,%sMaxOdd,function=yIsOdd,lower=0.5,upper=1.5 \
                 "-redefine=column,Intensity,yIsOdd 1 == ? Intensity IntensityMaxOdd / : Intensity IntensityMaxEven / $ " \
                 | sddsconvert -pipe=in $output -delete=column,yIndex,yIsOdd,xIndex \
                 -delete=param,IntensityMaxOdd,IntensityMaxEven} result] {
        return -code error "APSNormalizeSddscontourFlagImage: $result"
    }
}

proc APSFindEdgesOnFlagHLImage {args} {
    set filename ""
    set columnFilter "*"
    set threshold 0
    set centerIndex 0
    APSStrictParseArguments {filename columnFilter threshold centerIndex}
    if {![string length $filename] || ![file exists $filename]} {
        return -code error "APSFindEdgesOnFlagHLImage: No filename given or file not found." }
    
    if [catch {exec sddsrowstats $filename "-sum=horProfile,$columnFilter" \
                 ${filename}.horProfileTmp
        exec sddssmooth $filename.horProfileTmp \
                 -pipe=out -column=horProfile -despike=neighbors=6,passes=3 \
                 | sddschanges -pipe -copy=Index -changesIn=horProfile -baseline=${filename}.horProfileTmp \
                 | sddsprocess -pipe -filter=column,ChangeInhorProfile,-99999999999999,$threshold,! \
                 "-define=column,distanceFromCenterIndex,Index $centerIndex -" \
                 "-process=distanceFromCenterIndex,maximum,leftEdge,functionOf=Index,topLimit=0,position" \
                 "-process=distanceFromCenterIndex,minimum,rightEdge,functionOf=Index,bottomLimit=0,position" \
                 | sdds2stream -pipe=in -parameter=leftEdge,rightEdge} result] {
        return -code error "APSFindEdgesOnFlagHLImage: $result"
    }
    set leftEdge [lindex $result 0]
    set rightEdge [lindex $result 1]
    catch {eval file delete [glob -nocomplain ${filename}.horProfileTmp]}
    return [list $leftEdge $rightEdge]
}

proc APSCalibrateLinacFlagHLImage {args} {
    set input ""
    set output ""
    set lineNamePrefix HLine
    APSStrictParseArguments {input output lineNamePrefix}
    if {![string length $input]} {
        return -code error "APSCalibrateLinacFlagHLImage: give input filenames" }

    # analyze original vertical lines to get the horizontal edges
    if [catch {APSFindEdgesOnFlagHLImage -filename $input \
                 -columnFilter "${lineNamePrefix}2\[4-6\]?" \
                 -threshold 80 \
                 -centerIndex 240} horizontalEdges] {
        return -code error "APSCalibrateLinacFlagHLImage: $horizontalEdges" }

    # Rotate the image and repeat to get the vertical edges.
    if [catch {exec sddsconvert $input -pipe=out -delete=column,Index \
                 | sddstranspose -pipe -root=VerticalLine -digits=3 \
                 | sddsprocess -pipe=in $input.trans \
                 -define=column,Index,i_row,type=long} result] {
        catch {file delete $input.trans}
        return -code error "APSCalibrateLinacFlagHLImage: $result"
    }
    if [catch {APSFindEdgesOnFlagHLImage -filename $input.trans \
                 -columnFilter "VerticalLine2\[5-7\]?" \
                 -threshold 80 -centerIndex 256} verticalEdges] {
        catch {file delete $input.trans}
        return -code error "APSCalibrateLinacFlagHLImage: $verticalEdges"
    }
    catch {file delete $input.trans}

    set leftEdge [lindex $horizontalEdges 0]
    set rightEdge [lindex $horizontalEdges 1]
    set bottomEdge [lindex $verticalEdges 0]
    set topEdge [lindex $verticalEdges 1]

    set horizontalPixelsPerMM [expr ($rightEdge - $leftEdge) / 36]
    set verticalPixelsPerMM [expr ($topEdge - $bottomEdge) / 36]

    if [string length $output] {
        if {[file exists $output] && \
              [catch {file delete $output} result]} {
            return -code error "APSCalibrateLinacFlagHLImage: Unable to delete old $output" }
        if [catch {sdds open $output w SDDS_BINARY} resultsID] {
            return -code error "APSCalibrateLinacFlagHLImage: $resultsID" }
        if [catch { sdds defineParameter $resultsID leftEdge -type SDDS_DOUBLE -fixedValue $leftEdge
            sdds defineParameter $resultsID rightEdge -type SDDS_DOUBLE -fixedValue $rightEdge
            sdds defineParameter $resultsID bottomEdge -type SDDS_DOUBLE -fixedValue $bottomEdge
            sdds defineParameter $resultsID topEdge -type SDDS_DOUBLE -fixedValue $topEdge
            sdds defineParameter $resultsID xPixelsPerMM -type SDDS_DOUBLE -fixedValue $horizontalPixelsPerMM
            sdds defineParameter $resultsID yPixelsPerMM -type SDDS_DOUBLE -fixedValue $verticalPixelsPerMM
            sdds writeLayout $resultsID
            sdds startPage $resultsID 0
            sdds writePage $resultsID
            sdds close $resultsID } result] {
            return -code error "APSCalibrateLinacFlagHLImage: $result"
        }
    }
    return [list $horizontalPixelsPerMM $verticalPixelsPerMM]
}

proc APSCaptureHLImageInSeries {args} {
    set beamline ""
    set rootname ""
    set imagePV ""
    set numberToAverage 0
    set numberToAcquire 0
    set backgroundsToAverage 1
    set wmonitorFile ""
    set lineNamePrefix HLine
    set indexDigits 2
    set index 0
    set hROI [list 30 470]
    set vROI [list 50 450]
    set beamTriggerNumber 1
    set backgroundTriggerNumber 29
    set statusCallback APSNoOp
    set doAnalysis 1
    set subtractBackground 1
    APSStrictParseArguments {beamline rootname numberToAverage numberToAcquire wmonitorFile lineNamePrefix indexDigits index \
                         hROI vROI backgroundsToAverage beamTriggerNumber backgroundTriggerNumber \
                         -statusCallback statusCallback doAnalysis subtractBackground imagePV}
    if {![string length $beamline] || ![string length $rootname] \
          || ![string length $wmonitorFile] || ($numberToAverage<1 && $numberToAcquire<1) \
          || ![string length $lineNamePrefix] || $index<0 \
          || $backgroundsToAverage<1  \
          || [llength $vROI]!=2 || [llength $hROI]!=2 \
          || $beamTriggerNumber<0 || $beamTriggerNumber>29 \
          || $backgroundTriggerNumber<0 || $backgroundTriggerNumber>29 \
          || $beamTriggerNumber==$backgroundTriggerNumber} {
        return -code error "APSCaptureHLImageInSeries: bad arguments" }

    if $indexDigits>0 {
        set rootname [format $rootname-%0${indexDigits}ld $index]
    } else {
        set rootname $rootname
    }

    set background $rootname.bkgnd
    catch {file delete $background.gz}
    if $subtractBackground {
        eval $statusCallback {"Getting background image."}
        if [catch {APSCaptureHLImage -beamline $beamline -takeBackground 1 -imagePV $imagePV \
                     -rootname $rootname -numberToAverage $backgroundsToAverage \
                     -numberToAcquire 0 \
                     -hROI $hROI -vROI $vROI -backgroundTriggerNumber $backgroundTriggerNumber \
                     -beamTriggerNumber $beamTriggerNumber \
                     -wmonitorFile $wmonitorFile -lineNamePrefix $lineNamePrefix \
                     -statusCallback $statusCallback
            exec gzip $background} result] {
            return -code error "APSCaptureHLImageInSeries: background image collection problem: $result" }
        set background $background.gz
    } else {
        set background ""
    }
    eval $statusCallback {"Getting beam image."}
    if [catch {APSCaptureHLImage -beamline $beamline -takeBackground 0 -imagePV $imagePV \
                 -rootname $rootname -backgroundFile $background -doAnalysis $doAnalysis \
                 -hROI $hROI -vROI $vROI -backgroundTriggerNumber $backgroundTriggerNumber \
                 -beamTriggerNumber $beamTriggerNumber -numberToAverage $numberToAverage \
                 -numberToAcquire $numberToAcquire \
                 -wmonitorFile $wmonitorFile -lineNamePrefix $lineNamePrefix \
                 -statusCallback $statusCallback} result] {
        return -code error "APSCaptureHLImageInSeries: $result" }
}

proc APSCaptureHLImage {args} {
    set beamline ""
    set rootname ""
    set imagePV ""
    set backgroundFile ""
    set numberToAverage 0
    set numberToAcquire 0
    set takeBackground 0
    set wmonitorFile ""
    set lineNamePrefix HLine
    set hROI [list 100 400]
    set vROI [list 100 400]
    set beamTriggerNumber 28
    set backgroundTriggerNumber 25
    set statusCallback APSNoOp
    set evenOddNorm 0
    set doAnalysis 1
    APSStrictParseArguments {takeBackground beamline rootname backgroundFile \
                         numberToAverage wmonitorFile lineNamePrefix vROI hROI \
                         beamTriggerNumber backgroundTriggerNumber statusCallback \
                         evenOddNorm doAnalysis numberToAcquire imagePV } 
    if {![string length $beamline] \
          || ![string length $rootname] \
          || ![string length $wmonitorFile] || ![string length $lineNamePrefix] \
          || ($numberToAverage<1 && $numberToAcquire<1) \
          || [llength $vROI]!=2 || [llength $hROI]!=2 \
          || $beamTriggerNumber<0 || $beamTriggerNumber>29 \
          || $backgroundTriggerNumber<0 || $backgroundTriggerNumber>29 \
          || $beamTriggerNumber==$backgroundTriggerNumber} {
        return -code error "APSCaptureHLImage: bad arguments"
    }

    set Linac 0
    set LEUTL 0
    set LTP 0
    set PTB 0
    set BTS 0
    set MV20 0
    if ![llength [info var $beamline]] {
        return -code error "APSCaptureHLImage: beamline $beamline is unknown"
    }
    set $beamline 1

    if {$Linac || $LTP || $LEUTL || $PTB} {
        # If taking background, set for pulse 26, otherwise pulse 28
        
        set triggerNumber [expr $takeBackground?$backgroundTriggerNumber:$beamTriggerNumber]
        if [catch {exec cavput -list=LI:VD1:videoRefBunchAO=$triggerNumber,LI:VD1:twoHzFrameDelayC=$triggerNumber -pend=20} result] {
            return -code error "APSCaptureHLImage: cavput problem: $result"
        }
        set upper [expr $triggerNumber+0.5]
        set lower [expr $triggerNumber-0.5]
        if [catch {exec cawait \
                     -waitFor=LI:VD1:videoRefBunchMonCO,lowerLimit=$lower,upperLimit=$upper \
                     -timeLimit=5} result] {
            return -code error "APSCaptureHLImage: cawait problem: $result"
        }
    } else {
        # If taking background, turn gun off, otherwise turn it on.
        exec cavput -list=L1:RG1:KIK:chargeTrigC=[expr $takeBackground?0:1],L1:RG2:KIK:chargeTrigC=[expr $takeBackground?0:1] -pend=20
        after 1000
    }
    
    set outTmp /tmp/[APSTmpString]
    if $takeBackground {
        set outputName $rootname.bkgnd
    } else {
        set outputName $outTmp
    }

    eval $statusCallback {"Running sddswmonitor in slave mode ($wmonitorFile $outputName)."}

    set myID [APSUniqueName sddsmonSlave]
    if $numberToAcquire {
        set wmonArg "-step=$numberToAcquire"
        set count $numberToAcquire
    } else {
        set wmonArg "-step=1  -accumulate=average,number=$numberToAverage" 
        set count $numberToAverage
    }
    set monitorProgram sddswmonitor
    set preacquireCommand "exec cavput -list=LI:VD1:imageGrabSU.PROC=1 -pend=20"
    if {[string compare $imagePV BTS:FS3:image1]==0} {
        set monitorProgram sddsimagemonitor
        set count 1
        set wmonArg "-step=1"
        set preacquireCommand ""
    }
    eval $statusCallback {"monitorProgram = $monitorProgram for imagePV = $imagePV $wmonArg"}
    global debugSddsmonitorSlave
    set debugSddsmonitorSlave 1
    if [catch {APSStartSddsmonitorSlave -ID $myID -sddsmonitorProgram $monitorProgram \
                 -sddsmonitorOptions \
                 "$wmonitorFile $outputName -erase $wmonArg " \
                 -preacquireCommand $preacquireCommand } result] {
        return -code error "APSCaptureHLImage: $result"
    }
    for {set i 0} {$i<$count} {incr i} {
        eval $statusCallback {"Triggering acquisition $i of $count"}
        if [catch {APSSddsMonitorSlaveTakePoint -ID $myID} result] {
            return -code error "APSCaptureHLImage: $result"
        }
        after 500
        update
    }
    if [catch {APSStopSddsmonitorSlave -ID $myID} result] {
        return -code error "APSCaptureHLImage: $result"
    }
    if $takeBackground {
        return
    }

    catch {file delete $rootname.raw.gz}
    if [string length $backgroundFile] {
        # subtract the named backgroundFile 
        eval $statusCallback {"Subtracting background."}
        if [catch {exec gzip -c $outTmp > $rootname.raw.gz
            exec sddschanges $outTmp $outTmp.sub -baseline=$backgroundFile \
                     -changesIn=${lineNamePrefix}* -copy=Index,${lineNamePrefix}*} result] {
            return -code error "APSCaptureHLImage: $result"
        }
        catch {file delete $outTmp}
        set dataFile $outTmp.sub
        set lineNamePrefix ChangeIn$lineNamePrefix
    } else {
        set dataFile $outTmp
        if [catch {exec gzip -c $outTmp > $rootname.raw.gz} result] {
            return -code error "APSCaptureHLImage: $result"
        }
    }
    catch {file delete $rootname.gz}
    if $evenOddNorm {
        # separate even and odd lines and normalize independently, then recombine
        set nums "02468"
        eval $statusCallback {"Doing even/odd normalization."}
        foreach type {Even Odd} {
            if [catch \
                  { exec sddsrowstats $dataFile -pipe=out -maximum=${lineNamePrefix}Max,${lineNamePrefix}*\[$nums\] \
                      | sddsconvert -pipe \
                      -retain=column,${lineNamePrefix}Max,${lineNamePrefix}*\[$nums\],Index \
                      | sddsprocess -pipe=in $outTmp.norm.[string tolower $type] \
                      -process=${lineNamePrefix}Max,max,${lineNamePrefix}${type}Max \
                      "-redefine=column,%s,%s ${lineNamePrefix}${type}Max abs 1e-12 + /,select=${lineNamePrefix}*\[$nums\],type=float"
                  } result] {
                return -code error "APSCaptureHLImage: $result"
            }
            set nums "13579"
        }
        catch {file delete $dataFile}

        eval $statusCallback {"Doing even/odd recombination."}
        if [catch {exec sddsxref $outTmp.norm.even $outTmp.norm.odd -pipe=out \
                     | sddsconvert -pipe -delete=column,${lineNamePrefix}Max \
                     | sddstranspose -pipe \
                     | sddssort -pipe -column=OldColumnNames \
                     | sddstranspose -pipe \
                     | sddsprocess -pipe \
                     "-redefine=column,%s,%s,select=${lineNamePrefix}*\[0-9\],type=float" \
                     | gzip -c > $rootname.gz} result] {
            catch {file delete $outTmp.norm.even $outTmp.norm.odd}
            return -code error "APSCaptureHLImage: $result"
        }
        catch {file delete $outTmp.norm.even $outTmp.norm.odd}
    } else {
        if [catch {exec gzip -c $dataFile > $rootname.gz} result] {
            catch {file delete $dataFile}
            return -code error "APSCaptureHLImage: $result"
        }
        catch {file delete $dataFile}
    }

    if $doAnalysis {
        eval $statusCallback {"Doing h/v analysis."}
        catch {file delete $rootname.h $rootname.v}
        if [catch {APSAnalyzeHLImage -plane h -input $rootname.gz \
                     -output $rootname.h -lineNamePrefix $lineNamePrefix \
                     -ROI $hROI 
            APSAnalyzeHLImage -plane v -input $rootname.gz \
                     -output $rootname.v -lineNamePrefix $lineNamePrefix \
                     -ROI $vROI} result] {
            catch {eval file delete [glob -nocomplain $outTmp*]}
            return -code error "APSCaptureHLImage: $result"
        }
        catch {eval file delete [glob -nocomplain $outTmp*]}
    }
}
    
proc APSAnalyzeHLImage {args} {
    set input ""
    set output ""
    set lineNamePrefix HLine
    set plane h
    set ROI ""
    if {[APSStrictParseArguments {input output lineNamePrefix plane ROI}] || \
          ![string length $input] || ![string length $output] || \
          ![string length $lineNamePrefix] || ![string length $plane] || \
          [llength $ROI]!=2} {
        return -code error "APSAnalyzeHLImage: bad arguments"
    }
    set ROILow [lindex $ROI 0]
    set ROIHigh [lindex $ROI 1]
    switch $plane {
        X -
        x - 
        H -
        h {
            set tmpRoot /tmp/[APSTmpString]
            # 1. Sum the matching columns across rows (horizontal dimension)
            # 2. Gaussian fit the resulting row vs Index to get the sigma.
            if [catch {exec sddsrowstats $input $tmpRoot \
                         "-sum=HorizontalProfile,${lineNamePrefix}*" 
                exec sddsgfit $tmpRoot -pipe=out \
                         -columns=Index,HorizontalProfile -fitRange=$ROILow,$ROIHigh \
                         -limits=evaluations=1000 \
                         | sddsxref -pipe=in $tmpRoot $output -take=* } result] {
                catch {file delete -force -- $tmpRoot}
                return -code error "APSAnalyzeHLImage: $result"
            }
            catch {file delete -force -- $tmpRoot}
        }
        Y -
        V -
        y -
        v {
            set tmpRoot /tmp/[APSTmpString]
            # 1. Sum the matching columns.
            # 2. Transpose the resulting single-row file to get a single-column file.
            # 3. Define the Index column.
            # 4. Do gaussian fit.
            if [catch \
                  {exec sddsrunstats -pipe=out $input "-sum=${lineNamePrefix}*" -points=512 -noOverlap \
                     | sddstranspose -pipe \
                     | sddssort -pipe -column=OldColumnNames \
                     | sddsprocess "-redefine=column,Index,i_row,type=short" -pipe \
                     | sddsconvert -pipe=in $tmpRoot.data -rename=column,Column=VerticalProfile \
                     -delete=column,OldColumnNames 
                      exec sddsprocess $tmpRoot.data $tmpRoot.even -sparse=2,0 \
                     -process=VerticalProfile,sum,VerticalProfileSum \
                     "-redefine=column,VerticalProfile,VerticalProfile VerticalProfileSum 1e-6 + /"
                      exec sddsprocess $tmpRoot.data $tmpRoot.odd -sparse=2,1 \
                     -process=VerticalProfile,sum,VerticalProfileSum \
                     "-redefine=column,VerticalProfile,VerticalProfile VerticalProfileSum 1e-6 + /"
                      exec sddscombine $tmpRoot.even $tmpRoot.odd -pipe=out -merge \
                     | sddssort -column=Index -pipe=in $tmpRoot.sort
                     exec sddsgfit $tmpRoot.sort -pipe=out -columns=Index,VerticalProfile \
                     -fitRange=$ROILow,$ROIHigh -limits=evaluations=1000 \
                     | sddsxref -pipe=in $tmpRoot.sort $output -take=* } result] {
                catch {eval file delete $tmpRoot.data $tmpRoot.even $tmpRoot.odd $tmpRoot.sort}
                return -code error "APSAnalyzeHLImage: $result"
            }
            catch {eval file delete $tmpRoot.data $tmpRoot.even $tmpRoot.odd $tmpRoot.sort}
        }
        default {
            return -code error "APSAnalyzeHLImage: invalid plane: $plane"
        }
    }
}

proc APSCaptureImageProfileInSeries {args} {
    set beamline ""
    set rootname ""
    set numberToAverage 1
    set wmonitorFile ""
    set scalarsFile ""
    set indexDigits 2
    set index 0
    set beamTriggerNumber 1
    set backgroundTriggerNumber 29
    set statusCallback APSNoOp
    APSStrictParseArguments {beamline rootname numberToAverage wmonitorFile scalarsFile indexDigits index \
                         beamTriggerNumber statusCallback backgroundTriggerNumber}
    if {![string length $beamline] || ![string length $rootname] \
          || ![string length $wmonitorFile] || $numberToAverage<1 \
          || $index<0 || $beamTriggerNumber<0 || $beamTriggerNumber>29 \
          || $backgroundTriggerNumber<0 || $backgroundTriggerNumber>29 \
          || $beamTriggerNumber==$backgroundTriggerNumber} {
        return -code error "APSCaptureImageProfileInSeries: bad arguments" }

    if $indexDigits>0 {
        set output [format $rootname-%0${indexDigits}ld $index]
    } else {
        set output $rootname
    }

    if [catch {APSCaptureImageProfile -beamline $beamline -takeBackground 0 \
                 -output $output \
                 -beamTriggerNumber $beamTriggerNumber -numberToAverage $numberToAverage \
                 -backgroundTriggerNumber $backgroundTriggerNumber \
                 -wmonitorFile $wmonitorFile -scalarsFile $scalarsFile \
                 -statusCallback $statusCallback} result] {
        return -code error "APSCaptureImageProfileInSeries: $result" }
}

proc APSCaptureImageProfile {args} {
    set beamline ""
    set output ""
    set numberToAverage 1
    set wmonitorFile ""
    set scalarsFile ""
    set beamTriggerNumber 1
    set backgroundTriggerNumber 29
    set statusCallback APSNoOp
    APSStrictParseArguments {beamline output \
                         numberToAverage wmonitorFile scalarsFile \
                         beamTriggerNumber statusCallback backgroundTriggerNumber}
    if {![string length $beamline] || ![string length $output] \
          || ![string length $wmonitorFile] || $numberToAverage<1  \
          || $beamTriggerNumber<0 || $beamTriggerNumber>29  \
          || $backgroundTriggerNumber<0 || $backgroundTriggerNumber>29 \
          || $beamTriggerNumber==$backgroundTriggerNumber} {
        return -code error "APSCaptureImageProfile: bad arguments"
    }

    set Linac 0
    set LEUTL 0
    set LTP 0
    set PTB 0
    set BTS 0
    set MV20 0
    if ![llength [info var $beamline]] {
        return -code error "APSCaptureImageProfile: beamline $beamline is unknown"
    }
    set $beamline 1

    eval $statusCallback {"Storing background image..."}
    if [catch {exec cavput -list=LI:VD1:videoRefBunchAO=$backgroundTriggerNumber -pend=20
        after 500
        exec cavput -list=LI:VD1:captureBkSU.PROC=1 -pend=20
        after 500
        exec cavput -list=LI:VD1:videoRefBunchAO=$beamTriggerNumber,LI:VD1:viewSrcMI=Subtracted -pend=20} result] {
        return -code error "APSCaptureImageProfile: $result"
    }

    eval $statusCallback {"Running sddswmonitor."}
    set outTmp /tmp/[APSTmpString]
    if [string length $scalarsFile] {
        set extraArg -scalars=$scalarsFile
    } else {
        set extraArg ""
    }
    if [catch {eval exec sddswmonitor $wmonitorFile $output $extraArg -step=1 -erase \
                 -accumulate=average,number=$numberToAverage -interval=0.5,s} result] {
        return -code error "APSCaptureImageProfile: $result"
    }
}
    
proc APSConfigureCameraAndScreenPVs {args} {
    set flagIn 0
    set lampOn 0
    set flag ""
    set actuatorNumber 1
    set system ""
    set statusCallback APSNoOp
    set ignoreFlagReadback 0
    set cameraType CID
    APSStrictParseArguments {flagIn lampOn flag system statusCallback actuatorNumber ignoreFlagReadback cameraType}
    if {![string length $flag] || ![string length $system]} {
        return -code error "APSConfigureCameraAndScreenPVs: flag or system is null string" }

    if [catch {exec cavput "-list=LI:SelectCamModeC=VMUX only" -pend=20} result] {
        return -code error "APSConfigureCameraAndScreenPVs: $result" }
    
    switch $system {
        Linac -
        LEUTL -
        ITS {
            if [string length [string trim $cameraType]]==0 {
                set cType ":"
            } else {
                set cType ":$cameraType:"
            }
	    if {[regexp {S3C2} $flag] || [regexp {S2C2}  $flag]} {
		if $flagIn {
		    eval $statusCallback {"Inserting flag $flag."}
		    if [catch {exec cavput -list=${flag}:LampOnC=$lampOn,$flag:yagActC=1,$flag:CameraOnC=1 -pend=20} result] {
			return -code error "APSConfigureCameraAndScreenPVs (inserting flags): $result"
		    }
		} else {
		    eval $statusCallback {"Removing flag."}
		    if [catch {exec cavput -list=${flag}:LampOnC=$lampOn,$flag:yagActC=0 -pend=20 } result] {
			return -code error "APSConfigureCameraAndScreenPVs (inserting flags): $result"
		    }
		}
	    } else {
		if $flagIn {
		    eval $statusCallback {"Inserting flag $flag."}
		    if [lsearch -exact [list CCDL CCDH] $cameraType]!=-1 {
			# this is a kludge to get the camera to work 
                        if [catch {exec cavput -list=$flag:CCDL:SelectCamC=1 -pend=20
                            after 1000
                            exec cavput -list=$flag:CCDH:SelectCamC=1 -pend=20} result] {
                            return -code error "APSConfigureCameraAndScreenPVs: $result"
                        }
                    }
                    if {$system=="ITS"} {
                        if [catch \
                              {exec cavput -pend=20 \
				 -list=$flag:LampOnC=$lampOn,$flag:Act${actuatorNumber}InC=1,LI:VD1:cameraTrigDelayPD.GATE=1,$flag:CameraOnC=1} result] {
                            return -code error "APSConfigureCameraAndScreenPVs: $result"
                        }
                    } else {
                        if [catch \
                              {exec cavput -pend=20 \
				 -list=$flag${cType}SelectCamC=1,$flag:LampOnC=$lampOn,$flag:Act${actuatorNumber}InC=1,LI:VD1:cameraTrigDelayPD.GATE=1,$flag:CameraOnC=1} result] {
                            return -code error "APSConfigureCameraAndScreenPVs: $result"
                        }
                    }
		    set flagReadback $flag:Act${actuatorNumber}InM
		} else {
		    eval $statusCallback {"Removing flag."}
                    if {$system=="ITS"} {
                        if [catch \
                              {exec cavput -pend=20 \
				 -list=$flag:LampOnC=$lampOn,$flag:Act${actuatorNumber}InC=0,LI:VD1:cameraTrigDelayPD.GATE=1} result] {
                            return -code error "APSConfigureCameraAndScreenPVs: $result"
                        }
                    } else {
                        if [catch \
                              {exec cavput -pend=20 \
				 -list=$flag${cType}SelectCamC=0,$flag:LampOnC=$lampOn,$flag:Act${actuatorNumber}InC=0,LI:VD1:cameraTrigDelayPD.GATE=1} result] {
                            return -code error "APSConfigureCameraAndScreenPVs: $result"
                        }
                    }
		}
	    }
        }
        LTP {
            set command "exec cavput -pend=20 -list="
            set flagNumber [string index $flag 6]
            if $flagIn {
                eval $statusCallback {"Inserting flag and setting up camera."}
                append command $flag:act_cont_bo.VAL=1,\
                  $flag:LampOn=$lampOn,\
                  LI:VD:swMon2srcMI=10,\
                  LI:VD:swMV20srcMI=10,\
                  LTP:FLN:CameraSelMux=$flagNumber,\
                  LTP:FLN:Mux1=1,\
                  LTP:FLN:Mux2=2,\
                  MCR:CamSelMux1Mbbo=3,\
                  MCR:CamSelMux2Mbbo=2,\
                  LI:VD1:cameraTrigDelayPD.GATE=1
            } else {
                eval $statusCallback {"Removing flag and turning off camera."}
                append command $flag:act_cont_bo.VAL=0,\
                  LI:VD1:cameraTrigDelayPD.GATE=1
            }
            if [catch {eval $command} result] {
                return -code error "APSConfigureCameraAndScreenPVs: $result"
            }
            set flagReadback $flag:act_status_mbbi
        }
        PTB {
            set command "exec cavput -pend=20 -list="
            set flagNumber [string index $flag 6]
            if $flagIn {
                eval $statusCallback {"Inserting flag."}
                append command $flag:act_cont_bo.VAL=1,\
                  $flag:LampOn=$lampOn


                #THESE PVS are not available.
                 #PTB:FLN:CameraSelMux=$flagNumber
                #, LTP:FLN:Mux1=4,LTP:FLN:Mux2=2,LI:VD:swMon2srcMI=10,LI:VD:swMV20srcMI=10,MCR:CamSelMux1Mbbo=3,MCR:CamSelMux2Mbbo=2
            } else {
                eval $statusCallback {"Removing flag."}
                append command $flag:act_cont_bo.VAL=0,\
                  $flag:LampOn=0,\
                  PTB:FLN:SelSeq${flagNumber}.PROC=0
            }
            if [catch {eval $command} result] {
                return -code error "APSConfigureCameraAndScreenPVs: $result"
            }
            set flagReadback $flag:act_status_mbbi
        }
        BTS {
            set commandFlag "exec cavput -pend=20 -list=" 
            if $flagIn {
                eval $statusCallback {"Inserting BTS flag."}
                if {$flag=="BTX:FS"} {
                    #this is a new device, the flag it reversed
                    append commandFlag $flag:LampOnBo=$lampOn,\
                      $flag:act_cont_bo.VAL=0,
                } else {
                    append commandFlag $flag:LampOnBo=$lampOn,\
                      $flag:act_cont_bo.VAL=1,
                }
                append commandFlag $flag:SelSeq.PROC=1 
            } else {
                eval $statusCallback {"Removing flag."}
                append commandFlag $flag:act_cont_bo.VAL=0,$flag:SelSeq.PROC=0,$flag:LampOnBo=0
            }
            if [catch {eval $commandFlag} result] {
                return -code error "APSConfigureCameraAndScreenPVs: $result"}
            
            set flagReadback $flag:act_status_mbbi
        }
        default {
            return -code error "APSConfigureCameraAndScreenPVs: unknown system: $system"
        }
    }
    if {$flagIn && !$ignoreFlagReadback} {
        eval $statusCallback {"Waiting for flag to insert"}
        if {[catch {exec cawait \
                      -waitFor=$flagReadback,equal=1 -timeLimit=30} result]} {
            return -code error "APSConfigureCameraAndScreenPVs: screen not moving: $result"
        }
    }
}

proc APSSetBCCameraAperture {args} {
    set flagList ""
    set lowres 0
    set delta 10
    set tries 30
    set passes 3
    set alreadyInserted 0
    set saturatedPixelLimit 0
    set statusCallback APSNoOp
    set abortVariable ""
    APSStrictParseArguments {flagList lowres delta tries statusCallback passes alreadyInserted abortVariable saturatedPixelLimit}
    if [string length $abortVariable] {
        global $abortVariable
        set $abortVariable 0
    }
    if ![set delta [expr abs($delta)]] {
        return
    }

    if {$alreadyInserted && [llength $flagList]>1} {
        return -code error "APSSetBCCameraAperture: can't give -alreadyInserted 1 and list of flags"
    }

    if $lowres {
        set name Lo
        set type L
    } else {
        set name Hi
        set type H
    }

    foreach flag $flagList {
        if {[string length $abortVariable] && [set $abortVariable]} {
            return -code error "APSSetBCCameraAperture: aborted."
        }
        set expectedName ${flag}_${name}-Res
        set closePV $flag:CCD${type}:IR:setPosC
        set movingPV $flag:CCD${type}:IR:movingM
        set stopPV $flag:CCD${type}:IR:stopC

        global apsSetBCCameraApertureStop apsSetBCCameraAperturePosSet
        if [pv linkw {apsSetBCCameraApertureStop apsSetBCCameraAperturePosSet} \
              "$stopPV $closePV"] {
            global errorCode
            return -code error "APSSetBCCameraAperture: $errorCode"
        }
        eval $statusCallback {"Setting aperture for $flag"}

        if !$alreadyInserted {
            if [catch {APSConfigureCameraAndScreenPVs -flagIn 1 -lampOn 0 -flag $flag \
                         -actuatorNumber 1 -statusCallback $statusCallback -cameraType CCD$type \
                         -system LEUTL} result] {
                return -code error "APSSetBCCameraAperture: $errorCode"
            }
            after 5000
        }

        if [catch {exec cavput -list=LI:VD1:imageSourceC=Original,LI:VD1:enableHistogramC=1 -pend=20} result] {
            return -code error "APSSetBCCameraAperture: $result"
        }

        set tmpFile /tmp/[APSTmpString]
        APSAddToTmpFileList -ID APSSetBCCameraAperture -fileList $tmpFile

        set success 0
        set lastDir ""
        set dir close
        for {set ipass 0} {$ipass<$passes} {incr passes} {
            if {[string length $abortVariable] && [set $abortVariable]} {
                return -code error "APSSetBCCameraAperture: aborted."
            }
            if $success break
            set try0 0
            for {set try 0} {$try<$tries} {incr try} {
                if {[string length $abortVariable] && [set $abortVariable]} {
                    return -code error "APSSetBCCameraAperture: aborted."
                }
                if [catch {exec sddswmonitor $tmpFile -erase \
                             -pvnames=LI:VD1:imageHistogramWF -step=1 \
                             -interval=0.5,s -accumulate=average,number=10 
                    exec sddsprocess $tmpFile -pipe=out \
                             -filter=column,Index,220,256 \
                             -process=LI:VD1:imageHistogramWF,sum,HistSum \
                             -process=LI:VD1:imageHistogramWF,last,HistLast \
                             | sdds2stream -pipe -parameter=HistSum,HistLast} resultList] {
                    APSDeleteTmpFileList -ID APSSetBCCameraAperture
                    return -code error "APSSetBCCameraAperture: $resultList"
                }
                if {[string length $abortVariable] && [set $abortVariable]} {
                    return -code error "APSSetBCCameraAperture: aborted."
                }
                APSDeleteTmpFileList -ID APSSetBCCameraAperture
                set hsum [lindex $resultList 0]
                set hsat [lindex $resultList 1]
                set sumLimit [expr 4+$saturatedPixelLimit]
                if {$hsum>$sumLimit && $hsat<=$saturatedPixelLimit} {
                    set success 1
                    eval $statusCallback {"Intensity is within range"}
                    break
                }
                if $hsat>$saturatedPixelLimit {
                    eval $statusCallback {"Closing aperture"}
                    set dir close
                } else {
                    eval $statusCallback {"Opening aperture"}
                    set dir open
                }
                if {[string length $abortVariable] && [set $abortVariable]} {
                    return -code error "APSSetBCCameraAperture: aborted."
                }
                if [string length $lastDir] {
                    if [string compare $dir $lastDir] {
                        set delta [expr int($delta/3)]
                        if $delta<1 {
                            set delta 1
                        }
                        set try0 0
                    } else {
                        if $try0>4 {
                            set delta [expr $delta*2]
                            set try0 0
                        } else {
                            incr try0 
                        }
                    }
                }
                set lastDir $dir
                if [pv getw apsSetBCCameraAperturePosSet] {
                    global errorCode
                    return -code error "APSSetBCCameraAperture: $errorCode"
                }
                if {[string length $abortVariable] && [set $abortVariable]} {
                    return -code error "APSSetBCCameraAperture: aborted."
                }
                switch $dir {
                    open {
                        set apsSetBCCameraAperturePosSet \
                          [expr $apsSetBCCameraAperturePosSet-$delta]
                        if [pv putw apsSetBCCameraAperturePosSet] {
                            global errorCode
                            return -code error "APSSetBCCameraAperture: $errorCode"
                        }
                        after 1500
                        exec cawait -waitfor=$movingPV,equal=0 \
                          -waitfor=LI:VD1:numPixelsGtNormM,above=1 -or -timelimit=60
                    } 
                    close {
                        set apsSetBCCameraAperturePosSet \
                          [expr $apsSetBCCameraAperturePosSet+$delta]
                        if [pv putw apsSetBCCameraAperturePosSet] {
                            global errorCode
                            return -code error "APSSetBCCameraAperture: $errorCode"
                        }
                        after 1500
                        exec cawait -waitfor=$movingPV,equal=0 \
                          -waitfor=LI:VD1:numPixelsGtNormM,below=1 -or -timelimit=60
                    }
                }
                set apsSetBCCameraApertureStop 1
                if [pv putw apsSetBCCameraApertureStop] {
                    global errorCode
                    return -code error "APSSetBCCameraAperture: $errorCode"
                }
                # exec cavput -list=$stopPV=1
            }
        }
        if !$alreadyInserted {
            if [catch {APSConfigureCameraAndScreenPVs -flagIn 0 -lampOn 0 -flag $flag \
                         -actuatorNumber 1 -statusCallback $statusCallback -cameraType CCD$type \
                         -system LEUTL} result] {
                return -code error "APSSetBCCameraAperture: $result"
            }
        }
    }
}

proc APSSetLinacFlagROI {args} {
    set flagList ""
    set lowres 0
    set factor 2
    set xMinSize 0
    set yMinSize 0
    set xEdgeSize 5
    set yEdgeSize 5
    set clipSize 10
    set samples 10
    set statusCallback APSNoOp
    set alreadyInserted 0
    set suppressNoise 0
    APSStrictParseArguments {flagList lowres statusCallback factor xMinSize yMinSize clipSize \
                           xEdgeSize yEdgeSize alreadyInserted samples suppressNoise}

    if $lowres {
        set camera L
    } else {
        set camera H
    }
    if {$alreadyInserted && [llength $flagList]>1} {
        return -code error "APSSetLinacFlagROI: can't give -alreadyInserted 1 and list of flags"
    }
    set tmpFile /tmp/[APSTmpString]
    APSAddToTmpFileList -ID APSSetLinacFlagROI -fileList $tmpFile
    foreach flag $flagList {
        if !$alreadyInserted {
            if [catch {APSConfigureCameraAndScreenPVs -flagIn 1 -lampOn 0 -flag $flag \
                         -actuatorNumber 1 -statusCallback $statusCallback -cameraType CCD$camera \
                         -system LEUTL
                exec cavput -list=LI:VD1:imageSourceC=Subtracted -pend=20} result] {
                return -code error "APSSetLinacFlagROI: $result"
            }
            after 5000
        }
        if [catch {exec cavput -list=LI:VD1:roi -list=X,Y -list=StartC=1,SizeC=478 -pend=20} result] {
            return -code error "APSSetLinacFlagROI: $result"
        }
        after 1000

        if [catch {exec sddswmonitor -pvnames=LI:VD1:xDataWF,LI:VD1:yDataWF -erase $tmpFile \
                     -accumulate=average,number=$samples -interval=0.5,sec -step=1} result] {
            return -code error "APSSetLinacFlagROI: $result"
        }
        # exec sddsplot -col=Index,*WF -graph=line,vary $tmpFile

        if $suppressNoise {
            if [catch {exec sddsprocess $tmpFile -pipe=out -clip=$clipSize,$clipSize \
                         | sddsbaseline -pipe -column=*DataWF \
                         -select=antioutlier=5 -method=fit -nonnegative -despike=passes=5 \
                         | sddscliptails -pipe -column=*DataWF -afterzero \
                         | sddsconvert -pipe -edit=column,*WF,%/LI:VD1://%/DataWF/Profile/ \
                         | sddsprocess -pipe -nowarning \
                         -process=*Profile,max,%sMaxPos,position,functionOf=Index \
                         -process=*Profile,fwha,%sFWHA,functionOf=Index \
                         | sdds2stream -pipe \
                         -parameter=xProfileMaxPos,yProfileMaxPos,xProfileFWHA,yProfileFWHA} resultList] {
                return -code error "APSSetLinacFlagROI: $resultList"
            }
            #exec sddsplot -col=Index,*WF $tmpFile.x -graph=line,vary
            #file delete -force $tmpFile.x
        } else {
            if [catch {exec sddsprocess $tmpFile -pipe=out -clip=$clipSize,$clipSize \
                         | sddssmooth -pipe -pass=0 -despike -column=*WF \
                         | sddsconvert -pipe -edit=column,*WF,%/LI:VD1://%/DataWF/Profile/ \
                         | sddsprocess -pipe \
                         -process=*Profile,max,%sMaxPos,position,functionOf=Index \
                         -process=*Profile,fwha,%sFWHA,functionOf=Index \
                         | sdds2stream -pipe \
                         -parameter=xProfileMaxPos,yProfileMaxPos,xProfileFWHA,yProfileFWHA} resultList] {
                return -code error "APSSetLinacFlagROI: $resultList"
            }
        }
        APSSetVarsFromList -valueList $resultList -variableList "xMaxPos yMaxPos xFWHA yFWHA"
        # eval $statusCallback {"xMaxPos = $xMaxPos  yMaxPos = $yMaxPos  xFWHA = $xFWHA  yFWHA = $yFWHA"}
        foreach item {xFWHA yFWHA} {
            if [set $item]>250 {
                set $item 250
            }
        }
        set xFWTA [expr $xFWHA*2]
        set yFWTA [expr $yFWHA*2]

        foreach plane {X Y} offset {0 2} minSize [list $xMinSize $yMinSize] \
          edgeSize [list $xEdgeSize $yEdgeSize] maxPos [list $xMaxPos $yMaxPos ] \
          fwta [list $xFWTA $yFWTA] {
              set x0 [expr int($maxPos-$factor*$fwta/2.0)]
              set dx [expr int($factor*$fwta)]
              set xc [expr int($x0+$dx/2)]
              set dx [expr int($dx*$factor)]
              if {$minSize>0 && $minSize>$dx} {
                  set dx $minSize
              }
              set x0 [expr int($xc-$dx/2)]
              set x1 [expr $x0+$dx]
              if $x0<$edgeSize {
                  set x0 $edgeSize
                  set dx [expr $x1-$x0]
              }
              if $x1>[expr 479-$edgeSize] {
                  set x1 [expr 479-$edgeSize]
                    set dx [expr $x1-$x0]
              }
              # eval $statusCallback {"plane $plane: Start=$x0 Size=$dx"}
              if [catch {exec cavput -pend=20 -list=LI:VD1:roi${plane} \
                           -list=Start=$x0,Size=$dx -list=C} result] {
                  return -code error "APSSetLinacFlagROI: $result"
              }
          }
        
        after 2000
        eval $statusCallback {"Saving configuration"}
        if [catch {exec cavput -list=LI:VD1:CAL:applyAndSaveC.PROC=1 -pend=20} result] {
            return -code error "APSSetLinacFlagROI: $result"
        }
        eval $statusCallback {"Waiting for save"}
        if [catch {exec cawait -waitfor=LI:VD1:CAL:calChangedM,equal=0 \
                     -waitfor=LI:VD1:CAL:saveFileStatM,equal=2 -and} result] {
            return -code error "APSSetLinacFlagROI: $result"
        }
        eval $statusCallback {"Save detected"}
        if !$alreadyInserted {
            if [catch {APSConfigureCameraAndScreenPVs -flagIn 0 -lampOn 0 -flag $flag \
                         -actuatorNumber 1 -statusCallback $statusCallback -cameraType CCD$camera \
                         -system LEUTL} result] {
                return -code error "APSSetLinacFlagROI: $result"
            }
        }
    }
}


proc APSThreeScreenEmitMeas {args} {
    set system BCUsingYAGLowRes
    set abortVariable ""
    set samples 10
    set rootname ""
    set postSwitchPause 10
    set beamEnergy 150
    set dataToUse Raw
    set useMV200BGSubtraction 1
    set statusCallback APSNoOp
    set analyze 0
    set randomizations 50
    set autoExclude 1
    set autoROI 0
    set autoAperture 0
    set saveImage 0
    APSStrictParseArguments {system abortVariable samples rootname postSwitchPause beamEnergy \
                               dataToUse useMV200BGSubtraction statusCallback analyze \
                               randomizations autoExclude autoROI autoAperture saveImage}

    set configDir /home/helios/oagData/TransportLineEmittance/threeScreen
    set systemListFile $configDir/systems.sdds
    if [catch {exec sddsprocess $systemListFile -pipe=out \
                 -match=column,MeasurementName=$system -nowarning \
                 | sdds2stream -column=ConfigFileName -pipe} configFileName] {
        return -code error "APSThreeScreenEmitMeas: $result"
    }
    if {[llength $configFileName]!=1 || ![string length $configFileName]} {
        return -code error "APSThreeScreenEmitMeas: system \"$system\" is unknown or ambiguous"
    }
    if ![file exists $configFileName] {
        return -code error "APSThreeScreenEmitMeas: config file \"$configFileName\" not found"
    }
    set tmpFile /tmp/[APSTmpString]
    APSAddToTmpFileList -ID APSThreeScreenEmitMeas -fileList $tmpFile
    if [catch {exec sddssort $configFileName $tmpFile -column=ScreenPosition} result] {
        return -code error "APSThreeScreenEmitMeas: $result"
    }
    if [catch {sdds load $tmpFile ConfigData} result] {
        return -code error "APSThreeScreenEmitMeas: $result"
    }
    
    if [catch {APSTakeThreeScreenEmitMeasData -abortVariable $abortVariable \
                 -samplesList [APSReplicateItem -item $samples -number 3] \
                 -screenList [lindex $ConfigData(Column.ScreenPVRoot) 0] \
                 -positionList [lindex $ConfigData(Column.ScreenPosition) 0] \
                 -rootname $rootname -postSwitchPause $postSwitchPause \
                 -beamEnergy $beamEnergy -dataToUse $dataToUse \
                 -useMV200BGSubtraction $useMV200BGSubtraction \
                 -autoSetROIList [APSReplicateItem -number 3 -item $autoROI] \
                 -autoSetApertureList [APSReplicateItem -number 3 -item $autoAperture] \
                 -statusCallback $statusCallback \
                 -saveImage $saveImage \
                 -xCalList [lindex $ConfigData(Column.xCalibration) 0] \
                 -yCalList [lindex $ConfigData(Column.yCalibration) 0] \
                 -xResList [lindex $ConfigData(Column.xResolution) 0] \
                 -yResList [lindex $ConfigData(Column.yResolution) 0] \
                 -cameraTypeList [APSReplicateItem -number 3 \
                                    -item [lindex $ConfigData(Parameter.CameraTypeList) 0]] \
                 -actuatorNumberList [lindex $ConfigData(Column.ActuatorNumber) 0] \
                 -idealBeta [lindex $ConfigData(Parameter.IdealBeta) 0] \
                 -idealAlpha [lindex $ConfigData(Parameter.IdealAlpha) 0]} result] {
        return -code error "APSThreeScreenEmitMeas: $result"
    }
    if !$analyze {
        return -1
    }
    if [catch {APSAnalyzeThreeScreenEmitData \
                 -rootname $rootname -statusCallback $statusCallback \
                 -displayPrintout 0 -randomizations $randomizations \
                 -dataToUse $dataToUse -autoExclude $autoExclude} result] {
        return -code error "$result"
    }
    if [catch {exec sdds2stream -column=exNormMean,eyNormMean $rootname.results} result] {
        return -code error "$result"
    }
    return $result
}

proc APSTakeThreeScreenEmitMeasData {args} {
    set configDir /home/helios/oagData/TransportLineEmittance/threeScreen
    set abortVariable ""
    set samplesList [list 10 10 10]
    set screenList ""
    set positionList ""
    set actuatorNumberList [list 1 1 1]
    set xCalList [list 1 1 1]
    set yCalList [list 1 1 1]
    set xResList [list 0 0 0]
    set yResList [list 0 0 0]
    set xErrList [list 0 0 0]
    set yErrList [list 0 0 0]
    set autoSetROIList [list 1 1 1]
    set autoSetApertureList [list 0 0 0]
    set saturatedPixelLimitList [list 0 0 0]
    set cameraTypeList [list CCDH CCDH CCDH]
    set rootname ""
    set postSwitchPause 10
    set beamEnergy 150
    set dataToUse Raw
    set idealBeta 1
    set idealAlpha 0
    set useMV200BGSubtraction 1
    set saveImage 0
    set statusCallback APSNoOp
    APSStrictParseArguments {configDir abortVariable samplesList screenList positionList \
                               actuatorNumberList \
                               xCalList yCalList xResList yResList xErrList \
                               yErrList cameraTypeList rootname \
                               postSwitchPause beamEnergy dataToUse idealBeta \
                               idealAlpha useMV200BGSubtraction statusCallback saveImage \
                               autoSetROIList autoSetApertureList saturatedPixelLimitList}

    if [string length $abortVariable] {
        global $abortVariable
    } else {
        set abortVariable [APSUniqueName abortVarDummy]
    }
    set $abortVariable 0

    if {![file exists [file dirname $rootname]] || \
          ![file isdirectory [file dirname $rootname]]} {
        return -code error "directory doesn't exist: [file dirname $rootname]"
    }
    if [file exists $rootname.params] {
        return -code error "$rootname is already in use."
    }
    set result ""
    if {[catch {exec cavget -list=LI:VD1:syncCaptureRateC -noQuotes -pend=20} syncCaptureRate] || \
        [catch {exec cavput -pend=20 "-list=LI:VD1:syncCaptureRateC=Beam Rate"} result]} {
        return -code error "Problem getting or setting linac video synch capture rate: $syncCaptureRate\n$result"
    }
    set imageSource Original
    if $useMV200BGSubtraction {
        set imageSource Subtracted
    }
    if [catch {exec cavput -pend=20 "-list=LI:VD1:cameraTrigDelayPD.GATE=Enable,LI:VD1:imageSourceC=$imageSource,LI:VD1:bkgndCaptureRateC=2 Hz,LI:VD1:bkgndCaptureModeC=Specific Frame,LI:VD1:captureModeC=Ext. Trigger,LI:VD1:twoHzFrameDelayC=0"} result] {
        return -code error "Problem setting up video system"
    }
    set time [clock seconds]
    set experimentRecord(ParameterNames) "Time TimeStamp UserName beamEnergy IdealBeta IdealAlpha"
    set experimentRecord(Parameter.Time) $time
    set experimentRecord(Parameter.TimeStamp) [list [clock format $time]]
    global env
    set experimentRecord(Parameter.UserName) $env(USER)
    set experimentRecord(Parameter.beamEnergy) $beamEnergy
    set experimentRecord(Parameter.IdealAlpha) $idealAlpha
    set experimentRecord(Parameter.IdealBeta) $idealBeta
    set experimentRecord(ParameterInfo.beamEnergy) "type SDDS_DOUBLE units MeV"
    set experimentRecord(ColumnNames) "Screen Position Datafile xCalibration yCalibration xResolution yResolution xSigmaError ySigmaError"
    set experimentRecord(ColumnInfo.Position) "type SDDS_DOUBLE units m field_length 0"
    set experimentRecord(ColumnInfo.xCalibration) "type SDDS_DOUBLE units pixels/mm"
    set experimentRecord(ColumnInfo.yCalibration) "type SDDS_DOUBLE units pixels/mm"
    set experimentRecord(ColumnInfo.xResolution) "type SDDS_DOUBLE units um"
    set experimentRecord(ColumnInfo.yResolution) "type SDDS_DOUBLE units um"
    set experimentRecord(ColumnInfo.xSigmaError) "type SDDS_DOUBLE units um"
    set experimentRecord(ColumnInfo.ySigmaError) "type SDDS_DOUBLE units um"
    set experimentRecord(Column.Screen) [list $screenList]
    set experimentRecord(Column.Position) [list $positionList]
    foreach screen $screenList position $positionList actuatorNumber $actuatorNumberList \
      cameraType $cameraTypeList samples $samplesList \
      xCal $xCalList yCal $yCalList xRes $xResList yRes $yResList xErr $xErrList yErr $yErrList \
      autoROI $autoSetROIList autoAperture $autoSetApertureList \
      saturatedPixelLimit $saturatedPixelLimitList {
          if [set $abortVariable] break
          lappend experimentRecord(Column.xCalibration) $xCal
          lappend experimentRecord(Column.yCalibration) $yCal
          lappend experimentRecord(Column.xResolution) $xRes
          lappend experimentRecord(Column.yResolution) $yRes
          lappend experimentRecord(Column.xSigmaError) $xErr
          lappend experimentRecord(Column.ySigmaError) $yErr
          lappend experimentRecord(Column.cameraType) $cameraType
          eval $statusCallback {"Configuring screen and camera $screen."}
          if [catch {APSConfigureCameraAndScreenPVs -flagIn 1 -lampOn 0 -flag $screen \
                       -actuatorNumber $actuatorNumber -system LEUTL -statusCallback $statusCallback \
                       -cameraType $cameraType } result] {
              catch {exec cavput -pend=20 "-list=LI:VD1:syncCaptureRateC=$syncCaptureRate"}
              catch {eval file delete [glob $rootname.*]}
              return -code error "Problem setting up screen $screen---experiment failed: $result"
          }
          if [set $abortVariable] break
          after [expr int(1000*$postSwitchPause)]
          set lowres 1
          if [string compare $cameraType CCDH]==0 {
              set lowres 0
          }
          if {$autoROI && \
                [catch {APSSetLinacFlagROI -flagList $screen -lowres $lowres \
                          -statusCallback $statusCallback \
                          -factor 2 -xMinSize 40 -yMinSize 40 -alreadyInserted 1} result]} {
              catch {exec cavput -pend=20 "-list=LI:VD1:syncCaptureRateC=$syncCaptureRate"}
              catch {eval file delete [glob $rootname.*]}
              return -code error "Problem setting up screen $screen---experiment failed: $result"
          }
          if {$autoAperture &&
              [catch {APSSetBCCameraAperture -flagList $screen -lowres $lowres \
                        -abortVariable $abortVariable \
                        -saturatedPixelLimit $saturatedPixelLimit \
                        -statusCallback $statusCallback -tries 4 -alreadyInserted 1 
                  exec cavput -pend=20 -list=LI:VD1:imageSourceC=$imageSource} result]} {
              catch {exec cavput -pend=20 "-list=LI:VD1:syncCaptureRateC=$syncCaptureRate"}
              catch {eval file delete [glob $rootname.*]}
              return -code error "Problem setting up screen $screen---experiment failed: $result"
          }
          eval $statusCallback {"Taking data ($samples samples)."}
          set filename $rootname.$screen
          lappend experimentRecord(Column.Datafile) [file tail $rootname.$screen]
          if [catch {exec sddswmonitor $configDir/linacProfile.wmon $filename.tmp \
                       -scalars=$configDir/linacProfileScalars.mon \
                       -steps=$samples -interval=0.5,s -erase -ezcaTiming=0.1,1000
              exec sddsprocess $filename.tmp $filename \
                       "-define=parameter,SampleNumber,i_page,type=long" \
                       "-print=parameter,PageLabel1,Sample %ld   Flag $screen,SampleNumber" 
              file delete -force $filename.tmp } result] {
              file delete -force $filename.tmp
              catch {exec cavput -pend=20 "-list=LI:VD1:syncCaptureRateC=$syncCaptureRate"}
              catch {eval file delete [glob $rootname.*]}
              return -code error "Problem taking data: $result"
          }
          #save image file
          if $saveImage {
              set wmonFile /home/helios/OAG/oagData/TransportLineEmittance/monitorFiles/LinacHLImage.wmon
              set scalars /home/helios/OAG/oagData/TransportLineEmittance/monitorFiles/LI:VD1.scalars
              if [catch {exec  cavput -list=LI:VD1:imageGrabSU.PROC=1 
                  exec sddswmonitor $wmonFile -scalars=$scalars $rootname.$screen.image1 -step=1 -erase } result] {
                  return -code error "Error collecting image: $result"
              }
              if [catch {exec sddsprocess $rootname.$screen.image1 $rootname.$screen.image \
                           "-reprint=par,CameraName,$screen" \
                           "-reprint=par,Description,Image from $screen" } result] {
                  return -code error "Error define parameters for image file: $result"
              }
              file delete -force $rootname.$screen.image1
          }
          eval $statusCallback {"Data collected."}
          if [catch {APSConfigureCameraAndScreenPVs -flagIn 0 -flag $screen \
                       -actuatorNumber $actuatorNumber -system LEUTL -statusCallback $statusCallback \
                       -cameraType $cameraType } result] {
              catch {exec cavput -pend=20 "-list=LI:VD1:syncCaptureRateC=$syncCaptureRate"}
              catch {eval file delete [glob $rootname.*]}
              return -code error "Problem removing screen $screen---experiment failed: $result"
          }
      }
    if [catch {exec cavput -pend=20 "-list=LI:VD1:syncCaptureRateC=$syncCaptureRate,LI:VD1:imageSourceC=Original"} result] {
        eval $statusCallback {"$result"}
    }
    if [set $abortVariable] {
        catch {eval file delete [glob $directory/$rootname.*]}
        return -code error "Experiment aborted."
    }
    foreach item {xCalibration yCalibration xResolution yResolution xSigmaError ySigmaError Datafile} {
        set experimentRecord(Column.$item) [list $experimentRecord(Column.$item)]
    }
    if [catch {sdds save $rootname.params experimentRecord} result] {
        eval $statusCallback {"Problem saving configuration: $result"}
    }
    APSSCRDefineVariables
    global apsSCRRequestDir
    if [catch {exec sddsprocess $apsSCRRequestDir/LPL.req -pipe=out \
                 -match=column,Category=*PS \
                 | sddscasr -pipe=in $rootname.snap -save} result] {
        eval $statusCallback {"Problem saving PS values: $result"}
    }
    eval $statusCallback {"Data collection completed."}
}

proc APSAnalyzeThreeScreenEmitData {args} {
    set rootname ""
    set statusCallback APSNoOp
    set displayPrintout 0
    set randomizations 50
    set beamEnergy 0
    set excludeListArray ""
    set dataToUse Raw
    set autoExclude 0
    set outlierLimit 0
    set outlierPasses 0
    APSStrictParseArguments {rootname statusCallback displayPrintout randomizations beamEnergy \
                               excludeListArray dataToUse autoExclude outlierLimit outlierPasses}
    if $randomizations<3 {
        set randomizations 3
    }
    set directory [file dirname $rootname]
    set rootname [file tail $rootname]
    if [catch {sdds load $directory/$rootname.params configData} result] {
        return -code error "$result"
    }
    if [lsearch -exact $configData(ParameterNames) beamEnergy]!=-1 {
        set beamEnergy [lindex $configData(Parameter.beamEnergy) 0]
    }
    if [lsearch -exact $configData(ParameterNames) IdealBeta]!=-1 {
        set IdealBeta [lindex $configData(Parameter.IdealBeta) 0]
        set IdealAlpha [lindex $configData(Parameter.IdealAlpha) 0]
    } else {
        set IdealBeta 0
        set IdealAlpha 0
    }
    set positionList [lindex $configData(Column.Position) 0]
    set L1 [expr [lindex $positionList 1]-[lindex $positionList 0]]
    set LT [expr [lindex $positionList 2]-[lindex $positionList 0]]
    if [lsearch -exact $configData(ColumnNames) excludeList]==-1 {
        set configData(Column.excludeList) [list [list "" "" ""]]
    }
    set listIndex -1
    if {$outlierPasses>0 && $outlierLimit>0} {
        lappend outlierCmd sddsoutlier -pipe 
        lappend outlierCmd -columns=x${dataToUse}Sigma -passes=$outlierPasses
        lappend outlierCmd -stDevLimit=$outlierLimit
    } else {
        set outlierCmd cat
    }
    foreach index {1 2 3} \
      flag [lindex $configData(Column.Screen) 0] \
      datafile [lindex $configData(Column.Datafile) 0] \
      xCal [lindex $configData(Column.xCalibration) 0] \
      yCal [lindex $configData(Column.yCalibration) 0] \
      xRes [lindex $configData(Column.xResolution) 0] \
      yRes [lindex $configData(Column.yResolution) 0] \
      excludeList [lindex $configData(Column.excludeList) 0] {
          incr listIndex 
          if [lsearch -exact $configData(ColumnNames) xSigmaError]==-1 {
              set xSigmaError 0
              set ySigmaError 0
          } else {
              set xSigmaError [lindex [lindex $configData(Column.xSigmaError) 0] $listIndex]
              set ySigmaError [lindex [lindex $configData(Column.ySigmaError) 0] $listIndex]
          }
          lappend xSigmaErrorList $xSigmaError
          lappend ySigmaErrorList $ySigmaError
          # convert to mm
          set xRes [expr $xRes/1e3]
          set yRes [expr $yRes/1e3]
          set xSigmaError [expr $xSigmaError/1e3]
          set ySigmaError [expr $ySigmaError/1e3]
          APSAddToTmpFileList -ID APSAnalyzeThreeScreenEmitData -fileList "$directory/$datafile.tmp"

          if $autoExclude {
              # automatically remove bad profile data
              set tmpFile /tmp/[APSTmpString]
              APSAddToTmpFileList -ID APSAnalyzeThreeScreenEmitData -fileList "$tmpFile.1 $tmpFile.2"
              exec sddsprocess $directory/$datafile -pipe=out \
                "-redefine=column,xProfileData,xProfileData 0 < ? 0 : xProfileData \$ " \
                "-redefine=column,yProfileData,yProfileData 0 < ? 0 : yProfileData \$ " \
                | sddssmooth -pipe \
                -column=xProfileData,yProfileData -pass=0 -despike=pass=10 -newColumns \
                | sddsprocess -pipe -process=?ProfileData*,sum,%sSum \
                "-define=parameter,xRatio,xProfileDataSmoothedSum xProfileDataSum abs 1 + / abs" \
                "-define=parameter,yRatio,yProfileDataSmoothedSum yProfileDataSum abs 1 + / abs" \
                | sddscollapse -pipe \
                | sddsprocess -pipe=in $tmpFile.1 \
                -process=*ProfileDataSmoothedSum,percent,%sLevel90,percent=90 \
                "-define=column,xRatio90,xProfileDataSmoothedSum xProfileDataSmoothedSumLevel90 /" \
                "-define=column,yRatio90,yProfileDataSmoothedSum yProfileDataSmoothedSumLevel90 /" \
                -filter=column,xRatio90,0.25,1e10 -filter=column,yRatio90,0.25,1e10 \
                -filter=column,xRatio,0.5,1e10 -filter=column,yRatio,0.5,1e10 

              # prepare a list of excluded images 
              set pages [exec sdds2stream -npages=bare  $directory/$datafile ]
              set stepList [exec sdds2stream -column=Step $tmpFile.1]
              set excludeList ""
              for {set page 1} {$page<=$pages} {incr page} {
                  if [lsearch -exact $stepList [expr $page-1]]==-1 {
                      lappend excludeList $page
                  }
              }
              if [llength $excludeList] {
                  eval $statusCallback {"auto-excluding samples [join $excludeList ,] for screen $flag"}
              }
              set excludeListArray ""
          }
          # optionally remove excluded points
          set excluded($index) ""
          if [llength $excludeList] {
              if [string length $excludeListArray] {
                  global $excludeListArray
                  set duh [format %s(%s) $excludeListArray $flag]
                  set excludeList "[set $excludeListArray\($flag\)] $excludeList"
              }
              set excluded($index) [string trim $excludeList]
              APSAddToTmpFileList -ID APSAnalyzeThreeScreenEmitData -fileList $directory/$datafile.tmpe
              set matchOpt ""
              foreach excludeItem $excludeList {
                  if ![string length $matchOpt] {
                      set matchOpt "-test=parameter,i_page $excludeItem == pop pop "
                  } else {
                      append matchOpt "i_page $excludeItem == pop pop || "
                  }
              }
              append matchOpt " !"
              eval exec sddsprocess $directory/$datafile -pipe=out {"$matchOpt"} \
                | sddscollapse -pipe \
                | tee $directory/$datafile.coll.all  \
                | $outlierCmd \
                | tee $directory/$datafile.coll.oute  \
                | sddsprocess -pipe -process=x*,ave,%s -process=y*,ave,%s \
                -process=x*,sigma,%sMeasError \
                -process=y*,sigma,%sMeasError \
                -process=Step,count,DataPoints \
                | sddscollapse -pipe=in $directory/$datafile.tmpe
          } else {
              eval exec sddscollapse $directory/$datafile -pipe=out \
                | tee $directory/$datafile.coll.all  \
                | $outlierCmd \
                | tee $directory/$datafile.coll.oute  \
                | sddsprocess -pipe -process=x*,ave,%s -process=y*,ave,%s \
                -process=x*,sigma,%sMeasError \
                -process=y*,sigma,%sMeasError \
                -process=Step,count,DataPoints \
                | sddscollapse -pipe=in $directory/$datafile.tmpe
          }
          foreach plane {x y} {
              exec sddshist $directory/$datafile.coll.all \
                $directory/$datafile.coll.all.${plane}his \
                -size=1 -data=${plane}${dataToUse}Sigma 
              exec sddshist $directory/$datafile.coll.oute \
                $directory/$datafile.coll.oute.${plane}his \
                -size=1 -data=${plane}${dataToUse}Sigma 
              lappend histPlotOpts -column=${plane}${dataToUse}Sigma,frequency 
              lappend histPlotOpts $directory/$datafile.coll.all.${plane}his -legend=spec=All -filename 
              lappend histPlotOpts -column=${plane}${dataToUse}Sigma,frequency
              lappend histPlotOpts $directory/$datafile.coll.oute.${plane}his -legend=spec=Out.Elim. -filename -end
          }
          
          file delete -force $directory/$datafile.coll.all $directory/$datafile.coll.oute  
          set dataFileToUse $directory/$datafile.tmpe 

          # find sigmas using calibration and subtract resolution contribution
          if [catch {eval exec sddscombine -merge -pipe=out \
                       [APSReplicateItem -item $dataFileToUse -number $randomizations] \
                       | sddsprocess -pipe \
                       {"-define=column,Sx$index,x${dataToUse}Sigma x${dataToUse}SigmaMeasError grnd * + $xCal / sqr $xRes sqr - 0 > ? pop sqrt : 0 \$ 1000 /,units=m"} \
                       {"-define=column,Sx${index}Sigma,x${dataToUse}SigmaMeasError $xCal /,units=m"} \
                       {"-define=column,Sy$index,y${dataToUse}Sigma y${dataToUse}SigmaMeasError grnd * + $yCal / sqr $yRes sqr - 0 > ? pop sqrt : 0 \$ 1000 /,units=m"} \
                       {"-define=column,Sy${index}Sigma,y${dataToUse}SigmaMeasError $yCal /,units=m"} \
                       {"-define=column,DataPoints$index,DataPoints"} \
                       | sddsconvert -pipe=in $directory/$datafile.tmp \
                       -retain=column,Sx${index}*,Sy${index}*,DataPoints$index} result] {
              APSDeleteTmpFileList -ID APSAnalyzeThreeScreenEmitData
              return -code error "finding sigmas: $result"
          }
          lappend fileList $directory/$datafile.tmp
      }
    if [catch \
          {exec sddsxref [lindex $fileList 0] [lindex $fileList 1] -pipe=out \
             | sddsxref -pipe [lindex $fileList 2] \
             | sddsprocess -pipe \
             -define=parameter,L1,$L1,units=m \
             -define=parameter,LT,$LT,units=m \
             "-define=column,DataPoints,DataPoints1 DataPoints2 DataPoints3 + + 3 /" \
             "-define=column,S11,Sx1 sqr" \
             "-define=column,S12,L1 sqr LT sqr - S11 * LT Sx2 * sqr + L1 Sx3 * sqr - chs 2 / L1 / LT / L1 LT - /" \
             "-define=column,S22,LT L1 - Sx1 sqr * LT Sx2 sqr * - L1 Sx3 sqr * + L1 / LT / LT L1 - /" \
             "-define=column,ex,S11 S22 * S12 sqr - abs sqrt,units=m" \
             "-define=column,S33,Sy1 sqr" \
             "-define=column,S34,L1 sqr LT sqr - S33 * LT Sy2 * sqr + L1 Sy3 * sqr - chs 2 / L1 / LT / L1 LT - /" \
             "-define=column,S44,LT L1 - Sy1 sqr * LT Sy2 sqr * - L1 Sy3 sqr * + L1 / LT / LT L1 - /" \
             "-define=column,ey,S33 S44 * S34 sqr - abs sqrt,units=m" \
             | sddsprocess -pipe \
             "-define=column,betax,S11 ex /,units=m" \
             "-define=column,alphax,S12 ex / chs" \
             "-define=column,betay,S33 ey /,units=m" \
             "-define=column,alphay,S34 ey / chs" \
             "-redefine=column,%s,%s 1e6 *,units=um,select=e?" \
             "-define=column,IdealAlpha,$IdealAlpha" \
             "-define=column,IdealBeta,$IdealBeta,units=m" \
             "-define=column,IdealGamma,1 IdealAlpha sqr + IdealBeta /,units=1/m" \
             "-define=column,gammax,1 alphax sqr + betax /,units=1/m" \
             "-define=column,gammay,1 alphay sqr + betay /,units=1/m" \
             "-define=column,xMismatch,betax IdealGamma *  2 alphax * IdealAlpha * - gammax IdealBeta * + 2 /" \
             "-define=column,yMismatch,betay IdealGamma *  2 alphay * IdealAlpha * - gammay IdealBeta * + 2 /" \
             | sddsprocess -pipe \
             -process=beta?,ave,%sMean -process=alpha?,ave,%sMean \
             -process=e?,ave,%sMean \
             -process=beta?,stand,%sStDev -process=alpha?,stand,%sStDev \
             -process=e?,stand,%sStDev \
             -process=DataPoints,first,%s \
             -process=?Mismatch,ave,%sMean -process=?Mismatch,stand,%sStDev \
             | sddsprocess -pipe \
             "-define=parameter,%sSigma,%sStDev DataPoints sqrt /,units=m,select=beta?StDev,edit=%/StDev//" \
             "-define=parameter,%sSigma,%sStDev DataPoints sqrt /,select=alpha?StDev,edit=%/StDev//" \
             "-define=parameter,%sSigma,%sStDev DataPoints sqrt /,units=um,select=e?StDev,edit=%/StDev//" \
             "-define=parameter,%sSigma,%sStDev DataPoints sqrt /,units=um,select=?MismatchStDev,edit=%/StDev//" \
             | sddsprocess -pipe \
             "-define=parameter,%sNormMean,%sMean $beamEnergy .511 / *,units=um,select=e?Mean,edit=%/Mean//" \
             "-define=parameter,%sNormSigma,%sSigma $beamEnergy .511 / *,units=um,select=e?Sigma,edit=%/Sigma//" \
             "-process=S\[xy\]?,ave,%sAve" \
             "-process=S\[xy\]?Sigma,ave,%s" \
             | sddscollapse -pipe \
             | sddsprocess -pipe -delete=column,*StDev \
             "-define=parameter,IdealAlpha,$IdealAlpha" \
             "-define=parameter,IdealBeta,$IdealBeta,units=m" \
             "-define=parameter,IdealGamma,1 IdealAlpha sqr + IdealBeta /,units=1/m" \
             "-define=parameter,beamEnergy,$beamEnergy,units=MeV" \
             "-print=param,xCalText,[lindex $configData(Column.xCalibration) 0] (pixel/mm)" \
             "-print=param,yCalText,[lindex $configData(Column.yCalibration) 0] (pixel/mm)" \
             "-print=param,xResText,[lindex $configData(Column.xResolution) 0] (um)" \
             "-print=param,yResText,[lindex $configData(Column.yResolution) 0] (um)" \
             "-print=param,xSEText,$xSigmaErrorList (um)" \
             "-print=param,ySEText,$ySigmaErrorList (um)" \
             "-print=param,ExcludedFlag1,$excluded(1)" \
             "-print=param,ExcludedFlag2,$excluded(2)" \
             "-print=param,ExcludedFlag3,$excluded(3)" \
             | sddsconvert -pipe -delete=column,gamma* -delete=parameter,IdealGamma \
             | tee $directory/$rootname.results \
             | sddsprintout -pipe=in $directory/$rootname.printout  -width=120 \
             "-title=Analysis of experiment $directory/$rootname using $dataToUse values" \
             "-param=Ideal*,end" \
             "-param=xCalText,label=xCalibrations,end" \
             "-param=yCalText,label=yCalibrations,end" \
             "-param=xResText,label=xResolutions,end" \
             "-param=yResText,label=yResolutions,end" \
             "-param=xSEText,label=xSigmaErrors,end" \
             "-param=ySEText,label=ySigmaErrors,end" \
             "-param=ExcludedFlag1,end" \
             "-param=ExcludedFlag2,end" \
             "-param=ExcludedFlag3,end" \
             "-param=beamEnergy,end" \
             -column=betax*,format=%8.2f,edit=%/Mean//%/Sigma/Sig/ \
             -column=alphax*,format=%8.2f,edit=%/Mean//%/Sigma/Sig/%/alpha/alph/ \
             -column=exNorm*,format=%8.2f,edit=%/Mean//%/Sigma/Sig/ \
             -column=xMismatch*,format=%8.2f,end,edit=%/Mean//%/Sigma/Sig/ \
             -column=betay*,format=%8.2f,edit=%/Mean//%/Sigma/Sig/ \
             -column=alphay*,format=%8.2f,edit=%/Mean//%/Sigma/Sig/%/alpha/alph/ \
             -column=eyNorm*,format=%8.2f,edit=%/Mean//%/Sigma/Sig/ \
             -column=yMismatch*,format=%8.2f,end,edit=%/Mean//%/Sigma/Sig/ \
         } result] {
        APSDeleteTmpFileList -ID APSAnalyzeThreeScreenEmitData
        return -code error "computing betas: $result"
    }
    APSDeleteTmpFileList -ID APSAnalyzeThreeScreenEmitData
    if $displayPrintout {
        APSFileDisplayWindow [APSUniqueName .] -width 100 -height 24 \
          -comment "Three-Screen Emittance Measurement $directory/$rootname" \
          -fileName $directory/$rootname.printout -sddsExportableFile $directory/$rootname.results
        if {$outlierPasses>0 && $outlierLimit>0} {
            eval exec sddsplot -graph=line,vary $histPlotOpts &
        }
    }
}

