Author Topic: Visashot, a python script for capturing screenshots from vintage instruments  (Read 4468 times)

0 Members and 2 Guests are viewing this topic.

Offline damienTopic starter

  • Contributor
  • Posts: 17
  • Country: jp
    • Website
Hi all,

I've been working for a while on a python script for capturing screenshots from vintage instruments and would appreciate the community's feedback and help.

Here's a few samples to start with:







86 devices are supported, but don't get too excited because 1) it's easy to reach this number with some instruments having many variants (like the HP85xx series of spectrum analyzers) and 2) not all devices have been tested, and I even boldly assumed that if it works for one instrument of a series then it works for all of them. You will notice that all instruments are from HP at the moment but there should be nothing preventing us from supporting devices from other manufacturers.

HP-53310A Modulation domain analyzer
HP-8990A and 8991A Peak power analyzers [untested]
HP-8992A Digital video power analyzer [untested]
HP-5450/1xA/B Digital oscilloscopes (54501A, 54503A, 54504A, 54505A/B, 54506B, 54510A/B, 54512B) [untested]
HP-5452/4xA Oscilloscopes (54520A, 54522A, 54540A, 54542A)
HP-54600 Oscilloscopes (54600A, 54601A, 54602A, 54600B, 54601B, 54602B, 54603B, 54610B, 54615B, 54616B, 54645A, 54645D)
HP-54620A Logic analyzer [untested]
HP-83475B Lightwave communication analyzer [untested]
HP-5461/2xA/D Oscilloscopes (54621A/D, 54622A/D, 54624A, 54641A/D, 54642A/D)
HP-4395A and HP-4396B Spectrum / network / impedance analyzers
HP-4286A RF LCR meter [untested]
HP-4291A/B Impedance / material analyzer [untested]
HP-4294A Precision impedance analyzer [untested]
HP-4352B VCO/PLL analyzer
HP-859xE Spectrum analyzers (8591E, 8593E, 8594E, 8595E, 8596E)
HP-859xEM EMC analyzers (8591EM, 8593EM, 8594EM, 8595EM, 8596EM)
HP-859xL Spectrum analyzers (8590L, 8592L)
HP-856xE Spectrum analyzers (8560E, 8561E, 8562E, 8563E, 8564E, 8565E)
HP-E444x PSA Signal analyzers (E4440A, E4443B, E4445A, E4446B, E4447A, E4448A) [tested]
HP-E440/1x ESA Spectrum analyzers (E4401B, E4402B, E4403B, E4404B, E4405B, E4407B, E4408B, E4411B) [tested]
HP-E4406A VSA Signal analyzer [untested]
HP-894xx Signal analyzers (89441A, 89440A, 89410A, 89441V, 89450A, 89451A) [untested]

The script is 100% python and is based on pyvisa, so if you're interested in giving it a spin you will need a working pyvisa setup. ImageMagick is the only other dependency. Visashot has been tested on Linux and Windows, but I don't have a Windows machine myself so support for this platform may be a little spotty.

Please consider contributing, for example by:
- testing with your instruments
- sharing sexy screenshots
- helping to support more devices
- testing on other platforms (mac?)

Project page for download and more information: https://damien.douxchamps.net/elec/visashot/
Developer site: https://sourceforge.net/projects/visashot/ (yeah, I know SF is old skool...)
A mailing list is also available to facilitate testing and development discussions.

Cheers!
« Last Edit: January 26, 2024, 03:13:40 am by damien »
 
The following users thanked this post: egonotto, tv84, coromonadalix, jjoonathan, teddychn

Offline mankan

  • Regular Contributor
  • *
  • Posts: 106
  • Country: se
Tried on Windows with my E4407B:
 .\visashot.py -d 18 -i 1 -o screen.png -g
Unsupported instrument ' E4407B'. Aborting.
ERROR:Empty screenshot buffer

After stripping the model string I got a Visa timeout instead. I have NI-Visa installed.
Full "*IDN?" response is: "Hewlett-Packard, E4407B, MY41440643, A.14.06"

This is what I use for screenshots: https://github.com/mankangustafsson/gpib_playground/blob/master/esa_screen.py
 
The following users thanked this post: damien

Offline damienTopic starter

  • Contributor
  • Posts: 17
  • Country: jp
    • Website
Thanks for the feedback! Stripping is indeed required for more recent instruments.

The code I used for the E4400 is inspired from this repo https://github.com/angry-dad/E4407B-GPIB-Screen-Capture which has essentially the same ideas.

I've also increased the timeout to 10 seconds, if it doesn't work I suppose we could push it even further but it becomes a bit... strange? Ideally I'd like to find a way to do it without timeouts.

Git repo updated! :-)
 

Offline mankan

  • Regular Contributor
  • *
  • Posts: 106
  • Country: se
Still get timeouts but I do not think the instrument is slow. My script takes 5-6 seconds to complete.
It seems like there are some bad commands sent, see picture.
1987498-0
Also, you need to delete the file first.
 
The following users thanked this post: damien

Offline damienTopic starter

  • Contributor
  • Posts: 17
  • Country: jp
    • Website
I missed the different behavior for querying files on the E44xx. New updates in git!
Thanks again for the help, especially since you already have something working on your side <3
 

Offline mankan

  • Regular Contributor
  • *
  • Posts: 106
  • Country: se
Damien, you are welcome. I can now confirm that revision dcd818 is working for me on my E4407B.
 
The following users thanked this post: damien

Offline damienTopic starter

  • Contributor
  • Posts: 17
  • Country: jp
    • Website
Excellent! I'm surprised I managed to get it right in only two iterations ;-)
Oh if you have a screenshot I could use on the project page that'd be awesome.
 

Offline damienTopic starter

  • Contributor
  • Posts: 17
  • Country: jp
    • Website
New release 1.20 is now available with confirmed support for E44xx signal analyzers!
Big thanks to @mankan for the feedback!

Here's a gratuitous extra screenshot to celebrate:

 
The following users thanked this post: mankan

Offline DeepLink

  • Regular Contributor
  • *
  • Posts: 120
  • Country: dk
This is just the tool I need for my 53310A  ;D

But I have Zero knowledge with Python

I do run EZGPIB (pascal) with NI libraries, and have a NI USB interface
Can talk to the 53310A
IDN?: HEWLETT-PACKARD,53310A,0,3235
VERS?: 1990,0
OPT?: MEMORY,C,0,RF

But what do I need to download/install in order to use this tool?
 

Online abeyer

  • Frequent Contributor
  • **
  • Posts: 502
  • Country: us
This is just the tool I need for my 53310A  ;D

But I have Zero knowledge with Python

I do run EZGPIB (pascal) with NI libraries, and have a NI USB interface
Can talk to the 53310A
IDN?: HEWLETT-PACKARD,53310A,0,3235
VERS?: 1990,0
OPT?: MEMORY,C,0,RF

But what do I need to download/install in order to use this tool?

It uses pyvisa for communication with the instrument, so you'll need to install and configure it. There are instructions for installing and information about the backends it can use (including NI) here.
 

Offline fenugrec

  • Frequent Contributor
  • **
  • Posts: 252
  • Country: ca
It will be difficult to have automated regression testing for this, but I suggest you keep a database of example responses of different instruments. So you can document odd edge cases where e.g. different versions reply differently to *IDN etc. Otherwise I think it's easy to break previously-working support while fixing others...
 
The following users thanked this post: damien

Offline damienTopic starter

  • Contributor
  • Posts: 17
  • Country: jp
    • Website
It will be difficult to have automated regression testing for this, but I suggest you keep a database of example responses of different instruments. So you can document odd edge cases where e.g. different versions reply differently to *IDN etc. Otherwise I think it's easy to break previously-working support while fixing others...

Yes regressions are going to be hard to manage. As more people use the script my hope is that we'll have volunteers who can test their instruments before new releases. In the meantime your idea is great, I've started to list the IDN strings I know in an idn.txt file. I have also kept PCL buffers from various instruments for testing the parsing of that format, also in the repo now.

Here are the 5 IDNs strings I know. Note that serial numbers are partially blanked with zeros.
Please share your IDN if your instrument is supported!

HEWLETT-PACKARD,4396B,JP1KE00000,REV1.16
Hewlett-Packard, E4407B, MY41440000, A.14.06
HEWLETT-PACKARD,53310A,0,3235
HEWLETT-PACKARD,54542A,3400A00000,00.13,01.14,T3.33,01.01,0.001
AGILENT TECHNOLOGIES,54641D,MY42000000,A.02.31
 

Offline cloudstrife87

  • Contributor
  • Posts: 18
  • Country: my
Hi Damien,

I've been trying to run the Visashot script to capture from my Agilent 54642D, although 54642D stated "Remote Operation Completed", but I keep encountering error "FileNotFoundError: [WinError 2] The system cannot find the file specified". Has anyone here encountered this error?

I installed ImageMagick-6.9.13, PythonMagick 0.9.19, and tried inserting "shell=True" in subprocess code but has no luck in resolving this error.

Below are the console logs:
Code: [Select]
C:\Users\James\AppData\Local\Programs\Python\Python313\python.exe C:/Users/James/Downloads/visashot.py -s GPIB0::7::INSTR -o test.png
Traceback (most recent call last):
  File "C:\Users\James\Downloads\visashot.py", line 934, in <module>
    _main()
    ~~~~~^^
  File "C:\Users\James\Downloads\visashot.py", line 892, in _main
    rtn = capture(instr, output_filename, trim, margin, scale, recolor, foreground_color, background_color, swap_id_command_order)
  File "C:\Users\James\Downloads\visashot.py", line 748, in capture
    _execute_imagemagick([IMAGE_MAGICK_CONVERT, tmp_filename, filename])
    ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\James\Downloads\visashot.py", line 524, in _execute_imagemagick
    stdout, stderr = subprocess.Popen(command_array, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
                     ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\James\AppData\Local\Programs\Python\Python313\Lib\subprocess.py", line 1036, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
    ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                        pass_fds, cwd, env,
                        ^^^^^^^^^^^^^^^^^^^
    ...<5 lines>...
                        gid, gids, uid, umask,
                        ^^^^^^^^^^^^^^^^^^^^^^
                        start_new_session, process_group)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\James\AppData\Local\Programs\Python\Python313\Lib\subprocess.py", line 1548, in _execute_child
    hp, ht, pid, tid = _winapi.CreateProcess(executable, args,
                       ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
                             # no special security
                             ^^^^^^^^^^^^^^^^^^^^^
    ...<4 lines>...
                             cwd,
                             ^^^^
                             startupinfo)
                             ^^^^^^^^^^^^
FileNotFoundError: [WinError 2] The system cannot find the file specified

Process finished with exit code 1
 

Offline damienTopic starter

  • Contributor
  • Posts: 17
  • Country: jp
    • Website
Hi!

I don't have a windows machine so it's not easy to get things working on your platform. But let's try! ;D

I imagine that you found the possible solution "shell=True" here: https://stackoverflow.com/questions/73193119/python-filenotfounderror-winerror-2-the-system-cannot-find-the-file-specifie
It was the first hit on google for me :D The second one on that page could be useful too?

The first thing would be to try the latest version of visashot https://damien.douxchamps.net/elec/visashot/ but I doubt it will help becauseI've never heard about this issue before.

Next you could display the two filenames before the execution, adding just before line 760 (latest version):
Code: [Select]
logging.debug("tmp_filename: %s", tmp_filename)
logging.debug("filename: %s", filename)

If the execution fails the tmp_filename should still be present in your system and it should contain a valid image. I don't think this is the issue but at least we could be certain.

Could you also share the full logs? (using the -g option)
 

Offline cloudstrife87

  • Contributor
  • Posts: 18
  • Country: my
Hi Damien, I'm not too sure why but I somehow managed to get it working.

Last night I uninstalled ImageMagick-6.9.13 and installed the latest version ImageMagick-7.1.1-39-Q16-HDRI-x64, but still couldn't get it working.

Just now after seeing your post I tried again, it works! I didn't restart my computer or change any settings, so I'm not too sure what I did to fix it. I used the latest Visashot script from your website and didn't do any modification (not even the Shell=True).

Sorry for taking your precious time trying to figure this out.

I'm going to try on my HP 3563A next! Will report my results here.

Code: [Select]
C:\Users\James\AppData\Local\Programs\Python\Python313\python.exe C:/Users/James/Downloads/visashot.py -s GPIB0::7::INSTR -o agilent.png

Process finished with exit code 0

« Last Edit: October 17, 2024, 01:30:29 pm by cloudstrife87 »
 

Offline damienTopic starter

  • Contributor
  • Posts: 17
  • Country: jp
    • Website
Great!  ^-^

I haven't tested the 3563A so if it works it's (another) small miracle. But definitely worth a try. And I'm happy to discuss with you to make it happen.

Cheers!
 

Offline cloudstrife87

  • Contributor
  • Posts: 18
  • Country: my
Hi Damien,

Ok I've tried each device type scripts and so far not working: 3563A will first show "Unknown Mnemonic" and then "Command Too Long" before timeout.

Sorry I'm not familiar with HP-IB programming, I just started to read the 3563A programming manual:https://docs.ampnuts.ru/eevblog.docs/HP_Agilent_Keysight/HP%203563A%20Programming.pdf . In appendix B Example Program 1 would that be a good start? Though seems like this code passes the control to 3563A to trigger the print though.
 

Offline damienTopic starter

  • Contributor
  • Posts: 17
  • Country: jp
    • Website
A quick check into the manual you linked indicates that it's a vector graphic display. Visashot only works with bitmaps at the moment.

Still, it should be possible to get the HPGL buffer and render that into a bitmap. Or use an HPGL to SVG converter (apparently those exist!)
The relevant pages are 5-28 and following. There seems to be two commands to send:
- VBLK 1
- DVAS or DVAN or DVBN, depending on the output format
After that capturing the buffer should be possible.

You can first try visashot with the -I option, this should get the ID string right.

For developing the best would be to start by getting the buffer in the 3 different formats. Simplest would be to get a python console, connect to your instrument (see short sample on my website) and then send the two commands above (e.g. VBLK1 or VBLK01, followed by DVAS) Then maybe something like "READ?" but I'm not sure... Hacking required from this point ;-)
 

Offline cloudstrife87

  • Contributor
  • Posts: 18
  • Country: my
What you meant by hacking is reverse engineering and reconstructing the data right?  ;D

I did the DVAS, DVAN and DVBN tests below, so far I can complete each test command individually. If i mix 2 together (eg. VBLK1>DVAS>READ?>VBLK1>DVAN), I'll get into timeout error and can only recover after I restarted 3563a.


Test 1
>>> instrument.write("VBLK1")
7
[3563A displayed: "VECTOR DISP BLOCK 1"]

>>> instrument.write("DVAS")
6
[3563A displayed: "DUMP VECTOR DISP ASCII"]

>>> instrument.write("READ?")
7
No status changes in 3563A


Test 2
>>> instrument.write("VBLK1")
7
[3563A displayed: "VECTOR DISP BLOCK 1"]

>>> instrument.write("DVAN")
6
[3563A displayed: "DUMP VECTOR DISP ANSI"]

>>> instrument.write("READ?")
7
No status changes in 3563A


Test 3
>>> instrument.write("VBLK1")
7
[3563A displayed: "VECTOR DISP BLOCK 1"]

>>> instrument.write("DVBN")
6
[3563A displayed: "DUMP VECTOR DISP BINARY"]

>>> instrument.write("READ?")
7
No status changes in 3563A


Test 1
Code: [Select]
>>> import pyvisa
>>> ressourceManager = pyvisa.ResourceManager()
>>> instrument = ressourceManager.open_resource("GPIB0::20::INSTR")
>>> instrument.write("VBLK1")
7
>>> instrument.write("DVAS")
6
>>> instrument.write("READ?")
7

Test 2
Code: [Select]
>>> import pyvisa
>>> ressourceManager = pyvisa.ResourceManager()
>>> instrument = ressourceManager.open_resource("GPIB0::20::INSTR")
>>> instrument.write("VBLK1")
7
>>> instrument.write("DVAN")
6
>>> instrument.write("READ?")
7

Test 3
Code: [Select]
>>> import pyvisa
>>> ressourceManager = pyvisa.ResourceManager()
>>> instrument = ressourceManager.open_resource("GPIB0::20::INSTR")
>>> instrument.write("VBLK1")
7
>>> instrument.write("DVBN")
6
>>> instrument.write("READ?")
7

Combined Test with error
Code: [Select]
>>> import pyvisa
>>> ressourceManager = pyvisa.ResourceManager()
>>> instrument = ressourceManager.open_resource("GPIB0::20::INSTR")
>>> instrument.write("VBLK1")
7
>>> instrument.write("DVAS")
6
>>> instrument.write("READ?")
7
>>> instrument.write("VBLK1")
7
>>> instrument.write("DVAN")
Traceback (most recent call last):
  File "<python-input-7>", line 1, in <module>
    instrument.write("DVAN")
    ~~~~~~~~~~~~~~~~^^^^^^^^
  File "C:\Users\James\AppData\Local\Programs\Python\Python313\Lib\site-packages\pyvisa\resources\messagebased.py", line 196, in write
    count = self.write_raw(message.encode(enco))
  File "C:\Users\James\AppData\Local\Programs\Python\Python313\Lib\site-packages\pyvisa\resources\messagebased.py", line 156, in write_raw
    return self.visalib.write(self.session, message)[0]
           ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\James\AppData\Local\Programs\Python\Python313\Lib\site-packages\pyvisa\ctwrapper\functions.py", line 2795, in write
    ret = library.viWrite(session, data, len(data), byref(return_count))
  File "C:\Users\James\AppData\Local\Programs\Python\Python313\Lib\site-packages\pyvisa\ctwrapper\highlevel.py", line 226, in _return_handler
    return self.handle_return_value(session, ret_value)  # type: ignore
« Last Edit: October 17, 2024, 10:44:41 pm by cloudstrife87 »
 

Offline damienTopic starter

  • Contributor
  • Posts: 17
  • Country: jp
    • Website
Yeah, things are going to get a little dirty :D

From the 6/7 returned by the commands I can see that the termination characters you're using are "\r\n", which should work. Well actually it worked since you've got reasonable messages displayed on the 3563A. So I think talking with the instrument is not an issue. Noice.

What is the instrument ID string?
Code: [Select]
id_string = instrument.query("ID?")
print(id_string)

When using the READ? command you will need to actually read from the instrument after that. The fact that the instrument doesn't say anything on the screen when you do the READ? is probably a good thing (no error) but now it's just waiting for you to get the bytes, for example using:
Code: [Select]
buffer = instrument.read_raw()
Then you could save the content of the buffer to check what's in it, maybe after checking its length:
Code: [Select]
print("Buffer length: " + str(len(buffer)) + "\n")
file = open("buffer.raw", "wb")
file.write(buffer)
file.close()
... or something like that (the code above is untested but should may work)

If that works then try to get a buffer file for each of the output formats.

If there's more than 20kB or so you may need to read multiple times and concatenate the outputs, but let's forget about that for now...
 

Offline cloudstrife87

  • Contributor
  • Posts: 18
  • Country: my
Sorry I missed the instrument string: "HP3563A"

So far so good all 3 buffer types are less than 20kb, 3563A shows no errors or indication during buffer extraction, and managed to extract them as attached.

Code: [Select]
>>> import pyvisa
>>> ressourceManager = pyvisa.ResourceManager()
>>> instrument = ressourceManager.open_resource("GPIB0::20::INSTR")
>>> id_string = instrument.query("ID?")
>>> print(id_string)
HP3563A

>>> instrument.write("VBLK1")
7
>>> instrument.write("DVAS")
6
>>> buffer = instrument.read_raw()
>>> print("Buffer length: " + str(len(buffer)) + "\n")
Buffer length: 2012

>>> file = open("dvasbuffer.raw", "wb")
>>> file.write(buffer)
2012
>>> file.close()
>>> instrument.write("VBLK1")
7
>>> instrument.write("DVAN")
6
>>> buffer = instrument.read_raw()
>>> print("Buffer length: " + str(len(buffer)) + "\n")
Buffer length: 1068

>>> file = open("dvanbuffer.raw", "wb")
>>> file.write(buffer)
1068
>>> file.close()
>>> instrument.write("VBLK1")
7
>>> instrument.write("DVBN")
6
>>> buffer = instrument.read_raw()
>>> print("Buffer length: " + str(len(buffer)) + "\n")
Buffer length: 270

>>> file = open("dvbnbuffer.raw", "wb")
>>> file.write(buffer)
270
>>> file.close()
 

Offline damienTopic starter

  • Contributor
  • Posts: 17
  • Country: jp
    • Website
The size is way to small for a screenshot, even vectorized :-( All I was able to do is use the dvasbuffer.raw and after removing a couple of bytes at the beginning it can be loaded in octave/matlab. Don't get too excited though: the plot is not interesting. Not actually sure what it represents. See pic below.

There has to be another function for the screenshot or printing :(
 

Offline damienTopic starter

  • Contributor
  • Posts: 17
  • Country: jp
    • Website
I just made some minor improvements over the holidays and that means a new release 1.22!
  • Successful tests with HP-8563E
  • Improve handling of PRINT_DEVICES instruments
  • Switch to IM v7 while keeping some compat with v6.
  • Be robust against bad serial numbers in IDN string
  • HP 5450/1xA/B scopes successfully tested
  • Fix serial baud rate argument handling
  • Automatic timeout calculation for serial devices
Quick download link: https://damien.douxchamps.net/elec/visashot/visashot.py
Enjoy!
 

Offline damienTopic starter

  • Contributor
  • Posts: 17
  • Country: jp
    • Website
Yet another new release. The major addition in 1.23 is the support for the LeCroy 9300 series of scopes.
120 instruments are now supported, thanks to all the contributors!
Quick download link: https://damien.douxchamps.net/elec/visashot/visashot.py
Enjoy!

 
The following users thanked this post: croma641

Offline fenugrec

  • Frequent Contributor
  • **
  • Posts: 252
  • Country: ca
a stray '+' here maybe?
Code: [Select]
/visashot.py", line 815
    if model in LECROY_SCDP_DEVICES+: # Some devices are orange instead of green
                                    ^
SyntaxError: invalid syntax
 
The following users thanked this post: damien


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf