
proc APSSRResponseCheckMonitorCallback {fid flagVariable args} {
    global ControlStatus
    set statusCallback "puts stderr"
    APSStrictParseArguments {statusCallback}
    gets $fid input
    global $flagVariable
    if [string compare $input "Type <cr> to read, q to quit:"]==0 {
        set $flagVariable 1
        return
    }
    if [string compare $input "Done."]==0 {
        set $flagVariable 2
        return
    }
    if [string length $input] {
        # unknown string returned
        set $flagVariable -1
        eval $statusCallback {{Unknown response from sddsmonitor while acquiring data...}}
        eval $statusCallback {$input}
    }
}

proc APSSRTakeSamplesForResponseCheck {args} {
    set inputDir /home/helios/oagData/sr/findGoodBPMs/config

    set steps 20
    set corrector ""
    set delta 10
    set fileName ""
    set verbose  1
    set dryRun 0
    set statusCallback "puts stderr"
    APSStrictParseArguments {steps corrector delta fileName verbose dryRun statusCallback steps}

    
    set fid [open "|sddsvmonitor $inputDir/SR_bpm.vmon $fileName -steps=$steps -single=stdout" r+]

    global eventFlag
    set eventFlag 0

    fileevent $fid readable \
      "APSSRResponseCheckMonitorCallback $fid eventFlag -statusCallback $statusCallback"

    set iSwitch [expr int($steps/2)]
    for {set i 0} {$i < $steps} {incr i 1} {
        if {$i==$iSwitch} {
            if {$delta && !$dryRun} {
                exec cavput -list=${corrector}:CurrentAO=$delta -delta -pend=15
            }
            if $verbose {
                set value [exec cavget -pend=15 -list=${corrector}:CurrentAO]
                eval $statusCallback {"corrector $corrector changed (was $value)"}
            }
            after 2000
        }
        if !$eventFlag {
            for {set try 0} {$try<15} {incr try} {
                after 1000
                update
                if $eventFlag break
            }
            if !$eventFlag {
                return -code error "Problem taking samples (code $eventFlag)"
            }
        }
        if $eventFlag!=1 {
            return -code error "Problem taking samples"
        }
        set eventFlag 0
        after 500
        puts $fid "y"
        flush $fid
    }
    if {$delta && !$dryRun} {
        exec cavput -list=${corrector}:CurrentAO=$delta -delta=factor=-1 -pend=15
    }
    if $verbose {
        eval $statusCallback {"corrector $corrector reset"}
    }
    if [catch {close $fid} result] {
        if $verbose {
            eval $statusCallback {"$result"}
        }
        return -code ok $result
    }
}

proc APSSRProcessResponseCheckData {args} {
    set inputDir /home/helios/oagData/sr/findGoodBPMs/config
    set rootname ""
    set plane ""
    set delta -1
    set reprocess 1
    APSStrictParseArguments {rootname plane delta reprocess}

    if $delta<=0 {
        return -code error "APSSRProcessResponseCheckData: delta nonpositive"
    }
    if [string compare $plane x]==0 {
        set HV H
    } else {
        set HV V
    }

    set first 1
    set fileList [glob -nocomplain $rootname.*\[0-9\]\[AB\]${HV}\[1-4\]]
    if [llength $fileList] {
        set sumEqn "0 "
        foreach file $fileList {
            set corName [exec editstring $file -edit=10Z.ai/S/]
            if [catch \
                  {exec sddsenvelope $file -pipe=out -copy=Index -copy=Rootname -min=:ms:? -max=:ms:? \
                     | sddsprocess -pipe \
                     "-defi=col,%sSpread,:ms:%sMax :ms:%sMin -,select=:ms:?Max,edit=%/:ms://%/Max//,units=mm" \
                     | sddsconvert -pipe=in $file.env \
                     -rename=col,xSpread=${corName}xSpread,ySpread=${corName}ySpread} result] {
                return -code error "$result"
            }
            set sumEqn "$sumEqn ${corName}${plane}Spread sqr +"
            if $first {
                if [catch {exec mv $file.env $rootname.env} result] {
                    return -code error "$result"
                }
                set first 0
            } else {
                if [catch {exec sddsxref $rootname.env $file.env \
                             $rootname.env1 -take=*Spread -nowarning} result] {
                    return -code error "$result"
                }
                if [catch {exec mv $rootname.env1 $rootname.env} result] {
                    return -code error "$result"
                }
                catch {eval exec rm $file.env $rootname.env~}
            }
        }
        set sumEqn "$sumEqn sqrt"
        if {[catch \
               {exec sddsprocess $rootname.env -pipe=out \
                  "-define=column,${plane}Response0,$sumEqn,units=mm" \
                  | sddsxref -pipe $inputDir/aps.twi \
                  -match=Rootname=ElementName -take=beta$plane \
                  | sddsprocess -pipe=in $rootname.env1 \
                  "-define=col,${plane}Response,${plane}Response0 beta$plane sqrt /,units=mm/\$sr\$em" \

               } result] || \
              [catch {exec mv $rootname.env1 $rootname.env} result]} {
            return -code error "APSSRProcessResponseCheckData: $result"
        }
    } elseif !$reprocess {
        return -code error "APSSRProcessResponseCheckData: no data files--can't reprocess"
    }

    if ![file exists $rootname.env] {
        return -code error "APSSRProcessResponseCheckData: $rootname.env not found"
    }

        
    # expect variation of no more than 5mm/sqrt(m) for 10A delta
    set absLimit [expr 5.0/$delta]
    if [catch {exec sddssmooth $rootname.env -pipe=out -column=${plane}Response -new \
                 -difference -despike=neighbors=10 \
                 | sddsoutlier -markOnly -absLimit=$absLimit -column=${plane}ResponseUnsmooth -pipe \
                 | sddsoutlier -markOnly -chanceLimit=0.9 -column=${plane}ResponseUnsmooth -pipe \
                 | sddsprocess -pipe=in $rootname.${plane}resp \
                 "-define=column,${plane}RespFlag,1 IsOutlier -,type=long" } result] {
        return -code error "$result"
    }
}

proc APSMpSRFindGoodBPMsInfo {} {
    return "Applies statistical and orbit response tests to BPM data to determine which BPMs are good (i.e., very trustworthy) and which are bad (i.e., not entirely trustworthy).  The resultant file can be used with scorr to set up BPM selections for orbit correction."
}

proc APSMpSRFindGoodBPMs {args} {
    set inputDir /home/helios/oagData/sr/findGoodBPMs/config
    set filename ""
    set limit 2.5
    set response 0
    set verbose 0
    set plot 0
    set delta 10
    set orbitsToAverage 20
    set statusCallback "puts stderr"
    set reprocess 1
    set responseSteps 10
    set useErrorReadbacks 0

    if {[APSStrictParseArguments {filename limit response verbose plot delta \
                                    orbitsToAverage reprocess responseSteps \
                                    useErrorReadbacks}]} {
        APSMpReturn error "Incorrect arguments."
    }
    
    if [APSMpInterface] {
        set verbose 1
        APSMpStep init -promptDialog APSMpSRFindGoodBPMsInitDialog \
          -vars {filename limit response plot delta orbitsToAverage reprocess}
        set statusCallback "APSSetVarAndUpdate status"
        if [file exists $filename] {
            if {[string compare Abort \
                   [APSMultipleChoice [APSUniqueName .] -modal 1 \
                      -question "File $filename already exists." \
                      -labelList {Proceed Abort} -returnList {Proceed Abort}]]==0} {
                APSMpReturn ok
            }
        }
    }
    
    if [string length $filename]==0 {
        APSMpReturn error "Blank filename."
    }
    
    if {$reprocess || \
          [catch {exec cavget -pend=15 -list=S-DCCT:CurrentM,S-INJ:BCM:NumberOfBunchesFilledM \
                    "-delim= "} beamParam]} {
        set description "BPM Flag file by APSMpFindGoodBPMs"
    } else {
        set description "BPM Flag file ([format %.2f [lindex $beamParam 0]]mA in [lindex $beamParam 1] bunches) by APSMpFindGoodBPMs"
    }
    eval $statusCallback {"$description"}
    
    if !$reprocess {
        # Ensure that all BPMs have memory scanner weight of 11.  Sending 2 first helps, for
        # some reason.
        catch {exec caput S:bpm:memscan_wt_ao.VAL 2  }
        after 1000
        catch {exec caput S:bpm:memscan_wt_ao.VAL 11 }
    }

    if !$reprocess {
        APSMpStep BPMStatusCheck -desc "Check BPM status"
        lappend tmpFileList ${filename}.status
        if $verbose {
            eval $statusCallback {{Using burtrb to get the status for each BPM}}
        }
        if [catch {exec burtrb -f ${inputDir}/bpmStatus.req \
                     | sddsxref -pipe ${inputDir}/bpmStatus.req \
                     -match=ControlName -take=BPMName* -nowarn \
                     | sddsconvert -pipe=in $filename.status -rename=col,ValueString=Status} result] {
            eval $statusCallback "$result"
            catch {eval exec rm $tmpFileList} 
            APSMpReturn error "Problem getting status data: $result"
        }
    }

    if !$reprocess {
        APSMpStep OrbitAveraging -desc "Do orbit averaging"
        lappend tmpFileList ${filename}.orbit
        if $verbose {
            eval $statusCallback {"Using sddssnapshot to average $orbitsToAverage orbits"}
        }
        set reqFile ${inputDir}/bpmOrbit.req 
        if $useErrorReadbacks {
            set reqFile ${inputDir}/bpmOrbitError.req
        }
        if [catch {exec sddssnapshot ${inputDir}/bpmOrbit.req -pipe=out \
                     -name=Orbit -averages=$orbitsToAverage,1 \
                     | sddsxref -pipe=in $filename.status $filename.orbit \
                     -match=BPMNameWithPlane -take=Status} result] {
            eval $statusCallback {"$result"}
            catch {eval exec rm $tmpFileList} 
            APSMpReturn error "Error averaging orbit snapshots."
        }
        #    exec rm $filename.status
    } elseif ![file exists $filename.orbit] {
        APSMpReturn error "APSMpSRFindGoodBPMs: can't reprocess: $filename.orbit not found"
    }

    APSMpStep FindGoodBPMs -desc "Find good BPMs for sum, x, and y planes" -proc "APSMpSRFindGoodBPMsPlane"
    if [catch {APSMpSRFindGoodBPMsPlane -filename $filename.sumflag -plane sum -limit $limit \
                 -orbitFile $filename.orbit -statusCallback $statusCallback -delta $delta \
                 -response $response -verbose $verbose -plot $plot -reprocess $reprocess \
                 -responseSteps $responseSteps }  result] {
        APSMpReturn error "Problem finding good/bad bpms for sum: $result."
    }
    if [catch {APSMpSRFindGoodBPMsPlane -filename $filename.xflag -plane x -limit $limit \
                 -orbitFile $filename.orbit -statusCallback $statusCallback -delta $delta \
                 -response $response -verbose $verbose -plot $plot -reprocess $reprocess \
             -responseSteps $responseSteps} result] {
        APSMpReturn error "Problem finding good/bad bpms for x plane: $result."
    }
    if [catch {APSMpSRFindGoodBPMsPlane -filename $filename.yflag -plane y -limit $limit \
                 -orbitFile $filename.orbit -statusCallback $statusCallback -delta $delta \
                 -response $response -verbose $verbose -plot $plot -reprocess $reprocess \
                 -responseSteps $responseSteps} result] {
        APSMpReturn error "Problem finding good/bad bpms for y plane: $result."
    }

    APSMpStep MS/SCDUComparison -desc "Compare memory/scanner vs SCDU" -proc "APSSRCompareMSSCDU"
    if [catch {APSSRCompareMSSCDU -fileName $filename.msscdu -samples 60 -interval 1 \
             -statusCallback $statusCallback -reprocess $reprocess} result] {
        APSMpReturn error "Problem testing memory/scanner vs SCDU: $result"
    }
                 
    if [catch {exec sddsxref $filename.xflag $filename.yflag -pipe=out -match=BPMName \
                 -take=y*Flag \
                 | sddsxref -pipe $filename.sumflag -match=BPMName -take=sumFlag \
                 | sddsxref -pipe $filename.msscdu -match=BPMName -take=*FlagMSSCDU \
                 | sddsconvert -pipe -dele=column,Name -dele=param,NameType \
                 | sddsprocess -pipe=in $filename \
                 -print=column,Name,%s,BPMName \
                 "-print=param,Description,$description" \
                 {-print=param,NameType,MonitorNames} \
                 {-define=column,Flag,xFlag yFlag + sumFlag + FlagMSSCDU + 4 / int,type=long}} result] {
        APSMpReturn error "Problem computing bpm flag from x, y, sum, and MS/SCDU data: $result"
    }
    catch {exec sddsprocess $filename -filter=col,Flag,1,1 -pipe=out \
             -redefi=param,BPMCount,n_rows,type=long | sdds2stream -pipe -param=BPMCount} count
    if $verbose {
        eval $statusCallback {"$count good bpms"}
    }
    if [APSMpInterface] {
        set tmpFile /tmp/[APSTmpString]
        if {![catch {exec sddsprocess $filename -filter=col,Flag,0,0 -pipe=out \
                       | sddsprintout -pipe=in $tmpFile -column=Name \
                       -column=*Flag*}]} {
            APSFileDisplayWindow [APSUniqueName .a] -fileName $tmpFile -deleteOnClose 1 \
              -comment "Bad BPMs and flags for individual planes." -modal 1
        }
    }
    APSMpReturn ok "$count good bpms"
}

proc APSMpSRFindGoodBPMsPlane {args} {
    set inputDir /home/helios/oagData/sr/findGoodBPMs/config

    set plane x
    set filename ""
    set limit 2.5
    set response 1
    set verbose 0
    set plot 0
    set orbitFile  ""
    set delta 10
    set reprocess 1
    set responseSteps 10
    set statusCallback "puts stderr"

    if {[APSStrictParseArguments \
           {plane filename limit response verbose plot statusCallback orbitFile \
              delta reprocess responseSteps}] || \
          [string length $filename]==0 || [string length $orbitFile]==0} {
        APSMpReturn error "Incorrect arguments or blank filename."
    }
    if {!$reprocess && $response} {
        if {[catch {exec cavget -pend=15 -list=S:DesiredMode} mode] || \
              [string compare $mode USER_BEAM]==0} {
            APSMpReturn error "In user-beam or unknown mode---can't do response-based BPM test."
        }
    }

    if {[lsearch -exact {x y sum} $plane]==-1} {
        APSMpReturn error "plane must be x, y, or sum"
    }

    set tmpFileList ""

    if [catch \
          {exec sddsprocess $orbitFile $filename.orbit \
             -match=column,BPMNameWithPlane=*$plane \
             -redefine=column,Index,i_row,type=long} result] {
        eval $statusCallback {"$result"}
        APSMpReturn error "Error selecting $plane data from $orbitFile: $result"
    }
    lappend tmpFileList ${filename}.orbit

    APSMpStep EliminateOrbitOutliers -desc "Eliminate orbit outliers"
    if [string compare $plane sum] {
        # filter out obviously bad values
        # smooth the remaining values
        # compute the difference between the smoothed and unsmoothed values
        # add in status data
        # eliminate BPMs with bad status
        # do outlier elimination on differences
        # delete unneeded columns
        set maxStDev 0.2
        set units mm
        lappend tmpFileList ${filename}.good $filename.smOrbit 
        # set despiker parameters
        if [string compare $plane x]==0 {
            set neighbor 18
            set average 2
        } else {
            set neighbor 36
            set average 4
        }
        if [catch \
              {exec sddsprocess $filename.orbit -pipe=out \
                 -filter=col,Orbit,-${limit},${limit} \
                 -filter=col,OrbitStDev,0,$maxStDev -filter=col,OrbitStDev,0,0,! \
                 -match=col,Status=Valid \
                 | sddssmooth -pipe -column=Orbit -newcolumn -despike=neighbor=$neighbor,average=4 \
                 -difference \
                 | tee $filename.smOrbit \
                 | sddsconvert -dele=column,Difference,AbsDifference -pipe \
                 -rename=col,OrbitUnsmooth=Difference \
                 | sddsprocess -pipe "-rpnexpr=0 sto i" \
                 "-define=col,AbsDifference,Difference abs,units=$units" \
                 | sddsoutlier -pipe -column=Difference -stdev=2 -verb \
                 | sddssort -pipe -column=AbsDifference,inc \
                 | sddsprocess -pipe -define=column,IsGood,1,type=long \
                 | sddsconvert -pipe=in ${filename}.good -delete=col,* \
                 -retain=col,Index,BPMName,Orbit,ControlName,OrbitSmoothed,IsGood,Status,OrbitStDev -nowarn } result] {
            if [string first error [string tolower $result]]!=-1 {
                eval $statusCallback {"$result"}
                catch {eval exec rm $tmpFileList} 
                APSMpReturn error "Error doing outlier elimination."
            }
        }
    } else {
        # filter out obviously bad values
        # add in status data
        # eliminate BPMs with old status
        # delete unneeded columns
        set maxStDev 100
        set units V
        set response 0
        if [catch \
              {exec sddsprocess $filename.orbit -pipe=out -filter=col,Orbit,0,700,! \
                 -filter=col,OrbitStDev,0,$maxStDev -filter=col,OrbitStDev,0,0,! \
                 -match=col,Status=Old,! \
                 -define=column,IsGood,1,type=long \
                 | sddsconvert -pipe=in ${filename}.good -delete=col,* \
                 -retain=col,Index,BPMName,Orbit,ControlName,IsGood,Status,OrbitStDev -nowarn } result] {
            if [string first error [string tolower $result]]!=-1 {
                eval $statusCallback {"$result"}
                catch {eval exec rm $tmpFileList} 
                APSMpReturn error "Error doing outlier elimination."
            }
        }
    }
    if $verbose {
        eval $statusCallback {"$result"}
    }
    lappend tmpFileList ${filename}.good

    APSMpStep MakeBadBPMList -desc "Make bad BPM list"
    # make file containing only the bad BPMs
    if [catch {exec sddsselect ${filename}.orbit ${filename}.good \
                 -match=BPMName -invert -pipe=out \
                 | sddsprocess -pipe=in \
                 -define=column,IsGood,0,type=long ${filename}.bad} result] {
        eval $statusCallback {"$result"}
        catch {eval exec rm $tmpFileList} 
        APSMpReturn error "Error making bad BPM file."
    }
    lappend tmpFileList ${filename}.bad

    APSMpStep MakeCombinedGoodBadList -desc "Combine good and bad BPMs together"
    # make file containing good and bad BPMs together
    if [catch {exec sddscombine ${filename}.good ${filename}.bad -pipe=out -merge \
                 | sddssort -col=Index -pipe \
                 | sddsconvert -pipe=in -retain=col,Index,Status,IsGood,BPMName,Orbit,OrbitStDev \
                 ${filename}.orbResult} result] {
        eval $statusCallback {"$result"}
        catch {eval exec rm $tmpFileList} 
        APSMpReturn error "Error making combined good/bad BPM file."
    }
    lappend tmpFileList ${filename}.orbResult
    
    if {$response && !$reprocess} {
        APSMpStep CheckBPMResponse -desc "Check for responce of BPMs to beam motion"
        # check for response of BPMs to beam motion
        if $verbose {
            eval $statusCallback {{Taking response data...}}
        }
        if [string compare $plane "x"]==0 {
            lappend corrList1 S2A:H3 S7A:H3 S12A:H3 S17A:H3 S22A:H3 S27A:H3 S32A:H3 S37A:H3
            lappend corrList2 2AH3 7AH3 12AH3 17AH3 22AH3 27AH3 32AH3 37AH3
        } else {
            lappend corrList1 S2A:V3 S7A:V3 S12A:V3 S17A:V3 S22A:V3 S27A:V3 S32A:V3 S37A:V3
            lappend corrList2 2AV3 7AV3 12AV3 17AV3 22AV3 27AV3 32AV3 37AV3
        }
        set index 0
        foreach corrector $corrList1 {
            set file $filename.[lindex $corrList2 $index]
            incr index
            lappend tmpFileList $file
            if $verbose {
                eval $statusCallback {"$corrector..."}
            }
            if [file exists $file] {
                if $verbose {
                    eval $statusCallback {"Warning: file $file already exists--no data taken."} 
                }
            } else  {
                if [catch {APSSRTakeSamplesForResponseCheck -fileName $file -corrector $corrector \
                             -statusCallback $statusCallback -reprocess $reprocess \
                             -steps $responseSteps -delta $delta -verbose $verbose } result] {
                    eval $statusCallback {"$result"}
                    catch {eval exec rm $tmpFileList} 
                    APSMpReturn error "Error taking data for response check on $corrector."
                }
            }
        }
    }


    if [string compare $plane x]==0 {
        set HV H
    } else {
        set HV V
    }
    APSMpStep CollateData -desc "Collect data"
    if {$response && \
          ([llength [glob -nocomplain ${filename}.\[1-9\]\[AB\]${HV}\[1-4\]]] || \
             $reprocess)} {
        if $verbose {eval $statusCallback "Processing..."}
        APSSRProcessResponseCheckData -rootname $filename -plane $plane -delta $delta \
          -reprocess $reprocess
        if $verbose {
            eval $statusCallback {{Combining data...}}
        }
        if [catch {exec sddsxref $filename.orbResult $filename.${plane}resp -pipe=out \
                     -take=${plane}RespFlag -match=BPMName=Rootname \
                     | sddsprocess -pipe=in $filename \
                     -print=param,NameType,MonitorNames \
                     -print=column,Name,%s,BPMName \
                     "-defi=col,${plane}Flag,IsGood 0 == ${plane}RespFlag 0 == || ! ? 1 : 0 \$ ,type=short"} result] {
            eval $statusCallback {"Error combining response data with statistical data: $result"}
            catch {eval exec rm $tmpFileList} 
            APSMpReturn error "Error combining response data with statistical data."
        }
        if $verbose {
            eval $statusCallback {{Done combining data.}}
        }
    } else {
        if [catch {exec sddsprocess $filename.orbResult $filename \
                     "-define=col,${plane}Flag,IsGood,type=long"} result] {
            eval $statusCallback {"$result"}
            catch {eval exec rm $tmpFileList} 
            APSMpReturn error "Error defining flag column."
        }
    }

    if $verbose {
        eval $statusCallback \
          {"good bpms: [exec sddsprocess $filename -pipe=out -filter=col,${plane}Flag,1,1 \
| sdds2stream -rows -pipe]"}
    }


    if $plot {
        APSMpStep PlotResults -desc "Plot results"
        if $verbose {
            eval $statusCallback {{Plotting...}}
        }
        if [string compare $plane sum] {
            exec sddsplot  -limit=ymin=-25,ymax=25 "-title=Good/bad BPMs for $plane plane" \
              -col=Index,Orbit ${filename} -graph=impulse,vary \
              -split=column=${plane}Flag,width=1,complete &
        } else {
            exec sddsplot  -limit=ymin=0,ymax=3000 "-title=Good/bad BPMs for $plane plane" \
              -col=Index,Orbit ${filename} -graph=impulse,vary \
              -split=column=${plane}Flag,width=1,complete &
        }
    }
    APSWaitWithUpdate -waitSeconds 5 -updateInterval 1
    catch {eval exec rm $tmpFileList}
    APSMpReturn ok
}


proc APSMpSRFindGoodBPMsInitDialog {frame} {
    global APSMpSRFindGoodBPMs
    set dirName \
      /home/helios/oagData/sr/findGoodBPMs/[APSOffsetDateInfo -today 1 -dateFormat Y-J-MD]
    set index 0
    while {$index<1000} {
        set file [format "$dirName/goodbad-%03ld" $index]
        if ![llength [glob -nocomplain $file*]] {
            break
        }
        incr index
    }
    if {$index==1000} {
        set APSMpSRFindGoodBPMs(filename) $dirName
    } else {
        set APSMpSRFindGoodBPMs(filename) $file
        catch {exec mkdir $dirName}
    }

    APSLabeledEntry .le1 -parent $frame \
      -textVariable APSMpSRFindGoodBPMs(filename) \
      -label "Filename: " \
      -contextHelp "Enter filename for results."  \
      -width 80 
    APSLabeledEntry .le1a -parent $frame \
      -textVariable APSMpSRFindGoodBPMs(orbitsToAverage) \
      -label "Orbits to average (1s interval): " \
      -contextHelp "Enter the number of orbits to average to get statistics.  Each orbit takes approximately 1 second." \
      -width 10
    APSLabeledEntry .le2 -parent $frame \
      -label "BPM limit (mm): " \
      -textVariable APSMpSRFindGoodBPMs(limit) \
      -width 10
    APSRadioButtonFrame .rb1 -parent $frame  -orientation horizontal \
      -variable APSMpSRFindGoodBPMs(response) \
      -label "Do response test (MOVES BEAM!): " \
      -buttonList {Yes No} \
      -valueList {1 0}
    APSLabeledEntry .le3 -parent $frame \
      -label "Response test delta (Amps): " \
      -textVariable APSMpSRFindGoodBPMs(delta) \
      -width 10
    APSRadioButtonFrame .rb2 -parent $frame  -orientation horizontal \
      -variable APSMpSRFindGoodBPMs(plot) \
      -label "Do plots: " \
      -buttonList {Yes No} \
      -valueList {1 0}
    APSRadioButtonFrame .rb3 -parent $frame  -orientation horizontal \
      -variable APSMpSRFindGoodBPMs(reprocess) \
      -label "Reprocess only: " \
      -buttonList {Yes No} \
      -valueList {1 0}
}
