#Agilent 54854A scope command documentation
#http://cp.literature.agilent.com/litweb/pdf/54855-97017.pdf

#To use this software you must first set the apsScopeSettings
#array as described in APSInitializeScope.
#The commands are usually run in this order:
#APSResetScope
#APSInitializeScope
#APSAcquireDataFromScope
#APSMeasurementsFromScope
#APSTransferDataFromScope

proc APSSendCommandToScope {command} {
    global apsScopeSettings
    if {[catch {exec agilentcomm -ip $apsScopeSettings(ip) -c "$command"} results]} {
        puts "Error: $results"
    }
    update
}
proc APSSetValueOnScope {name value} {
    global apsScopeSettings
    if {[catch {exec agilentcomm -ip $apsScopeSettings(ip) -c "$name $value"} results]} {
        puts "Error: $results"
    }
    update
}
proc APSGetValueFromScope {name} {
    global apsScopeSettings
    if {[catch {exec agilentcomm -ip $apsScopeSettings(ip) -c "${name}?"} results]} {
        puts "Error: $results"
    } else {
        return "$results"
    }
    update
}
proc APSGetValuesFromScope {names} {
    global apsScopeSettings
    set options ""
    foreach name $names {
	lappend options ${name}?
    }
    set options [join $options \;]
    if {[catch {exec agilentcomm -ip $apsScopeSettings(ip) -c "$options"} results]} {
        puts "Error: $results"
    } else {
        return "$results"
    }
    update
}
proc APSGetValue2FromScope {name option} {
    global apsScopeSettings
    if {[catch {exec agilentcomm -ip $apsScopeSettings(ip) -c "${name}? $option"} results]} {
        puts "Error: $results"
    } else {
        return "$results"
    }
    update
}

proc APSResetScope {} {
    APSSendCommandToScope *RST
    APSSendCommandToScope *CLS
}

proc APSInitializeScope {} {
    # Requires the following array elements of apsScopeSettings to be set:
    #set apsScopeSettings(ip) 164.54.3.74
    #set apsScopeSettings(:TIMEBASE:REFERENCE) LEFT
    #set apsScopeSettings(:TIMEBASE:RANGE) 5E-04
    #set apsScopeSettings(:TIMEBASE:POSITION) 0
    #set apsScopeSettings(:CHANNEL1:RANGE) 1.6
    #set apsScopeSettings(:CHANNEL1:OFFSET) 0
    #set apsScopeSettings(:CHANNEL2:RANGE) 1.6
    #set apsScopeSettings(:CHANNEL2:OFFSET) 0
    #set apsScopeSettings(:CHANNEL3:RANGE) 1.6
    #set apsScopeSettings(:CHANNEL3:OFFSET) 0
    #set apsScopeSettings(:CHANNEL4:RANGE) 1.6
    #set apsScopeSettings(:CHANNEL4:OFFSET) 0
    #set apsScopeSettings(:TRIGGER:SWEEP) AUTO
    #set apsScopeSettings(:TRIGGER:EDGE:SOURCE) CHAN1
    #set apsScopeSettings(:TRIGGER:EDGE:SLOPE) POS
    #set apsScopeSettings(:TRIGGER:LEVEL) 0
    #set apsScopeSettings(pointsToAverage) 1
    #set apsScopeSettings(:ACQUIRE:POINTS) 500
    #set apsScopeSettings(:ACQUIRE:SRATE) 1e6
    #set apsScopeSettings(line1) On
    #set apsScopeSettings(line2) Off
    #set apsScopeSettings(line3) Off
    #set apsScopeSettings(line4) Off
    #set apsScopeSettings(measurement1label) FREQUENCY
    #set apsScopeSettings(measurement2label) VMAX
    #set apsScopeSettings(measurement3label) VMIN
    #set apsScopeSettings(measurement4label) PWIDTH
    #set apsScopeSettings(doExport) 0

    global apsScopeSettings

    APSSetValueOnScope :SYSTEM:HEADER OFF
    APSSetValueOnScope :TIMEBASE:REFERENCE $apsScopeSettings(:TIMEBASE:REFERENCE)
    if {[string length $apsScopeSettings(:TIMEBASE:RANGE)] > 0} {
        APSSetValueOnScope :TIMEBASE:RANGE $apsScopeSettings(:TIMEBASE:RANGE)
    }
    if {[string length $apsScopeSettings(:TIMEBASE:POSITION)] > 0} {
        APSSetValueOnScope :TIMEBASE:POSITION $apsScopeSettings(:TIMEBASE:POSITION)
    }
    for {set i 1} {$i <= 4} {incr i} {
        if {[string length $apsScopeSettings(:CHANNEL${i}:RANGE)] > 0} {
            APSSetValueOnScope :CHANNEL${i}:RANGE $apsScopeSettings(:CHANNEL${i}:RANGE)
        }
        if {[string length $apsScopeSettings(:CHANNEL${i}:OFFSET)] > 0} {
            APSSetValueOnScope :CHANNEL${i}:OFFSET $apsScopeSettings(:CHANNEL${i}:OFFSET)
        }
    }
    APSSetValueOnScope :TRIGGER:SWEEP $apsScopeSettings(:TRIGGER:SWEEP)
    APSSetValueOnScope :TRIGGER:EDGE:SOURCE $apsScopeSettings(:TRIGGER:EDGE:SOURCE)
    APSSetValueOnScope :TRIGGER:EDGE:SLOPE $apsScopeSettings(:TRIGGER:EDGE:SLOPE)
    APSSetValueOnScope :TRIGGER:LEVEL "$apsScopeSettings(:TRIGGER:EDGE:SOURCE),$apsScopeSettings(:TRIGGER:LEVEL)"
    APSSetValueOnScope :ACQUIRE:MODE RTIME
    if {([string length $apsScopeSettings(pointsToAverage)] > 0) && ($apsScopeSettings(pointsToAverage) > 1)} {
        APSSetValueOnScope :ACQUIRE:COUNT $apsScopeSettings(pointsToAverage)
        APSSetValueOnScope :ACQUIRE:AVERAGE 1
    } else {
        APSSetValueOnScope :ACQUIRE:AVERAGE 0
    }
    if {[string length $apsScopeSettings(:ACQUIRE:POINTS)] > 0} {
        APSSetValueOnScope :ACQUIRE:POINTS $apsScopeSettings(:ACQUIRE:POINTS)
    }
    APSSetValueOnScope :ACQuire:SRATe:AUTO OFF
    APSSetValueOnScope :ACQUIRE:SRATE $apsScopeSettings(:ACQUIRE:SRATE)
}

proc APSAcquireDataFromScope {} {
    # APSInitializeScope should be called before APSAcquireDataFromScope
    global apsScopeSettings
    set channels ""
    for {set i 1} {$i <= 4} {incr i} {
        if {$apsScopeSettings(line$i) == "On"} {
            lappend channels CHANNEL$i
        }
    }
    APSSetValueOnScope :DIGITIZE [join $channels ,]
}

#Results stored in:
# apsScopeSettings(measurement1)
# apsScopeSettings(measurement2)
# apsScopeSettings(measurement3)
# apsScopeSettings(measurement4)
proc APSMeasurementsFromScope {} {
    global apsScopeSettings
    set c 1
    for {set i 1} {$i <= 4} {incr i} {
        if {$apsScopeSettings(line$i) == "On"} {
            set c $i
            break
        }
    }
    set blanks 1
    for {set i 1} {$i <= 4} {incr i} {
        if {$apsScopeSettings(measurement${i}label) != ""} {
	    set blanks 0
	    break
	}
    }
    if {$blanks} {
	return
    }

    APSSetValueOnScope :MEASURE:SENDVALID OFF

    for {set i 1} {$i <= 4} {incr i} {
        switch $apsScopeSettings(measurement${i}label) {
            AREA -
            VAVERAGE {
                set apsScopeSettings(measurement${i}) [APSGetValue2FromScope :MEASURE:$apsScopeSettings(measurement${i}label) DISPLAY,CHANNEL$c]
            } 
            FALLTIME -
            FREQUENCY -
            NWIDTH -
            PWIDTH -
            RISETIME -
            TMAX -
            TMIN -
            VAMPLITUDE -
            VBASE -
            VLOWER -
            VMAX -
            VMIDDLE -
            VMIN -
            VPP -
            VTOP -
            VUPPER {
                set apsScopeSettings(measurement${i}) [APSGetValue2FromScope :MEASURE:$apsScopeSettings(measurement${i}label) CHANNEL$c]
            }
            VRMS-AC {
                set apsScopeSettings(measurement${i}) [APSGetValue2FromScope :MEASURE:VRMS DISPLAY,AC,CHANNEL$c]
            }
            VRMS-DC {
                set apsScopeSettings(measurement${i}) [APSGetValue2FromScope :MEASURE:VRMS DISPLAY,DC,CHANNEL$c]
            }
        }
    }
}

proc APSTransferDataFromScope {args} {
    # APSAcquireDataFromScope should be called before APSTransferDataFromScope
    # This procedure can optionally save the data to SDDS files.
    set waveformVariable1 ""
    set waveformVariable2 ""
    set waveformVariable3 ""
    set waveformVariable4 ""
    set timeVariable ""
    APSStrictParseArguments {waveformVariable1 waveformVariable2 waveformVariable3 waveformVariable4 timeVariable}
    
    global apsScopeSettings $waveformVariable1 $waveformVariable2 $waveformVariable3 $waveformVariable4

    APSSetValueOnScope :WAVEFORM:FORMAT ASCII

    set c 1
    for {set i 1} {$i <= 4} {incr i} {
        if {$apsScopeSettings(line$i) == "On"} {
            set c $i
            break
        }
    }

    for {set i 1} {$i <= 4} {incr i} {
        if {$apsScopeSettings(line$i) == "On"} {
            APSSetValueOnScope :WAVEFORM:SOURCE CHANNEL$i
            set tmp [APSGetValueFromScope :WAVEFORM:DATA]
            if {[lindex $tmp 0] == "xvi11_user:"} {
                puts stderr $tmp
                return
            }
            set waveformdata$i [split $tmp ,]
        } else {
            set waveformdata$i ""
        }
    }

    set results [split [APSGetValuesFromScope ":WAVEFORM:XINCREMENT :WAVEFORM:XORIGIN :TIMEBASE:RANGE"] "\;"]
    set xinc [lindex $results 0]
    set xorg [lindex $results 1]
    set range [lindex $results 2]

    set points [llength $waveformdata1]
    set t $xorg
    set vList1 ""
    set vList2 ""
    set vList3 ""
    set vList4 ""
    if {$apsScopeSettings(:TIMEBASE:REFERENCE) == "LEFT"} {
        foreach d $waveformdata1 d2 $waveformdata2 d3 $waveformdata3 d4 $waveformdata4 {
            append vList1 "$d "
            append vList2 "$d2 "
            append vList3 "$d3 "
            append vList4 "$d4 "
            set t [expr $t + $xinc]
            if {$t > $range} {
                break
            }
        }
        set points [llength [set vList$c]]
        set increment [expr {1.0 * ($range - 0) / $points}]
        set p $xorg
    } elseif {($apsScopeSettings(:TIMEBASE:REFERENCE) == "CENTER") || ($apsScopeSettings(:TIMEBASE:REFERENCE) == "CENT")} {
        set min [expr $range * -.5]
        set max [expr $range * .5]
        set t1set 0
        foreach d $waveformdata1 d2 $waveformdata2 d3 $waveformdata3 d4 $waveformdata4 {
            set t [expr $t + $xinc]
            if {($t >= $min) && ($t <= $max)} {
                append vList1 "$d "
                append vList2 "$d2 "
                append vList3 "$d3 "
                append vList4 "$d4 "
                if {!$t1set} {
                    set t1 $t
                    set t1set 1
                }
            } elseif {$t > $max} {
                break
            }
        }
        set points [llength [set vList$c]]
        set increment [expr {1.0 * ($range - 0) / $points}]
        set p $t1
    } elseif {($apsScopeSettings(:TIMEBASE:REFERENCE) == "RIGHT") || ($apsScopeSettings(:TIMEBASE:REFERENCE) == "RIGH")} {
        set min [expr $range * -1]
        set t1set 0
        foreach d $waveformdata1 d2 $waveformdata2 d3 $waveformdata3 d4 $waveformdata4 {
            set t [expr $t + $xinc]
            if {$t >= $min} {
                append vList1 "$d "
                append vList2 "$d2 "
                append vList3 "$d3 "
                append vList4 "$d4 "
                if {!$t1set} {
                    set t1 $t
                    set t1set 1
                }
            }
        }
        set points [llength [set vList$c]]
        set increment [expr {1.0 * ($range - 0) / $points}]
        set p $t1
    }
    if {([llength $timeVariable]) || $apsScopeSettings(doExport)} {
	for {set n 0} {$n < $points} {incr n} {
	    lappend xdata $p
	    set p [expr {$p + $increment}]
	}
	if {[llength $timeVariable]} {
	    global $timeVariable
	    set $timeVariable $xdata
	}
    }
    if {[llength $waveformVariable1]} {
        set $waveformVariable1 $vList1
    }
    if {[llength $waveformVariable2]} {
        set $waveformVariable2 $vList2
    }
    if {[llength $waveformVariable3]} {
        set $waveformVariable3 $vList3
    }
    if {[llength $waveformVariable4]} {
        set $waveformVariable4 $vList4
    }
    
    if {$apsScopeSettings(doExport)} {
        incr apsScopeSettings(doExport) -1
	global apsScopeData
	if {$apsScopeSettings(doExport) == 0} {
	    set apsScopeData(Layout.DataMode.Mode) binary
	    set apsScopeData(ColumnNames) "Time"
	    set apsScopeData(ColumnInfo.Time) "type SDDS_DOUBLE"
	    lappend apsScopeData(Column.Time) $xdata
	    for {set i 1} {$i <= 4} {incr i} {
		if {$apsScopeSettings(line$i) == "On"} {
		    lappend apsScopeData(ColumnNames) "Channel$i"
		    set apsScopeData(ColumnInfo.Channel${i}) "type SDDS_DOUBLE"
		    lappend apsScopeData(Column.Channel${i}) [set vList$i]
		}
	    }
	    if {[catch {sdds save $apsScopeSettings(exportFile) apsScopeData} results]} {
		puts stderr "Error: $results"
	    }
	    unset -nocomplain apsScopeData
	} else {
	    lappend apsScopeData(Column.Time) $xdata
	    for {set i 1} {$i <= 4} {incr i} {
		lappend apsScopeData(Column.Channel${i}) [set vList$i]
	    }
	}
    }
}
