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

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


set CVSRevisionAuthor "\$Revision: 1.9 $ \$Author: soliday $"

APSApplication . -name "SR Beta Function Measurement for a Quadrupole Family" \
  -overview "SR beta function measurement for a quadrupole family procedure interface." -version $CVSRevisionAuthor

cd /home/helios/oagData/sr/familyBetaFunctionData

# Use the /tmp directory for upgrade/debugging purposes.
# Set the magnet data directory variable.

set directory .

# This procedure sets the status variable used by the status widget.

set quadFamilyList {Q1 Q2 Q3 Q4 Q5}
set quadFamilyVarList {Q1 Q2 Q3 Q4 Q5}
set quadSubFamilyList {A B}
set quadSubFamilyVarList {A B}

set ControlStatus "Ready."

# Make application and status window widgets.  Returns 1 when completed.

proc MakeBetaMeasurementStatusWidget {widget args} {

    global ControlStatus
    set parent ""
    APSParseArguments {parent}
    
    set w $parent$widget
    APSScrolledStatus .status -parent $w -textVariable ControlStatus -width 95 -packOption {-side top}
    return 1
}

#Make sector selection widgets.  Returns 1 when completed

proc MakeSRSectorsWidget {widget args} {
 
    global SectorSelection
    set parent ""
    APSParseArguments {parent}
    set w $parent$widget
    frame $w -bd 4 -relief raised
    pack $w -side top -fill x
    
    label $w.title -text "Storage Ring Sector Selection" -relief ridge
    pack $w.title -side top -fill x

    frame $w.ops -bd 2
    pack $w.ops -side top -fill x
    APSButton .setAll -text "SELECT ALL" -command "SetAllSectors 1" -parent $w.ops
    APSButton .clearAll -text "CLEAR ALL" -command "SetAllSectors 0" -parent $w.ops
    APSButton .toggle -text "TOGGLE" -command "ToggleAllSectors" -parent $w.ops
    
    foreach zone {A B C D E F} {
        APSButton .setZone$zone -text "ZONE $zone" \
          -command "SetZoneSectors $zone" -parent $w.ops
    }
        
    frame $w.check -bd 2
    pack $w.check -side top -fill x
    set sector 1

    for {set quad 1} {$quad<5} {incr quad} {
        frame $w.check.q$quad
        pack $w.check.q$quad -side top -fill x
        set start [expr ($quad-1)*10+1]
        set end   [expr $start+9]
        for {set sector $start} {$sector<=$end} {incr sector} {
            set cbLabel $sector
            if {$sector<10} {set cbLabel 0$sector}
            set SectorSelection($sector) 0
            checkbutton $w.check.q$quad.s$sector -text S$cbLabel \
              -variable SectorSelection($sector) -relief ridge
            pack $w.check.q$quad.s$sector -side left -fill x
        }
    }
    return 1
}

# Sets all sector checkbutton variables.  Returns 1 when completed.

proc SetAllSectors {value} {
    global SectorSelection
    for {set sector 1} {$sector<41} {incr sector} {
        set SectorSelection($sector) $value
    }
    return 1
}

# Complements selected and unselected sector checkbutton variables.  Returns 1 when completed.

proc ToggleAllSectors {} {
    global SectorSelection
    for {set sector 1} {$sector<41} {incr sector} {
        set value [subst \$SectorSelection($sector)]
        set SectorSelection($sector) [expr $value?0:1]
    }
    return 1
}

# Sets one zones worth of checkbutton variables.  Returns 1 when completed.

proc SetZoneSectors {zone} {
    global SectorSelection
    SetAllSectors 0
    switch $zone {
        A {lappend SectorList 2 3 4 5 6 7 8 9 }
        B {lappend SectorList 10 11 12 13 14 15 16 17}
        C {lappend SectorList 18 19 20 21 22 23}
        D {lappend SectorList 24 25 26 27 28 29}
        E {lappend SectorList 30 31 32 33 34 35}
        F {lappend SectorList 36 37 38 39 40  1}
        default {
            SetStatus "Invalid zone $zone"
        }
    }
    foreach sector $SectorList {
        set SectorSelection($sector) 1
    }
    return 1
}

# Makes sector bpm checkbuttons.  Returns 1 when completed.

proc MakeSRQuadFamilyWidget {widget args} {
        
    global BPMSelection quadFamilyList quadFamilyVarList quadSubFamilyList quadSubFamilyVarList
    set parent ""
    APSParseArguments {parent}
    
    APSFrame $widget -parent $parent -label "Storage Ring Quadrupole Family Selection" \
      -height 30 \
      -packOption {-side top -expand 1}
    
    set w $parent$widget.frame
    
    APSCheckButtonFrame .srQuadFamilyCheckButtonFrame -parent $w -label "" \
      -packOption {-side left} \
      -buttonList $quadFamilyList -variableList "$quadFamilyVarList" \
      -allNone 1 \
      -orientation horizontal \
      -contextHelp "These checkbuttons are used to select the quadrupole family in each selected sector."

    APSCheckButtonFrame .srQuadSubFamilyCheckButtonFrame -parent $w -label "" \
      -packOption {-side right} \
      -buttonList $quadSubFamilyList -variableList "$quadSubFamilyVarList" \
      -allNone 1 \
      -orientation horizontal \
      -contextHelp "These checkbuttons are used to select the quadrupole sub-family (A or B) in each selected sector."
    return 1
}

# Makes labelled entry widgets for the memory scanner boxcar average, BPM gain mode, number of BPM Self Tests, and
# self test data file.  The data file is automatically generated based on the most recent version.

proc MakeLabelledEntryWidgets {widget args} {

    global directory HTuneFreq VTuneFreq NumOfPoints Description WavefrmNumber
    global HTune VTune uniqueFileLabelList fileLabelList hpvsaSelection

    set parent ""
    APSParseArguments {parent}
    set uniqueFileLabelList ""
    set fileLabelList ""

    set year ""
    set month ""
    set day ""

    APSFrame $widget -parent $parent \
      -height 30 \
      -packOption {-side top -expand 1}
    
    set w $parent$widget.frame

    APSFrame .labelledEntryFrame -parent $w \
      -height 30 \
      -packOption {-side left -expand 1}

    set w1 ${w}.labelledEntryFrame.frame 
    
    APSLabeledEntry .htuneFrequencyFrame -parent $w1 -width 9 \
      -textVariable HTuneFreq \
      -packOption {-side top -fill x} \
      -label "Horizontal Tune Frequency (Hz)." \
      -contextHelp "Frequency of the horizontal tune peak.  This frequency needs to be determined manually using the HP Network Analyzer and input as part of the start up procedure for this measurement."

    APSLabeledOutput .htuneFrame -parent $w1 -width 9 \
      -textVariable HTune \
      -packOption {-side top -fill x} \
      -label "Fractional Horizontal Tune." \
      -contextHelp "Fractional horizontal tune determined from the horizontal tune frequency."

    APSLabeledEntry .vtuneFrequencyFrame -parent $w1 -width 9 \
      -textVariable VTuneFreq \
      -packOption {-side top -fill x} \
      -label "Vertical Tune Frequency (Hz)." \
      -contextHelp "Frequency of the vertical tune peak.  This frequency needs to be determined manually using the HP Network Analyzer and input as part of the start up procedure for this measurement."

    APSLabeledOutput .vtuneFrame -parent $w1 -width 9 \
      -textVariable VTune \
      -packOption {-side top -fill x} \
      -label "Fractional Vertical Tune." \
      -contextHelp "Fractional vertical tune determined from the vertical tune frequency."

    APSLabeledEntry .quadPointsFrame -parent $w1 -width 5 \
      -textVariable NumOfPoints \
      -packOption {-side top -fill x} \
      -label "Number of Data Points (Quadrupole Currents)." \
      -contextHelp "Number of quadrupole current points the tunes are measured at."

    APSLabeledEntry .descriptionFrame -parent $w1 -width 50 \
      -textVariable Description \
      -packOption {-side top -fill x} \
      -label "Data Description." \
      -contextHelp "Description for sdds data file (lattice characteristics, beam current etc.)"

    APSFrame .tuneMeasurementButtonFrame -parent $w \
      -height 30 \
      -packOption {-side left -expand 1}

    set w2 ${w}.tuneMeasurementButtonFrame.frame 

    APSButton .measureInitialTunesButtonFrame -parent $w2 \
      -text "Measure\nTunes" \
      -command {MeasureInitialTunes -statusCallback SetStatus} \
      -packOption {-side top -fill x} \
      -contextHelp "This button invokes a procedure that measures the horizontal and vertical tune at the start of a measurement."

    APSRadioButtonFrame .hpvsaChoiceFrame -parent $w2 \
      -orientation vertical \
      -packOption {-side top -fill x} \
      -label "HPVSA Selection" \
      -variable hpvsaSelection \
      -buttonList {"SR" "Booster"} \
      -valueList {hpvecsr hpvecboo} \
      -contextHelp "This radio button is used to select the HPVSA used to measure the tunes."
}

proc MakeButtonActionWidgets {widget args} {

    global directory BeamEnergy HTuneFreq VTuneFreq NumOfPoints Description AbortFlag

    set parent ""
    APSParseArguments {parent}
    
    APSFrame $widget -parent $parent \
      -height 30 \
      -packOption {-side top -expand 1}
    
    set w $parent$widget.frame

    APSButton .betaMeasurementButton -parent $w \
      -text "Measure" \
      -command {PerformSRBetaMeasurement -statusCallback SetStatus} \
      -packOption {-side left -fill x} \
      -contextHelp "This button invokes a procedure that performs a SR beta function measurement on selected quads."

    APSButton .abortBetaMeasurementButton -parent $w \
      -text "Abort" \
      -command {set AbortFlag 1} \
      -packOption {-side left -fill x} \
      -contextHelp "This button aborts the SR beta function measurement procedure when it is running."

    APSButton .plotRawAndFittedDataFrame -parent $w \
      -text "Plot Raw and Fitted Data" \
      -command {PlotRawAndFittedData -statusCallback SetStatus} \
      -packOption {-side left -fill x} \
      -contextHelp "This button pops up a dialog box to plot the raw and fitted tune/beta function measurement data."

}

proc SetStatus {text} {
    global ControlStatus
    set ControlStatus $text
    update
}

# Disable buttons once one is pressed.

proc DisableButtons {} {
    APSDisableButton .userFrame.buttons.frame.betaMeasurementButton.button
}

# Enable buttons once one is pressed and its associated procedure finishes.

proc EnableButtons {} {
    APSEnableButton .userFrame.buttons.frame.betaMeasurementButton.button
}

# Make a list of sectors selected using the checkbuttons.  Procedure returns the list.

proc MakeSelectedSectorList {} {

    global SectorSelection
    set selectList ""
    for {set sector 1} {$sector < 41} {incr sector} {
        if {$SectorSelection($sector)==1} {
            lappend selectList $sector
        }
    }
    return $selectList
}

# Make a list of quads selected using the checkbuttons.  Procedure returns the list.

proc MakeSelectedQuadList {} {

    global quadFamilyList quadSubFamilyList quadFamilyVarList quadSubFamilyVarList
    global Q1 Q2 Q3 Q4 Q5 A B selectedQuadFamilyList selectedQuadSubFamilyList
    
    set selectList ""
    set selectedQuadFamilyList ""
    set selectedQuadSubFamilyList ""

    set sublistindex 0    

    foreach quadSubFamily $quadSubFamilyVarList {
        if {[subst $[lindex $quadSubFamilyVarList $sublistindex]]} {
            lappend selectedQuadSubFamilyList $quadSubFamily
        }
        incr sublistindex
    }

    set listindex 0

    foreach quadFamily $quadFamilyList { 
        if {[subst $[lindex $quadFamilyVarList $listindex]]} {
            lappend selectedQuadFamilyList $quadFamily
        }
        incr listindex
    }
    
    set sublistindex 0
    foreach quadSubFamily $quadSubFamilyVarList {
        set listindex 0
        foreach quadFamily $quadFamilyList {
            if {[subst $[lindex $quadFamilyVarList $listindex]] && [subst $[lindex $quadSubFamilyVarList $sublistindex]]} {
                lappend selectList ${quadSubFamily}:${quadFamily}
            }
            incr listindex
        }
        incr sublistindex
    }
    return $selectList        
}

# Procedure computes the tune from the specified tune frequency and returns the tune.

proc ComputeTuneFromFrequency {args} {

    set tuneFreq ""
    APSStrictParseArguments {tuneFreq}

    set rfFrequency [exec cavget -list=A014-IETS:BTC:SRSetFreqM -pendIoTime=25]
    set revFrequency [expr $rfFrequency / 1296.0]
    
    return [format %.5f [expr int($tuneFreq / $revFrequency) + 1.0 - ($tuneFreq / $revFrequency)]]
}

# Procedure determines the SR quadrupole serial number name from the
# standard Device Name.  Returns the# quadrupole serial number name.
# Requires the file ${directory}/SQ_xref.sdds.

proc DetermineQuadSerialNumberName {quadFamily} {

    global directory selectedQuadSubFamilyList selectedSectorList
    
    set magnetDir /home/helios/SR/controlFiles/magnets/quad
    set selectedQuadExcitationFileList ""
    foreach sector $selectedSectorList {
        foreach quadSubFamily $selectedQuadSubFamilyList {
            set quadName S${sector}${quadSubFamily}:${quadFamily}
            set excitationFileLabel [exec sddsprocess -pipe=out ${directory}/SQ_xref.sdds \
                                       -match=col,DeviceName=${quadName} \
                                       | sdds2stream -pipe -col=MagnetName]
            set excitationFile ${excitationFileLabel}_excit.sdds
            lappend selectedQuadExcitationFileList ${magnetDir}/$excitationFile
        }
    }
    return $selectedQuadExcitationFileList
}

# Procedure sets the driver striplines to drive horizontally and restores the saved HPVSA state (HTWCHP)
# that should show the horizontal tune.  The vertical tune HPVSA state is VTWCHP.  The important parameters
# of these states are Span = 10 kHz, RBW = 25 Hz, NumberOfPoints = 401, Averages = 100 (rms video), 
# and Market Peak Tracking is ON.

proc SetupHPVSAAndDrvStriplines {} {

    global streamID

    exec cavput -list=SR:TUNE:sig1bo=0,SR:TUNE:sig3bo=0,SR:TUNE:sig2bo=1,SR:TUNE:sig4bo=1,SR:TUNE:sig5bo=0,SR:TUNE:sig7bo=0,SR:TUNE:sig6bo=0,SR:TUNE:sig8bo=0
    APSWriteToTelnetStream -command "MMEM:LOAD:STAT 1,'NVRAM:HTWCHP'" -streamID $streamID
    return 1
}

# This procedure sets the HPVSA frequency to either one of the tune frequencies
# and the span to 5 kHz.  Returns 1 when completed.  Returns 0 if $plane != Horizontal || Vertical.

proc SetHPVSAHVRange {plane index} {

    global HTuneFreq newHTuneFreq VTuneFreq newVTuneFreq streamID

    if {$index==0} {
        set newHTuneFreq $HTuneFreq
        set newVTuneFreq $VTuneFreq
    }
    
    if {![string compare $plane Horizontal]} {
        APSWriteToTelnetStream -command "PAUSE" -streamID $streamID
        APSWriteToTelnetStream -command "FREQ:CENT ${newHTuneFreq} Hz" -streamID $streamID
        APSWriteToTelnetStream -command "BAND 300 Hz" -streamID $streamID
        APSWriteToTelnetStream -command "ABOR;*WAI" -streamID $streamID
        update
        after 3000
        APSWriteToTelnetStream -command "PAUSE" -streamID $streamID
        set newHTuneFreq [APSWriteToTelnetStream -command "CALC:MARK:X?" -streamID $streamID]
        APSWriteToTelnetStream -command "BAND 25 Hz" -streamID $streamID
        APSWriteToTelnetStream -command "FREQ:CENT $newHTuneFreq Hz" -streamID $streamID
        APSWriteToTelnetStream -command "PAUSE" -streamID $streamID
        APSWriteToTelnetStream -command "DISP:WIND1:TRAC:Y:AUTO ONCE" -streamID $streamID
        APSWriteToTelnetStream -command "ABOR;*WAI" -streamID $streamID
        update
        after 10000
        return 1
    } elseif {![string compare $plane Vertical]} {
        APSWriteToTelnetStream -command "PAUSE" -streamID $streamID
        APSWriteToTelnetStream -command "FREQ:CENT ${newVTuneFreq} Hz" -streamID $streamID
        APSWriteToTelnetStream -command "BAND 300 Hz" -streamID $streamID
        APSWriteToTelnetStream -command "ABOR;*WAI" -streamID $streamID
        update
        after 3000
        APSWriteToTelnetStream -command "PAUSE" -streamID $streamID
        set newVTuneFreq [APSWriteToTelnetStream -command "CALC:MARK:X?" -streamID $streamID]
        APSWriteToTelnetStream -command "BAND 25 Hz" -streamID $streamID
        APSWriteToTelnetStream -command "FREQ:CENT $newVTuneFreq Hz" -streamID $streamID
        APSWriteToTelnetStream -command "PAUSE" -streamID $streamID
        APSWriteToTelnetStream -command "DISP:WIND1:TRAC:Y:AUTO ONCE" -streamID $streamID
        APSWriteToTelnetStream -command "ABOR;*WAI" -streamID $streamID
        update
        after 10000
        return 1
    } else {
        return 0
    }
}

# Procedure to perform the beta function measurement by looping over selected quadrupoles families.
# Returns 0 if no sectors or quads are selected or if the measurement is aborted.  Returns
# 1 when the measurement of all the selected quadrupole families is completed.

proc PerformSRBetaMeasurement {args} {

    global Description HTuneFreq VTuneFreq NumOfPoints quadName AbortFlag selectedSectorList
    global IniQuadISetpoint streamID hpvsaSelection selectedQuadFamilyList selectedQuadSubFamilyList
 
    DisableButtons
    update

    if {[catch {exec cavget -pend=5 -list=S:DesiredMode -numerical} mode] || \
          $mode==1 || $mode==2} {
        SetStatus "The machine is in user operations or beamline studies mode---can't go to test mode."
        EnableButtons
        update
        return
    }

    APSParseArguments {statusCallback}
    MeasureInitialTunes -statusCallback SetStatus
    set streamID [APSOpenTelnetStream -IPaddress $hpvsaSelection]
    SetupHPVSAAndDrvStriplines 
    after 5000
    update

    # Create sector and quadrupole selection lists.  Determine if anything has been selected.
    # Return 0 if no sectors, quads, or both are selected.

    set selectedSectorList ""
    set selectedSectorList [MakeSelectedSectorList]
    set selectedQuadList ""
    set selectedQuadList [MakeSelectedQuadList]
    if {$selectedSectorList=="" || $selectedQuadList==""} {
        EnableButtons
        update
        if {$statusCallback!="" && $selectedSectorList=="" && $selectedQuadList!=""} {
            $statusCallback "No Sectors Selected."
            return 0
        } elseif {$statusCallback!="" && $selectedSectorList!="" && $selectedQuadList==""} {
            $statusCallback "No Quads Selected."
            return 0
        } elseif {$statusCallback!="" && $selectedSectorList=="" && $selectedQuadList==""} {
            $statusCallback "No Sectors or Quads Selected."
            return 0
        }
    }

    # Setup quadrupole family measurement loop.  Set abort flag to zero initially.  
    # Return 0 if it is set to 1.
    
    set dataFileUniqueLabel [APSOffsetDateInfo -today 1 -dateFormat Y-M-D]_[lindex [exec date] 3]
    set AbortFlag 0

    foreach quadFamily $selectedQuadFamilyList {
        
        # Compute quadrupole current and tune ranges.

        set sector [lindex $selectedSectorList 0]
        set quad [join "[lindex $selectedQuadSubFamilyList 0] $quadFamily" :]
        set quadName S${sector}${quad}
        set IniQuadISetpoint [exec cavget -list=${quadName}:CurrentAO -pendIoTime=25]

        update

        if {![AcquireWaveformData $quadFamily $dataFileUniqueLabel -statusCallback SetStatus]} {
            if {$AbortFlag==1} {
                EnableButtons
                update
                if {$statusCallback!=""} {
                    $statusCallback "Restoring $quad family current to [format %0.3f $IniQuadISetpoint]."
                }
                if {$statusCallback!=""} {
                    $statusCallback "$quadFamily current restored to the nominal [format %0.3f $IniQuadISetpoint].  Measurement aborted."
                    $statusCallback "Note: $quadFamily current restored to the nominal [format %0.3f $IniQuadISetpoint] amperes but not reverse biased to return the tunes to nominal."
                }
                return 0
            }
        }
        update
    }
    if {$statusCallback!=""} {
        $statusCallback "Beta function measurement completed."
    }
    APSCloseTelnetStream -streamID ${streamID}
    EnableButtons
    update
    return 1
}

# Procedure performs tune waveform data acquisition for a given
# quadruopole family.  Returns 1 when completed.  Returns 0 if the
# quadrupole family current increment is undefined (number of
# quadrupole current points is <= 1 or if abort is pressed.

proc AcquireWaveformData {quadFamily dataFileUniqueLabel args} {

    global IniQuadISetpoint NumOfPoints quadIInc FnlQuadISetpoint selectedQuadFamilyList selectedQuadSubFamilyList
    global HTuneFreq VTuneFreq AbortFlag directory hpvsaSelection selectedSectorList quadName
    
    APSParseArguments {statusCallback}

    set quadIInc [DetermineQuadIInc  $NumOfPoints -statusCallback SetStatus]
    if {$quadIInc==-1} {
        return 0
    }

    # Setup quadrupole family current and waveform measurement loops. 
    
    set quadISetpoint $IniQuadISetpoint
    set restoreIniQuadI 0

    for {set setpoint 0} {$setpoint < $NumOfPoints} {incr setpoint} {
        if $setpoint!=0 {
            # Compute the quadrupole family current setpoint and apply it
            set quadISetpoint [expr $quadISetpoint + $quadIInc]
            set restoreIniQuadI [expr $restoreIniQuadI + $quadIInc]
        }
        
        # wait for the quad family to settle.
        after 2000
        if {$statusCallback!=""} {
            $statusCallback "Setting quad family $quadFamily to [format %0.3f $quadISetpoint] amps."
            $statusCallback "This is $quadFamily family current data point [expr $setpoint + 1.0]."
        }
        update

        foreach plane {Horizontal Vertical} {

            if {$statusCallback!=""} {
                $statusCallback "Taking HPVSA waveform for the $plane plane..."
            }                        

            # If Abort is pressed return 0 and print an abort message

            if {$AbortFlag==1} {
                if {$statusCallback!=""} {
                    $statusCallback "Tune Waveform data acquisition for quadrupole family $quadFamily aborted."
                }
                set FnlQuadISetpoint $quadISetpoint
                exec cavput -list=S -list=[join $selectedSectorList ,] -list=[join $selectedQuadSubFamilyList ","] -list=: -list=${quadFamily}:CurrentAO=${restoreIniQuadI} -deltaMode=factor=-1.0 -pendIoTime=25
                set tmpHFileList [glob -nocomplain ${quadName}_${dataFileUniqueLabel}_Horizontal_??.sdds]
                set tmpVFileList [glob -nocomplain ${quadName}_${dataFileUniqueLabel}_Vertical_??.sdds]
                eval exec rm $tmpHFileList $tmpVFileList
                update
                return 0
            }

            set waveformFile [CreateTuneWaveformFile ${quadFamily} ${dataFileUniqueLabel} $setpoint $plane]
            SetHPVSAHVRange $plane $setpoint
            update
            exec hpVecTrace -${hpvsaSelection} a $waveformFile
            update
            AddScalarPVsToWaveformFile $waveformFile ${quadName}
            AddSDDSDescription $waveformFile $quadFamily
            update
        }
        exec cavput -list=S -list=[join $selectedSectorList ,] -list=[join $selectedQuadSubFamilyList ","] -list=: -list=${quadFamily}:CurrentAO=${quadIInc} -deltaMode -pendIoTime=25
        after 1000
    }
    set FnlQuadISetpoint $quadISetpoint
    
    if {$statusCallback!=""} {
        $statusCallback "Waveform data acquisition for quadrupole family $quadFamily completed."
    }

    if {$statusCallback!=""} {
        $statusCallback "Done with beta function measurement for quadrupole family ${quadFamily}.  Collecting data into files..."
    }
    CollectDataAndCleanupFiles ${dataFileUniqueLabel} $quadFamily
    if {$statusCallback!=""} {
        $statusCallback "Done Collecting data into files.  Plotting raw data."
    }

    PlotRawData $dataFileUniqueLabel $quadFamily -statusCallback SetStatus
    update
    AnalyzeRawTuneData ${dataFileUniqueLabel} $quadFamily -statusCallback SetStatus
    update
    PlotFittedData $dataFileUniqueLabel $quadFamily -statusCallback SetStatus
    update
    RestoreNominalTune $dataFileUniqueLabel $quadFamily -statusCallback SetStatus
    update
    return 1
}

# Procedure to add the description and contents field to the beta
# function measurement data file.  Returns 1 when completed.

proc AddSDDSDescription {quadDataFile quadFamily} {

    global Description selectedQuadSubFamilyList quadName

    exec sddsprocess $quadDataFile -print=param,Description,${Description} \
      "-print=param,Contents,Beta Function Measurement Data" \
      -print=param,QuadVaried,$quadFamily \
      "-print=param,CurrentString,${quadFamily}:Current = %.3f Amperes,${quadName}:Setpoint" -nowarnings 
    exec rm ${quadDataFile}~
    return 1
}

# Procedure to determine the quadrupole current increment based on the
# initial and final values of the quadrupole current.  Returns the
# increment.  Returns 0 and sets the current increment to -1 if the
# number of points is <=1.

proc DetermineQuadIInc {points args} {

    global selectedSectorList selectedQuadFamilyList
    global selectedQuadSubFamilyList quadNumberSelected

    set subFamilyNumber [llength $selectedQuadSubFamilyList]
    set sectorNumber [llength $selectedSectorList]

    set quadNumberSelected [expr $subFamilyNumber * $sectorNumber]

    APSParseArguments {statusCallback}

    # Make sure the number of quadrupole current points are
    # greater than 1.  Otherwise Return 0 and an error
    # message.  The variable quadIInc must be positive and
    # defined.  Quadrupole current range set to 10 amperes.
    # Scale the quadrupole current increment by the number
    # of magnets selected in the measurement.

    set range [expr (10.0 / 79.0) * ($quadNumberSelected - 1.0) + 10.0]

    if {$statusCallback!=""} {
        $statusCallback "Error:  Base current range for quad changes is ${range}."
    }   

    if {$points > 1} {
        set quadIInc [expr abs(${range} / (($points - 1.0) * $quadNumberSelected))]
        return $quadIInc
    } else {
        if {$statusCallback!=""} {
            $statusCallback "Error:  Number of quadrupole current points must be an integer > 1."
        }   
    set quadIInc -1
    return $quadIInc
    }
}

# Procedure creates and returns the temporary scalars input file for a given quadrupole.

proc CreateTuneWaveformFile {quadFamily dataFileUniqueLabel index plane} {

    global directory

    set fileRoot ${quadFamily}_${dataFileUniqueLabel}
    set waveformFile ${directory}/${fileRoot}_${plane}_[format %.2d ${index}].sdds
    return $waveformFile
}

# This procedure adds useful scalar PVs to the waveform data files and deletes the original waveform file.
# Returns 1 when completed.

proc AddScalarPVsToWaveformFile {file quadName} {

    set scalarPVList [exec cavget -list=S-DCCT:CurrentM,S-INJ:MPS:LifetimeMinutesM,${quadName}:CurrentAO -pendIoTime=25]
    set currentPV [lindex $scalarPVList 0]
    set lifeTimePV [lindex $scalarPVList 1]
    set quadCurrentPV [lindex $scalarPVList 2]

    exec sddsprocess ${file} \
      -define=param,SRCurrent,${currentPV},units=mA \
      -define=param,SRlifeTime,${lifeTimePV},units=minutes \
      -define=param,${quadName}:Setpoint,${quadCurrentPV},units=Amperes \
      -nowarnings
    exec rm ${file}~
    return 1
}

# Procedure computes the tune by first acquiring the waveform then computing the tune via the integral method.
# The procedure returns the tune computed from the acquired waveform.

proc DetermineTuneFromWaveform {} {
            
    global hpvsaSelection
    set waveformFile /tmp/waveform.sdds
    exec hpVecTrace -${hpvsaSelection} a $waveformFile
    set selectedTune [expr 1.0e6 * [exec sddssmooth -pipe=out $waveformFile \
                                      -col=Waveform -pass=0 \
                                      -despike=neighbors=5,average=5,passes=5 \
                                      | sddsprocess -pipe -process=Waveform,minimum,WvfMin \
                                      "-redefine=col,Waveform,Waveform WvfMin -" \
                                      | sddsinteg -pipe -integrate=Waveform -versus=Frequency \
                                      | sddsprocess -pipe -process=WaveformInteg,maximum,WvfMax \
                                      "-redefine=col,WaveformInteg,WaveformInteg WvfMax / 0.5 -" \
                                      -process=WaveformInteg,zerocrossing,TuneFreq,functionOf=Frequency \
                                      | sdds2stream -pipe=in -param=TuneFreq -ignore]]
    exec rm $waveformFile
    set selectedTune [format %0.1f $selectedTune]
    return ${selectedTune}
}

# Procedure iteratively finds the quadrupole current to restore the
# nominal 7 GeV tune after the beta function measurement to within 50
# Hz.  Returns 1 when completed. 0 if AbortFlag is set or feedback
# requests 3 changes smaller than the quadrupole dac bit resolution of
# 6 mA.

proc RestoreNominalTune {fileLabel quadFamily args} {

    global FnlQuadISetpoint IniQuadISetpoint HTuneFreq VTuneFreq AbortFlag quadNumberSelected
    global directory selectedSectorList selectedQuadSubFamilyList quadName

    APSParseArguments {statusCallback}

    set fileRoot ${quadFamily}_${fileLabel}

    set Q1FamilyRevBias [expr -1.3 / ${quadNumberSelected}]
    set Q2FamilyRevBias [expr -1.6 / ${quadNumberSelected}]
    set Q3FamilyRevBias [expr -1.2 / ${quadNumberSelected}]
    set Q4FamilyRevBias [expr -1.8 / ${quadNumberSelected}]
    set Q5FamilyRevBias [expr -1.8 / ${quadNumberSelected}]

    set quadRevBiasCurrentList "$Q1FamilyRevBias $Q2FamilyRevBias $Q3FamilyRevBias $Q4FamilyRevBias $Q5FamilyRevBias"
    set tuneFeedbackPlaneList {h h v v v}
    set quadNumber [lindex [split [lindex [split $quadName ":"] 1] "Q"] 1]
    set quadNumberListIndex [expr int(${quadNumber} - 1.0)]
    set quadRevBiasCurrent [lindex ${quadRevBiasCurrentList} ${quadNumberListIndex}]
    set tuneFeedbackPlane [lindex ${tuneFeedbackPlaneList} ${quadNumberListIndex}]
    set quadISetpoint $FnlQuadISetpoint
    set loopNumber 3.0
    set quadIInc [expr ($FnlQuadISetpoint - $IniQuadISetpoint) / ${loopNumber}]
    set rfFrequency [lindex [exec sdds2stream ${directory}/${fileRoot}_HTuneData.sdds \
                               -param=rfFreq] 0]
    update
    for {set setpoint 0} {$setpoint < ${loopNumber}} {incr setpoint} {
        
        set quadISetpoint [expr $quadISetpoint - ${quadIInc}]

        if {$statusCallback!=""} {
            $statusCallback "Setting quad family $quadFamily to [format %0.3f $quadISetpoint] amps."
        }
        exec cavput -list=S -list=[join $selectedSectorList ,] -list=[join $selectedQuadSubFamilyList ","] -list=: -list=${quadFamily}:CurrentAO=${quadIInc} -deltaMode=factor=-1.0 -pendIoTime=25
        update
    }

    if {$statusCallback!=""} {
        $statusCallback "Reverse biasing quad family $quadFamily by [format %0.3f $quadRevBiasCurrent] amperes."
    }

    exec cavput -list=S -list=[join $selectedSectorList ,] -list=[join $selectedQuadSubFamilyList ","] -list=: -list=${quadFamily}:CurrentAO=${quadRevBiasCurrent} -deltaMode -pendIoTime=25
    update
    
    if {![string compare $tuneFeedbackPlane h]} {
        SetHPVSAHVRange Horizontal 0
        set presentTuneFreq [DetermineTuneFromWaveform]
        set difference [expr (${HTuneFreq} - ${presentTuneFreq}) / (${rfFrequency} / 1296.0)]
        set quadSlope [exec sdds2stream -param=Slope ${directory}/${fileRoot}_HTuneData_IFit.sdds]
    } elseif {![string compare $tuneFeedbackPlane v]} {
        SetHPVSAHVRange Vertical 0
        set presentTuneFreq [DetermineTuneFromWaveform]
        set difference [expr (${VTuneFreq} - ${presentTuneFreq}) / (${rfFrequency} / 1296.0)]
        set quadSlope [exec sdds2stream -param=Slope ${directory}/${fileRoot}_VTuneData_IFit.sdds]
    }

    set tolerance [expr 100.0 / (${rfFrequency} / 1296.0)]
    
    if {$statusCallback!=""} {
        $statusCallback "Start difference of tune from nominal is [format %0.4f ${difference}]."
        $statusCallback "Using $tuneFeedbackPlane plane to for feedback."
    }

    # While loop to do feedback.  Exits if the absolute tune difference
    # (from nominal) is < tolerance or if quad current changes are less
    # than the bit level of 6 mA for at least 3 iterations of the loop.
    # Variable maxIterations is used for this test.  AbortFlag allows
    # manual aborting of the feedback if AbortFlag is set to 1.

    set maxIterations 0
    while {[expr abs(${difference})]>${tolerance} && ![string compare $AbortFlag 0] && ${maxIterations}<3} {
        set newQuadCurrent [expr (${difference} / ${quadSlope}) * -1.0]
        exec cavput -list=S -list=[join $selectedSectorList ,] -list=[join $selectedQuadSubFamilyList ","] -list=: -list=${quadFamily}:CurrentAO=${newQuadCurrent} -deltaMode -pendIoTime=25
        update
        if {$statusCallback!=""} {
            $statusCallback "Changing quadrupole family ${quadFamily} current by [format %0.3f ${newQuadCurrent}] amperes."
            $statusCallback "Using $tuneFeedbackPlane plane for feedback."
        }

        if {![string compare $tuneFeedbackPlane h]} {
            SetHPVSAHVRange Horizontal 0
            set presentTuneFreq [DetermineTuneFromWaveform]
            set difference [expr (${HTuneFreq} - ${presentTuneFreq}) / (${rfFrequency} / 1296.0)]
        } elseif {![string compare $tuneFeedbackPlane v]} {
            SetHPVSAHVRange Vertical 0
            set presentTuneFreq [DetermineTuneFromWaveform]
            set difference [expr (${VTuneFreq} - ${presentTuneFreq}) / (${rfFrequency} / 1296.0)]
        }
        if {[expr abs(${newQuadCurrent})<0.006]} {
            incr maxIterations
        }
        update
    }

    if {![string compare $AbortFlag 1]} {
        if {$statusCallback!=""} {
            $statusCallback "Warning:  Reverse bias feedback aborted.  Setting of final tunes and reverse bias current was not finished."
        }
        update
    }
    
    if {${maxIterations}==3 && [expr abs(${difference})]>${tolerance}} {
        if {$statusCallback!=""} {
            $statusCallback "Warning:  Reverse bias feedback stopped due to request current changes less than the quadrupole magnet current bit resolution of 6 mA."
        }
        update
    }

    SetHPVSAHVRange Horizontal 0
    set presentTuneFreq [DetermineTuneFromWaveform]
    set presentHTune [expr ((int(${presentTuneFreq} / (${rfFrequency} / 1296.0)) + 1.0) * (${rfFrequency} / 1296.0) - ${presentTuneFreq}) / (${rfFrequency} / 1296.0)]
    set finalQuadCurrent [exec cavget -list=${quadName}:CurrentAO -pendIoTime=25]

    SetHPVSAHVRange Vertical 0
    set presentTuneFreq [DetermineTuneFromWaveform]
    set presentVTune [expr ((int(${presentTuneFreq} / (${rfFrequency} / 1296.0)) + 1.0) * (${rfFrequency} / 1296.0) - ${presentTuneFreq}) / (${rfFrequency} / 1296.0)]

    if {$statusCallback!=""} {
        $statusCallback "Finished with feedback biasing of quadrupole family ${quadFamily}."
        $statusCallback "Final HTune is [format %0.4f ${presentHTune}]."
        $statusCallback "Final VTune is [format %0.4f ${presentVTune}]."
        $statusCallback "Final quadrupole family current is [format %0.4f ${finalQuadCurrent}]."
    }

    exec sddsprocess ${directory}/${fileRoot}_HTuneData.sdds \
      "-define=param,revBiasDifferenceCurrent,$IniQuadISetpoint ${finalQuadCurrent} -,units=Amperes" \
      -define=param,RestoredHTune,${presentHTune} \
      -nowarnings

    exec sddsprocess ${directory}/${fileRoot}_VTuneData.sdds \
      "-define=param,revBiasDifferenceCurrent,$IniQuadISetpoint ${finalQuadCurrent} -,units=Amperes" \
      -define=param,RestoredVTune,${presentVTune} \
      -nowarnings
    
    exec rm ${directory}/${fileRoot}_HTuneData.sdds~
    exec rm ${directory}/${fileRoot}_VTuneData.sdds~
    update
    return 1
}

# This procedure collects and combines all the tune waveform data files for a given quad into one file.  Returns 1 when completed.

proc CollectDataAndCleanupFiles {fileLabel quadFamily} {

    global directory selectedQuadSubFamilyList quadNumberSelected selectedSectorList

    set fileRoot ${quadFamily}_$fileLabel

    set dataFileHList [lsort [glob -nocomplain ${directory}/${fileRoot}_Horizontal_??.sdds]]
    set dataFileVList [lsort [glob -nocomplain ${directory}/${fileRoot}_Vertical_??.sdds]]
    eval exec sddscombine $dataFileHList ${directory}/${fileRoot}_HTuneData.sdds -overwrite
    eval exec sddscombine $dataFileVList ${directory}/${fileRoot}_VTuneData.sdds -overwrite
    set rfFrequency [exec cavget -list=A014-IETS:BTC:SRSetFreqM -floatFormat=%.1f -pendIoTime=25]
    exec sddsprocess ${directory}/${fileRoot}_HTuneData.sdds \
      -define=param,rfFreq,${rfFrequency},units=Hz \
      "-print=parameter,SectorList,Selected Sectors : $selectedSectorList" \
      "-print=parameter,QuadFamily,Selected Quad Family : $quadFamily" \
      "-print=parameter,QuadSubFamily,Selected Quad SubFamily : $selectedQuadSubFamilyList" \
      "-print=parameter,QuadNumber,Quad Number Selected : $quadNumberSelected" \
      -nowarnings
    exec sddsprocess ${directory}/${fileRoot}_VTuneData.sdds \
      -define=param,rfFreq,${rfFrequency},units=Hz \
      "-print=parameter,SectorList,Selected Sectors : ${selectedSectorList}" \
      "-print=parameter,QuadFamily,Selected Quad Family : $quadFamily" \
      "-print=parameter,QuadSubFamily,Selected Quad SubFamilys : $selectedQuadSubFamilyList" \
      "-print=parameter,QuadNumber,Quad Number Selected : $quadNumberSelected" \
      -nowarnings
    eval exec rm ${dataFileHList} ${dataFileVList}
    return 1
}

# This procedure plots the raw data for a given filelabel.  Returns 1 when finished.

proc PlotRawData {fileLabel quadFamily args} {

    APSParseArguments {statusCallback}

    set fileRoot ${quadFamily}_${fileLabel}

    set HFileName ${fileRoot}_HTuneData.sdds
    set VFileName ${fileRoot}_VTuneData.sdds

    if {$statusCallback!=""} {
        $statusCallback "Plotting beta function measurement data in file ${fileRoot}_H,VTuneData.sdds..."
    }

    catch {exec sddsplot -title=@SectorList -labelsize=0.03 -split=pages -sep=pages \
             -col=Frequency,Waveform -same                               \
             "-topline=Horizontal Tune Waveforms for Average Quad Family Beta Data" \
             -ticks=yfactor=1e6                                          \
             -string=@Description,p=0.05,q=0.95,scale=1.25               \
             -string=@Contents,p=0.05,q=0.90,scale=1.25                  \
             -string=@CurrentString,p=0.05,q=0.85,scale=1.25             \
             -string=@QuadFamily,p=0.05,q=0.80,scale=1.25                \
             -string=@QuadSubFamily,p=0.05,q=0.75,scale=1.25             \
             -string=@QuadNumber,p=0.05,q=0.7,scale=1.25                 \
             $HFileName -filenamesOnTopline -end                         \
             -col=Frequency,Waveform -same                               \
             "-topline=Vertical Tune Waveforms data for Average Quad Family Beta" \
             -ticks=yfactor=1e6                                          \
             -string=@Description,p=0.05,q=0.95,scale=1.25               \
             -string=@Contents,p=0.05,q=0.90,scale=1.25                  \
             -string=@CurrentString,p=0.05,q=0.85,scale=1.25             \
             -string=@QuadFamily,p=0.05,q=0.80,scale=1.25                \
             -string=@QuadSubFamily,p=0.05,q=0.75,scale=1.25             \
             -string=@QuadNumber,p=0.05,q=0.7,scale=1.25                 \
             $VFileName  -filenamesOnTopLine \&}

    if {$statusCallback!=""} {
        $statusCallback "Done."
    }
    return 1
}

# Procedure plots the fitted tune vs quadrupole current data.  Returns 1 when completed.

proc PlotFittedData {fileLabel quadFamily args} {

    APSParseArguments {statusCallback}

    set fileRoot ${quadFamily}_${fileLabel}

    if {$statusCallback!=""} {
        $statusCallback "Plotting beta function tune vs quad family current and fitted data in file ${fileRoot}_H,VTuneData_BnLFit.sdds..."
    }

    set HFileName ${fileRoot}_HTuneData_BnLFit.sdds
    set VFileName ${fileRoot}_VTuneData_BnLFit.sdds
    
    catch {exec sddsplot -title=@SectorList -labelsize=0.03                           \
             -col=BnL,HTune -graph=symbol,scale=3,subtype=1 $HFileName                \
             -col=BnL,HTuneFit -graph=line,type=1 $HFileName -legend=specified=HTune  \
             "-topline=Nominal Horizontal Tune vs Quad Family Integrated Field Data"  \
             -string=@BetaXString,p=0.4,q=0.9,scale=1.25                              \
             -string=@QuadFamilyString,p=0.05,q=0.9,scale=1.25 -end                   \
             -col=BnL,VTune -graph=symbol,scale=3,subtype=2 $VFileName                \
             -col=BnL,VTuneFit -graph=line,type=2 $VFileName -legend=specified=VTune  \
             "-topline=Nominal Vertical Tune vs Quad Family Integrated Field Data"    \
             -string=@BetaYString,p=0.4,q=0.9,scale=1.25                              \
             -string=@QuadFamilyString,p=0.05,q=0.9,scale=1.25}
    return 1
}

# Procedure computes the beta function from the raw tune data.  Uses an
# integration method to find the tune from the center of the distribution.

proc AnalyzeRawTuneData {fileLabel quadFamily args} {

    global directory quadName quadNumberSelected
    global selectedSectorList

    APSParseArguments {statusCallback}

    set fileRoot ${quadFamily}_${fileLabel}

    set quadLengthList {0.5 0.8 0.5 0.5 0.6}
    set quadNumber [lindex [split [lindex [split $quadName ":"] 1] "Q"] 1]
    set quadNumberListIndex [expr $quadNumber - 1.0]
    set quadLength [lindex $quadLengthList [expr int($quadNumberListIndex)]]
    
    exec sddsprocess ${directory}/aps.twi ${directory}/${quadFamily}_aps.twi \
      -match=col,ElementName=${quadName} \
      -process=s,first,s \
      "-redefine=param,s,s ${quadLength} 2.0 / -,units=m"
              
    set excitationFileList [DetermineQuadSerialNumberName $quadFamily]
              
    if {$statusCallback!=""} {
        $statusCallback "Processing beta function measurement data for family ${quadFamily}..."
    }

    set HFileName ${directory}/${fileRoot}_HTuneData.sdds
    set HBnLFileName ${directory}/${fileRoot}_HTuneData_BnLFit.sdds
    set HIFileName ${directory}/${fileRoot}_HTuneData_IFit.sdds
    set VFileName ${directory}/${fileRoot}_VTuneData.sdds
    set VBnLFileName ${directory}/${fileRoot}_VTuneData_BnLFit.sdds
    set VIFileName ${directory}/${fileRoot}_VTuneData_IFit.sdds
    set rfFrequency [expr [lindex [exec sdds2stream ${fileRoot}_HTuneData.sdds -param=rfFreq] 0] / 1.0e6]

    exec sddssmooth -pipe=out $HFileName \
      -col=Waveform -pass=0 \
      -despike=neighbors=5,average=5,passes=5 \
      | sddsprocess -pipe -process=Waveform,minimum,WvfMin \
      "-redefine=col,Waveform,Waveform WvfMin -" \
      | sddsinteg -pipe -integrate=Waveform -versus=Frequency \
      | sddsprocess -pipe -process=WaveformInteg,maximum,WvfMax \
      "-redefine=col,WaveformInteg,WaveformInteg WvfMax / 0.5 -" \
      -process=WaveformInteg,zerocrossing,HFreq,functionOf=Frequency \
      "-define=param,HTune,${rfFrequency} 1296 / sto revf HFreq revf / int 1 + revf * HFreq - revf /" \
      | sddsxref -pipe $HFileName \
      -transfer=parameter,s,${quadName}:Setpoint,SRCurrent,SRlifeTime,Description,Contents,QuadVaried,CurrentString,FileName \
      "-leave=*" \
      | sddscollapse -pipe \
      | sddssort -pipe -col=${quadName}:Setpoint,increasing \
      | sddspfit -pipe \
      -terms=2 -generate \
      -col=${quadName}:Setpoint,HTune \
      | sddsprocess -pipe=in ${directory}/${fileRoot}_HTuneData_IFit.sdds \
      "-print=param,QuadFamilyString,${quadFamily}" \
      "-print=parameter,SectorList,Selected Sectors : $selectedSectorList" \
      "-print=parameter,QuadNumber,Quad Number Selected : $quadNumberSelected"

    update

    eval exec sddscombine -pipe=out $excitationFileList \
      | sddsinterp -pipe \
      -columns=I,BnL \
      "{-fileValues=${directory}/${fileRoot}_HTuneData_IFit.sdds,col=${quadName}:Setpoint}" \
      | sddsenvelope -pipe \
      -mean=BnL \
      | sddsconvert -pipe -rename=col,BnLMean=BnL \
      | sddsxref -pipe ${directory}/${fileRoot}_HTuneData_IFit.sdds -take=HTune \
      | sddspfit -pipe -terms=2 -generate \
      -col=BnL,HTune \
      | sddsprocess -pipe=in ${directory}/${fileRoot}_HTuneData_BnLFit.sdds \
      "{-define=param,BetaX,4.0 pi * Slope * 7.0 * 0.3 / $quadNumberSelected / abs,units=m}" \
      "{-define=param,BetaXSigma,4.0 pi * SlopeSigma * 7.0 * 0.3 / $quadNumberSelected /,units=m}" \
      "{-print=param,BetaXString,\$sl\$e\$gb\$r\$bx\$n\$sg\$e = %.2f \$sa\$e %.2f %s,BetaX,BetaXSigma,BetaX.units}" \
      "-print=param,QuadFamilyString,${quadFamily}" \
      "{-print=parameter,SectorList,Selected Sectors : $selectedSectorList}" \
      "{-print=parameter,QuadNumber,Quad Number Selected : $quadNumberSelected}" \
      -process=HTune,first,InitialHTune

    catch {exec sddsxref ${directory}/${fileRoot}_HTuneData_BnLFit.sdds $HFileName \
             -transfer=param,revBiasDifferenceCurrent,RestoredHTune \
             "-leave=*"}

    catch {sddsxref $HFileName ${directory}/${fileRoot}_HTuneData_BnLFit.sdds \
             -transfer=parameter,InitialHTune}

    catch {exec sddsxref ${HFileName} ${directory}/${quadFamily}_aps.twi \
             "-leave=*" \
             -transfer=parameter,s}

    catch {exec sddsxref ${HBnLFileName} ${directory}/${quadFamily}_aps.twi \
             "-leave=*" \
             -transfer=parameter,s}

    catch {exec sddsxref ${HIFileName} ${directory}/${quadFamily}_aps.twi \
             "-leave=*" \
             -transfer=parameter,s}
    update

    exec sddssmooth -pipe=out $VFileName \
      -col=Waveform -pass=0 \
      -despike=neighbors=5,average=5,passes=5 \
      | sddsprocess -pipe -process=Waveform,minimum,WvfMin \
      "-redefine=col,Waveform,Waveform WvfMin -" \
      | sddsinteg -pipe -integrate=Waveform -versus=Frequency \
      | sddsprocess -pipe -process=WaveformInteg,maximum,WvfMax \
      "-redefine=col,WaveformInteg,WaveformInteg WvfMax / 0.5 -" \
      -process=WaveformInteg,zerocrossing,VFreq,functionOf=Frequency \
      "-define=param,VTune,${rfFrequency} 1296 / sto revf VFreq revf / int 1 + revf * VFreq - revf /" \
      | sddsxref -pipe $VFileName \
      -transfer=parameter,s,${quadName}:Setpoint,SRCurrent,SRlifeTime,Description,Contents,QuadVaried,CurrentString,FileName \
      "-leave=*" \
      | sddscollapse -pipe \
      | sddssort -pipe -col=${quadName}:Setpoint,increasing \
      | sddspfit -pipe \
      -terms=2 -generate \
      -col=${quadName}:Setpoint,VTune \
      | sddsprocess -pipe=in ${directory}/${fileRoot}_VTuneData_IFit.sdds \
      "-print=param,QuadFamilyString,${quadFamily}" \
      "-print=parameter,SectorList,Selected Sectors : $selectedSectorList" \
      "-print=parameter,QuadNumber,Quad Number Selected : $quadNumberSelected"

    update

    eval exec sddscombine -pipe=out $excitationFileList \
      | sddsinterp -pipe \
      -columns=I,BnL \
      "{-fileValues=${directory}/${fileRoot}_VTuneData_IFit.sdds,col=${quadName}:Setpoint}" \
      | sddsenvelope -pipe \
      -mean=BnL \
      | sddsconvert -pipe -rename=col,BnLMean=BnL \
      | sddsxref -pipe ${directory}/${fileRoot}_VTuneData_IFit.sdds -take=VTune \
      | sddspfit -pipe -terms=2 -generate \
      -col=BnL,VTune \
      | sddsprocess -pipe=in ${directory}/${fileRoot}_VTuneData_BnLFit.sdds \
      "{-define=param,BetaY,4.0 pi * Slope * 7.0 * 0.3 / $quadNumberSelected / abs,units=m}" \
      "{-define=param,BetaYSigma,4.0 pi * SlopeSigma * 7.0 * 0.3 / $quadNumberSelected /,units=m}" \
      "{-print=param,BetaYString,\$sl\$e\$gb\$r\$by\$n\$sg\$e = %.2f \$sa\$e %.2f %s,BetaY,BetaYSigma,BetaY.units}" \
      "-print=param,QuadNameString,${quadName}" \
      "-print=param,QuadFamilyString,${quadFamily}" \
      "{-print=parameter,SectorList,Selected Sectors : $selectedSectorList}" \
      "{-print=parameter,QuadNumber,Quad Number Selected : $quadNumberSelected}" \
      -process=VTune,first,InitialVTune

    catch {exec sddsxref ${directory}/${fileRoot}_VTuneData_BnLFit.sdds $VFileName \
        -transfer=param,revBiasDifferenceCurrent,RestoredVTune \
        "-leave=*"}

    catch {sddsxref $VFileName ${directory}/${fileRoot}_VTuneData_BnLFit.sdds \
             -transfer=parameter,InitialVTune}

    catch {exec sddsxref $VFileName ${directory}/${quadFamily}_aps.twi \
             "-leave=*" \
             -transfer=parameter,s}

    catch {exec sddsxref ${VBnLFileName} ${directory}/${quadFamily}_aps.twi \
             "-leave=*" \
             -transfer=parameter,s}

    catch {exec sddsxref ${VIFileName} ${directory}/${quadFamily}_aps.twi \
             "-leave=*" \
             -transfer=parameter,s}

    if {$statusCallback!=""} {
        $statusCallback "Done."
    }

    catch {eval exec rm [glob *~]}
    catch {eval exec rm ${directory}/${quadFamily}_aps.twi}
    update
    return 1
}

# Procedure measures and sets the initial tune values.  Returns 1 when completed.

proc MeasureInitialTunes {args} {

    global directory HTuneFreq HTune VTuneFreq VTune streamID hpvsaSelection
    
    APSParseArguments {statusCallback}
    
    if {$statusCallback!=""} {
        $statusCallback "Measuring and setting the horizontal tune..."
    }

    set streamID [APSOpenTelnetStream -IPaddress $hpvsaSelection]
    SetHPVSAHVRange Horizontal 0
    update
    
    set HTuneFreq [DetermineTuneFromWaveform]
    set HTune [ComputeTuneFromFrequency -tuneFreq $HTuneFreq]

    if {$statusCallback!=""} {
        $statusCallback "Measuring and setting the vertical tune..."
    }


    SetHPVSAHVRange Vertical 0
    update

    set VTuneFreq [DetermineTuneFromWaveform]
    set VTune [ComputeTuneFromFrequency -tuneFreq $VTuneFreq]

    if {$statusCallback!=""} {
        $statusCallback "Finished setting the tunes."
    }
    APSCloseTelnetStream -streamID ${streamID}
    return 1
}

# Procedure plots raw and fitted data for a given measurement.  Returns 1 when complete.
# Returns 0 if no file is selected.

proc PlotRawAndFittedData {args} {

    global directory

    APSParseArguments {statusCallback}

    set fileLabel ""
    set fileLabel [APSFileSelectDialog .dataFileSelectWidget \
                     -path ${directory} \
                     -pattern Q*_*_*TuneData.sdds \
                     -title "Tune/Beta Data File Selection"]

    if {![string length $fileLabel]} {
        if {$statusCallback!=""} {
            $statusCallback "Please select a valid data file to plot."
        }
        return 0
    }

    set quadFamily [lindex [split $fileLabel {_}] 0]
    set quadDateLabel [lindex [split $fileLabel {_}] 1]
    set quadTimeStamp [lindex [split $fileLabel {_}] 2]
                
    if {$statusCallback!=""} {
        $statusCallback "Plotting raw and fitted beta function data for quad family ${quadFamily}..."
    }
    
    PlotRawData ${quadDateLabel}_${quadTimeStamp} ${quadFamily} -statusCallback SetStatus
    PlotFittedData ${quadDateLabel}_${quadTimeStamp} ${quadFamily} -statusCallback SetStatus

    if {$statusCallback!=""} {
        $statusCallback "Done."
    }

    return 1
}

set HTuneFreq 357302914
set HTune [ComputeTuneFromFrequency -tuneFreq $HTuneFreq]
set VTuneFreq 357283119
set VTune [ComputeTuneFromFrequency -tuneFreq $VTuneFreq]
set NumOfPoints 5
set hpvsaSelection hpvecsr

MakeBetaMeasurementStatusWidget .userFrame .
MakeSRSectorsWidget .sectorCheckbuttons -parent .userFrame
MakeSRQuadFamilyWidget .quadCheckButtons -parent .userFrame
MakeLabelledEntryWidgets .labelledEntries -parent .userFrame
MakeButtonActionWidgets .buttons -parent .userFrame
