Remote Oscilloscope Operation with Python and VISA

Example Python code is provided to perform basic remote operations with a Rohde and Schwarz RTO1044 Oscilloscope including waveform capture, display, and FFT.

advertisement

This article provides a basic foundational script (below) to interact with an oscilloscope over Ethernet using Python, VISA, and PyVISA. The system configuration is provided in the list below:

  • Rohde & Schwarz RTO1044, 4 GHz scope
  • Bench-top frequency generator (any signal source will do)
  • Windows 10 PC with Gigabit Ethernet interfacing to RTO1044 via a GigE switch and installed with:

The figure below shows a screen shot of our RTO1044 capturing a sawtooth waveform on channel 2. The following parameters can be observed:

  • Measured frequency: 21.439 kHz
  • Record Length: 1 KSa (1,000 samples)
  • Sampling Frequency: 1 MSa/s ( 10**6 samples / sec)
Figure 1. RTO1044 Screenshot
RTO1044 4GHz Scope Screenshot

Although Rohde and Schwarz provides their own version of VISA, we use TEK's VISA with good results for both TEK and non TEK equipment. The figure below shows the TEK resource / instrument manager dialog box. The "rto1044" "string was assigned in the properties box. Our testing shows that we need to first bring up the resource manager before interacting with our scope remotely.

Figure 2. Tek Open Choice VISA Instrument Panel
Tek Open Choice Instrument Panel

The source for our python script "rto_basic.py" is provided at the bottom of this article. Invoking the script with the "-h" argument returns the following:

c:\pytest> python .\rto_basic.py -h

usage: rto_basic.py [-h] [-b] [-c CHANNEL] [-n NAME] [-t] [-p] [-f]
                    [-s SAMPLING]

RTO remove waveform demo utility

optional arguments:
  -h, --help            show this help message and exit
  -b, --binary          save binary file of floats as a pickle
  -c CHANNEL, --channel CHANNEL
                        scope channel to use
  -n NAME, --name NAME  filename
  -t, --text            save text file of floats
  -p, --plot            plot captured waveform
  -f, --plotfft         plot captured waveform as FFT
  -s SAMPLING, --sampling SAMPLING
                        sampling frequency, default is 1 M

Version: 0.1

To plot a waveform:

c:\pytest> python .\rto_basic.py -p -c 2	# plot channel 2

Please note that "python" maps to Anaconda's python3.5 on this particular Windows 10 installation.

Figure 3. Pyplot of Captured Waveform
Pyplot of Captured Waveform

To plot an FFT:

c:\pytest> python .\rto_basic.py -f -c 2	# FFT channel 2
Figure 4. Pyplot of FFT
Pyplot of FFT

The provided script also supports saving the captured waveform as either a text or binary file. In the latter case, the file is a python pickle, which makes life very easy storing and retrieving data (as shown below):

	
$ python3 
Python 3.5.3 (default, Jan 21 2017, 15:44:58) 

> import pickle
> f=open('sawtooth.pickle','rb')
> values=pickle.load(f)
> len(values)
	1000
> values[0:5]
	[-4.505928993225098, -4.426877498626709, -4.268774509429932, -4.189723491668701, -4.1106719970703125]
python3 script, rto_basic.py
##############################################################################
#
# Copyright (c) 2017 Mind Chasers Inc.
# All Rights Reserved.
#
#    file: rto_basic.py
#
#    basic / demo waveform capture utility
#
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################

import sys
import visa
import pickle
import argparse
import numpy as np
import matplotlib.pyplot as plt

VERSION = 0.1



if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='RTO remove waveform demo utility',
                                 epilog='Version: ' + str(VERSION))    
    parser.add_argument('-b','--binary',help='save binary file of floats as a pickle',action='store_true')
    parser.add_argument('-c','--channel',help='scope channel to use',action='store', default='1', type=int, choices=range(1, 5))
    parser.add_argument('-n','--name',help='filename', action='store', default='signal')
    parser.add_argument('-t','--text',help='save text file of floats',action='store_true')
    parser.add_argument('-p','--plot',help='plot captured waveform',action='store_true')
    parser.add_argument('-f','--plotfft',help='plot captured waveform as FFT',action='store_true')
    parser.add_argument('-s','--sampling',help='sampling frequency, default is 10 M', action='store', default='1000000')
    
    
    args = parser.parse_args()
    ch = 'channel'+str(args.channel)
    
    try:
        rm = visa.ResourceManager()
        inst = rm.open_resource("rto1044")
        print(inst.query("*IDN?"))
    except Exception as e:
        print("Error creating instance: {0}".format(e))
        sys.exit()
        
    # grab the waveform
    try:
        inst.query(ch + ":data:header?")
        inst.write("format real,32")
        values=inst.query_binary_values(ch + ":data:values?", datatype='f')
    except Exception as e:
        print("Error retrieving the waveform: {0}".format(e))
        print("Did you specify the right channel?")
        sys.exit()
    
    # do something with the waveform
    if args.plot:
        plt.plot(values)
        plt.show()

    if args.text:
        fsig = open(args.file+".txt","w")
        for value in values:
            fsig.write("%s\n" % value)
        fsig.close()
        
    if args.binary:
        fsig = open(args.file+".pickle","wb")
        pickle.dump(values,fsig,pickle.HIGHEST_PROTOCOL)
        fsig.close()
                
    if args.plotfft:
        Fs = float(args.sampling)       # sampling frequency
        n=len(values)                   # number of samples
        Ts=1/Fs                         # sampling period
        s=np.fft.rfft(values)           # computer fft for real input.  s is the complex transform array
        fs=np.fft.rfftfreq(n,Ts)        # return the frequencies
        plt.plot(fs,np.absolute(s))     
        plt.show()
        
    inst.close()
    print("finished")   

Please help us improve this article by adding your comment or question:

email addresses are neither displayed nor shared