Search code examples
pythonmatlabmatlab-classgpib

Translating GPIB in MATLAB to PyVISA


I've inherited some MATLAB code used to program an XYZ stage through a GPIB connection. To make it more compatible with some existing code in Python, I need to somehow translate it, e.g. using the PyVISA package. I would really like some help with that!

So, what I got working so far is just the basic stuff, i.e.

from visa import *
stage = instrument("GPIB::2")

From this I can use the identification command and correctly get the ID of my device:

stage.write("*IDN?")

So, any idea how to convert the following MATLAB into the appropriate PyVISA commands? My biggest issue is I don't really know how to translate the syntax...

classdef cascade12000b < handle
    properties(Constant)
        GPIB_ADDRESS = 28;
        DEVICE_TAG = 'Cascade 12000B Probe Station';
        DEVICE_ID = 2;
    end

    properties
        gpib_conn;
    end

    methods
        function [obj] = cascade12000b()
            obj.open();
        end

        function [x, y, z] = get_position(obj)
            [r] = obj.exec_command(sprintf(':MOV:ABS? %d', cascade12000b.DEVICE_ID));
            tmp = sscanf(r, '%d %d %d');
            x = tmp(1);
            y = tmp(2);
            z = tmp(3);
        end

        function [] = move_absolute(obj, x, y)
            [~, ~, z] = obj.get_position();
            obj.exec_command(sprintf(':MOV:ABS %d %d %d %d', cascade12000b.DEVICE_ID, x, y, z));
        end

        function [] = move_relative(obj, dx, dy)
            obj.exec_command(sprintf(':MOV:REL %d %d %d %d', cascade12000b.DEVICE_ID, dx, dy, 0));
        end

Solution

  • Something like this would work

    class Cascade12000b(object):
        """A Cascade12000b driver.
    
        :param connection: A connection object, used to communicate with the real device.
            The connection interface should conform to the following interface.
            It must have two methods:
    
            * `.write()` taking a string message
            * `.ask()` taking a string message, returning a string response
    
        :param int id: The device id
    
        """
        def __init__(self, connection, id=2):
            self.connection = connection
            self.id = int(id)
    
        def position(self):
            """Returns a tuple `(x,y,z)` with the position coordinates."""
            response = self.connection.ask(':MOV:ABS? {0:d}'.format(self.id))
            # assuming whitespace separated response
            return tuple(int(x) for x in reponse.split())
    
        def move_absolute(self, x, y, z=None):
            """Sets the position in absolute coordinates."""
            if z is None:
                _, _, z = self.position()
            self.connection.write(':MOV:ABS {0:d} {1:d} {2:d} {3:d}'.format(self.id, x, y, z)
    
        def move_relative(self, dx, dy, dz=0):
            """Sets the position in relative coordinates."""
            self.connection.write(':MOV:REL {0:d} {1:d} {2:d} {3:d}'.format(self.id, dx, dy, dz)
    

    You would use it like this

    # Injecting the connection has the advantage that you can change the implementation, e.g. # to linux-gpib
    >>>connection = visa.instrument('GPIB::28')
    >>>device = Cascade12000b(connection)
    >>>device.move_absolute(10, 13, 20)
    >>>device.position()
    10, 13, 20
    >>>device.move_relative(2,2)
    >>>device.position()
    12,15,20
    

    If you have to write more than one device driver, you might want to look at some python packages like slave (Note: I'm the author of slave) or lantz.