Bode plot on Siglent oscilloscope with non-Siglent signal generator
More than just a program example, this was a project unto itself...
(In fact, this is why I started setting up GPIB in the first place.)
See more details on its own page. The popular SDS 1104X-E oscilloscope by Siglent has a useful "Bode plot" function, which requires a Siglent AWG (Arbitrary Waveform Generator) to work. There exist various utilities to mimic the Siglent AWG with other manufacturers' devices using a microcontroller or Raspberry Pi to translate commands and responses between the two. I did the same using my Agilent E4421B RF signal generator, which is connected via GPIB (although it does have a serial port as well). The program I made for this purpose, sds_pyvisa_bode.py, uses PyVISA to talk to the RF signal generator, and should in principle work with any RF signal generator, regardless of manufacturer, as long as it understands a few standard SCPI commands. And, thanks to PyVISA, it can be connected via GPIB, serial/RS232, USB/USBTMC or Ethernet/VXI-11! Read more about the program here! |
Screenshot from my HP 54501A oscilloscope
#!/usr/bin/env python3 addr = "GPIB0::7::INSTR" #replace the 7 with your scope's GPIB address! import pyvisa from PIL import Image rm = pyvisa.ResourceManager() instr = rm.open_resource(addr) instr.write(':PRINT?') answer = instr.read_raw(30742) img = Image.frombytes("1", (640, 384), answer[23:]) img = img.crop((0, 0, 595, 384)) img.save("output.png")Some comments about the code: I had to limit the read operation to 30742 bytes (determined experimentally) in order not to have the scope complain of "Query UNTERMINATED" as it seemed to do with an unlimited read. The image conversion starts at an offset of 23 bytes, as those first bytes are probably commands intended for the HP printer on the same GPIB interface, instead of actual image data. Also the right-hand edge of the image is garbage, probably printer commands again, so that has to be cropped away.
Screenshot from my Siglent SDS 1104X-E oscilloscope
The more modern Siglent SDS 1104X-E oscilloscope provides
screenshots directly in a variety of standard image formats. They can
easily be saved to a USB thumb drive, but it's more convenient to grab
them directly over the network. There's a Python script
on Siglent's website
which does just that by messing with sockets and wait times and all kinds
of low-level stuff.
My script below does the same thing: it requests a screenshot over the
network and saves it as a PNG file, and demonstrates how simple
PyVISA makes this operation. Plus, if you were to connect the scope by
USB instead of Ethernet, the low-level Siglent script would absolutely not
work! My script would, if you just change the address to whatever
|
#!/usr/bin/env python3 address = "TCPIP::10.42.47.104::INSTR" #use your scope's address! import pyvisa from PIL import Image rm = pyvisa.ResourceManager() instr = rm.open_resource(address) instr.write('SCDP') answer = instr.read_raw() img = Image.open(io.BytesIO(answer)) img.save('output.png')
Screenshot from my HP 8562B spectrum analyzer
Getting a screenshot from my HP 8562B spectrum
analyzer was a bit more involved. Firstly, it would not voluntarily
send the screenshot without extra coaxing (it needs to believe it's talking
to a plotter on the same GPIB bus, but I honestly don't know how this
"coaxing" I did differs from an ordinary GPIB transaction).
Secondly, when it finally gave up its jealously guarded data, it came in
HPGL format! But eventually I was able to convert it to a relatively clean
looking SVG output. Here is a spectrum
of the FM radio band, for example, measured using nothing but a rubber duck
antenna on the input port. (Here the SVG has further been converted to PNG.
The script does not do that automatically.) The script to get the screenshot and to convert it to SVG vector graphics (perhaps more common nowadays than HPGL) is shown below. It requires the python-hpgl library by Alex Forencich for the image conversion. |
#!/usr/bin/env python3 GPIB_ADDR = 18 # change to suit your instrument import pyvisa, hpgl, sys, io rm=pyvisa.ResourceManager() instr=rm.open_resource('GPIB0::'+str(GPIB_ADDR)+'::INSTR') bus=rm.open_resource("GPIB0::INTFC") instr.write("PLOT") bus.send_command(bytes([0x3f, 0x20, 0x5f, 0x40+GPIB_ADDR])) hpgl_data=bus.visalib.buffer_read(bus.session,32000) # Fix the HPGL fonts, convert to SVG and fix linewidth hpgl_data=hpgl_data[0].replace(b';SR1.67,1.78;', b';SR0.1,0.1;') svg_data=hpgl.hpgl2svg(io.StringIO(hpgl_data.decode("utf-8"))) svg_data=svg_data.replace('stroke-width="14.000"', 'stroke-width="1.500"') with open("output.svg", "w") as f: f.write(svg_data)Some comments on this script:
python setup.py install
",
which you must run as root.
bus.send_command(...)
transmits four GPIB
commands—not SCPI
commands that are sent to the instrument, but single-byte
GPIB
commands which control the GPIB bus and how devices communicate over
it! The commands specify the spectrum analyzer to be the sole
"talker" device, and the host adapter to be the sole
"listener". These commands are sent to the GPIB
host adapter immediately after the "PLOT" SCPI
command has been sent to the instrument. Immediately following
that, a low-level read is performed on the host adapter again to
receive the data. The read command requires a maximum length
parameter—the value of 32000 is purely arbitrary and certainly
big enough, as the output typically seems to be about 12k bytes. One
of these commands causes an IOCTL error to be reported to stderr,
for whatever reason, but it seems harmless. (In the web server cgi-bin
version below, I used the _no_stderr()
wrapper to get rid
of it.)
replace
command to match!
Finally, as a quick brush up on how cgi-bins and forms work, I wrote a web
interface to run arbitrary SCPI commands on any PyVISA instrument
(similar to the SCPI command interface offered on the web interface of
some modern instruments e.g. Siglent's oscilloscopes).
The script, direct_scpi.py, is
placed in the web server's cgi-bin directory. A text file
called "scpi.include " is placed in the same
directory, containing PyVISA resource names and optional comments, one
per line, for example:
TCPIP::10.42.47.104::INSTR Siglent SDS 1104X-E oscilloscope GPIB0::7::INSTR HP 54501A oscilloscope GPIB0::19::INSTR Agilent E4421B RF signal generator GPIB0::11::INSTR HP 34401A multimeterThese instruments can then be selected from a list, or you can enable "Scan for instruments" and hit "Refresh" to also find any unlisted GPIB or USB/USBTMC instruments, as well as all serial/RS232 ports (whether or not there's an instrument connected)—but not Ethernet/VXI-11 instruments however. You can also enter a valid resource ID manually. After selecting the instrument, enter e.g. " *IDN? " as the query and hit Submit to see
what happens.
|
list_resources()
if none of the instruments
on the GPIB bus are switched on. I initially had the bus scanned for new
instruments automatically every time, but eventually had to add a checkbox
to enable scanning only when needed.
Some obvious notes:
I wrote these simple scripts for my own enjoyment, my own use, and for a bit of exercise in Python, which I'm a complete newbie in. Please do not assume these to be secure, sensible, safe, or even functional pieces of code! Please do not use these in any mission critical application without thoroughly checking what they do and adding necessary fail safes! I use these in my home lab, and do not do anywhere near enough error checking in them. Please do not place the cgi-bin versions on any publicly accessible Internet-facing server! These most likely contain every single kind of code injection vulnerability ever documented, and then some. All the code on this page is in the public domain, free to use as you wish, but whatever you do, don't blame me for what happens!
And sorry if my Python looks like I'm coding with my left foot—I said I'm an absolute, complete, helpless newbie in Python! :)