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

# First version of script by N. Sereno.
#
# $Log: not supported by cvs2svn $
# Revision 1.14  2009/02/10 16:45:36  emery
# Added explicit MainCurrent parameter transfer in sddsxref in Apply procedure.
#
# Revision 1.13  2009/02/10 14:11:42  emery
# Somehow the sign of the gain in the calculation needed to be changed to
# positive. I guess the corrector error already contains the negative sign,
# and the mapping matrix, which was used previously, had a sign error.
#
# Revision 1.12  2009/02/10 13:00:36  emery
# Split the two notch filters in separate sddsfdfilter commands.
# Don't know why I have to do that, but the output was wrong
# when two notch filters were in the same command.
#
# Revision 1.11  2009/02/10 12:46:43  emery
# Corrected "none" to no for useMapping.
#
# Revision 1.10  2009/02/10 00:56:49  emery
# Greatly simplified the processing and creating of new CPU correction
# files with the single-page structure that is allowed now in the CPU AC
# mode files. Set useMapping to none to ignore the mapping matrix as the
# CPU RTFB matrix uses location and calibration of the CPU correctors.
# Set the matrix to unity to avoid confusion. Added buttons for updating
# only h or y planes. Removed reference file sinc eit is expected that
# users will transfer the RTFB bpms setpoints with
# ~sr/bin/transferRTFBbpmReadbacks
#
# Revision 1.9  2009/02/09 18:13:11  emery
# Changed sign of h-map matrix.
#
# Revision 1.8  2008/11/05 11:58:10  emery
# Wrote in a better floor and ceiling rpn expression for
# corrector setting limits.
#
# Revision 1.7  2008/11/05 11:14:38  emery
# Added numerical limits to the settings of dipole correctors.
#
# Revision 1.6  2008/11/04 06:53:15  emery
# Retain only two parameters for the combined file in CalculateNewFile
# to reduce clutter, as this multi-page file is reused in later iterations.
#
# Revision 1.5  2008/11/04 06:14:47  emery
# Delete column MainCurrent? in sddsconvert in Apply before created new ones.
#
# Revision 1.4  2008/06/10 12:49:31  emery
# Extended the range of the first part of the waveform for averaging.
# Added 60 Hz notch filter to the data. This will improve the averaging
# for DC levels.
# Use mapping matrix for H/V1 -> h/vcorr?.
#
# Revision 1.3  2008/02/12 12:07:38  emery
# Now that the RTFB can trigger on the injection trigger (external
# trigger) we don't have to pulse the S:IS1 septum.
#
# Revision 1.2  2008/01/25 05:19:49  emery
# Added ramping to S:IS1 down to 0 V plus a cawait for checking that
# the ramping is complete before pulsing the septum.
# Hopefully we will eventually get a proper trigger for RTFB so
# that we don't have to use the Freeze trigger.
#
# Revision 1.1  2007/01/02 19:15:56  emery
# First installation of a modified CPUWaveformCorrection
# that corrects only the long-term DC part of the perturbation.
# Intended to be used before the transient part is corrected.
#

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)]

set CVSRevisionAuthor "\$Revision: 1.15 $ \$Author: lemery $"

APSApplication . -name "CPU Waveform Measurement and Correction of the DC part only." \
  -version $CVSRevisionAuthor \
  -overview "Operator Interface to the CPU measurement and correction of the long-term part."

bind all <Double-Button-3> { 
    APSInfoWindow .contextHelp -name "Context Help" \
      -infoMessage [APSNearestContextHelp %W] \
      -width 15
}

set ControlStatus "Ready"
set CPUMainRelAmplitude 0.1
set Title {1A = 0.008 mrad}
set Delay 0
set plotDirectory ./

proc MakeCPUStatus {} {
    global ControlStatus
    
    APSScrolledStatus .status -parent .userFrame -textVariable ControlStatus -width 75 -packOption {-side top}
}

proc MakeCPUSetupWidget {widget args} {
    set parent ""
    APSParseArguments {parent}
    
    APSFrame $widget -parent $parent -label "CPU Control and Measurement Functions" \
      -height 30 \
      -packOption {-side top -fill x} 
    set w $parent$widget.frame
    MakeCPUToggleWidget .cpuAmpFrame -parent $w
    MakeDSPFileSetupWidget .file -parent $w
}

proc MakeCPUToggleWidget {widget args} {
    
    set parent ""
    APSParseArguments {parent}
    
    APSFrame $widget -parent $parent \
      -height 30 \
      -packOption {-side top -fill x} 
    
    set w $parent$widget.frame
    APSLabeledEntry .cpuMainRelAmp -parent $w -width 7 \
      -textVariable CPUMainRelAmplitude \
      -packOption {-side left} \
      -label "CPU Main Toggle Amplitude." \
      -contextHelp "Amplitude for toggling the CPU in order to re-read the waveforms."
    
    APSButton .toggleCPUAmplitude -parent $w \
      -text "Toggle" \
      -command {toggleCPURelAmplitude $CPUMainRelAmplitude -statusCallback SetStatus} \
      -packOption {-side left} \
      -contextHelp "This button toggles the CPU amplitude in order to re-read the waveforms."
}

proc MakeDSPFileSetupWidget {widget args} {
    global outputDir root index comment abort

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

    APSLabeledEntry .outputDir -parent $w \
      -label "Output directory:" -textVariable outputDir \
      -contextHelp "Enter a name for the output file directory." -width 55

    APSButton .daily -parent $w.outputDir -packOption "-anchor e" \
      -text "daily" -size small \
      -command {set outputDir [APSGoToDailyDirectory -subdirectory CPU]}

    APSLabeledEntry .root -parent $w -label "Root name of file: " \
      -textVariable root \
      -contextHelp "Enter the root name of the file for the corrector error output <root>-nnn."

    APSLabeledEntry .index -parent $w -label "Index (incremented just before execution): " -textVariable index \
      -contextHelp "Enter the index (minus 1) to be part of the name of the output file. The index is incremented by 1 before execution."

    APSLabeledEntry .comment -parent $w -label "Comment: " -textVariable comment \
      -width 50 \
      -contextHelp "Enter a comment."

}

proc MakeCPUMeasWidget  {widget args} {

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

    APSButton .dspScopeSetup -parent $w \
      -text "DSP Setup" \
      -command {DSPScopeSetup -statusCallback SetStatus} \
      -packOption {-side left} \
      -contextHelp "This button sets up the DSP scope for data collection."

    APSButton .collectData -parent $w \
      -text "Collect Data" \
      -command {incr index; CollectData -output $outputDir/$root-[format %03ld $index] -comment $comment -statusCallback SetStatus} \
      -packOption {-side left} \
      -contextHelp "This button collect DSP scope data."

    APSButton .abortbutton -parent $w -text "Abort" \
      -packOption "-side left" \
      -command {set abort 1} \
      -contextHelp "Abort data collection."

    APSButton .plotData -parent $w \
      -text "Plot Data" \
      -command {PlotData -file $outputDir/$root-[format %03ld $index]} \
      -packOption {-side left} \
      -contextHelp "This button plots the DSP scope data."

    APSButton .plotAvgData -parent $w \
      -text "Plot Averaged Data" \
      -command {PlotData -file $outputDir/$root-[format %03ld $index].ave} \
      -packOption {-side left} \
      -contextHelp "This button plots the averaged DSP scope data along with the original data."

    APSButton .plotDataZ -parent $w \
      -text "Plot Zoomed Data" \
      -command {PlotData -zoomed 1 \
                  -file $outputDir/$root-[format %03ld $index]} \
      -packOption {-side left} \
      -contextHelp "This button plots the zoomed-in DSP scope data."
    return
}

proc PlotData {args} {
    set file ""
    set zoomed 0
    APSParseArguments {file zoomed}
    if {![file exists $file]} {
        return -code error "PlotData: File $file doesn't exist"
    }
    if $zoomed {
        set options "-layout=1,2 -filter=col,t,0,0.5"
    } else {
        set options "-layout=1,2"
    }
    if [catch {eval exec sddsplot $options \
                 -col=t,(S*H*,S*V*) -grap=line,vary -sep $file \
                 -file -topline=@Comment &} \
          result] {
        return -code error $result
    }
    return
}

proc MakeCPUProcessWidget {widget args} {
    global outputDir root index comment abort firstWaveform startTime period tau referenceFile

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

    APSRadioButtonFrame .firstWaveform -parent $w -orientation horizontal \
      -label "First waveform: " -variable firstWaveform \
      -buttonList {Pos Neg} \
      -valueList {Pos Neg} -contextHelp \
      "Choose the waveform type which occurs first in the RTFB dsp scope signal."

    APSLabeledEntry .start -parent $w -label "Start time (ms): " -textVariable startTime \
      -width 40 \
      -contextHelp "Start time of first waveform as it appears on the RTFB corrector error signal triggered on P0."

    APSLabeledEntry .period -parent $w -label "Full period (s): " \
      -textVariable period \
      -width 40 \
      -contextHelp "Period of CPU cycle. Usually 0.5 s if triggering exteranlly from P0."

    APSLabeledEntry .triggerTime -parent $w -label "Duration of first waveform/state (s): " \
      -textVariable hperiod1 \
      -width 40 \
      -contextHelp "Time duration of first waveform. Not necessarily equal to one half of full period."

#    APSRadioButtonFrame .matrix -parent $w -orientation horizontal \
      -label "Use mapping matrix below:" -variable useMapping \
      -buttonList {no first last} -valueList {no first last} \
      -contextHelp "Use mapping matrix to convert S4A:H/V3 and S5A:H/V3 to CPU correctors."

#    APSLabeledEntry .reference -parent $w -label "Reference File: " \
      -textVariable referenceFile \
      -width 40 \
      -contextHelp "File for which the CPU is running at 0 A. The important data here is the corrector errror long-term value."

    APSLabeledEntryFrame .hmap1  -parent $w -label [format %-35s "H-mapping matrix:"] \
      -variableList [list hmap(corr1.S4) hmap(corr1.S5)] \
      -orientation horizontal -type real \
      -contextHelp "The matrix elements are used to convert the S4A:H3 S5A:H3 waveform into a cpu corrector waveform.\n\n | hcorr1 |   | X   X |  | S4A:H3 |\n |        | = |       |  |        |\n | hcorr2 |   | X   X |  | S5A:H3 |\n"
    APSLabeledEntryFrame .hmap2  -parent $w -label [format %-35s ""] \
      -variableList [list hmap(corr2.S4) hmap(corr2.S5)] \
      -orientation horizontal -type real \
      -contextHelp "The matrix elements are used to convert the S4A:H3 S5A:H3 waveform into a cpu corrector waveform.\n\n | hcorr1 |   | X   X |  | S4A:H3 |\n |        | = |       |  |        |\n | hcorr2 |   | X   X |  | S5A:H3 |\n"
    APSLabeledEntryFrame .vmap1  -parent $w -label [format %-35s "V-mapping matrix:"] \
      -variableList [list vmap(corr1.S4) vmap(corr1.S5)] \
      -orientation horizontal -type real \
      -contextHelp "The matrix elements are used to convert the S4A:V3 S5A:V3 waveform into a cpu corrector waveform.\n\n | vcorr1 |   | X   X |  | S4A:V3 |\n |        | = |       |  |        |\n | vcorr2 |   | X   X |  | S5A:V3 |\n"
    APSLabeledEntryFrame .vmap2  -parent $w -label [format %-35s ""] \
      -variableList [list vmap(corr2.S4) vmap(corr2.S5)] \
      -orientation horizontal -type real \
      -contextHelp "The matrix elements are used to convert the S4A:V3 S5A:V3 waveform into a cpu corrector waveform.\n\n | vcorr1 |   | X   X |  | S4A:V3 | \n |        | = |       |  |        | \n | vcorr2 |   | X   X |  | S5A:V3 | \n"

    APSButton .processData -parent $w \
      -text "Process Data" \
      -command {ProcessData -input $outputDir/$root-[format %03ld $index].ave \
                  -outputRoot $outputDir/$root-[format %03ld $index] \
                  -reference $referenceFile \
                  -period $period \
                  -hperiod1 $hperiod1 \
                  -gain $gain \
                  -statusCallback SetStatus} \
      -packOption {-side left} \
      -contextHelp "This button processes the DSP scope data and creates 4 corrector waveform files."

    APSButton .plotAdjustment -parent $w \
      -text "Plot Adjustment Data" \
      -command {PlotAdjustmentData -input $outputDir/$root-[format %03ld $index] \
                  -statusCallback SetStatus} \
      -packOption {-side left} \
      -contextHelp "This button plots the adjustment waveform calculated."

}

proc MakeCPUModWaveformWidget {widget args} {
    global outputDir root index comment abort firstWaveform startTime period

    set parent ""
    APSParseArguments {parent}
    
    APSFrame $widget -parent $parent \
      -height 30 \
      -packOption {-side top -fill x} 
    
    set w $parent$widget.frame
    APSLabeledEntry .gain -parent $w -label "Gain:" \
      -textVariable gain \
      -width 40 \
      -contextHelp "Gain to apply to correction." 
    
    APSLabeledEntry .outputRoot -parent $w -label "Output root:" \
      -textVariable outputRoot \
      -width 40 \
      -contextHelp "Output root for data file for uploading."
    
    APSLabeledEntry .outputVersion -parent $w -label "Index (incremented after creating new file):" \
      -textVariable outputVersion \
      -width 40 \
      -contextHelp "Index for output root for data file for uploading."
    
    APSButton .calculate -parent $w \
      -text "Create New Files" \
      -command {incr outputVersion; Apply -root $outputDir/$root-[format %03ld $index] \
                  -outputRoot $outputRoot -outputVersion $outputVersion \
                  -mode calculate \
                  -gain $gain \
                  -statusCallback SetStatus} \
      -packOption {-side left} \
      -contextHelp "This button calculates new files based on original uploaded files and new correction files. The files are found in /home/helios/oagData/sr/IDs/CPU/ACmodes."
    
    APSButton .plot -parent $w \
      -text "Plot Data Files" \
      -command {Apply -root $outputDir/$root-[format %03ld $index] \
                  -outputRoot $outputRoot -outputVersion $outputVersion \
                  -mode plot \
                  -statusCallback SetStatus} \
      -packOption {-side left} \
      -contextHelp "This button plots the new files to be uploaded."

    APSButton .upload -parent $w \
      -text "Upload Data Files" \
      -command {Apply -root $outputDir/$root-[format %03ld $index] \
                  -outputRoot $outputRoot -outputVersion $outputVersion \
                  -mode load \
                  -statusCallback SetStatus} \
      -packOption {-side left} \
      -contextHelp "This button uploads to new corrector files."
     APSButton .uploadx -parent $w \
      -text "Upload H-plane Data Files" \
      -command {Apply -root $outputDir/$root-[format %03ld $index] \
                  -outputRoot $outputRoot -outputVersion $outputVersion \
                  -mode loadx \
                  -statusCallback SetStatus} \
      -packOption {-side left} \
      -contextHelp "This button uploads to new corrector files for h plane only."
    APSButton .uploady -parent $w \
      -text "Upload V-plane Data Files" \
      -command {Apply -root $outputDir/$root-[format %03ld $index] \
                  -outputRoot $outputRoot -outputVersion $outputVersion \
                  -mode loady \
                  -statusCallback SetStatus} \
      -packOption {-side left} \
      -contextHelp "This button uploads to new corrector files for y plane only."
   
}

proc Apply {args} {
    set root ""
    set gain 1.0
    set statusCallback ""
    set mode calculate
    set outputRoot ""
    set outputVersion ""
    APSParseArguments {root gain mode statusCallback \
                         outputRoot outputVersion}

    if {![string length $outputRoot] || \
          ![string length $outputVersion]} {
        return -code error "Apply: invalid syntax"
    }
    set tmpfile /tmp/[APSTmpString]
    set baseDir /home/helios/oagData

    if [catch {exec cavget -list=ID04b:corrFile_ -list=1,2,3,4} loadedFiles] {
        return -code error "Apply: $loadedFile"
    }
    set loadedFile(vcorr2) [lindex $loadedFiles 0]
    set loadedFile(hcorr2) [lindex $loadedFiles 1]
    set loadedFile(hcorr1) [lindex $loadedFiles 2]
    set loadedFile(vcorr1) [lindex $loadedFiles 3]
    
    set newFile(hcorr1) /sr/IDs/CPU/ACmodes/$outputRoot.hcorr1.[format %03ld $outputVersion]
    set newFile(hcorr2) /sr/IDs/CPU/ACmodes/$outputRoot.hcorr2.[format %03ld $outputVersion]
    set newFile(vcorr1) /sr/IDs/CPU/ACmodes/$outputRoot.vcorr1.[format %03ld $outputVersion]
    set newFile(vcorr2) /sr/IDs/CPU/ACmodes/$outputRoot.vcorr2.[format %03ld $outputVersion]

    set corrList {vcorr2 hcorr2 hcorr1 vcorr1}

    switch $mode {
        calculate {
            if [string length $statusCallback] {
                $statusCallback "Calculating upload files..."
            }
            
            foreach corr $corrList {
                # input adjustment file is $root.$corr.adj
                # original data file (to modify) $baseDir$loadedFile($corr)
                # output file is $baseDir/$newFile($corr)
                if [catch {CalculateNewFile -input $root.$corr.adj \
                             -original $baseDir$loadedFile($corr) \
                             -output $baseDir/$newFile($corr) \
                             -gain $gain \
                         } result ] {
                    return -code error "Apply: $result"
                }
            }
            if [string length $statusCallback] {
                $statusCallback "Correction files calculated."
            }
        }
        plot {
            exec sddsplot -layout=2,2 \
              -split=pages -sep=pages,request -group=pages \
              -same -topline=@CurrentString -grap=line,vary \
              -leg=edit=%/Current// \
              -col=TimeIndex,Current??? $baseDir/$newFile(hcorr1) \
              -ylabel=hcorr1  \
              -col=TimeIndex,Current??? $baseDir/$newFile(hcorr2) \
              -ylabel=hcorr2  \
              -col=TimeIndex,Current??? $baseDir/$newFile(vcorr1) \
              -ylabel=vcorr1  \
              -col=TimeIndex,Current??? $baseDir/$newFile(vcorr2) \
              -ylabel=vcorr2   \
              &
        }
        load {
            #load in the files
            if [catch {exec cavput -list=ID04b:corrFile_ -list=1=$newFile(vcorr2),2=$newFile(hcorr2),3=$newFile(hcorr1),4=$newFile(vcorr1)} results] {
                return -code error $result
            }
            if [catch {exec cavput -list=ID04b:loadCorr_=1 -list=1,2,3,4 \
                     } results] {
                return -code error $result
            } 
            if [string length $statusCallback] {
                $statusCallback "Correction files uploaded."
            }
        }
        loadx {
            #load in the files in h plane only
            if [catch {exec cavput -list=ID04b:corrFile_ -list=2=$newFile(hcorr2),3=$newFile(hcorr1)} results] {
                return -code error $result
            }
            if [catch {exec cavput -list=ID04b:loadCorr_=1 -list=2,3 \
                     } results] {
                return -code error $result
            } 
            if [string length $statusCallback] {
                $statusCallback "Correction files in h plane uploaded."
            }
        }
        loady {
            #load in the files
            if [catch {exec cavput -list=ID04b:corrFile_ -list=1=$newFile(vcorr2),4=$newFile(vcorr1)} results] {
                return -code error $result
            }
            if [catch {exec cavput -list=ID04b:loadCorr_=1 -list=1,4 \
                     } results] {
                return -code error $result
            } 
            if [string length $statusCallback] {
                $statusCallback "Correction files in y-plane uploaded."
            }
        }
        default {
            return -code error "Apply: mode $mode unknown"
        }
    }
}

proc CalculateNewFile {args} {
    set input ""
    set original ""
    set output ""
    set gain 1
    APSParseArguments {input original output gain}
    set tmpfile /tmp/[APSTmpString]
    

    # maximum allowable setting for coils
    set maxH 10
    set maxV 9
    if [regexp hcorr $input] {
        set max $maxH
    } else {
        set max $maxV
    }

# Assume that the input file has only one page, and has at least 70 time index points.
# As of 2/9/2009 it is possible to run CPU with one page now.

    set pages [exec sdds2stream -npages=bare $original]
    if {$pages > 1} {
        return -code error "CalculateNewFile: Cannot handle multi-page file for -original (file $original"
    }
    
    if [catch {exec sddsconvert $input -pipe=out \
                 -rename=col,CurrentPos=CurrentPosAdj \
                 -rename=col,CurrentNeg=CurrentNegAdj \
                 | sddsprocess -pipe=in $tmpfile.adj \
                 -filter=col,TimeIndex,0,70 \
             } result ] {
        return -code error "CalculateNewFile: Problem with file $input: $result"
    }

    # file $tmpfile.adj is the file that we need to add to waveform file.
    # assume that the time index points line up.
    exec sddsconvert $original -pipe=out \
      -dele=para,MainCurrent \
      | sddsxref -pipe $tmpfile.adj -pipe \
      -take=CurrentPosAdj,CurrentNegAdj \
      -transfer=para,MainCurrent \
      | sddsprocess -pipe -noWarning \
      "-redef=col,CurrentPos,CurrentPos CurrentPosAdj $gain * + $max min2 -$max max2" \
      "-redef=col,CurrentNeg,CurrentNeg CurrentNegAdj $gain * + $max min2 -$max max2" \
      | sddsconvert -pipe=in $output \
      -noWar \
      -retain=para,MainCurrent,CurrentString \
      -retain=col,TimeIndex,CurrentNeg,CurrentPos
    
    return
}

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

# Sets the relative CPU amplitude factor and toggles the cpu main supply current.

proc toggleCPURelAmplitude {relAmpfactor args} {
    
    global ControlStatus

    APSParseArguments {statusCallback}

    if {$relAmpfactor > 1 || $relAmpfactor < 0} {
        if {$statusCallback!=""} {
            $statusCallback "CPU relative amplitude factor must be in the range 0 <= amplitude <= 1."
        }
        APSAlertBox .alert -errorMessage "CPU relative amplitude factor must be in the range 0 <= amplitude <= 1."
        return 0
    } else {
        catch {exec cavput -list=ID04b:set_V_coil=0.1 -delta=factor=1}
        catch {exec cavput -list=ID04b:set_V_coil=0.1 -delta=factor=-1}
        if {$statusCallback!=""} {
            $statusCallback "CPU main coil toggled with relative factor of $relAmpfactor."
        }
    }
}

proc DSPScopeSetup {args} {

    global ControlStatus configFile
    
    APSParseArguments {statusCallback}

    APSRestoreSRFBDspScope -filename $configFile -timing 1 \
      -channels 1 -clearMode 1 
    
    if {$statusCallback!=""} {
        $statusCallback "DSP scope setup complete."
    }
}

proc CollectData {args} {
    global configFile scalarsFile abort

    APSParseArguments {statusCallback output comment}

    if ![string length $output] {
        return -code error "CollectData: No output file specified."
    }
    if [file exists $output] {
        return -code error "CollectData: File $output already exists!"
    }
    if {[exec cavget -pend=15 -list=SRFB:DS:SampleBO] == "Off"} {
        armDSPscope -statusCallback $statusCallback
    }
    set abort 0
    
    # Triggers from external trigger, which is the 2 Hz injection trigger.
    waitForTrigger -statusCallback $statusCallback
    if {$statusCallback!=""} {
        $statusCallback "Collecting data from DSPscope...[exec date +%H:%M:%S]"
    }
    update
    if [catch {exec sddswmonitor $configFile $output \
                 -comment=Comment,[APSMakeSafeQualifierString $comment] \
                 -ezca=0.01,2000 -scalars=$scalarsFile \
             } result] {
        if {$statusCallback!=""} {
            $statusCallback "CollectData: Error with sddswmonitor: $result"
            $statusCallback "Continuing anyway..."
        }
    }
    if [catch {exec sddsprocess $output -nowarn \
                 "-def=para,TickTime,SRFB:fsAO rec,units=sec" \
                 "-redef=col,t,Index TickTime *,units=sec" \
             } result] {
        return -code error "CollectData: Error with sddsprocess: $result"
    }

    if [catch {exec cavget -list=ID04b:set_V_coil.VAL  \
             } mainCurrent ] {
        return -code error "CollectData: Problem with getting main current (V current) for CPU"
    }
    
    if [catch {exec sddsprocess $output -noWarning \
                 -define=para,MainCurrent,$mainCurrent \
             } result ] {
        return -code error "CollectData: $result"
    }
    
    if [catch {AverageCorrectorErrorData -inputFile $output} result] {
        return -code error "CollectData: Error with AverageCorrectorErrorData: $result"
    }

    file delete -force ${output}~
    if {$statusCallback!=""} {
        $statusCallback "Collecting done."
    }
}

proc armDSPscope {args} {
    global abort
    
    APSParseArguments {statusCallback}
    set dum 1
    while {[exec cavget -pend=15 -list=SRFB:GBL:FeedbackBlockedBI] == "Blocked"} {
        if {$dum == 1} {
            set status "Waiting for feedback block to clear... [exec date +%H:%M:%S]"
            set dum 0
        }
        APSWaitWithUpdate -waitSeconds 1 -updateInterval 0.5
    }
    
    set dum 1
    while {(([exec cavget -pend=15 -list=SRFB:GBL:HorzTripBI] == "Trip") || \
              ([exec cavget -pend=15 -list=SRFB:GBL:VertTripBI] == "Trip")) && \
             ($abort == 0) } {
        if {$dum == 1} {
            if {$statusCallback!=""} {
                $statusCallback "Waiting for trips to be reset... [exec date +%H:%M:%S]"
            }
            set dum 0
        }
        APSWaitWithUpdate -waitSeconds 1 -updateInterval 0.5
    }

    if {$abort == 0} {
        if {$statusCallback!=""} {
            $statusCallback "Arming DSPscope... [exec date +%H:%M:%S]"; update
        }
        exec cavput -pend=15 -list=SRFB:DS:SampleBO=1
        while {[exec cavget -pend=15 -list=SRFB:DS:SampleBO] == "Off"} {
            APSWaitWithUpdate -waitSeconds 1 -updateInterval 0.5
        }
    }
}

proc waitForTrigger {args} {
    global abort

    APSParseArguments {statusCallback}

    if {$abort == 0} {
        set triggered 0
        if {$statusCallback!=""} {
            $statusCallback "Waiting for dspscope to trigger... [exec date +%H:%M:%S]"; update
        }
        while {($triggered == 0) && ($abort == 0)} {
            if {[exec cavget -pend=15 -list=SRFB:DS:SampleBO] == "Off" } {
                set triggered 1
            }
            APSWaitWithUpdate -waitSeconds 1 -updateInterval 1
        }
        
        if {$triggered == 1} {
            if {$statusCallback!=""} {
            $statusCallback "Triggered [exec date +%H:%M:%S]"
            }
            update
        } else {
            if {$statusCallback!=""} {
                $statusCallback "Aborted... [exec date +%H:%M:%S]"
            }
            update
            set triggered 0
        }
    }
}

# Should be called with an average corrector error waveform as input
proc ProcessData {args} {
    global hmap vmap 
    set outputRoot ""
    set startTime 0.012
    set period 0.5000
    set firstWaveform Pos
    set hperiod1 0.2
    set tau 0.007
    set useMapping first
    set reference ""
    APSParseArguments {input reference outputRoot startTime period hperiod1 firstWaveform tau useMapping}

    if ![file exists $input] {
        return -code error "ProcessData: Can't find file $input for input in [pwd]"
    }

    APSSetVarAndUpdate ControlStatus "Processing data..."
    set tmpfile /tmp/[APSTmpString]

# no more need for reference file since the transfer bpm setpoint scrirpt will make the
# reference corrector error zero, or close to it.
    if [string length $reference] {
        if ![file exists $reference] {
            return -code error "ProcessData: Can't find file \"$reference\" for reference in [pwd]"
        }
        SetStatus "Reference file $reference not used. Application assumes that you have transfered the RTFB bpm setpoints using /home/helios/SR/bin/transferRTFBbpmReadbacks."
    }

# maps the correction of the S4 and S5 correctors to the CPU correctors
    if [string match $useMapping "first"] {
        exec sddsprocess $input $tmpfile.1 -noWarning \
          "-def=col,hcorr1,S4A:H3:Error  $hmap(corr1.S4) * S5A:H3:Error  $hmap(corr1.S5) * +" \
          "-def=col,hcorr2,S4A:H3:Error  $hmap(corr2.S4) * S5A:H3:Error  $hmap(corr2.S5) * +" \
          "-def=col,vcorr1,S4A:V3:Error  $vmap(corr1.S4) * S5A:V3:Error  $vmap(corr1.S5) * +" \
          "-def=col,vcorr2,S4A:V3:Error  $vmap(corr2.S4) * S5A:V3:Error  $vmap(corr2.S5) * +" 
    }
    if [string match $useMapping "no" ] {
        exec sddsprocess $input $tmpfile.1 -noWarning \
          -def=col,hcorr1,S4A:H3:Error \
          -def=col,hcorr2,S5A:H3:Error \
          -def=col,vcorr1,S4A:V3:Error \
          -def=col,vcorr2,S5A:V3:Error 
    }

    exec sddsconvert $tmpfile.1 -noWarning \
      -delete=col,S4A:H3:Error*,S5A:H3:Error*,S4A:V3:Error*,S5A:V3:Error* 
    
    exec sddsplot -lay=2,2 -split=page -sep=pages -sep=nameindex  \
      "-topline=After matrix transformation (or after none)" \
      -col=t,(hcorr?,vcorr?) $tmpfile.1 \
      &

    
# create files with parameters of corrector offsets (DC levels) to apply to 
# CPU tables
    foreach type {Neg Pos} tFirst "0.1 [expr $hperiod1 + 0.1]" tLast "[expr $hperiod1 - 0.01] $period" {
        #  Split data into two pieces
        if [catch {exec sddsprocess $tmpfile.1 $outputRoot.$type \
                     -filter=col,t,$tFirst,$tLast \
                     -proc=?corr?,ave,%sAve \
                 } result ] {
            return -code error "ProcessData(2): $result" 
        }
    }

    # 75 points is used to make sure we have more than the minimum 70 points
    # in the Apply procedure.
    set corrList {vcorr2 hcorr2 hcorr1 vcorr1}
    foreach corr $corrList {
        exec sddssequence -pipe=out -def=TimeIndex \
          -sequence=beg=0,number=75,delta=1 \
          | sddsxref -pipe $outputRoot.Neg \
          -transfer=para,${corr}Ave -leave=* \
          -rename=para,${corr}Ave=${corr}Neg \
          | sddsxref -pipe $outputRoot.Pos \
          -transfer=para,${corr}Ave -leave=* \
          -rename=para,${corr}Ave=${corr}Pos \
          | sddsxref -pipe $input \
          -transfer=para,MainCurrent -leave=* \
          | sddsprocess -pipe=in $outputRoot.${corr}.adj \
          -def=col,CurrentPos,${corr}Pos \
          -def=col,CurrentNeg,${corr}Neg
        
    }

    exec sddsplot -grap=line,vary -axes=x \
      -col=TimeIndex,Current??? -leg $outputRoot.hcorr1.adj -file -end \
      -col=TimeIndex,Current??? -leg $outputRoot.hcorr2.adj -file -end \
      -col=TimeIndex,Current??? -leg $outputRoot.vcorr1.adj -file -end \
      -col=TimeIndex,Current??? -leg $outputRoot.vcorr2.adj -file -end \
      &

    APSSetVarAndUpdate ControlStatus "Processing done."
    return
}

proc AverageCorrectorErrorData {args} {
    set inputFile ""
    APSStrictParseArguments {inputFile}

    # 60 Hz is important to vertical plane.  It's ok to filter out 60
    # Hz in horizontal plane because there is no energy at 60 Hz.

    if [catch {exec sddsprocess $inputFile  -pipe=out \
                 "-def=col,Period,t 0.5 / int,type=long" \
                 | sddsfdfilter -pipe \
                 -col=t,*Error \
                 -notch=center=60,flat=8 \
                 | sddsfdfilter -pipe \
                 -col=t,*Error \
                 -notch=center=120,flat=8 \
                 | sddsbreak -pipe \
                 -increaseof=Period \
                 | sddsconvert -pipe \
                 -keeppages=5 \
                 | sddsenvelope -pipe \
                 -mean=S*\[HV\]* -copy=Index,t \
                 | sddsprocess -pipe \
                 -process=t,first,tFirst \
                 -process=Index,first,IndexFirst \
                 "-redef=col,Index,Index IndexFirst -" \
                 "-redef=col,t,t tFirst -" \
                 | sddsxref -pipe $inputFile \
                 -transfer=para,Comment,TickTime,MainCurrent \
                 -leave=* \
                 | sddsconvert -pipe=in $inputFile.ave \
                 -edit=col,*Mean*,%/Mean// \
             } result ] {
        return -code error "AverageCorrectorErrorData: $result"
    }
    return
}

# Procedure to plot the most recent cpu data.

proc PlotMostRecentCPUData {args} {

    global ControlStatus Title Delay

    APSStrictParseArguments {statusCallback}
    set mostRecentFile [lindex [lsort [glob ??????-s*s*-*s.sdds]] end]
    
    catch {regexp {(......)-s.*s.*-.*s.sdds} $mostRecentFile {} root}

    if ![string length $root] {
        if {$statusCallback!=""} {
            $statusCallback "You must give a valid rootname."
        }
        APSAlertBox .alert -errorMessage "You must give a valid rootname."
        return 0
    }

    catch {exec sddsplot -lay=2,2 -title= \
             -limit=xmin=[expr 0.0 + $Delay],xmax=[expr 0.4 + $Delay]\
             -topline=$root \
             -col=t,(S*H*Error,S*V*Error) -sep -mode=y=offset \
             -same $root-s4s5-2.4s.sdds \
             -title=[APSMakeSafeQualifier $Title] -end \
             -col=t,(S*H3,S*V3) -sep -mode=y=offset \
             -same $root-s4s5-2.4s.sdds \
             "-title=$Title" -end &}
}

proc PlotCPUData {args} {

    global ControlStatus Title Delay plotDirectory
    
    APSParseArguments {title delay statusCallback}
    
    set fileList [glob ??????-s*s*-*s.sdds]
    set itemList ""
    foreach file $fileList {
        catch {regexp {(......)-s.*s.*-.*s.sdds} $file {} root}
        lappend itemList $root
    }

    set selectFile [APSChooseItemFromList \
                      -name "Raw Data Selection" \
                      -itemList $itemList \
                      -returnList $fileList \
                      -returnIndices 0 \
                      -multiItem 0 \
                      -contextHelp "Select a CPU data file to plot"]

    catch {regexp {(......)-s.*s.*-.*s.sdds} $selectFile {} root}

    exec sddsplot -lay=2,2 -title= \
      -limit=xmin=[expr 0.0 + $Delay],xmax=[expr 0.4 + $Delay]\
      -topline=$root \
      -col=t,(S*H*Error,S*V*Error) -sep -mode=y=offset \
      -same ${selectFile} \
      -title=$Title -end \
      -col=t,(S*H3,S*V3) -sep -mode=y=offset \
      -same ${selectFile} \
      "-title=$Title" -end \
      &
}

# Build Application

MakeCPUStatus
MakeCPUSetupWidget .setup -parent .userFrame
MakeCPUMeasWidget .meas -parent .userFrame
MakeCPUProcessWidget .proc -parent .userFrame
MakeCPUModWaveformWidget .mod -parent .userFrame

set configFile /home/helios/oagData/sr/RTFB/monitorFiles/S4S5CorrError.dspscope
set scalarsFile /home/helios/oagData/sr/RTFB/monitorFiles/SRFBTrigReadStatus.monitor

set index -1
set root corrError
set comment test
set outputVersion 0
set outputRoot [clock format [clock seconds] -format "%y%m%d"]

set startTime 0.008
set firstWaveform Neg
set period 0.5000
set hperiod1 0.202
set tau 0.007
set outputDir .
set index 0
set abort 0
set gain 1.0

# example of map data: /home/helios/SR/daily/0611/28/3/CPU/hConversion
# need to change the sign I think.
# old values
set hmap(corr1.S4) 4.069047
set hmap(corr1.S5) 1.848339
set hmap(corr2.S4) -4.321229
set hmap(corr2.S5) -2.462897

set vmap(corr1.S4) -3.054718
set vmap(corr1.S5) 2.873522
set vmap(corr2.S4) 3.717703
set vmap(corr2.S5) -3.691528

#new values from  /home/helios/SR/daily/0611/28/3/CPU/hConversion and vConversion
set hmap(corr1.S4) -4.898358
set hmap(corr1.S5) -2.321237
set hmap(corr2.S4) 5.370239
set hmap(corr2.S5) 3.024471
# I don't understand why the new conversion matrix has much larger, and different sign.
set vmap(corr1.S4) -19.36352
set vmap(corr1.S5) 18.16372
set vmap(corr2.S4) 22.05179
set vmap(corr2.S5) -22.69837


# If we are using h.cpuH1 and v.cpuV1, then the mapping is
# from S4B:H1 to hcorr[12]
# See measurement in ~/daily/0806/10/1/CPU/Notebook
# Sign changed 2/3/2009
set hmap(corr1.S4) -1.54
set hmap(corr1.S5) 1.71
set hmap(corr2.S4) 0.21
set hmap(corr2.S5) -2.95

set vmap(corr1.S4) -7.04
set vmap(corr1.S5) 8.18
set vmap(corr2.S4) 1.82
set vmap(corr2.S5) -13.02
set useMapping first

set hmap(corr1.S4) 1
set hmap(corr1.S5) 0
set hmap(corr2.S4) 0
set hmap(corr2.S5) 1

set vmap(corr1.S4) 1
set vmap(corr1.S5) 0
set vmap(corr2.S4) 0
set vmap(corr2.S5) 1
set useMapping no

set referenceFile corrError-001
set referenceFile ""


# Local Variables:
# mode: tcl
# indent-tabs-mode: nil
# End:
