#!/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)]

#
# $Log: not supported by cvs2svn $
# Revision 1.12  2005/09/27 21:33:14  borland
# Use APSBGExec now.
#
# Revision 1.11  2005/04/11 17:14:11  borland
# Added -packOption "-fill x" to APSScrolledStatus.
#
# Revision 1.10  2005/04/08 00:29:06  borland
# Fixed bug that prevented fitting anything except polynomials.
#
# Revision 1.9  2004/08/23 17:28:19  borland
# Tabified the fit type selection.
#
# Revision 1.8  2004/07/30 18:54:57  borland
# Added file selection and command buttons to file entry boxes.
#
# Revision 1.7  2004/02/08 03:01:53  borland
# The auto-offset feature is now off by default for polynomial fits.
# There is a widget on the polynomial panel for controlling this.
#
# Revision 1.6  2004/02/03 15:34:27  shang
# added residual plot to polynormial fit plots and made the sparse option work
# in polynormial fit. Added autoOffset option to exponential fit and set it on
# by default.
#
# Revision 1.5  2003/10/10 19:03:03  borland
# Now uses -autoOffset feature for sddsexpfit when Time is the independent
# variable.
#
# Revision 1.4  2003/10/10 18:36:31  borland
# This essentially undoes the last change and uses the new -autoOffset option
# for sddspfit.
#
# Revision 1.3  2003/10/09 22:32:00  borland
# For polynomial fitting, automatically detects if the independent variable
# is "Time".  If it is, sets the offset to the minimum value of Time.
# This prevents numerical problems in sddspfit.
#
# Revision 1.2  2002/01/09 22:35:48  soliday
# These now work on Windows.
#
# Revision 1.1  1997/08/18 17:09:05  borland
# First version, by B. Dolin.
#
#

APSRenameExecToAPSBGExec

set fitHistory /tmp/[APSTmpString]
set fitOutput /tmp/[APSTmpString]

set fitOutputList ""
set filename ""
set filerecord ""
set begin ""
set end ""
set sigmaVar None
set equation ""

set terms 3
set porders 0,1,2
set sparseInterval 1
set reviseThreshold .1
set normalizeOrder 0
set xFactor 1
set autoOffset 0
set xOffset 0
set sigmaValue .05

set etolerance .00000001
set econstant 1
set efactor 1
set erate 1

set gtolerance .00000001
set evalLimit 5000
set passLimit 100
set gbaseline 1
set gmean 1
set gheight 1
set gsigma 1
set gstepsize .01

set argList "type filename fitfile begin end yearStart yearEnd monthStart monthEnd dayStart dayEnd hourStart hourEnd xVar yVar sigmaVar \
	equation fitHistory fitOutput filterOn doTimeFilter \
	terms porders sparseOn sparseInterval reviseOn reviseThreshold normalizeOn normalizeOrder xOffset xFactor symmetry generate \
	sigmaChoice sigmaValue \
	etolerance clue econstant efactor erate eGuessOn \
	gtolerance evalLimit passLimit gbaseline gmean gheight gsigma gGuessOn gstepsize autoOffset"

proc SetStatus {text} {
    global status
    set status "$text"
    update
}

APSApplication . -name DataFit -version 1.0 -overview "Allows the user to find \
	a function of best fit for a set of data, with optional prefiltering. Also plots the raw data \
	only, or the raw data along with the function of best fit."

SetStatus Ready...
APSScrolledStatus .status -parent .userFrame -textVariable status -width 95 -packOption "-fill x"

if [llength $argv] {
    set args $argv
    APSParseArguments {filename}
}

# Disables and grays a list of entry boxes

proc boxState {args} {
    APSParseArguments {on boxes}
    foreach box $boxes {
	if !$on {$box configure -state disabled -background grey80 -foreground grey30}
	if $on {$box configure -state normal -background white -foreground black}
    }
}    

# Creates windows to allow user to select the columns for indep. and dep. variables
set varList ""
proc varSelect {args} {
    global xVar yVar sigmaVar filerecord varList xOffset autoOffset
    APSParseArguments {var filename}
    if [string match $filerecord $filename] {
	if [string match sigma $var] {set varList [linsert $varList 0 None]; set varname Sigma}
	if [string match x $var] {set varname Independent}
	if [string match y $var] {set varname Dependent}
    } else {
        set filerecord $filename
        set varList [lsort [exec sddsquery -column $filename]]
        if [string match x $var] {set varname Independent} 
        if [string match y $var] {set varname Dependent}
        if [string match sigma $var] {
            set varname Sigma
            set varList [linsert $varList 0 None]
        }
    }
    APSScrolledListWindow .$var\Select -height 10 -name "$varname variable" -itemList "$varList" -selectionVar ${var}Var
    if {[string match x $var] && [string compare [set ${var}Var] Time]==0} {
        set autoOffset 1
    }
}

# Makes filename selection box and button

proc makeFilenameFrame {} {
    global filename fitfile
    APSFrame .file -parent .userFrame
    APSFrame .t -parent .userFrame.file.frame -relief flat -packOption "-fill x"
    APSLabeledEntry .filename -parent .userFrame.file.frame.t.frame -packOption "-side left" -label "Filename:" \
            -commandButton 1 \
	    -textVariable filename -width 48 -contextHelp "Enter filename here or call up selection box with button."
    APSButton .fileselect -parent .userFrame.file.frame.t.frame -packOption "-side left" -size small -text "F" \
	    -contextHelp "Press to call up file selection dialog box." \
	    -command {
                set filename [APSFileSelectDialog .filebox -title "SDDS File" -pattern *.sdds]
                set fitfile [file rootname $filename].fit.sdds
                varSelect -var x -filename $filename 
                varSelect -var y -filename $filename
            }
    APSLabeledEntry .fitfile -parent .userFrame.file.frame -packOption "-side left" -label "Output file:" \
	    -textVariable fitfile -width 48 -contextHelp "Enter the name of the sdds output file for the raw data\
	    and the fit information." \
            -fileSelectButton 1 -commandButton 1
    set fitfile [file rootname $filename].fit.sdds
}



# Makes entry box and buttons for variable selection	

proc makeVariableFrame {} {
    global xVar yVar sigmaVar
    APSFrame .vars -parent .userFrame -label Variables
    set vf .userFrame.vars.frame
    APSFrame .x -parent $vf -relief flat
    APSLabeledEntry .xVar -width 30 -label "Independent variable:" -textVariable xVar -parent $vf.x.frame \
	    -packOption "-side left" -contextHelp "Enter the name of the independent variable here \
	    or call up selection box with the button."
    APSButton .xselect -parent $vf.x.frame -packOption "-side right" -text "Select..." -command \
	    {varSelect -var x -filename $filename} -contextHelp "Press to call up x variable selection box."
    APSFrame .y -parent $vf -relief flat
    APSLabeledEntry .yVar -width 30 -label "  Dependent variable:" -textVariable yVar -parent $vf.y.frame \
	    -packOption "-side left" -contextHelp "Enter the name of the dependent variable here or call up \
	    selection box with the button."
    APSButton .xselect -parent $vf.y.frame -packOption "-side right" -text "Select..." -command \
	    {varSelect -var y -filename $filename} -contextHelp "Press to call up y variable selection box."
    APSFrame .s -parent $vf -relief flat
    APSButton .sigmaselect -parent $vf.s.frame -packOption "-side right" -text "Select..." -command \
	    {varSelect -var sigma -filename $filename} -contextHelp "Press to call up y-sigma variable selection box."
    APSLabeledEntry .sigma -width 26 -label "Dependent variable sigma:" -textVariable sigmaVar -parent $vf.s.frame \
	    -packOption "-side right" -contextHelp "Enter the name of the dependent variable sigma here or press \
	    button to call up selection box."
}

# Creates frame for both filters and entry boxes for the standard filter

proc makeFilterFrameAndStandardFilter {} {
    global begin end standardBoxes
    APSFrame .filters -parent .userFrame -label "Filters"
    APSFrame .standard -parent .userFrame.filters.frame -relief flat
    set sf .userFrame.filters.frame.standard.frame
    set standardBoxes "$sf.begin.entry $sf.end.entry"
    APSRadioButtonFrame .rb -parent $sf -label "Standard filter:" \
	    -buttonList "Yes No" -valueList "1 0" -variable filterOn -orientation horizontal \
	    -contextHelp "Activate or deactivate the standard independent variable filter." -packOption "-side left" \
	    -commandList {"boxState -on 1 -boxes $standardBoxes" "boxState -on 0 -boxes $standardBoxes"} 
    APSLabeledEntry .begin -width 7 -label "Start:" -textVariable begin -parent $sf \
	    -contextHelp "Ignore all values before 'Begin.'" -packOption "-side left"
    APSLabeledEntry .end -width 7 -label "End:" -textVariable end -parent $sf \
	    -contextHelp "Ignore all values after 'End.'" -packOption "-side left"
    $sf.rb.frame.button2 invoke
}

# Creates the list of orders based on the number of terms selected, unless orders box is disabled

proc pordersList {terms} {
    global porders
    if [string match $porders n/a] return
    if [string match $terms ""] {set porders ""; return}
    if $terms>1000 {return}
    set i 1
    set porders 0
    while {$i<$terms} {
	append porders ,$i
	incr i
    }
}

# Disables and grays the orders entry box if the symmetry is odd or even 

proc pordersProtect {protectOn} {
    global porders terms pf
    if $protectOn {
	set porders n/a
	$pf.l.frame.orders.entry configure -state disabled -background grey80
    }
    if !$protectOn {
	set porders ""
	pordersList $terms
	$pf.l.frame.orders.entry configure -state normal -background white
    }
}

# Sets up the universal widgets

makeFilenameFrame
makeVariableFrame
makeFilterFrameAndStandardFilter

proc setFitType {theType} {
    global type 
    set type $theType
}

# Sets up the swap widget frame for the various fit options

set tabWidgetList [APSTabFrame .fo -parent .userFrame -label "Fit Options: " \
    -labelList "Polynomial Exponential Gaussian" \
    -commandList [list "setFitType polynomial" "setFitType exponential" "setFitType gaussian"] -width 900 -height 200]
	
# Polynomial fit options
# Left frame
set pf [lindex $tabWidgetList 0]
APSFrame .l -parent $pf -relief flat -packOption "-side left"
APSLabeledEntry .terms -width 3 -label "Number of terms:" -textVariable terms -parent $pf.l.frame \
	-contextHelp "Specifies the number of terms that the fit equation should have. Enter 2 for linear, 3 for quadratic, etc."    
APSLabeledEntry .orders -width 22 -label "Orders:" -textVariable porders -parent $pf.l.frame \
	-contextHelp "Specifies the polynomial orders to be used in fitting."
APSRadioButtonFrame .symmetry -label Symmetry: -variable symmetry -parent $pf.l.frame \
	-contextHelp "Use to specify odd (linear, cubic, etc.) or even (constant, quadratic, etc.) symmetry." \
	-buttonList {None Odd Even} -valueList {none odd even} -orientation horizontal \
	-commandList {"pordersProtect 0" "pordersProtect 1" "pordersProtect 1"}
$pf.l.frame.symmetry.frame.button1 invoke
APSFrame .norm -parent $pf.l.frame -relief flat
APSLabeledEntry .xOffset -width 7 -label "x offset:" -textVariable xOffset -parent $pf.l.frame.norm.frame \
	-contextHelp "Enter an offset value. Note that the user must interpret given coefficients. The transformation \
	is x --> (x-offset)/factor." -packOption "-side left"
APSLabeledEntry .xFactor -width 7 -label "factor:" -textVariable xFactor -parent $pf.l.frame.norm.frame \
	-contextHelp "Enter a factor value. Note that the user must interpret given coefficients. The transformation \
	is x --> (x-offset)/factor." -packOption "-side left"
APSRadioButtonFrame .autooffset -parent $pf.l.frame -label "Auto Offset:" -variable autoOffset \
  -orientation horizontal -buttonList {Yes No} -valueList {1 0} \
  -contextHelp "turn on/off auto-offset option for fit" -packOption "-side left"

# Middle frame
# Sparse Option
APSFrame .m -parent $pf -relief flat -packOption "-side left"
APSFrame .sparse -parent $pf.m.frame -relief flat -packOption "-fill x"
APSRadioButtonFrame .sparserb -label "Sparse data:  " -variable sparseOn -parent $pf.m.frame.sparse.frame \
	-buttonList {Yes No} -valueList {1 0} -orientation horizontal -packOption "-side left" \
	-commandList {"boxState -on 1 -boxes $pf.m.frame.sparse.frame.interval.entry" \
	"boxState -on 0 -boxes $pf.m.frame.sparse.frame.interval.entry"} \
	-contextHelp "Specifies sparsing the data prior to fitting in order to speed computations."
APSLabeledEntry .interval -width 5 -label "Interval:" -textVariable sparseInterval -parent $pf.m.frame.sparse.frame \
	-contextHelp "Specifies the sparse interval for the input data if sparsing is selected." -packOption "-side right"
$pf.m.frame.sparse.frame.sparserb.frame.button2 invoke

# Revise orders option
APSFrame .revise -parent $pf.m.frame -relief flat -packOption "-fill x"
APSRadioButtonFrame .reviserb -label "Revise orders:" -variable reviseOn -parent $pf.m.frame.revise.frame \
	-buttonList {Yes No} -valueList {1 0} -orientation horizontal -packOption "-side left" \
	-commandList {"boxState -on 1 -boxes $pf.m.frame.revise.frame.threshold.entry" \
	"boxState -on 0 -boxes $pf.m.frame.revise.frame.threshold.entry"} \
	-contextHelp "Specifies adaptive fitting to eliminate spurious terms.When invoked, this switch causes sddspfit to \
	repeatedly fit the first page of data with each term individually eliminated. If the resultant fit is not \
	significantly worse than the fit containing the term, then the term is judged spurious and is eliminated. This \
	stops only when elimination of any single term makes a significant worsening of the fit. By default, the \
	criterion for a worse fit is one that has a larger reduced chi-squared. The threshold qualifier is used to \
	specify how much larger the reduced chi-squared may be and still eliminate a term."
APSLabeledEntry .threshold -label "Threshold:" -width 5 -textVariable reviseThreshold -parent $pf.m.frame.revise.frame \
	-contextHelp "If Revise orders is selected, specifies how much larger the reduced chi-squared may be and still \
	eliminate a term." -packOption "-side right"
$pf.m.frame.revise.frame.reviserb.frame.button2 invoke

# Normalize option
APSFrame .normalize -parent $pf.m.frame -relief flat -packOption "-fill x"
APSRadioButtonFrame .normalizerb -label "Normalize:    " -variable normalizeOn -parent $pf.m.frame.normalize.frame \
	-buttonList {Yes No} -valueList {1 0} -orientation horizontal -contextHelp "Specifies that coefficients be \
	normalized so that the coefficient for the indicated order is unity." -packOption "-side left" \
	-commandList {"boxState -on 1 -boxes $pf.m.frame.normalize.frame.normalize.entry" \
	"boxState -on 0 -boxes $pf.m.frame.normalize.frame.normalize.entry"}
APSLabeledEntry .normalize -width 5 -label "For order:" -textVariable normalizeOrder -parent $pf.m.frame.normalize.frame \
	-contextHelp "Normalize coefficients so that the coefficient for the given order is unity." \
	-packOption "-side right"
$pf.m.frame.normalize.frame.normalizerb.frame.button2 invoke

# Sigmas option
APSFrame .sigmas -parent $pf.m.frame -relief flat -packOption "-fill x"
set sbox $pf.m.frame.sigmas.frame.sigmaval.entry
APSRadioButtonFrame .sigmasrb -label "Specify sigmas:" -variable sigmaChoice -parent $pf.m.frame.sigmas.frame \
	-buttonList {Absolute Fraction No} -valueList {1 2 0} -contextHelp \
	"Generate independent-variable error using a specifed or fractional value for all points." \
	-orientation horizontal -commandList {"boxState -on 1 -boxes $sbox" \
	"boxState -on 1 -boxes $sbox" "boxState -on 0 -boxes $sbox"}
APSLabeledEntry .sigmaval -label Value: -textVariable sigmaValue -parent $pf.m.frame.sigmas.frame \
	-packOption "-side right" -width 15 -contextHelp "Enter the absolute or fractional value that will \
	specify the sigmas."
$pf.m.frame.sigmas.frame.sigmasrb.frame.button3 invoke

# Right frame
APSFrame .r -parent $pf -relief flat -packOption "-side left"    
APSRadioButtonFrame .generate -parent $pf.r.frame -label "Generate sigmas:" \
	-buttonList {"Keep generated" "Keep largest" "Keep smallest" "No"} \
	-valueList {1 2 3 0} -variable generate \
	-contextHelp "Specifies that independent-variable errors be generated from the variance of an \
	initial equal-weights fit. The user can specify that the generated values be retained, or that \
	the largest (or smallest) of each of the values given by the sigma variable and the generated values \
	be retained. Also, the user can select that values not be generated at all." 
$pf.r.frame.generate.frame.button4 invoke

# Exponential fit options
# Tolerance and clue options
set ef [lindex $tabWidgetList 1]
APSFrame .l -parent $ef -relief flat -packOption "-side left"
APSLabeledEntry .etolerance -width 15 -label "Tolerance:" -textVariable etolerance -parent $ef.l.frame \
	-contextHelp "Specifies how close the program will attempt to come to the optimum fit, \
	in terms of the mean squared residual."
APSRadioButtonFrame .clue -parent $ef.l.frame -label "Clue:   " -variable clue \
	-buttonList {Grows Decays} -valueList {grows decays} -orientation horizontal \
	-contextHelp "Specify grows or decays in order to help the fitting program."
$ef.l.frame.clue.frame.button1 invoke
# Guess options
set eGuessBoxes "$ef.guess.frame.econstant.entry $ef.guess.frame.erate.entry $ef.guess.frame.efactor.entry"
APSRadioButtonFrame .guess -parent $ef -label "Guess:" -variable eGuessOn -orientation horizontal \
	-buttonList {Yes "No  "} -valueList {1 0} -packOption "-side left" \
	-commandList {"boxState -on 1 -boxes $eGuessBoxes" "boxState -on 0 -boxes $eGuessBoxes"} \
	-contextHelp "Specifies a starting point for each of the three values."
APSLabeledEntry .econstant -width 10 -label "Constant:" -textVariable econstant -parent $ef.guess.frame
APSLabeledEntry .efactor -width 10 -label "Factor:" -textVariable efactor -parent $ef.guess.frame
APSLabeledEntry .erate -width 10 -label "Rate:" -textVariable erate -parent $ef.guess.frame
$ef.guess.frame.button2 invoke
APSRadioButtonFrame .autooffset -parent $ef -label "Auto Offset:" -variable autoOffset \
  -orientation horizontal -buttonList {Yes No} -valueList {1 0} \
  -contextHelp "turn on/off auto-offset option for fit" -packOption "-side left"

# Gaussian fit options
set gf [lindex $tabWidgetList 2]
APSFrame .l -parent $gf -relief flat -packOption "-side left"
APSLabeledEntry .gtolerance -width 15 -label "Tolerance:" -textVariable gtolerance -parent $gf.l.frame \
	-contextHelp "Specifies how close the program will attempt to come to the optimum fit, \
	in terms of the mean sqared residual."
APSLabeledEntry .evalLimit -width 5 -label "Fit evaluation limit:" -textVariable evalLimit -parent $gf.l.frame \
	-contextHelp "Specifies limit on how many fit funtion evaluations will be done."
APSLabeledEntry .passLimit -width 5 -label "Minimization pass limit:" -textVariable passLimit -parent $gf.l.frame \
	-contextHelp "Specifies limit on how many minimization passes will be done."
APSLabeledEntry .stepsize -width 5 -label "Starting stepsize:" -textVariable gstepsize -parent $gf.l.frame \
	-contextHelp "Specifies the starting stepsize as a fraction of the starting values."

# Guess options
set gGuessBoxes "$gf.guess.frame.baseline.entry $gf.guess.frame.mean.entry \
	$gf.guess.frame.height.entry $gf.guess.frame.sigma.entry"
APSRadioButtonFrame .guess -parent $gf -label "Guess:" -variable gGuessOn -orientation horizontal \
	-buttonList {Yes "No  "} -valueList {1 0} -packOption "-side left" \
	-commandList {"boxState -on 1 -boxes $gGuessBoxes" "boxState -on 0 -boxes $gGuessBoxes"} \
	-contextHelp "Specifies a starting point for each of the three values."
APSLabeledEntry .baseline -width 10 -label "Baseline:" -textVariable gbaseline -parent $gf.guess.frame
APSLabeledEntry .mean -width 10 -label "Mean:" -textVariable gmean -parent $gf.guess.frame
APSLabeledEntry .height -width 10 -label "Height:" -textVariable gheight -parent $gf.guess.frame
APSLabeledEntry .sigma -width 10 -label "Sigma:" -textVariable gsigma -parent $gf.guess.frame
$gf.guess.frame.button2 invoke

set type polynomial

proc makeHistory {command} {
    global fitOutput fitHistory
    set fitOutputID [open $fitOutput]
    set fitOutputString [read $fitOutputID]
    set fitOutputString [append command "\n\n$fitOutputString"]
    append fitOutputString "_____________________________________________________________________________ \n"
    if [winfo exists .histbox] {
	.histbox.userFrame.file.text insert end "\n$fitOutputString"
	.histbox.userFrame.file.text yview end
    }
    set fitHistoryID [open $fitHistory a]
    puts $fitHistoryID "$fitOutputString"
    close $fitHistoryID
    close $fitOutputID
}
    
proc polynomialFit {args} {
    global argList 
    eval global $argList
    set columns "-columns=$xVar,$yVar"
    set command {sddspfit $filename.filtered $fitfile $columns -terms=$terms \
	    -xOffset=$xOffset -xFactor=$xFactor -symmetry=$symmetry -verbose }
    if $autoOffset {
        append command " -autoOffset "
    }
    if {$sigmaChoice && ![string match $sigmaVar None] && $generate!=1} {error "You can't specify sigmas and a dependent\
	    variable sigma name.  Select 'No' to 'Specify sigmas' or 'None' to 'Dependent variable sigma,' or select 'Keep\
	    generated' sigmas."}
    if $generate==1 {append command "-generateSigmas "}
    if $generate==2 {
	if [string match $sigmaVar None] {
	    append command "-generateSigmas "
	} else {
	append columns ",ySigma=$sigmaVar"
	append command "-generateSigmas=keepLargest "
	}
    }
    if $generate==3 {
	if [string match $sigmaVar None] {
	    append command "-generateSigmas "
	} else {
	append columns ",ySigma=$sigmaVar"
	append command "-generateSigmas=keepSmallest "
	}
    }
    if $generate==0 {
	if ![string match $sigmaVar None] {append columns ",ySigma=$sigmaVar"}
    }
    if $sigmaChoice==1 {append command "-sigmas=$sigmaValue,absolute "}
    if $sigmaChoice==2 {append command "-sigmas=$sigmaValue,fractional "}
    if $reviseOn {
	if {[string match $sigmaVar None] && !$generate && !$sigmaChoice} {
	    error "You must select or generate the dependent variable sigma in order to revise orders."
	} else {
	    append command "-reviseOrders=threshold=$reviseThreshold "
	}
    }
    if $sparseOn {append command "-sparse=$sparseInterval "}
    if $normalizeOn {append command "-normalize=$normalizeOrder "}
    if ![string match $porders "n/a"] {append command "-orders=$porders "}
    set command [subst $command] 
    eval exec $command 2> $fitOutput
    makeHistory $command
    global equation
    set fit [sdds open $fitfile]
    set equation [sdds getParameter $fit sddspfitLabel]
    set equation [string trim "$equation" "\{ \}"]
    sdds close $fit
}

proc exponentialFit {args} {
    global argList
    eval global $argList
    set command "sddsexpfit $filename.filtered $fitfile -fullOutput -columns=$xVar,$yVar -tolerance=$etolerance \
	    -clue=$clue -verbosity=1 "
    if $autoOffset {
        append command "-autoOffset "
    }
    if $eGuessOn {append command "-guess=$econstant,$efactor,$erate "}
    #puts stderr "$command"
    eval exec $command 2> $fitOutput
    makeHistory $command
    global equation
    set fit [sdds open $fitfile]
    set C [sdds getParameter $fit expfitConstant]
    set F [sdds getParameter $fit expfitFactor]
    set R [sdds getParameter $fit expfitRate]
    set equation [format "$yVar = %g+%g*exp(%g\\,$xVar)" $C $F $R]
    sdds close $fit
}

proc gaussianFit {args} {
    global argList
    eval global $argList
    set command "sddsgfit $filename.filtered $fitfile -columns=$xVar,$yVar -fullOutput -tolerance=$gtolerance \
	    -limits=evaluations=$evalLimit,passes=$passLimit -stepSize=$gstepsize -verbosity=1 "
    if $gGuessOn {append command "-guesses=baseline=$gbaseline,mean=$gmean,height=$gheight,sigma=$gsigma "}
    eval exec $command 2> $fitOutput
    makeHistory $command
    global equation
    set fit [sdds open $fitfile]
    set B [sdds getParameter $fit gfitBaseline]
    set H [sdds getParameter $fit gfitHeight]
    set mu [sdds getParameter $fit gfitMean]
    set sigma [sdds getParameter $fit gfitSigma]
    set equation [format "$yVar = %g+%g*-e/2*pow(($xVar-%g)/%g\\,2)" $B $H $mu $sigma]
    sdds close $fit
}

proc filter {args} {
    global argList
    eval global $argList
    file copy -force -- $filename $filename.filtered
    if $filterOn {
	SetStatus "Doing standard filter on $filename..."
	exec sddsprocess $filename.filtered $filename.filtered -filter=column,$xVar,$begin,$end -noWarnings
    }
    if $doTimeFilter {
	SetStatus "Doing time filter on $filename..."
	if {[catch {APSConvertTimeToHours $hourStart} hour0] || \
		[catch {APSConvertTimeToHours $hourEnd} hour1]} {
            SetStatus "Bad hour syntax in time filter"
            return
        }
        if {[catch {exec timeconvert \
		-breakDown=year=$yearStart,month=$monthStart,day=$dayStart,hour=$hour0} \
		time0] || \
		[catch {exec timeconvert \
		-breakDown=year=$yearEnd,month=$monthEnd,day=$dayEnd,hour=$hour1} \
		time1]} {
            SetStatus "Error in time syntax for time filter"
            return
        }
        exec sddsprocess $filename.filtered $filename.filtered -filter=column,Time,$time0,$time1 -noWarnings
    }
}

proc fitProcessing {args} {
    global argList
    eval global $argList
    filter
    SetStatus "Fitting $type function for $filename..."
    ${type}Fit
    SetStatus "Function:  $equation"
    regsub = "$equation" \\= equation
    SetStatus "Plotting $type function and data from $filename..."
    set timeOption "-legend=specified=Fit"
    if {[string match $xVar Time] &&
        !($autoOffset && [string compare $type exponential]==0)} {
        set timeOption "-ticks=xtime"
    }
    exec sddsplot $fitfile -sep=2 -layout=1,2 "-topline=$equation" \
       $timeOption \
      -yscales=id=${yVar} \
      -columnName=$xVar,$yVar -graphics=symbol -legend=specified=Data \
      -columnName=$xVar,${yVar}Fit -graphics=line,type=1 -legend=specified=Fit  \
      -columnName=$xVar,${yVar}Residual -graphices=sym,conn,type=1 -legend=specified=Residual \
      -yscales=id=${yVar}Residual &
}

proc plotProcessing {args} {
    global argList
    eval global $argList
    filter
    SetStatus "Plotting $filename..."
    set timeOption "-graphics=symbol"
    if [string match $xVar Time] {set timeOption -ticks=xtime}
    exec sddsplot $filename.filtered -columnName=$xVar,$yVar -graphics=symbol $timeOption -legend=Specified=Data &
}

APSFrame .buttons -parent .userFrame -packOption "-side bottom"
   
APSButton .plot -parent .userFrame.buttons.frame -packOption "-side left" -text "PLOT DATA" \
	-contextHelp "Press to plot data.  The data is prefiltered if the standard and/or time filter \
	is turned on." -command {plotProcessing}

APSButton .fit -parent .userFrame.buttons.frame -packOption "-side left" -text "FIT AND PLOT" \
	-contextHelp "Press to find the function of best fit. The data is prefiltered if the standard and/or \
	time filter is turned on.  A graph of the function is displayed along with the original data." \
	-command {fitProcessing}

APSButton .history -parent .userFrame.buttons.frame -packOption "-side left" -text "DETAILED FIT HISTORY" \
	-contextHelp "Press to display a scrollable window with details from previous fits." \
	-command {APSFileDisplayWindow .histbox -comment "Fit History" -fileName $fitHistory}

set doTimeFilter 0

proc makeTimeFilterWidget {widget args} {
    set parent .userFrame.filters.frame
    APSParseArguments {parent}
    global hourStart dayStart monthStart yearStart
    global hourEnd dayEnd monthEnd yearEnd doTimeFilter

    if !$doTimeFilter {
        set hourStart 0
        set hourEnd 24
        APSDateBreakDown -dayVariable dayStart -monthVariable monthStart \
          -yearVariable yearStart -twoDigitYear 0 -leadingZeros 0
        APSDateBreakDown -dayVariable dayEnd -monthVariable monthEnd \
          -yearVariable yearEnd -twoDigitYear 0 -leadingZeros 0
    }

    APSFrame $widget -parent $parent -label "" -relief flat \
      -contextHelp "Provides for calendar-time-based filtering of the data.  Will work only if the \
      data contains the Time column (e.g., sddsmonitor data)."
    set w $parent$widget.frame
    
    global timeBoxes
    set timeBoxes "$w.start.frame.entry1 $w.start.frame.entry2 $w.start.frame.entry3 \
	    $w.start.frame.entry4 $w.end.frame.entry1 $w.end.frame.entry2 \
	    $w.end.frame.entry3 $w.end.frame.entry4"

    APSRadioButtonFrame .filter -parent $w -orientation horizontal \
      -variable doTimeFilter -label "Time filter: " -packOption "-side left" \
      -buttonList "Yes No" -valueList "1 0" -contextHelp \
      "Choose whether or not to apply a time filter to the data." \
      -commandList {"boxState -on 1 -boxes $timeBoxes" "boxState -on 0 -boxes $timeBoxes"}
    APSLabeledEntryFrame .start -parent $w -label " Start (Y/M/D/H): " \
      -contextHelp "Enter the start time in year, month, day, and hour." \
      -variableList {yearStart monthStart dayStart hourStart} \
      -orientation horizontal -width 5 -packOption "-side left" 
    APSLabeledEntryFrame .end   -parent $w -label " End: " \
      -contextHelp "Enter the end time in year, month, day, and hour." \
      -variableList {yearEnd monthEnd dayEnd hourEnd} \
      -orientation horizontal -width 5 -packOption "-side left" 
}

makeTimeFilterWidget .timeFilter
.userFrame.filters.frame.timeFilter.frame.filter.frame.button2 invoke

# Binds term number to porders to create list of orders to evaluate
bind $pf.l.frame.terms.entry <Any-KeyRelease> {pordersList $terms}

# Binds filename entry box to output box to create output-file name
bind .userFrame.file.frame.t.frame.filename.entry <Any-KeyRelease> {set fitfile [file rootname $filename].fit.sdds}
