Commit f6638d51 authored by Peter Jansweijer's avatar Peter Jansweijer

add Siglent_SDS1000X (hobby) oscilloscope support

parent 9bc9f91b
#!/usr/bin/python
"""
Siglent SDS1000X remote control
-------------------------------------------------------------------------------
Copyright (C) 2020 Peter Jansweijer, 'stolen' from Tjeerd Pinkert and freely
adjusted
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
-------------------------------------------------------------------------------
Usage:
Siglent_SDS1000X.py <IP#> [-c 1234] [-a<averages>] [-o <dir>] [-m <measurments>]
Siglent_SDS1000X.py -h | --help
IP IP number of the Oscilloscope
(for example: 192.168.178.31)
Options:
-h --help Show this screen.
-c 1234 channel number(s)
-a number of averages, default: 1
-o <dir> optional directory for output file storage, default: "data/"
-m <number> number of measurement output files
"""
import os
import sys
import time
import scipy
import struct
import datetime
import pdb
#TJP: installed from web python-vxi Alex
import vxi11
import matplotlib.pyplot as plt
plt.rcParams['lines.linewidth'] = 1
############################################################################
def get_sweeps(scope, chan_str):
sweep_str = str(scope.ask(chan_str + ':INSPECT? SWEEPS_PER_ACQ')).split(':')[2]
# returns something like:
# u'C1:INSP "SWEEPS_PER_ACQ : 30 "'
sweep = int(sweep_str.strip('"').strip()) # strip the last " and spaces before cast to int
return sweep
############################################################################
def replace_comma(str):
"""
Replace any comma or space in str by an underscore
"""
newstr = ""
for i in str:
if (i== "," or i== " "):
newstr = newstr + "_"
else:
newstr = newstr + i
return newstr
############################################################################
def get_waveforms(scope, channels=[1,2,3,4], num_avg=1, output_dir="data"):
"""
Measure and save Siglent SDS1000X waveforms
scope -- instance of python-vxi connected to the oscilloscope
channels -- channels that are going to be measured for example '1,2'
num_avg -- the number of averages taken by the oscilloscope
output_dir -- name of the directory where measured waveform files will be stored
the file output format is as described below:
----------------------------------
#WaveformData:Siglent SDS1000X
#version:0.2
#type:RAW
#channel:1,2,..
#time:21 Jan 2016 11:03:38
#byteorder:LSBFIRST
#preamble:
<channel 1 preamble>
#preamble:
<channel 2 preamble>
#waveform_data:
<channel 1 data>
#waveform_data:
<channel 2 data>
----------------------------------
"preamble" data is a comma separated string
"waveform_data" is binary data
"""
#scope.write(":SYSTem:HEADer OFF")
#scope.write(":ACQuire:MODE RTIME")
#scope.write(":ACQuire:COMPlete 100")
#scope.write(":WAVeform:BYTeorder LSBFirst")
#scope.write(":WAVeform:FORMat WORD")
# Set COMMAND_HEADER response mode. Message decode depends on the returned format.
scope.write("CHDR OFF")
# WAVEFORM_SETUP:
# SP = 1 sends all data points.
# NP = 0 sends all data points.
# FP = 0 corresponds to the first data point.
scope.write("WAVEFORM_SETUP SP,1,NP,0,FP,0")
# Changing ACQUIRE_WAY is only acceptes when ACQUIRE is in ARM mode
'''
scope.write("ARM")
if num_avg > 1:
scope.write("ACQUIRE_WAY AVERAGE")
scope.write("AVERAGE_ACQUIRE "+str(num_avg))
else:
scope.write("ACQUIRE_WAY SAMPLING")
'''
scope.write("STOP") #Stop any running acquisition
#scope.write(":ACQuire:POINts "+str(record_len))
for chan in channels:
scope.write("C"+str(chan)+":TRACE ON")
# Arm single trigger
scope.write("TRIG_MODE SINGLE")
scope.write("ARM")
# Wait for trigger (INR bit 0: "A new signal has been acquired")
# and clear triggered bit in INternal state change Register (INR).
while (int(scope.ask("INR?"))&1 != 1):
pass
scope.ask("INR?")
# add trailing slash if not present
output_dir = os.path.join(output_dir,'')
if os.path.exists(output_dir) != True:
os.mkdir(output_dir)
print("Output directory does not exist => created: "+output_dir)
file_header = "#WaveformData:Siglent SDS1000X\n"
file_header += "#version:0.2\n"
file_header += "#type:RAW\n"
file_header += "#channel:"
for chan in channels:
file_header += str(chan)+","
file_header += "\n"
timestamp = datetime.datetime.now() #micro seconds timing
filename = output_dir+timestamp.strftime("%y%m%dT%H%M%S_%f")+"_scope_siglent_sds1000x_bin"
file_header += "#date:"+timestamp.strftime("%d %b %Y")+"\n"
file_header += "#time:"+timestamp.strftime("%H:%M:%S")+"\n"
print("save waveform into file:",filename)
file_header += "#byteorder:LSBFIRST\n"
channel_preamble = []
channel_data = []
_types={
"AVERAGE":2,
"SAMPLING":6,
"PEAK_DETECT":10,
"HIGH_RES":11,
}
_couplings={
"A1M":0,
"D1M":1,
"D50":2,
"GND":10,
"A50":11,
}
_units={
"UNKNOWN" :0,
"V" :1,
"SECOND" :2,
"CONSTANT":3,
"A" :4,
"DECIBEL" :5,
}
# Bandwidth Limit is returned for all channels (irrespective if enabled)
bwl = scope.ask("BANDWIDTH_LIMIT?").split(",")
bwl_ch = []
for chan in range(4):
if bwl[chan*2+1] == "ON":
bwl_ch.append(20E6) # 20 MHz
else:
bwl_ch.append(100E6) # 100 MHz
# Detect which python version is used:
python_version = sys.version_info[0]
# Python 3.x uses type <bytes> instead of <str> (Pyhton 2.x)
# scope.read() returns <str>
# scope.read_raw() returns <bytes>
# returned data = <bytes>
if python_version == 3:
for chan in channels:
scope.write("C"+str(chan)+":WAVEFORM? DAT2")
rawdata = scope.read_raw()
# Read first 16 bytes and find the "#" marker
mrk_idx = rawdata[:16].decode("utf-8").index('#')
# First byte after "#" is the amount of bytes used ofr the waveform size
size_byt = int(rawdata[mrk_idx+1:mrk_idx+2].decode("utf-8"))
size_wav = int(rawdata[mrk_idx+2:mrk_idx+2+size_byt].decode("utf-8"))
# Siglent SDS1000X does not read a dedicated preamble; we have to construct it ourselves
# Construct channel preamble
ch_preamble_bytes = (b"#preamble:\n")
# format = 1 = BYTE
ch_preamble_bytes += (b"1,")
# type = SAMPLING,PEAK_DETECT,AVERAGE,HIGH_RES
acquire_way = scope.ask("ACQUIRE_WAY?")
ch_preamble_bytes += (str(_types[(acquire_way.split(",")[0])]).encode()+b",")
# points
ch_preamble_bytes += (str(int(rawdata[mrk_idx+2:mrk_idx+2+size_byt])).encode()+b",")
# count (when type is AVERAGE)
if len(acquire_way.split(",")) > 1:
ch_preamble_bytes += (str(acquire_way.split(",")[1]).encode()+b",") # count = average
else:
ch_preamble_bytes += (b"1,") # count = 1
# x_increment (= 1/Sample Rate)
ch_preamble_bytes += (str(1/float(scope.ask("SARA?"))).encode()+b",")
# x_origin (Note: SDS1000X display has 14 horizontal divisions, hence origin = tdiv)
ch_preamble_bytes += ((str(float(scope.ask("TIME_DIV?"))*(-14/2))).encode()+b",")
# x_reference
ch_preamble_bytes += (scope.ask("TRIG_DELAY?").encode()+b",")
# y_increment, y_origin: see Digital Oscilloscopes Programming Guide PG01-E02C, Page 256:
# voltage value (V) = code value *(vdiv /25) - voffset.
# y_increment
ch_preamble_bytes += ((str(float(scope.ask("C"+str(chan)+":VDIV?"))/(25))).encode()+b",")
# y_origin
ch_preamble_bytes += ((str(float(scope.ask("C"+str(chan)+":OFST?"))*(1))).encode()+b",")
# y_reference
ch_preamble_bytes += (b"0.0,")
# coupling _coupling
ch_preamble_bytes += (str(_couplings[(scope.ask("C"+str(chan)+":COUPLING?").split(",")[0])]).encode()+b",")
# x_display_range
ch_preamble_bytes += (b"0.0,")
# x_display_origin
ch_preamble_bytes += (b"0.0,")
# y_display_range
ch_preamble_bytes += (b"0.0,")
# y_display_origin
ch_preamble_bytes += (b"0.0,")
# date
ch_preamble_bytes +=(b"\""+timestamp.strftime("%d %b %Y").encode()+b"\",")
# time
ch_preamble_bytes +=(b"\""+timestamp.strftime("%H:%M:%S").encode()+b"\",")
# frame_model
id_str=scope.ask("*IDN?")
ch_preamble_bytes +=(b"\""+replace_comma(id_str).encode()+b"\",")
# acquisition_mode (= "RTIME")
ch_preamble_bytes += (b"0,")
# completion
ch_preamble_bytes += (b"100,")
# x_units _units (2:"SECOND")
ch_preamble_bytes += (b"2,")
# y_units _units
ch_preamble_bytes += (str(_units[(scope.ask("C"+str(chan)+":UNIT?"))]).encode()+b",")
# bandwidth_maximum
ch_preamble_bytes += (str(bwl_ch[int(chan)-1]).encode()+b",")
# bandwidth_minimum
ch_preamble_bytes += (b"0.0\n")
ch_preamble_bytes += (b"#preamble_end:\n")
channel_preamble.append(ch_preamble_bytes)
ch_data_bytes = (b"#waveform:\n")
ch_data_bytes += rawdata[mrk_idx+2+size_byt:mrk_idx+2+size_byt+size_wav]
channel_data.append(ch_data_bytes)
# Raw data should end with \n\n
if (rawdata[mrk_idx+2+size_byt+size_wav:] != b"\n\n"):
print("### Incomplete waveform for channel "+str(chan)+"\n")
sys.exit()
# Python 3 write bytes
file=open(filename,"wb")
# Write out file_header, followed by all preambles,
# followed by all descriptors, followed by all channel_data
file.write(file_header.encode()) # encode() <str> into <bytes>
data = file_header.encode()
for i in channel_preamble:
data += i
file.write(i)
for i in channel_data:
data += i
file.write(i)
file.close()
# Python 2.x uses type <str>
# scope.read() returns <unicode>
# scope.read_raw() returns <str>
# returned data = <list> with items type <str>
elif python_version == 2:
print("### Python 2.x not supported\n")
sys.exit()
return data, filename
############################################################################
def preamble_string_to_dict(preamble_string):
"""
Create DSO preamble dict from raw preamble string
preamble_string -- raw preamble string as obtained from oscilloscope query
returns: dict with parsed preamble string
"""
_formats={
0:"ASCII",
1:"BYTE",
2:"WORD",
3:"LONG",
}
_types={
1:"RAW",
2:"AVERAGE",
3:"VHISTOGRAM",
4:"HHISTOGRAM",
6:"INTERPOLATE",
10:"PDETECT",
}
_couplings={
0:"AC",
1:"DC",
2:"DCFIFTY",
3:"LFREJECT",
10:"GND",
11:"ACFIFTY",
}
_acquisition_modes={
0:"RTIME",
1:"ETIME",
3:"PDETECT",
}
_units={
0:"UNKNOWN",
1:"VOLT",
2:"SECOND",
3:"CONSTANT",
4:"AMP",
5:"DECIBEL"
}
_preamble=None
_preamble_raw=preamble_string
preamble_fields=preamble_string.split(",")
_preamble={}
_preamble["format"]=_formats[int(preamble_fields.pop(0))]
_preamble["type"]=_types[int(preamble_fields.pop(0))]
_preamble["points"]=int(preamble_fields.pop(0))
_preamble["count"]=int(preamble_fields.pop(0))
_preamble["x_increment"]=float(preamble_fields.pop(0))
_preamble["x_origin"]=float(preamble_fields.pop(0))
_preamble["x_reference"]=float(preamble_fields.pop(0))
_preamble["y_increment"]=float(preamble_fields.pop(0))
_preamble["y_origin"]=float(preamble_fields.pop(0))
_preamble["y_reference"]=float(preamble_fields.pop(0))
_preamble["coupling"]=_couplings[int(preamble_fields.pop(0))]
_preamble["x_display_range"]=float(preamble_fields.pop(0))
_preamble["x_display_origin"]=float(preamble_fields.pop(0))
_preamble["y_display_range"]=float(preamble_fields.pop(0))
_preamble["y_display_origin"]=float(preamble_fields.pop(0))
_preamble["date"]=str(preamble_fields.pop(0).strip("\""))
_preamble["time"]=str(preamble_fields.pop(0).strip("\""))
_preamble["frame_model_#"]=str(preamble_fields.pop(0).strip("\""))
_preamble["acquisition_mode"]=_acquisition_modes[int(preamble_fields.pop(0))]
_preamble["completion"]=int(preamble_fields.pop(0))
_preamble["x_units"]=_units[int(preamble_fields.pop(0))]
_preamble["y_units"]=_units[int(preamble_fields.pop(0))]
_preamble["bandwidth_maximum"]=float(preamble_fields.pop(0))
_preamble["bandwidth_minimum"]=float(preamble_fields.pop(0))
if not len(preamble_fields)==0:
_preamble=None
raise Exception("preamble_string_to_dict: Excess information in preamble")
return _preamble
############################################################################
def raw_to_scipy_array (waveform_raw, byte_order, preamble):
"""
Interpreting the waveform block of raw data according to the preamble data.
The waveform binary data is transformed to an x_data, y_data array in scale units
given in preamble.
preamble -- preamble dictionary.
waveform_raw -- raw block datastream excluding length header
byteorder -- byteorder (MSBFIRST, LSBFIRST) of the BINARY, BYTE and WORD formats.
returns: <type 'numpy.ndarray'> array([[x1,x2,x3,...,xn],[y1,y2,y3,...,yn]])
"""
#initialise class constants
_byteorder_conversion={
"MSBFIRST":{
"BYTE":[">","b"],
"WORD":[">","h"],
"BINARY":[">","i"],
},
"LSBFIRST":{
"BYTE":["<","b"],
"WORD":["<","h"],
"BINARY":["<","i"],
},
}
format_string = _byteorder_conversion[byte_order][preamble['format']][0]
format_string += str(preamble["points"])
format_string += _byteorder_conversion[byte_order][preamble['format']][1]
#Convert to unit values.
y_data=scipy.array(struct.unpack(format_string, waveform_raw),
dtype=scipy.float64) * preamble["y_increment"] - preamble["y_origin"]
#y_data=scipy.array(struct.unpack(format_string, waveform_raw),
# dtype=scipy.float64) /25 * float(2.0)-float(2.0)
x_data=scipy.arange(preamble["points"]) * preamble["x_increment"] + preamble["x_origin"]
#pdb.set_trace()
#create array with x,y axis values.
waveform_data = scipy.array([x_data,y_data])
return waveform_data
############################################################################
def check_waveforms(waveform_data):
"""
This function checks for the consistency of the captured waveform.
waveform_data -- <type 'dict'> waveform_data (as returned by function "file_to_waveform")
returns: number of points (of the first waveform found)
"""
first = True
for ch in waveform_data.keys():
if first== True:
first = False
channel = ch
points = waveform_data[ch]["preamble"]["points"]
print("Info: Record Length is", points,"samples")
count = waveform_data[ch]["preamble"]["count"]
x_inc = waveform_data[ch]["preamble"]["x_increment"]
print("Info: Sample Period is", x_inc)
timebase = waveform_data[ch]["preamble"]["x_display_range"]
else:
if waveform_data[ch]["preamble"]["points"] != points:
print("### WARNING! Different array length!")
print(" Channel:",channel,points)
print(" Channel:",ch,waveform_data[ch]["preamble"]["points"])
if waveform_data[ch]["preamble"]["count"] != count:
print("### WARNING! Different sweep counts per acquisition!")
print(" Channel:",channel,count)
print(" Channel:",ch,waveform_data[ch]["preamble"]["count"])
if waveform_data[ch]["preamble"]["x_increment"] != x_inc:
print("### WARNING! Different time base sample interval!")
print(" Channel:",channel,x_inc)
print(" Channel:",ch,waveform_data[ch]["preamble"]["x_increment"])
print(" You may want to check the 'Interpolation' setting in the 'pre-Processing' tab of the oscilloscopes channel setup")
if waveform_data[ch]["preamble"]["x_display_range"] != timebase:
print("### WARNING! Different time base!")
print(" Channel:",channel,timebase)
print(" Channel:",ch,waveform_data[ch]["preamble"]["x_display_range"])
return(points)
############################################################################
def file_to_waveform(filename):
"""
Retrieve the waveforms from a bytestring which is normally read from file.
filename -- source file from which to retrieve data.
returns: <type 'dict'> waveform_data with keys to a <type 'dict'> for each channel number
each channel number is again a <type 'dict'> with keys:
'byte_order' : <type 'str'>
'preamble' : <type 'dict'>
'waveform' : <type 'numpy.ndarray'>
each preamble is a <type 'dict'> with keys:
'acquisition_mode' : <type 'str'>
'bandwidth_maximum' : <type 'float'>
'bandwidth_minimum' : <type 'float'>
'completion' : <type 'int'>
'count' : <type 'int'>
'coupling' : <type 'str'>
'date' : <type 'str'>
'format' : <type 'str'>
'frame_model_#' : <type 'str'>
'points' : <type 'int'>
'time' : <type 'str'>
'type' : <type 'str'>
'x_display_origin' : <type 'float'>
'x_display_range' : <type 'float'>
'x_increment' : <type 'float'>
'x_origin' : <type 'float'>
'x_reference' : <type 'float'>
'x_units' : <type 'str'>
'y_display_origin' : <type 'float'>
'y_display_range' : <type 'float'>
'y_increment' : <type 'float'>
'y_origin' : <type 'float'>
'y_reference' : <type 'float'>
'y_units' : <type 'str'>
an example waveform_data dict looks like this:
{1: { 'byte_order': 'LSBFIRST',
'preamble': {'y_display_origin': 30707.0,
'bandwidth_minimum': 0.0,
'y_units': 'Unit Name = V',
:
'x_units': 'Unit Name = S',
'time': '13:50:02'},
'waveform': '\x8e\xef\x8b\.....'
},
2: {'byte_order': 'LSBFIRST',
'preamble': {'y_display_origin': 30707.0,
'bandwidth_minimum': 0.0,
'y_units': 'Unit Name = V',
:
'x_units': 'Unit Name = S',
'time': '13:50:02'},
'waveform': 'C\x90X\x90]\x90N\x90@\x90G\x90a\....'}
}
"""
# Open data file for "read", in "binary" format
# Then readline() returns type <bytes> that can be
# decoded using "utf-8" into string
data_file = open(filename,"rb")
# create an empty wavefrom_data dictionairy
waveform_data = {}
line = data_file.readline().decode("utf-8")
if "#WaveformData:Siglent SDS1000X" not in line:
#print("Exception: file_to_waveform: Not a Siglent SDS1000X Waveform Data file.")
Exception("file_to_waveform: Not a Siglent SDS1000X Waveform Data file.")
data_file.close()
return
line = data_file.readline().decode("utf-8")
version = line.strip().split(":")
if not(("#version" in version[0]) and ("0.2" in version[1])):
# if version[0]=="#version" and version[1]=="0.1":
#print("Exception: file_to_waveform: Siglent SDS1000X wrong version Waveform Data file.")
Exception("file_to_waveform: Siglent SDS1000X wrong version Waveform Data file.")
data_file.close()
return
channels=[]
preamble_idx = 0
waveform_idx = 0
while 1:
# Note that readline() scans the file in "binary" mode.
# Some readline actions should be kept binary (the raw
# waveform data) and some should be decoded using "utf-8"
line=data_file.readline()
if len(line)==0:
break
if "#channel:" in str(line):
chan_str=line.decode("utf-8").split(":")[1].strip()
channels=chan_str.split(',')
if "#date:" in str(line):
date_in_file=line.decode("utf-8").split(":")[1].strip()
if "#time:" in str(line):
time_lst=line.decode("utf-8").split(":")
time_in_file=time_lst[1].strip()+":"+time_lst[2].strip()+":"+time_lst[3].strip()
if "#byteorder:" in str(line):
byte_order = line.decode("utf-8").split(":")[1].strip()
# Preambles come first in the file.
# Create a dictionairy channel entry for each
if "#preamble:" in str(line):
preamble_chan = int(channels[preamble_idx])
waveform_data[preamble_chan] = {}
waveform_data[preamble_chan]["byte_order"] = byte_order
preamble_string=data_file.readline().decode("utf-8")
waveform_data[preamble_chan]["preamble"] = preamble_string_to_dict(preamble_string)
preamble_idx += 1
# Next comes wavefrom data.
# Create a dictionairy channel entry for each
if "#waveform:" in str(line):
waveform_chan = int(channels[waveform_idx])
'''
wf_header = data_file.read(1) # read the waveform_header "#"
print(wf_header)
wf_no_of_digits = int(data_file.read(1)) # read the number of digits "5"
print(wf_no_of_digits)
wf_samples = int(data_file.read(wf_no_of_digits))
print(wf_samples)
'''
wf_samples = waveform_data[waveform_idx+1]["preamble"]["points"]
#print("chan_no",waveform_chan)
#print(wf_header)
#print(wf_no_of_digits)
#print(wf_samples)
# Add the waveform to the <type 'dict'> waveform_data but first convert it the RAW
# waveform data in the file to scipy array with x,y axis values
waveform_data[waveform_chan]["waveform"] = raw_to_scipy_array (data_file.read(wf_samples), byte_order, waveform_data[waveform_chan]['preamble'])
#line = data_file.readline() # waveform_data ends with a "\n". Read it!
waveform_idx += 1
data_file.close()
return waveform_data
############################################################################
def osc_init(scope, init):
"""
Initialize the Siglent SDS1000X DSO
scope -- instance of python-vxi connected to the oscilloscope
init -- <dict> for example:
init = { \
'channel' : [ 1 , 0 , 1 , 0 ], \
'offset' : [ 0.0, 0.0, 0.0 , 0.0 ], \
'volt_div' : [ 0.5, 1.0, 0.125, 1.0 ], \
#'50ohm' : [ 1 , 0 , 1 , 0 ], \ No 50 Ohm for Siglent SDS1000X
'sinxx' : [ 0 , 0 , 0 , 0 ], \
'trig' : [ 1 ], \
'trig_level' : [ 0.14 ], \
'timebase' : [ 50e-9 ], \
'refclk' : [ 'ext' ], \
"""
#scope = vxi11.Instrument("192.168.178.31")
print(scope.ask("*IDN?"))
# Returns 'Siglent Technologies,SDS1104X-E,SDSMMEBQ4R5537,8.1.6.1.35R2'
# Initialize the oscilloscope trigger
scope.write("TRIG_MODE NORM")
#print("TRIG_MODE NORM")
# select trig channel
trig = str(init['trig'][0])
# trigger level depends on probe attenuation of the selected cannel
probe_attn = float((scope.ask("C"+trig+":ATTENUATION?")).split(' ')[1])
triglevel=str(float(init['trig_level'][0])/probe_attn)
scope.write("C"+trig+":TRIG_LEVEL "+ triglevel)
#print("C"+trig+":TRIG_LEVEL "+ triglevel)
"""
# For Keysight sinxx interpolation is a "Horizontal" setting that
# applies to all enabled channels! Default = False
sinxx = False
"""
for ch in range(4):
# Set channel ON/OFF
if init['channel'][ch] == 1:
scope.write("C"+str(ch+1)+":TRACE ON")
#print("C"+str(ch+1)+":TRACE ON")
# Set channel Offset
scope.write("C"+str(ch+1)+":OFFSET "+str(float(init['offset'][ch])))
#print("C"+str(ch+1)+":OFFSET "+str(float(init['offset'][ch])))
# Set channel Volt/Div
scope.write("C"+str(ch+1)+":VOLT_DIV "+str(float(init['volt_div'][ch])))
#print("C"+str(ch+1)+":VOLT_DIV "+str(float(init['volt_div'][ch])))
# Set channel Coupling
#'50ohm' : [ 1 , 0 , 1 , 0 ], \
# Note: No 50 Ohm for Siglent SDS1104X-E! Both D50 and D1M end up in D1M
if init['50ohm'][ch] == 1:
scope.write("C"+str(ch+1)+":COUPLING D50")
#print("C"+str(ch+1)+":COUPLING D50")
else:
scope.write("C"+str(ch+1)+":COUPLING D1M")
#print("C"+str(ch+1)+":COUPLING D1M")
else:
scope.write("C"+str(ch+1)+":TRACE OFF")
#print("C"+str(ch+1)+":TRACE OFF")
# Set Interpolation for all used channels to "16 point Sin(x)/x"
#'sinxx' : [ 1 , 0 , 1 , 0 ], \
# Check if one of the channels is set to sinxx then enable sinxx for all
if init['sinxx'][ch] == 1:
sinxx = True
if sinxx:
scope.write("SINXX_SAMPLE ON")
else:
scope.write("SINXX_SAMPLE OFF")
# Trigger in the centre of the screen and set timebase
scope.write("TRIG_DELAY 0")
#print("TRIG_DELAY 0")
scope.write("TIME_DIV "+str(float(init['timebase'][0])))
#print("TIME_DIV "+str(float(init['timebase'][0])))
# Set internal/external 10 MHz timebase
# Note: no external 10MHz timebase facility on SDS1000X!
if init['refclk'][0] == 'ext':
print("### no external 10MHz timebase facility on SDS1000X!")
return
############################################################################
##
## If run from commandline, we can test the library
##
"""
Siglent SDS1000X remote control
Usage:
Siglent_SDS1000X.py <IP#> [-c 1234] [-a<averages>] [-o <dir>] [-m <number>]
Siglent_SDS1000X.py -h | --help
IP IP number of the Oscilloscope
(for example: 192.168.178.31)
Options:
-h --help Show this screen.
-c 1234 channel number(s)
-a number of averages, default: 1
-o <dir> optional directory for output file storage, default: "data/"
-m <number> number of measurement output files
"""
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("scope", help="IP-Number of the oscilloscope")
parser.add_argument("-channels", default=[1,2,3,4], type=list)
parser.add_argument("-num_avg", default=1, type=int)
parser.add_argument("-output_dir", default="data")
parser.add_argument("-measurements", default=1, type=int)
args = parser.parse_args()
print ("Use oscilloscope IP address: " + str(args.scope))
scope = vxi11.Instrument(args.scope)
#scope = vxi11.Instrument("192.168.178.31")
print(scope.ask("*IDN?"))
# Returns 'Siglent Technologies,SDS1104X-E,SDSMMEBQ4R5537,8.1.6.1.35R2'
print ("channels:",args.channels, "num_avg", args.num_avg)
'''
init = { \
'channel' : [ 1 , 0 , 0 , 0 ], \
'offset' : [ 0.0 , 0.0, 0.0 , 0.0 ], \
'volt_div' : [ 1 , 1 , 1 , 1 ], \
'50ohm' : [ 0 , 0 , 0 , 0 ], \
'trig' : [ 1 ], \
'trig_level' : [ 1.4 ], \
'timebase' : [ 100e-6 ], \
'refclk' : [ 'ext' ], \
}
osc_init(scope, init)
'''
# Trigger in the centre of the screen; important for maximum estimations
# forwarded to function average_edge_to_sfd
#scope.write("TRIG_DELAY 0")
for i in range(0, args.measurements):
d,filename = get_waveforms(scope, channels=args.channels, num_avg=args.num_avg, output_dir=args.output_dir)
#wf_data = file_to_waveform("data/200220T151435_375210_scope_keysight_dso_s_254A_bin")
#sys.exit()
wf_data = file_to_waveform(filename)
check_waveforms(wf_data)
waveforms = plt.figure("channel:"+str(args.channels))
ax = waveforms.add_subplot(111)
ax.set_xlabel('Time')
ax.set_ylabel('Volt')
for chan in wf_data.keys():
x = wf_data[chan]["waveform"][0]
y = wf_data[chan]["waveform"][1]
ax.plot(x,y)
plt.show()
sys.exit()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment