Author Topic: Python-based instrument control  (Read 44979 times)

0 Members and 2 Guests are viewing this topic.

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Python-based instrument control
« on: March 05, 2014, 07:40:30 am »
There seems to be a growing interest on the forum in terms of cross-platform, extensible instrument control that isn't tied to a specific vendor.  I have been working for some time on a collection of Python scripts that make up a cross-platform instrument control solution called Python IVI. 

http://github.com/alexforencich/python-ivi

The idea is to create an abstraction layer that makes using various instruments straightforward, based on the IVI standard.  Python IVI can then leverage Python VXI11, Python USBTMC, PySerial, and Linux GPIB to communicate with instruments.  Python VXI11 is a pure Python VXI11 driver for LAN based instruments while Python USBTMC leverages PyUSB for instruments that support USBTMC.  Everything except Linux GPIB support is cross-platform and has been tested in Linux, Windows, and Mac OS.  Python IVI is also written so that it will work correctly in both Python 2 and Python 3. 

Python IVI is desgined so that it is easy to extend its functionality either with additional instrument control protocols (say, serial to GPIB devices like the Prologix units) or additional instruments. 

Currently, Python IVI supports a number of different instrument families - Agilent Infiniivision and Infiniium oscilloscopes, Agilent and Tektronix programmable power supplies, HP spectrum analyzers, Tektronix AWGs, and a number of other odds and ends.  Things like waveform and screenshot readout are supported for most instruments. 

I would love for Python IVI to support a wide range of instruments, especially Rigol oscilloscopes.  Unfortunately, I cannot write drivers for instruments that I do not have access to, so I would like to call upon anyone who is interested in extensible instrument control to lend a hand. 

If anyone here finds Python IVI useful and wishes to contribute, I would me more than happy to include your changes in Python IVI so that it can become an even more useful tool. 
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 
The following users thanked this post: Bryce, RoGeorge

Offline fake-name

  • Regular Contributor
  • *
  • Posts: 75
Re: Python-based instrument control
« Reply #1 on: March 05, 2014, 08:13:36 am »
I use python a lot, and have access to both a DS4000 and DS1000Z oscilloscope.

If I get a chance, I'll have a poke around. This is a cool project!
 

Offline wiss

  • Frequent Contributor
  • **
  • Posts: 486
  • Country: ch
Re: Python-based instrument control
« Reply #2 on: March 18, 2014, 10:44:13 pm »
Just got my gpib-card last week and started to play around a bit with python, I'll check this out!
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #3 on: March 18, 2014, 10:47:43 pm »
Just got my gpib-card last week and started to play around a bit with python, I'll check this out!

Nice!  I presume you got a PCI GPIB card, then?  What operating system are you using?  If you're running linux, then Python IVI has support for using the linux-gpib python wrapper.  I'm not sure what will work for windows, though.  You may need to install the NI software and PyVISA.  It's probably about time for me to add support for PyVISA to Python IVI to support that sort of setup. 
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline psycho0815

  • Regular Contributor
  • *
  • Posts: 150
  • Country: de
    • H-REG Blog
Re: Python-based instrument control
« Reply #4 on: March 19, 2014, 12:30:01 pm »
looks not bad. i'll have a look at the documentation on how to write drivers. If it's not too much hassle, i might see what i can do about drivers for the rigol ds1052e and dg1022.
If you like, check out my blog (german):
http://h-reg.blogspot.de
 

Offline wiss

  • Frequent Contributor
  • **
  • Posts: 486
  • Country: ch
Re: Python-based instrument control
« Reply #5 on: March 19, 2014, 02:11:34 pm »
Just got my gpib-card last week and started to play around a bit with python, I'll check this out!

Nice!  I presume you got a PCI GPIB card, then?  What operating system are you using?  If you're running linux, then Python IVI has support for using the linux-gpib python wrapper.  I'm not sure what will work for windows, though.  You may need to install the NI software and PyVISA.  It's probably about time for me to add support for PyVISA to Python IVI to support that sort of setup.

Yup, Gentoo-Linux, no labview, I have only been talking to my multimeters but I do have a HP oscope, Philips counter and HP generator also.

Are there any friendly GUI-widgets for like trend-plots and stuff somewhere?
I'm using the matplotlib window now.
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #6 on: March 19, 2014, 05:32:26 pm »
I use python a lot, and have access to both a DS4000 and DS1000Z oscilloscope.

If I get a chance, I'll have a poke around. This is a cool project!
I use python a lot, and have access to both a DS4000 and DS1000Z oscilloscope.

If I get a chance, I'll have a poke around. This is a cool project!

Writing drivers is not all that complicated.  The hard part is just the sheer number of commands that each instrument supports.  There is some documentation included with Python IVI that describes how to create a new driver. 

If you want to write some drivers for Python IVI, you should fork the repo and commit your changes to that, then send me a pull request. 

If you have any questions, feel free to ask by posting here or posting on the Python IVI google group. 
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #7 on: March 19, 2014, 05:40:47 pm »
Yup, Gentoo-Linux, no labview, I have only been talking to my multimeters but I do have a HP oscope, Philips counter and HP generator also.

Are there any friendly GUI-widgets for like trend-plots and stuff somewhere?
I'm using the matplotlib window now.

Well, there might not be drivers yet for all of your equipment, but feel free to contribute if you have the time because I'm sure they will be useful to someone else. 

Right now Python IVI is just an abstraction layer that tries to provide common interfaces to similar instruments (e.g. for any scope, you should be able to do scope.channels[0].measurement.fetch_waveform() to read out the waveform data).  There is no explicit support for graphing and logging, but these are rather simple operations to implement in python, especially if you want to do something more complicated (e.g. multiple instruments, conditional logging, etc.).  Unfortunately I can't point you to anything other than matplotlib for plotting.  Generally what I do is dump a csv file (or similar) and then process that separately, either in Excel/OpenOffice/LibreOffice or with another python script.  No sense in a bug in the graphing portion of the script nixing the data before it's saved on disk. 
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline wiss

  • Frequent Contributor
  • **
  • Posts: 486
  • Country: ch
Re: Python-based instrument control
« Reply #8 on: March 19, 2014, 06:04:34 pm »

Well, there might not be drivers yet for all of your equipment, but feel free to contribute if you have the time because I'm sure they will be useful to someone else. 


I guess that implementing the DMM-class will be the start :)

Quote

Right now Python IVI is just an abstraction layer that tries to provide common interfaces to similar instruments (e.g. for any scope, you should be able to do scope.channels[0].measurement.fetch_waveform() to read out the waveform data).  There is no explicit support for graphing and logging, but these are rather simple operations to implement in python, especially if you want to do something more complicated (e.g. multiple instruments, conditional logging, etc.).  Unfortunately I can't point you to anything other than matplotlib for plotting.  Generally what I do is dump a csv file (or similar) and then process that separately, either in Excel/OpenOffice/LibreOffice or with another python script.  No sense in a bug in the graphing portion of the script nixing the data before it's saved on disk.

I'm very fond of the trend-plot on the 34461, which I do not have at home, seems like "trivial" to implement using PC/gpib. That I do not intend for saving data but rather real time viewing.
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #9 on: March 19, 2014, 06:19:01 pm »

I guess that implementing the DMM-class will be the start :)


Which DMM are you using?


I'm very fond of the trend-plot on the 34461, which I do not have at home, seems like "trivial" to implement using PC/gpib. That I do not intend for saving data but rather real time viewing.

Yeah, it should be pretty easy to implement.  I have been mulling over a few GUI type extensions as well, but I do not think they belong in Python IVI directly.  A separate package that imports Python IVI, sure.

One thing that would be really nice is an offline scope trace viewer that supports all of the standard measurements, cursors, zooming, scaling, etc. as well as processing plugins for DSP filtering, serial decoding, clock recovery, line code removal for 8b/10b and 64b/66b, high level decoding (ethernet frames, etc.), single trace eye diagram generation, etc.  The sort of stuff the Infiniium scopes can do (especially if you pay $$$$ for the decoding plugins), but with more flexibility and extensibility.  It could even capture data in more-or-less realtime from a scope (or multiple scopes, even) with Python IVI as a back-end, as a step up from using VNC.  That sort of thing would definitely be enough for a separate project.  I think the sigrok project may do some of this, but I have not looked at that project in some time. 
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline wiss

  • Frequent Contributor
  • **
  • Posts: 486
  • Country: ch
Re: Python-based instrument control
« Reply #10 on: March 19, 2014, 07:10:33 pm »

I guess that implementing the DMM-class will be the start :)


Which DMM are you using?

Prema 6001 and Keithley 192

Quote


I'm very fond of the trend-plot on the 34461, which I do not have at home, seems like "trivial" to implement using PC/gpib. That I do not intend for saving data but rather real time viewing.

Yeah, it should be pretty easy to implement.  I have been mulling over a few GUI type extensions as well, but I do not think they belong in Python IVI directly.  A separate package that imports Python IVI, sure.


Yes, like a labview widget thingie library.
 

Offline wiss

  • Frequent Contributor
  • **
  • Posts: 486
  • Country: ch
Re: Python-based instrument control
« Reply #11 on: March 19, 2014, 10:26:54 pm »

I guess that implementing the DMM-class will be the start :)


Which DMM are you using?

Prema 6001 and Keithley 192


It looks like the linux-gpib is written for SCPI-instruments, that have the ask concept, neither of my multimeters do it that way. Maybe there should be a linux-gpib-scpi class?

To read something from the prema I send "VDR3L1PFS0" and when doing a read I get "+1.000001E+1VDR3A0T4S0Q0MOP0D0B0" back, as far as I understand from the manual that is the only way to communicate and don't really fit the "ask"-model.
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #12 on: March 19, 2014, 10:46:01 pm »

It looks like the linux-gpib is written for SCPI-instruments, that have the ask concept, neither of my multimeters do it that way. Maybe there should be a linux-gpib-scpi class?

To read something from the prema I send "VDR3L1PFS0" and when doing a read I get "+1.000001E+1VDR3A0T4S0Q0MOP0D0B0" back, as far as I understand from the manual that is the only way to communicate and don't really fit the "ask"-model.

Well, what is missing?  As far as I understand it, the basic operations on the bus are 'read' and 'write', while 'ask ' is just a shortcut that calls 'write' and then 'read'.  This is how the VXI-11 and USBTMC fdrivers work, anyway, and they're supposed to be equivalent to GPIB.  You can also read just a specific number of characters, if reading more than that causes problems.  It looks like when you read, you get a measurement, plus a bunch of status information.  I imagine you get a similar string if you read without issuing a write call first.  I think you're going to have to do a bit of creative parsing to get all of the status information organized, but I don't think a rewrite of the underlying library is required.  Take a peek at the power meter drivers in Python IVI, agilent436A and agilent437B.  They aren't terribly complete, but they have a very non-SCPI like interface. 
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline zapta

  • Super Contributor
  • ***
  • Posts: 6189
  • Country: us
Re: Python-based instrument control
« Reply #13 on: March 20, 2014, 06:55:07 am »
I have been working for some time on a collection of Python scripts that make up a cross-platform instrument control solution called Python IVI. 

http://github.com/alexforencich/python-ivi

Do you have screenshots?

I am designing a simple battery current monitor and  possibly Python IVI can provide the realtime display and gui.
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #14 on: March 20, 2014, 07:05:09 am »
Python IVI is only an interface layer, there is no GUI component to it. You could use python IVI to read data from your instruments and then use matplotlib or similar to graph the data. I am not sure what the best real-time plotting solution would be, unfortunately.
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline wiss

  • Frequent Contributor
  • **
  • Posts: 486
  • Country: ch
Re: Python-based instrument control
« Reply #15 on: March 20, 2014, 07:23:32 pm »

It looks like the linux-gpib is written for SCPI-instruments, that have the ask concept, neither of my multimeters do it that way. Maybe there should be a linux-gpib-scpi class?

To read something from the prema I send "VDR3L1PFS0" and when doing a read I get "+1.000001E+1VDR3A0T4S0Q0MOP0D0B0" back, as far as I understand from the manual that is the only way to communicate and don't really fit the "ask"-model.

Well, what is missing?  As far as I understand it, the basic operations on the bus are 'read' and 'write', while 'ask ' is just a shortcut that calls 'write' and then 'read'.  This is how the VXI-11 and USBTMC fdrivers work, anyway, and they're supposed to be equivalent to GPIB.  You can also read just a specific number of characters, if reading more than that causes problems.  It looks like when you read, you get a measurement, plus a bunch of status information.  I imagine you get a similar string if you read without issuing a write call first.  I think you're going to have to do a bit of creative parsing to get all of the status information organized, but I don't think a rewrite of the underlying library is required.  Take a peek at the power meter drivers in Python IVI, agilent436A and agilent437B.  They aren't terribly complete, but they have a very non-SCPI like interface.

I can not read without first having sent a command and when I do read a new measurement will be taken, the prema will block for one second in continuous mode (1 Hz update is the default) per read, and probably if set to measure on read will trigger a new measurement.
What I'm thinking now is to...  < :-/O >... hum, good! The front-panel buttons on the prema locks (apart from the "LOCAL" button) </  :-/O >
that there is a set of variables in the prema-dmm class holding the last read configuration and returning those values when a client request information. If a user changes the settings the information of course will be invalid. I guess that for SCPI-instruments the information is taken from the instrument directly?
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #16 on: March 20, 2014, 07:48:41 pm »
I can not read without first having sent a command and when I do read a new measurement will be taken, the prema will block for one second in continuous mode (1 Hz update is the default) per read, and probably if set to measure on read will trigger a new measurement.
What I'm thinking now is to...  < :-/O >... hum, good! The front-panel buttons on the prema locks (apart from the "LOCAL" button) </  :-/O >
that there is a set of variables in the prema-dmm class holding the last read configuration and returning those values when a client request information. If a user changes the settings the information of course will be invalid. I guess that for SCPI-instruments the information is taken from the instrument directly?

Hmm, that's interesting that it forces a measurement when you read.  There is no way to read without taking a measurement, just to get all of the status information?

Most of the SCPI type drivers also cache everything in local variables.  The caching means that multiple reads of the same parameter will only result in one actual query to the instrument.  It's assumed that nobody is monkeying around with the instrument while it is being controlled remotely.  If you want to ensure this isn't a problem, enable the local lockout.  If you don't want to make this assumption, then you can turn off caching or clear the cache (either set instrument.driver_operation.cache = False to disable the cache, or call instrument.driver_operation.invalidate_all_attributes() to clear the cache).  Take a look at the existing drivers to see how the caching system is set up.  It's pretty simple, and the cache check methods actually look at the call stack so you don't have to pass in the property name every time you need to check or update the cache, unless you are updating the cache for a different property.  You will notice that on some drivers, setting certain properties will invalidate several others. 

The caching is probably reasonable for your instrument.  I'm just wondering what the best implementation would be.  You may have to have two 'master methods' - one to set all of the proper settings and take a measurement, and one to take a dummy measurement and then read in all of the settings.  Unless you can find a way to read the settings without taking a measurement, anyway.  The danger of forcing a measurement is that if it's set to take a triggered measurement, you may get a rather unexpected timeout and then possibly leave the instrument in a strange state (waiting for trigger).  This may or may not be a problem with your meter, though. 
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline wiss

  • Frequent Contributor
  • **
  • Posts: 486
  • Country: ch
Re: Python-based instrument control
« Reply #17 on: March 20, 2014, 08:07:43 pm »
I think this is the only way, the gpib-section in the manual is only a few pages... but on the other hand I've only got like 3 evenings of experience :)
I'm thinking of taking a dummy VDC reading in "initialize" and then update the cache on every read or software change.
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #18 on: March 20, 2014, 08:13:21 pm »
I would say take a dummy reading on the first read. No sense in taking a dummy reading on init if nothing is ever read. Also, enough experimentation could find a way to skip the dummy read, but you never know.
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline wiss

  • Frequent Contributor
  • **
  • Posts: 486
  • Country: ch
Re: Python-based instrument control
« Reply #19 on: March 20, 2014, 09:04:24 pm »
This dummy-read will be the first point where the non-presences of the instrument will be noticed (like GPIB-cable fell out), therefore I'd like to have it in the "fopen"-routine.
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #20 on: March 21, 2014, 07:12:04 am »
Well, whatever you think works best.  The DiCon fiber optic switch driver does something along those lines; it queries the switch configuration (system:config?) on initialization to figure out what modules are installed and populate all of the proper fields accordingly. 

Also, I just finished reworking how Python IVI uses the instrument interface.  If the ask, ask_raw, read, and/or write calls are missing, they will be emulated.  The only required calls are now read_raw and write_raw.  So if a custom interface is required, all it has to do is implement those two functions, and then it can be passed to Python IVI in place of a resource string.  This might be useful for uncommon interfaces.  It also means that you should now be able to use Python IVI with PyVISA, simply by passing a PyVISA instrument to Python IVI in place of the resource string.  Unfortunately, I am unable to test this at the moment as I do not have an accessible installation of NI VISA. 
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #21 on: March 21, 2014, 09:33:03 pm »
I just got my Scientific Linux VM working again, and I properly implemented PyVISA support.  Now, if you have PyVISA installed, Python IVI will fall back on PyVISA if other communication interfaces are not installed.  It is also possible to tell Python IVI to prefer PyVISA, in which case it will try PyVISA first and then fall back on other options. 
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline awallin

  • Frequent Contributor
  • **
  • Posts: 694
Re: Python-based instrument control
« Reply #22 on: March 22, 2014, 09:24:18 am »
FWIW, some notes on how to use GPIB under linux, tested with two different NI devices (USB and PCI-E card)
http://www.anderswallin.net/2013/11/gpib-on-linux/

I've also used a time-interval counter through Ethernet based LXI, but I fail to remember the python library name...
 

Offline Noise Floor

  • Regular Contributor
  • *
  • Posts: 117
  • Country: us
Re: Python-based instrument control
« Reply #23 on: March 22, 2014, 06:09:40 pm »
Python IVI is only an interface layer, there is no GUI component to it. You could use python IVI to read data from your instruments and then use matplotlib or similar to graph the data. I am not sure what the best real-time plotting solution would be, unfortunately.

NumPy + Matplotlib?
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #24 on: March 25, 2014, 05:10:35 am »
Python IVI is only an interface layer, there is no GUI component to it. You could use python IVI to read data from your instruments and then use matplotlib or similar to graph the data. I am not sure what the best real-time plotting solution would be, unfortunately.

NumPy + Matplotlib?

That would be one solution.  Not sure how well matplotlib works for real-time plotting, though. 

There was a mention of Kst on another post here for plotting logged data.  It looks like a pretty promising solution.  I will look in to putting together a sample logging script that can be used with Kst, as that could be a very powerful combination. 
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline hochopeper

  • Supporter
  • ****
  • Posts: 2
Re: Python-based instrument control
« Reply #25 on: March 25, 2014, 12:09:39 pm »
For realtime plots I think the new plotting library being developed mainly by the continuum.io folks ... http://bokeh.pydata.org

Bokeh has client server architecture and realtime plotting appears to be on the agenda for improvements. This should I hope (I've not used it yet) allow the GUI of the plot operate independently of the data acquisition.
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #26 on: March 26, 2014, 08:20:51 am »
For realtime plots I think the new plotting library being developed mainly by the continuum.io folks ... http://bokeh.pydata.org

Bokeh has client server architecture and realtime plotting appears to be on the agenda for improvements. This should I hope (I've not used it yet) allow the GUI of the plot operate independently of the data acquisition.

That looks like it could be pretty interesting as well.  Unfortunately, it seems the AUR package for Arch Linux is broken, so I have not been able to install it yet. 

I think I'm going to start by taking a look at Kst - it seems to have some sort of real-time capability.  I will look in to writing a basic logging script for my 34401A this week. 
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline mrflibble

  • Super Contributor
  • ***
  • Posts: 2051
  • Country: nl
Re: Python-based instrument control
« Reply #27 on: March 27, 2014, 10:09:37 pm »
For realtime plots I think the new plotting library being developed mainly by the continuum.io folks ... http://bokeh.pydata.org

Bokeh has client server architecture and realtime plotting appears to be on the agenda for improvements. This should I hope (I've not used it yet) allow the GUI of the plot operate independently of the data acquisition.

I think I'm going to start by taking a look at Kst - it seems to have some sort of real-time capability.  I will look in to writing a basic logging script for my 34401A this week. 
I'd have to agree on the choice of kst. Bokeh looks sorta cute, but "yes, we are working on improvements for real-time" != "works well in real-time". If you want bokeh-like pretty-plot functionality but significantly more awesome++ (IMO), then it's ggplot2 time. And I noticed that ggplot2 now also works from python, for example through rpy. w00t! So I'll be checking that one for low update rate high quality plots, which just happen to also be highly pdf exportable with zero extra effort. ;) And for high update rate & large amounts of data & real time goodness kst indeed looks promising.

And thank you very much Mr Alex sir, for python-ivi and python-vxi11.  :-+ Today I received my DSA815-TG, and got it python scripted in < 30 mins from T_out_of_box. :) I also made a little script based on your python-vxi11 that automates  rikey_dsa calls, so execute script ==> install the 10 Hz RBW license (*). :)

(*) Or any of the other licenses you care for. I just used the 10 Hz RBW license to test the procedure. Worked in one go! Well, two goes. Had to remove the minus characters from the license key that rikey_dsa spits out.

So far I like python-vxi11, and will use it in future scripts. Haven't really used the ivi side of it yet. I'll probably try to extend some random class for use with the DSA815-TG, and see how that goes. Oh yeah, one thing I noticed: the timeouts are hardcoded? Or did I miss the magic method?
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #28 on: March 28, 2014, 03:19:27 am »
I'd have to agree on the choice of kst. Bokeh looks sorta cute, but "yes, we are working on improvements for real-time" != "works well in real-time". If you want bokeh-like pretty-plot functionality but significantly more awesome++ (IMO), then it's ggplot2 time. And I noticed that ggplot2 now also works from python, for example through rpy. w00t! So I'll be checking that one for low update rate high quality plots, which just happen to also be highly pdf exportable with zero extra effort. ;) And for high update rate & large amounts of data & real time goodness kst indeed looks promising.

And thank you very much Mr Alex sir, for python-ivi and python-vxi11.  :-+ Today I received my DSA815-TG, and got it python scripted in < 30 mins from T_out_of_box. :) I also made a little script based on your python-vxi11 that automates  rikey_dsa calls, so execute script ==> install the 10 Hz RBW license (*). :)

(*) Or any of the other licenses you care for. I just used the 10 Hz RBW license to test the procedure. Worked in one go! Well, two goes. Had to remove the minus characters from the license key that rikey_dsa spits out.

So far I like python-vxi11, and will use it in future scripts. Haven't really used the ivi side of it yet. I'll probably try to extend some random class for use with the DSA815-TG, and see how that goes. Oh yeah, one thing I noticed: the timeouts are hardcoded? Or did I miss the magic method?

Kst does look pretty good, I have been using it to look at a couple of scope traces that I saved in csv format and it appears to work quite nicely.  I have not had a chance to try out real-time plotting yet, though. 

If you want to work on a python-ivi driver for the Rigol DSA units, that would be excellent.  Actually, the spectrum analyzer base class is not completely finished at the moment, not much more than the base class is implemented.  The basic functionality is covered, though.  Finishing that off is on my to-do list.  Also, the IVI spectrum analyzer spec doesn't include tracking generators, so I will have to find a way to add that functionality in somehow.  I have an HP 8595E with a tracking generator that I would like to support completely.  I would suggest working off of the Agilent 8590E driver.  You may just need to substitute the proper commands from the Rigol programmer's manual to get most of it working. 

In terms of setting timeouts, that is certainly possible.  If you're using vxi11 directly, then you can just set instr.timeout to what you need.  Right now the units are in ms, but I suppose that could be changed to seconds.  If you're using vxi11 through python-ivi, the vxi11 instance will be called _interface so you can just set instr._interface.timeout to what you need. 
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #29 on: March 31, 2014, 08:27:22 am »
Alright, here is some example logging code for use with Kst.  There are a few variables at the top - file_name, sample_period, and sample_count - as well as the call to Python IVI to connect to the instrument.  Configure these as necessary.  This script does not actually configure the instrument for whatever measurement you need; all it does is read samples from a multimeter.  So you can either configure the meter manually before starting the script, or you can add code after the ivi call to set up the measurement.  The samples (along with a timestamp) are written to a csv file.  Before starting, the script also writes out an xml file for Kst.  Now, the XML file that it writes is a bit hacked together, I just saved one out of Kst and hen stripped out a bunch of stuff, so it might not be terribly ideal.  But it worked for the limited testing I have done so far.  To use this thing, start the script in python, then open up the generated .kst file in Kst.  As the data is collected, Kst will automatically load and display the new data points.  The script will stop automatically after collecting the specified number of samples.  It should also stp if there is an error communicating with the instrument, or if the script is interrupted with ctrl c. 

The timing loop is set up so that the script attempts to 'make up for lost time' if there is a mis-timed sample somewhere.  However, I am not sure if the time calls will have the same resolution on all platforms. 

Code: [Select]
import ivi
import time

dmm = ivi.agilent.agilent34401A("TCPIP::192.168.1.105::gpib,22::INSTR")

file_name = 'log'

sample_period = 2.0
sample_count = 100

fp = open(file_name '.csv', 'w')

kp = open(file_name '.kst', 'w')

kp.write("""<?xml version="1.0" encoding="UTF-8"?>
<kst version="2.0">
    <data>
        <source reader="ASCII file" file="{0}.csv">
            <properties vector="INDEX" interpretation="1" delimiters="#" columntype="2" columndelimiter="," headerstart="1" fields="0" readfields="true" usedot="true" columnwidthisconst="false" readunits="false" units="0" limitFileBuffer="false" limitFileBufferSize="0" useThreads="0" asciiTimeFormat="hh:mm:ss.zzz" dataRate="1" offsetDateTime="false" offsetFileDate="false" offsetRelavive="true" dateTimeOffset="2014-03-24T22:16:59" relativeOffset="0"/>
        </source>
    </data>
    <variables>
        <datavector file="{0}.csv" field="time" start="0" count="15985" skip="-1" doAve="false" startUnits="" rangeUnits="" initialVNum="1" initialXNum="25"/>
        <datavector file="{0}.csv" field="sample" start="0" count="15985" skip="-1" doAve="false" startUnits="" rangeUnits="" initialVNum="2" initialXNum="37"/>
    </variables>
    <objects/>
    <relations>
        <curve xvector="time (V1)" yvector="sample (V2)" color="#0000ff" headcolor="#008000" barfillcolor="#000000" haslines="true" linewidth="0" linestyle="0" haspoints="false" pointtype="3" pointdensity="0" pointsize="12" hasbars="false" ignoreautoscale="false" hashead="false" headtype="0" initialCNum="1"/>
    </relations>
    <graphics>
        <view name="View &amp;1" width="1470" height="633" color="#ffffff" style="1">
            <plot tiedxzoom="false" tiedyzoom="false" leftlabelvisible="true" bottomlabelvisible="true" rightlabelvisible="true" toplabelvisible="true" globalfont="Sans,10,-1,5,50,0,0,0,0,0" globalfontscale="16" globalfontcolor="#000000" showlegend="true" hidebottomaxislabel="false" hidetopaxislabel="false" hideleftaxislabel="false" hiderightaxislabel="false" numberaxislabelscale="true" initialPlotNum="1" name="Plot">
                <relativesize width="1" height="1" centerx="0.5" centery="0.5" posx="0" posy="0" leftx="0" lefty="1" rightx="1" righty="1" fixaspect="false" lockpostodata="false"/>
                <cartesianrender name="Cartesian Plot" type="1">
                    <relation tag="sample vs time (C1)"/>
                </cartesianrender>
            </plot>
        </view>
    </graphics>
</kst>""".format(file_name))

kp.close()

fp.write("time, sample\n")

target_time = time.time()   sample_period

for sample in range(sample_count):
    last_sample_time = time.time()
    sample_value = dmm.measurement.read(1)
    print(sample_value)
   
    fp.write("%f, %g\n" % (last_sample_time, sample_value))
    fp.flush()
   
    offset = target_time - time.time()
   
    if offset > 0:
        time.sleep(offset)
   
    target_time  = sample_period
   
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline mrflibble

  • Super Contributor
  • ***
  • Posts: 2051
  • Country: nl
Re: Python-based instrument control
« Reply #30 on: April 01, 2014, 02:36:34 am »
I've been mucking about with kst a bit as well. Getting a simple plot working was totally trivial, but for things like a DataMatrix I find the available documentation and examples frustratingly lacking in detail. I hope I am just blind and unable to find the right docs. But what I could find (KstBook.pdf or some such) does not do the trick for me. "Use this incomplete example and it will work". Yes, except for all the other instances and 45987 permutations of those instances where it doesn't. "You can use xml files." Oh cool! Any docs on that? "Docs? What now?"

Do you happen to have an example of a datasource (defined in xml or kstscript or whatever) using an ascii file for the matrix data. Or at this point, anything that works at all? :P

Like I said, simple plot, too easy. Datasource + datamatrix according to the docs ... nope.
Mostly I am confuzled by the "[MATRIX,size-or-something,0,0,1,1]" part of it. That I can understand reading the docs. Except that it doesn't work as advertised.

Oh another thing that maybe you know ... is there any way to "overwrite" old datapoints in real time? To get a sort of oscilloscope display as it were. I suppose you could do that by just resetting the data + refreshing the plot, but that seems a bit lame.

At the moment I can get a spectral waterfall plot with a static matrix as data. I can also grab the spectrum from the DSA815. Oh if only certain documentation didn't suck so bad, then I could do a waterfall plot from the DSA815 in real-time. Grrrrr.

I'll get back to it later, but for now I am fed up with this.
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #31 on: April 01, 2014, 03:40:07 am »
I have not looked at any Kst documentation so far, actually.  All I did was put together something decent in the Kst GUI, then I saved the xml file and played around with it.  But I wasn't trying to do anything more complicated than a single curve of one variable, so this might not be the best strategy for everyone. 

It's not surprising the documentatio is bad, that tends to happen a lot with open source projects.  For Pyton IVI,  am trying to mitigate this issue to some extent by merging the documentation with the code as much as possible, but it remains to be seen how well this will work.  And this is certainly not the best solution for every project. 

I would suggest trying to save an XML file from Kst, and then playing around with that file and reloading it.  It may be faster to figure out how the application works by actually running it than from looking at outdated and/or incomplete documentaion. 
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline Noise Floor

  • Regular Contributor
  • *
  • Posts: 117
  • Country: us
Re: Python-based instrument control
« Reply #32 on: May 22, 2014, 01:31:24 am »
I've been mucking about with kst a bit as well. Getting a simple plot working was totally trivial, but for things like a DataMatrix I find the available documentation and examples frustratingly lacking in detail. I hope I am just blind and unable to find the right docs. But what I could find (KstBook.pdf or some such) does not do the trick for me. "Use this incomplete example and it will work". Yes, except for all the other instances and 45987 permutations of those instances where it doesn't. "You can use xml files." Oh cool! Any docs on that? "Docs? What now?"

I too have been playing with Kst and have the same complaint.  However this is par for the course for projects like this, if it catches on, documentation will eventually improve.

Alex F., any thought on starting a GitHub project around this? 
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #33 on: May 22, 2014, 01:54:25 am »
I've been mucking about with kst a bit as well. Getting a simple plot working was totally trivial, but for things like a DataMatrix I find the available documentation and examples frustratingly lacking in detail. I hope I am just blind and unable to find the right docs. But what I could find (KstBook.pdf or some such) does not do the trick for me. "Use this incomplete example and it will work". Yes, except for all the other instances and 45987 permutations of those instances where it doesn't. "You can use xml files." Oh cool! Any docs on that? "Docs? What now?"

I too have been playing with Kst and have the same complaint.  However this is par for the course for projects like this, if it catches on, documentation will eventually improve.

Alex F., any thought on starting a GitHub project around this?

Around what?  Scripts for writing out Kst files?  General python-based data logging?  Combination of the two?  Give me a nice projct name and I'll throw up a repo. 

Unfortunately, my time has been almost completely devoted to a few other projects the past few weeks (fyi: the Altera reference design for PCIe gen 3 SUCKS, I found 3 different bugs in it already  |O), and they Python instrument control stuff is currently on the back burner. 
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline Noise Floor

  • Regular Contributor
  • *
  • Posts: 117
  • Country: us
Re: Python-based instrument control
« Reply #34 on: May 22, 2014, 02:40:59 am »
I've been mucking about with kst a bit as well. Getting a simple plot working was totally trivial, but for things like a DataMatrix I find the available documentation and examples frustratingly lacking in detail. I hope I am just blind and unable to find the right docs. But what I could find (KstBook.pdf or some such) does not do the trick for me. "Use this incomplete example and it will work". Yes, except for all the other instances and 45987 permutations of those instances where it doesn't. "You can use xml files." Oh cool! Any docs on that? "Docs? What now?"

I too have been playing with Kst and have the same complaint.  However this is par for the course for projects like this, if it catches on, documentation will eventually improve.

Alex F., any thought on starting a GitHub project around this?

Around what?  Scripts for writing out Kst files?  General python-based data logging?  Combination of the two?  Give me a nice projct name and I'll throw up a repo. 

Unfortunately, my time has been almost completely devoted to a few other projects the past few weeks (fyi: the Altera reference design for PCIe gen 3 SUCKS, I found 3 different bugs in it already  |O), and they Python instrument control stuff is currently on the back burner.

Ignore my comment, I now noticed your signature.   |O

I hear you on having to many projects, this falls in the "nice to do" category.
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #35 on: May 22, 2014, 03:14:40 am »

Ignore my comment, I now noticed your signature.   |O

I hear you on having to many projects, this falls in the "nice to do" category.

No worries.  Actually, the instrument control stuff is something that we're looking in to using for taking some measurements on some experimental photonic switching chips (we have two different ones we're hoping to work with soon, one MEMS based and one silicon photonics based).  Taking measurements of these switches requires coordinating not only the signal sources, sinks, and routing, but also changing the switch configuration itself, so it's a pretty complicated interfacing issue.  The idea is to use Python to control the test equipment as well as an FPGA to drive the DUT.  So even if it's on the back burner for the moment, it's going to pick up again once we're gearing up to test one of the switches. 
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline miguelvp

  • Super Contributor
  • ***
  • Posts: 5550
  • Country: us
Re: Python-based instrument control
« Reply #36 on: May 20, 2015, 07:28:44 am »
Waking up this thread.

So in: https://www.eevblog.com/forum/testgear/new-rigol-ds1054z-oscilloscope/msg675845/#msg675845
Bingo found this link for the DS1000z series to perform a screen capture.

Python screendump for windows via LXI (TCP/IP)
https://hackaday.io/project/5807-driverless-rigol-ds1054z-screen-capture-over-lan

I did try it on my DS2000 scope but I guess it doesn't have telnet capabilities. So I did stumble on alex.forencich's vxi11 and I substitute it and I was able to do the capture on my DS2000, but it took 2 minutes and 40 seconds per capture.

https://www.eevblog.com/forum/testgear/new-rigol-ds1054z-oscilloscope/msg676102/#msg676102

So I used wireshark to check what is going on, and for some reason it doesn't like to read large packets and I did modified the script again so it wouldn't go past the MTU size of 1514 bytes and:

Quote
C:\Users\Miguel\Downloads\Rigol>python OscScreenGrabLAN.py

Usage:
    OscScreenGrabLAN.py

This program capture the image displayed
    by a Rigol DS2000 series oscilloscope, then save it on the computer
    as a PNG file with a timestamp in the file name.

    The program is using LXI protocol, so the computer
    must have LAN connection with the oscilloscope.
    USB and/or GPIB connections are not used by this software.

    No VISA, IVI or Rigol drivers are needed.

Instrument ID:
RIGOL TECHNOLOGIES,DS2302,DS2A$$$$$$$$,00.03.03.SP1 (blanked my serial number for the paste)
Receiving: [##########] 100%
Read took 3 seconds
Saved file: DS2302_2015-05-20_01.54.15.png

And it took just 4 seconds (I know it says 3 but a wireshark capture shows it was 3.98 seconds).

So here is the newest version of the script:
Code: [Select]
__author__ = 'RoGeorge'
#
# Modified to work with DS2000 by miguelvp
# using vxi11 by Alex Forencich and Michael Walle
# [url]https://github.com/alexforencich/python-vxi11[/url]
#
import vxi11
import time
import sys
import os
import Image
import StringIO
from datetime import datetime

# Update the next lines for your own default settings:
path_to_save = ""
IP_DS2000 = "192.168.1.6"

expected_len = 1152067
TMC_header_len = 11
terminator_len = 2

company = 0
model = 1
serial = 2

# Check parameters
script_name = os.path.basename(sys.argv[0])

# Print usage
print
print "Usage:"
print "    " + script_name
print
print "This program capture the image displayed"
print "    by a Rigol DS2000 series oscilloscope, then save it on the computer"
print "    as a PNG file with a timestamp in the file name."
print
print "    The program is using LXI protocol, so the computer"
print "    must have LAN connection with the oscilloscope."
print "    USB and/or GPIB connections are not used by this software."
print
print "    No VISA, IVI or Rigol drivers are needed."
print

# Create/check if 'path' exists

# Check network response (ping)
response = os.system("ping -n 1 -w 20 " + IP_DS2000 + " > nul")
if response != 0:
print
print "No response pinging " + IP_DS2000
print "Check network cables and settings."
print "You should be able to ping the oscilloscope."
sys.exit("ERROR")

# Open a VXI11 session
instr =  vxi11.Instrument(IP_DS2000)
instrument_id = instr.ask("*IDN?")

# Check if instrument is set to accept LAN commands
if instrument_id == "command error":
print instrument_id
print "Check the oscilloscope settings."
print "Utility -> IO Setting -> LAN Set"
sys.exit("ERROR")

# Check if instrument is indeed a Rigol DS2000 series
id_fields = instrument_id.split(",")
if (id_fields[company] != "RIGOL TECHNOLOGIES") or \
(id_fields[model][:3] != "DS2"):
print
print "ERROR: No Rigol from series DS2000 found at ", IP_DS2000
sys.exit("ERROR")

print "Instrument ID:"
print instrument_id

# Prepare filename as C:\MODEL_YYYY-MM-DD_HH.MM.SS
timestamp = time.strftime("%Y-%m-%d_%H.%M.%S", time.localtime())
filename = path_to_save + id_fields[model] + "_" + timestamp

# Ask for an oscilloscope display print screen
instr.write_raw("display:data?")

# Time transfer duration
starttime = datetime.now()

# Get TMC Header
buff = instr.read_raw(TMC_header_len)
# Initialize previous progress so we only update on changes
previous_progress = -1;
# Read only 1420 bytes at a time until done, a bigger amount stalls the rpc and makes the transfer slow
while len(buff) < expected_len:
tmp = instr.read_raw(1420)
if len(tmp) == 0:
break
buff += tmp
# Compute and print progress bar
progress =  100 - (expected_len - len(buff))*100/expected_len
if (previous_progress != progress):
sys.stdout.write('\rReceiving: [{0}] {1}%'.format('#'*(progress/10)+' '*(10-progress/10), progress))
sys.stdout.flush()
previous_progress = progress

# Print delta time in seconds
currenttime = datetime.now()
delta = currenttime - starttime
print
print "Read took", delta.seconds, "seconds"

# Strip TMC Blockheader and terminator bytes
buff = buff[TMC_header_len:-terminator_len]

# Save as PNG
im = Image.open(StringIO.StringIO(buff))
im.save(filename + ".png", "PNG")
print "Saved file:", filename + ".png"

# Save as BMP
# scr_file = open(filename + ".bmp", "wb")
# scr_file.write(buff)
# scr_file.close()
# print "Saved file:", filename + ".bmp"

# Close Instrument
instr.close()

It has a progress bar and all :)

So it's 40 times faster than the 160 seconds it was taking before and it's even faster than saving it to a thumb drive!  :scared:

Thank you Alex for providing this :-+

If you notice I did change the raw read to 1420 at a time instead of:

Code: [Select]
buff = instr.ask_raw("display:data?", expected_len)

So I decided to change vxi11.py open from:
Code: [Select]
        self.max_recv_size = min(max_recv_size, 1073741824)

to:
Code: [Select]
        self.max_recv_size = min(max_recv_size, 1420)

and I can revert back the code and achieve the same speed, but I do like the progress bar so I think I'll keep mine like it is and changed the size in both places.

Since you support other protocols other than VXI-11 over TCP I guess I'll leave it up to you to do the change to have the right MTU per protocol, but for TCPIP it seems 1420 is the best, if I use 1421 it causes extra TCP continuation packets and it stalls the transfer but with 1420 it's just perfect!

Wireshark happy:


Capture PNG:


Thank you again alex.forencich!!
« Last Edit: May 20, 2015, 07:34:54 am by miguelvp »
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #37 on: May 20, 2015, 07:34:28 am »
Nice work!  Is there any issue with just doing instr.max_recv_size = 1420 and then using the standard read call?

Edit: never mind, I just looked at the code for python-vxi11.  I wonder if I need to add a max_block_size parameter so that this will be done automatically in read_raw.  Also, are you sure the screenshots are always exactly the same size?
« Last Edit: May 20, 2015, 07:39:36 am by alex.forencich »
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline miguelvp

  • Super Contributor
  • ***
  • Posts: 5550
  • Country: us
Re: Python-based instrument control
« Reply #38 on: May 20, 2015, 07:48:28 am »
As for the screenshots, on the ds2000 they are always the same:



But on the DS1000Z it seems it has an extra byte at the end.

I didn't try the instr.max_recv_size = 1420 I guess that would work too, I just found your python project yesterday so I didn't dig that much into it :)


Edit: I guess I could just parse the blockheader and figure out the size of what to read.
« Last Edit: May 20, 2015, 07:53:32 am by miguelvp »
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #39 on: May 20, 2015, 08:00:59 am »
There is actually some code to do that in python-ivi, see read_ieee_block.  It deals with parsing the header correctly and then reading out the correct number of bytes.  However, it does try to read it all in one shot, so it seems that an additional parameter to specify the maximum read size would be a good idea. 
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline miguelvp

  • Super Contributor
  • ***
  • Posts: 5550
  • Country: us
Re: Python-based instrument control
« Reply #40 on: May 20, 2015, 08:07:14 am »
That's awesome, thanks.

One question, why are the timeouts set to 10 seconds? I did change them locally to 1 second and it works fine.

_timeout & _lock_timeout
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #41 on: May 20, 2015, 08:13:28 am »
Actually, the timeouts should probably be extended quite a bit.  I have to remember to change the timeout setting to a minute or so when yanking screenshots from a few older instruments (namely my 8593E SA) through my GPIB to LAN box.  The default right now was just something that seemed reasonable at the time.  I would say that it's probably a better idea to extend those timeouts as much as is reasonable as it's better to have to wait a while for a timeout than to get a timeout during a large transfer.  With something like python-ivi, this could even be changed at run time for specific commands.  This is a major issue with the USBTMC side of things as getting a timeout during a transfer usually requires rebooting the instrument to recover. 
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline miguelvp

  • Super Contributor
  • ***
  • Posts: 5550
  • Country: us
Re: Python-based instrument control
« Reply #42 on: May 20, 2015, 09:12:36 am »
Maybe the high timeouts are needed due to the overhead of using large read sizes.

Didn't look at the code and it's late, but splitting the reads to smaller sub chunks will require smaller timeouts, right?
 

Offline gmb42

  • Frequent Contributor
  • **
  • Posts: 294
  • Country: gb
Re: Python-based instrument control
« Reply #43 on: May 20, 2015, 11:20:14 am »
FWIW, to anonymise Wireshark captures have a look at TraceWrangler.
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #44 on: May 20, 2015, 03:56:45 pm »
It should never timeout during normal operation. 
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline VintageNut

  • Frequent Contributor
  • **
  • Posts: 534
  • Country: 00
Re: Python-based instrument control
« Reply #45 on: May 20, 2015, 07:00:44 pm »
I have written short demo python programs for Keithley 2230-30-1 power supply, Keithley 236 and 237 SMUs for transistor curve tracing and Keithley 3706A with 3720 switch card. These were all efforts to get a particular instrument behavior characterized and/or take readings.

I have a Keithley model 7001 on the way that will receive the python treatment for nanovolt measurements.
« Last Edit: May 20, 2015, 07:02:38 pm by VintageNut »
working instruments :Keithley 260,261,2750,7708, 2000 (calibrated), 2015, 236, 237, 238, 147, 220,  Rigol DG1032  PAR Model 128 Lock-In amplifier, Fluke 332A, Gen Res 4107 KVD, 4107D KVD, Fluke 731B X2 (calibrated), Fluke 5450A (calibrated)
 
The following users thanked this post: smoothVTer

Online bingo600

  • Super Contributor
  • ***
  • Posts: 1976
  • Country: dk
Re: Python-based instrument control
« Reply #46 on: May 20, 2015, 08:57:06 pm »
Waking up this thread.

...
...
...

Thank you again alex.forencich!!

Great find  :-+ :-+ (debugging)

Now i'll have to install VXI-11 , also because i know Alex might have improved the linux-gpib support

/Bingo
 

Offline eas

  • Frequent Contributor
  • **
  • Posts: 601
  • Country: us
    • Tech Obsessed
Re: Python-based instrument control
« Reply #47 on: July 24, 2015, 09:37:18 pm »
I have written short demo python programs for Keithley 2230-30-1 power supply, Keithley 236 and 237 SMUs for transistor curve tracing and Keithley 3706A with 3720 switch card. These were all efforts to get a particular instrument behavior characterized and/or take readings.

I have a Keithley model 7001 on the way that will receive the python treatment for nanovolt measurements.

Did you create IVI instrument drivers for any of these?  If so, do you mind sharing them somewhere? I have started working with some K2000 and K2700 DMMs and would like to see how much can be reused, particularly for switch control. Right now I'm just issuing SCPI commands over VXI-11 (I'm using an ICS 8065).
 

Offline VintageNut

  • Frequent Contributor
  • **
  • Posts: 534
  • Country: 00
Re: Python-based instrument control
« Reply #48 on: July 25, 2015, 07:09:00 pm »
I did not create any driver(s). I just send SCPI commands via PyVisa which is via NI Visa. I use Python 2.7
working instruments :Keithley 260,261,2750,7708, 2000 (calibrated), 2015, 236, 237, 238, 147, 220,  Rigol DG1032  PAR Model 128 Lock-In amplifier, Fluke 332A, Gen Res 4107 KVD, 4107D KVD, Fluke 731B X2 (calibrated), Fluke 5450A (calibrated)
 

Offline eas

  • Frequent Contributor
  • **
  • Posts: 601
  • Country: us
    • Tech Obsessed
Re: Python-based instrument control
« Reply #49 on: July 26, 2015, 07:03:54 am »
Thanks for the clarification.
 

Offline chaugen1

  • Newbie
  • Posts: 2
  • Country: us
Re: Python-based instrument control
« Reply #50 on: April 18, 2016, 02:39:38 pm »
Sorry to wake up an old thread.  Can start a new one if necessary.

I would love to contribute to this project.  I am working on a driver for the Agilent U2701A USB Modular Oscilloscope and would be happy to share once I have it done and tested.

In the meantime, I need some help.  I have usbtmc installed and it can see the instrument but when I try to ask it for anything, PyUSB throws up a "Error 32: Pipe Error"

Here is output:  first the device as seen from PyUSB:

Code: [Select]
DEVICE ID 0957:2818 on Bus 002 Address 005 =================
 bLength                :   0x12 (18 bytes)
 bDescriptorType        :    0x1 Device
 bcdUSB                 :  0x200 USB 2.0
 bDeviceClass           :    0x0 Specified at interface
 bDeviceSubClass        :    0x0
 bDeviceProtocol        :    0x0
 bMaxPacketSize0        :   0x40 (64 bytes)
 idVendor               : 0x0957
 idProduct              : 0x2818
 bcdDevice              :  0x100 Device 1.0
 iManufacturer          :    0x1 Agilent Technologies
 iProduct               :    0x2 Agilent U2701A/U2702A
 iSerialNumber          :    0x3 MY********
 bNumConfigurations     :    0x1
  CONFIGURATION 1: 100 mA ==================================
   bLength              :    0x9 (9 bytes)
   bDescriptorType      :    0x2 Configuration
   wTotalLength         :   0x61 (97 bytes)
   bNumInterfaces       :    0x1
   bConfigurationValue  :    0x1
   iConfiguration       :    0x0
   bmAttributes         :   0xc0 Self Powered
   bMaxPower            :   0x32 (100 mA)
    INTERFACE 0: Reserved ==================================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x0
     bAlternateSetting  :    0x0
     bNumEndpoints      :    0x4
     bInterfaceClass    :    0x0 Reserved
     bInterfaceSubClass :    0x0
     bInterfaceProtocol :    0x0
     iInterface         :    0x0
      ENDPOINT 0x81: Interrupt IN ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x81 IN
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x1
      ENDPOINT 0x1: Interrupt OUT ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x1 OUT
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x1
      ENDPOINT 0x82: Bulk IN ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x82 IN
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :  0x200 (512 bytes)
       bInterval        :    0x0
      ENDPOINT 0x2: Bulk OUT ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x2 OUT
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :  0x200 (512 bytes)
       bInterval        :    0x0
    INTERFACE 0, 1: Reserved ===============================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x0
     bAlternateSetting  :    0x1
     bNumEndpoints      :    0x6
     bInterfaceClass    :    0x0 Reserved
     bInterfaceSubClass :    0x0
     bInterfaceProtocol :    0x0
     iInterface         :    0x0
      ENDPOINT 0x81: Interrupt IN ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x81 IN
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x1
      ENDPOINT 0x1: Interrupt OUT ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x1 OUT
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x1
      ENDPOINT 0x82: Bulk IN ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x82 IN
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :  0x200 (512 bytes)
       bInterval        :    0x0
      ENDPOINT 0x2: Bulk OUT ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x2 OUT
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :  0x200 (512 bytes)
       bInterval        :    0x0
      ENDPOINT 0x83: Isochronous IN ========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x83 IN
       bmAttributes     :    0x1 Isochronous
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x1
      ENDPOINT 0x3: Isochronous OUT ========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x3 OUT
       bmAttributes     :    0x1 Isochronous
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x1

And here is what I get when I try to probe it:

Code: [Select]
>>> import usbtmc
>>> instr = usbtmc.Instrument(0x0957,0x2818)
>>> print(instr.ask("*IDN?"))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "build/bdist.linux-x86_64/egg/usbtmc/usbtmc.py", line 607, in ask
  File "build/bdist.linux-x86_64/egg/usbtmc/usbtmc.py", line 587, in write
  File "build/bdist.linux-x86_64/egg/usbtmc/usbtmc.py", line 467, in write_raw
  File "build/bdist.linux-x86_64/egg/usbtmc/usbtmc.py", line 341, in open
  File "build/bdist.linux-x86_64/egg/usbtmc/usbtmc.py", line 677, in clear
  File "/usr/local/lib/python2.7/dist-packages/usb/core.py", line 1043, in ctrl_transfer
    self.__get_timeout(timeout))
  File "/usr/local/lib/python2.7/dist-packages/usb/backend/libusb1.py", line 883, in ctrl_transfer
    timeout))
  File "/usr/local/lib/python2.7/dist-packages/usb/backend/libusb1.py", line 595, in _check
    raise USBError(_strerror(ret), ret, _libusb_errno[ret])
usb.core.USBError: [Errno 32] Pipe error
>>>


Anyone have any thoughts on this?
Thanks so much.
 

Offline douardda

  • Regular Contributor
  • *
  • Posts: 82
  • Country: fr
Re: Python-based instrument control
« Reply #51 on: April 18, 2016, 05:48:11 pm »
For realtime plots I think the new plotting library being developed mainly by the continuum.io folks ... http://bokeh.pydata.org

Bokeh has client server architecture and realtime plotting appears to be on the agenda for improvements. This should I hope (I've not used it yet) allow the GUI of the plot operate independently of the data acquisition.
People looking for plotting libraries for python might be  interested in  pyqtgraph also http://www.pyqtgraph.org/ i haven't really used it  for now but it looks very  powerful

David


Envoyé de mon GT-N7105 en utilisant Tapatalk

 

Offline bson

  • Supporter
  • ****
  • Posts: 2265
  • Country: us
Re: Python-based instrument control
« Reply #52 on: April 18, 2016, 09:14:16 pm »
Anyone have any thoughts on this?

The datasheet says USBTMC (GPIB tunneled over USB) but googling seems to suggest Agilent actually uses an FTDI serial port.  If so you may need to add a device rule for that vid,pid to configure a serial port (canonically named /dev/ttyUSB<n>).  If that works, use the serial port to talk to it, issuing the same SCPI commands or whatever it accepts.  The drawback with this scheme is the device can't issue SRQs or otherwise asynchronously request the host's attention.
« Last Edit: April 18, 2016, 09:15:55 pm by bson »
 

Offline chaugen1

  • Newbie
  • Posts: 2
  • Country: us
Re: Python-based instrument control
« Reply #53 on: April 18, 2016, 11:58:51 pm »
Thanks for the feedback.  I took a look and there is in fact no new device that shows up when I plug in the o-scope and check ls /dev.  Out of curiosity, I took it apart and the USB controller is actually NXP ISP1583BS.

Do any of you know if this controller is USBTMC compatible/capable?
 

Offline bson

  • Supporter
  • ****
  • Posts: 2265
  • Country: us
Re: Python-based instrument control
« Reply #54 on: April 19, 2016, 01:25:16 am »
Perhaps you need a rule for it to create /dev/usbtmc for that vid,pid and a usbtmc driver?  Not sure what the python code does and if it expects a driver.

Did you follow the instructions for configuring udev? https://github.com/python-ivi/python-usbtmc

What if you do "lsusb -t" to view the device tree, do you see the driver for it?  Is the kernel usbtmc driver loaded (lsmod)?  If not, try loading it after adding the device udev rules.  Or try unloading it if it is.
« Last Edit: April 19, 2016, 01:31:27 am by bson »
 

Offline KSP

  • Regular Contributor
  • *
  • Posts: 98
  • Country: gb
Re: Python-based instrument control
« Reply #55 on: October 03, 2016, 12:00:14 pm »
Sorry to revive this thread once more, and sorry again if this is a repeat question as I have not trawled through all three pages of this thread  :-[ But does python-ivi require you to write ivi drivers for all devices used, or is it possible for an existing ivi-c or ivi-com driver to be used?

I've had a little success with ivi-com drivers using the 'comtypes' module, but it is in no way seamless
 

Offline alex.forencichTopic starter

  • Frequent Contributor
  • **
  • Posts: 397
  • Country: us
    • Alex Forencich
Re: Python-based instrument control
« Reply #56 on: February 15, 2017, 05:26:13 am »
Sorry to wake up an old thread.  Can start a new one if necessary.

I would love to contribute to this project.  I am working on a driver for the Agilent U2701A USB Modular Oscilloscope and would be happy to share once I have it done and tested.

In the meantime, I need some help.  I have usbtmc installed and it can see the instrument but when I try to ask it for anything, PyUSB throws up a "Error 32: Pipe Error"

Here is output:  first the device as seen from PyUSB:

Code: [Select]
DEVICE ID 0957:2818 on Bus 002 Address 005 =================
 bLength                :   0x12 (18 bytes)
 bDescriptorType        :    0x1 Device
 bcdUSB                 :  0x200 USB 2.0
 bDeviceClass           :    0x0 Specified at interface
 bDeviceSubClass        :    0x0
 bDeviceProtocol        :    0x0
 bMaxPacketSize0        :   0x40 (64 bytes)
 idVendor               : 0x0957
 idProduct              : 0x2818
 bcdDevice              :  0x100 Device 1.0
 iManufacturer          :    0x1 Agilent Technologies
 iProduct               :    0x2 Agilent U2701A/U2702A
 iSerialNumber          :    0x3 MY********
 bNumConfigurations     :    0x1
  CONFIGURATION 1: 100 mA ==================================
   bLength              :    0x9 (9 bytes)
   bDescriptorType      :    0x2 Configuration
   wTotalLength         :   0x61 (97 bytes)
   bNumInterfaces       :    0x1
   bConfigurationValue  :    0x1
   iConfiguration       :    0x0
   bmAttributes         :   0xc0 Self Powered
   bMaxPower            :   0x32 (100 mA)
    INTERFACE 0: Reserved ==================================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x0
     bAlternateSetting  :    0x0
     bNumEndpoints      :    0x4
     bInterfaceClass    :    0x0 Reserved
     bInterfaceSubClass :    0x0
     bInterfaceProtocol :    0x0
     iInterface         :    0x0
      ENDPOINT 0x81: Interrupt IN ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x81 IN
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x1
      ENDPOINT 0x1: Interrupt OUT ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x1 OUT
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x1
      ENDPOINT 0x82: Bulk IN ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x82 IN
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :  0x200 (512 bytes)
       bInterval        :    0x0
      ENDPOINT 0x2: Bulk OUT ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x2 OUT
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :  0x200 (512 bytes)
       bInterval        :    0x0
    INTERFACE 0, 1: Reserved ===============================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x0
     bAlternateSetting  :    0x1
     bNumEndpoints      :    0x6
     bInterfaceClass    :    0x0 Reserved
     bInterfaceSubClass :    0x0
     bInterfaceProtocol :    0x0
     iInterface         :    0x0
      ENDPOINT 0x81: Interrupt IN ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x81 IN
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x1
      ENDPOINT 0x1: Interrupt OUT ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x1 OUT
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x1
      ENDPOINT 0x82: Bulk IN ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x82 IN
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :  0x200 (512 bytes)
       bInterval        :    0x0
      ENDPOINT 0x2: Bulk OUT ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x2 OUT
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :  0x200 (512 bytes)
       bInterval        :    0x0
      ENDPOINT 0x83: Isochronous IN ========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x83 IN
       bmAttributes     :    0x1 Isochronous
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x1
      ENDPOINT 0x3: Isochronous OUT ========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x3 OUT
       bmAttributes     :    0x1 Isochronous
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x1

And here is what I get when I try to probe it:

Code: [Select]
>>> import usbtmc
>>> instr = usbtmc.Instrument(0x0957,0x2818)
>>> print(instr.ask("*IDN?"))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "build/bdist.linux-x86_64/egg/usbtmc/usbtmc.py", line 607, in ask
  File "build/bdist.linux-x86_64/egg/usbtmc/usbtmc.py", line 587, in write
  File "build/bdist.linux-x86_64/egg/usbtmc/usbtmc.py", line 467, in write_raw
  File "build/bdist.linux-x86_64/egg/usbtmc/usbtmc.py", line 341, in open
  File "build/bdist.linux-x86_64/egg/usbtmc/usbtmc.py", line 677, in clear
  File "/usr/local/lib/python2.7/dist-packages/usb/core.py", line 1043, in ctrl_transfer
    self.__get_timeout(timeout))
  File "/usr/local/lib/python2.7/dist-packages/usb/backend/libusb1.py", line 883, in ctrl_transfer
    timeout))
  File "/usr/local/lib/python2.7/dist-packages/usb/backend/libusb1.py", line 595, in _check
    raise USBError(_strerror(ret), ret, _libusb_errno[ret])
usb.core.USBError: [Errno 32] Pipe error
>>>


Anyone have any thoughts on this?
Thanks so much.

The U2701A should be USBTMC capable.  However, it needs to be initialized first.  If it comes up with idProduct 0x2818, it's in some sort of firmware update mode.  The Agilent drivers then check the firmware version, possibly update it, then send a command to reset into the actual firmware.  In this case, idProduct will be 0x2918 and it will operate as a USBTMC device.  This needs to be emulated when the Agilent drivers are not present.  At least, this is extrapolating from the U2723A that I have; I figure all of the Agilent instruments of that style all operate the same way. 

Support for the U2701A/U2702A is currently in the works, see: https://github.com/python-ivi/python-usbtmc/issues/31 .  I'm waiting on a wireshark trace with the boot sequence. 
Python-based instrument control: Python IVI, Python VXI-11, Python USBTMC
 

Offline LEDPaul

  • Newbie
  • Posts: 4
  • Country: no
Re: Python-based instrument control
« Reply #57 on: April 05, 2018, 02:02:04 pm »
Just started to learn about Python-IVI. So far I wrote my own stuff, but to redo everything for each new machine is not really efficient. So probably better to use Python-IVI. Next machine I will add is probably the Agilent/Keysight 34465A DMM. It has some functions that  the older models do not have, or I have not found it yet, or the support was not written yet. So I need to learn to expand it. I have some other machines that I probably can convert, but that will be later.

Is this the place to best ask questions on Pyhthon-IVI or do that better on github?

Also if anyone has some more complex examples on hot to use a dmm in python-ivi I would love to see that.
 

Offline alm

  • Super Contributor
  • ***
  • Posts: 2840
  • Country: 00
Re: Python-based instrument control
« Reply #58 on: April 08, 2018, 10:09:16 am »
I attached a script that I used to collect measurements from a DMM through Python-IVI at different integration times, so I could determine the noise (is it better to average 100 measurements with 1 s integration time, or 10 measurements with 10 s integration time?). This is the general pattern I have been using: in init_func() I set up whatever instruments I want to use and set their range, function, etc, and in loop_func() I collect the measurements. If I need to do something else, I only have to modify these two functions.
 
The following users thanked this post: LEDPaul

Offline LEDPaul

  • Newbie
  • Posts: 4
  • Country: no
Re: Python-based instrument control
« Reply #59 on: April 23, 2018, 03:18:40 pm »
I mannaged to get my 34465A working with python-ivi. I can measure the current at 200us interval and read 1000 or even 50k samples. To be able to do this I added some functions to python-IVI locally. Below the user code to read the current:

Code: [Select]
dmm = ivi.agilent.agilent34465A("USB0::0x2A8D::0x0101::MYxxxxxxxx::0::INSTR")
dmm.configure('dc_current', 10e-3, 1e-9)
dmm.advanced.auto_zero = 'once'
dmm.advanced.aperture_time = 200e-6
dmm.trigger.delay = 0.0
dmm.trigger.source = 'bus'
dmm.trigger.multi_point.configure(trigger_count=1,
                                  sample_count=1000,
                                  sample_trigger='TIM',
                                  sample_interval=200e-6)
dmm.measurement.initiate()
dmm.send_software_trigger()
sleep(1000 * 200e-6)
res = dmm.measurement.fetch_multi_point(1000 * 200e-6, 1000)

code for the 34465A can be found here: https://github.com/phsdv/python-ivi
« Last Edit: April 25, 2018, 12:20:04 pm by LEDPaul »
 
The following users thanked this post: nugglix, alm

Online RoGeorge

  • Super Contributor
  • ***
  • Posts: 6145
  • Country: ro
Re: Python-based instrument control
« Reply #60 on: January 02, 2021, 06:46:08 pm »
Just installed python-ivi and python-vxi11 these days from https://github.com/python-ivi/python-ivi
and it works great, thank you!   :D

Also, found yesterday another repository https://github.com/alexforencich/python-ivi/
And yet another branch with a pull request for Rigol oscilloscope drivers (I am not very good with git/github):
https://github.com/ianrrees/python-ivi/

I am looking for python-ivi drivers for Rigol DS1104Z (DS1000Z) oscilloscope and Rigol DG4202 (DG4000) generator.

Not sure if there are any working python-ivi drivers for the above Rigol osc and generator, or which one to use.  I have other questions, too, mainly regarding finding the latest python-ivi drivers, summarized in a separate topic:
https://www.eevblog.com/forum/metrology/software-for-automated-testing-(scpi)/msg3397446/#msg3397446

Any hints, please?
« Last Edit: January 02, 2021, 06:48:00 pm by RoGeorge »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf