191 lines
5.2 KiB
Go
191 lines
5.2 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"flag"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var chancount = flag.Int("channels", 4, "number of channels to collect")
|
|
var host = flag.String("host", "rigol", "hostname or IP address")
|
|
var port = flag.Int("port", 5555, "tcp port to use")
|
|
var interval = flag.Int("interval", 1, "number of seconds between readings")
|
|
var count = flag.Int("count", -1, "number of measurements to take. -1 = no limit")
|
|
var fVavg = flag.Bool("vavg", true, "include Vavg")
|
|
var fVmin = flag.Bool("vmin", false, "include Vmin")
|
|
var fVmax = flag.Bool("vmax", false, "include Vmax")
|
|
var fVpp = flag.Bool("vpp", false, "include Vpp")
|
|
var fVrms = flag.Bool("vrms", false, "include Vrms")
|
|
var fFreq = flag.Bool("freq", false, "include frequency")
|
|
var fScreen = flag.Bool("screen", false, "collect screenshots in PNG format")
|
|
var fClear = flag.Bool("clear", false, "clear stats after collection")
|
|
|
|
func main() {
|
|
|
|
flag.Parse()
|
|
|
|
connstr := fmt.Sprintf("%s:%d", *host, *port)
|
|
conn, err := net.Dial("tcp", connstr)
|
|
if err != nil {
|
|
fmt.Printf("Unable to connect to %s\n", connstr)
|
|
fmt.Println(err)
|
|
os.Exit(2)
|
|
}
|
|
|
|
toRun, header := buildQuery()
|
|
|
|
// Output CSV header
|
|
fmt.Printf("%s, %s, %s\n", "timestamp", header, "querytime(ms)")
|
|
|
|
for ; *count != 0; *count-- {
|
|
|
|
tstart := time.Now()
|
|
|
|
// Collect and write screenshot if the screen flag is set. Do this first because queries can take a little while.
|
|
if *fScreen {
|
|
img := getScreenshot(conn)
|
|
if img != nil {
|
|
writeScreenshot(img)
|
|
}
|
|
}
|
|
|
|
result := queryScope(conn, toRun)
|
|
// Return from scope is semicolon separated, so just switch for commas.
|
|
result = strings.Replace(result, ";", ", ", -1)
|
|
|
|
// Clear history
|
|
if *fClear {
|
|
fmt.Fprintf(conn, ":CLE;MEAS:STAT:RES\n")
|
|
}
|
|
|
|
tdone := time.Now()
|
|
taken := tdone.Sub(tstart)
|
|
takenms := int64(taken / time.Millisecond)
|
|
|
|
fmt.Printf("%s, %s, %d\n", tdone.Format(time.RFC3339), result, takenms)
|
|
|
|
// Shorten the interval to allow for the time taken for the previous run.
|
|
// but if the time taken is longer than the interval, no need to sleep at all.
|
|
intervalms := int64(*interval * 1000)
|
|
if takenms < intervalms {
|
|
sleeptime := time.Duration(intervalms-takenms) * time.Millisecond
|
|
time.Sleep(sleeptime)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
func queryScope(conn net.Conn, query string) string {
|
|
fmt.Fprintf(conn, fmt.Sprintf("%s\n", query))
|
|
c1, _ := bufio.NewReader(conn).ReadString('\n')
|
|
return strings.TrimSuffix(c1, "\n")
|
|
}
|
|
|
|
func buildQuery() (string, string) {
|
|
var command []string
|
|
var header []string
|
|
for i := 0; i < *chancount; i++ {
|
|
|
|
// Stacking queries together seems to knock about 10% off the query time
|
|
// compared to requesting one measurement at a time.
|
|
// 2 channels, 4 measurements resulted in 3.9 vs 3.5 seconds on my DS1054Z.
|
|
|
|
// Tell the scope which channel the following commands are for
|
|
//command = append(command, fmt.Sprintf(":MEAS:SOUR CHAN%d", i+1))
|
|
|
|
// Query measurement unit.
|
|
command = append(command, fmt.Sprintf(":CHAN%d:UNIT?", i+1))
|
|
header = append(header, fmt.Sprintf("CH%d Unit", i+1))
|
|
|
|
if *fVavg {
|
|
command = append(command, fmt.Sprintf(":MEAS:ITEM? VAVG,CHAN%d", i+1))
|
|
header = append(header, fmt.Sprintf("CH%d Vavg", i+1))
|
|
}
|
|
if *fVmin {
|
|
command = append(command, fmt.Sprintf(":MEAS:ITEM? VMIN,CHAN%d", i+1))
|
|
header = append(header, fmt.Sprintf("CH%d Vmin", i+1))
|
|
}
|
|
if *fVmax {
|
|
command = append(command, fmt.Sprintf(":MEAS:ITEM? VMAX,CHAN%d", i+1))
|
|
header = append(header, fmt.Sprintf("CH%d Vmax", i+1))
|
|
}
|
|
if *fVpp {
|
|
command = append(command, fmt.Sprintf(":MEAS:ITEM? VPP,CHAN%d", i+1))
|
|
header = append(header, fmt.Sprintf("CH%d Vpp", i+1))
|
|
}
|
|
if *fVrms {
|
|
command = append(command, fmt.Sprintf(":MEAS:ITEM? VRMS,CHAN%d", i+1))
|
|
header = append(header, fmt.Sprintf("CH%d Vrms", i+1))
|
|
}
|
|
if *fFreq {
|
|
command = append(command, fmt.Sprintf(":MEAS:ITEM? FREQ,CHAN%d", i+1))
|
|
header = append(header, fmt.Sprintf("CH%d freq", i+1))
|
|
}
|
|
}
|
|
|
|
return strings.Join(command, ";"), strings.Join(header, ", ")
|
|
}
|
|
|
|
func check(e error) {
|
|
if e != nil {
|
|
panic(e)
|
|
}
|
|
}
|
|
|
|
func getScreenshot(conn net.Conn) []byte {
|
|
// Args are Colour, Invert, Format.
|
|
fmt.Fprintf(conn, ":DISP:DATA? ON,FALSE,PNG\n")
|
|
reader := bufio.NewReader(conn)
|
|
header1, _ := reader.ReadByte()
|
|
|
|
// should be a #, but let's not panic if it's not
|
|
if header1 != 35 {
|
|
return nil
|
|
}
|
|
|
|
// Next character shows us the length of the length of the datastream!
|
|
header2, _ := reader.ReadByte()
|
|
buffsize := int(header2 - 48)
|
|
|
|
// Quick sanity check, skip if not valid.
|
|
if buffsize < 1 || buffsize > 9 {
|
|
return nil
|
|
}
|
|
|
|
// Read the length data from the buffer.
|
|
header3 := make([]byte, buffsize)
|
|
for i := 0; i < buffsize; i++ {
|
|
t, err := reader.ReadByte()
|
|
check(err)
|
|
header3[i] = t
|
|
}
|
|
|
|
// This is now the image size
|
|
buffsize, _ = strconv.Atoi(string(header3))
|
|
|
|
imgdata := make([]byte, buffsize)
|
|
for i := 0; i < buffsize; i++ {
|
|
t, err := reader.ReadByte()
|
|
check(err)
|
|
imgdata[i] = t
|
|
}
|
|
|
|
return imgdata
|
|
}
|
|
|
|
func writeScreenshot(img []byte) {
|
|
// Filename safe date format
|
|
tstamp := time.Now().Format("2006-01-02.150405")
|
|
filename := fmt.Sprintf("%s.%s.png", "screenshot", tstamp)
|
|
|
|
f, err := os.Create(filename)
|
|
check(err)
|
|
defer f.Close()
|
|
_, err = f.Write(img)
|
|
check(err)
|
|
}
|