Instrument Documentation

Instruments

The Instrument class is designed to dramatically reduce the amount of boilerplate code required for defining device drivers. The following (from picosecond.py) amounts to the majority of the driver definition:

class Picosecond10070A(SCPIInstrument):
    """Picosecond 10070A Pulser"""
    amplitude      = FloatCommand(scpi_string="amplitude")
    delay          = FloatCommand(scpi_string="delay")
    duration       = FloatCommand(scpi_string="duration")
    trigger_level  = FloatCommand(scpi_string="level")
    period         = FloatCommand(scpi_string="period")
    frequency      = FloatCommand(scpi_string="frequency",
                      aliases=['freq'])
    offset         = FloatCommand(scpi_string="offset")
    trigger_source = StringCommand(scpi_string="trigger",
                      allowed_values=["INT", "EXT", "GPIB"])

    def trigger(self):
        self.interface.write("*TRG")

Each of the Commands is converted into the relevant driver code as detailed below.

Commands

The Command class variables are parsed by the MetaInstrument metaclass, and automatically expanded into setters and getters (as appropriate) and a property that gives convenient access to commands. For example, the following Command:

frequency = FloatCommand(scpi_string='frequency')

will be expanded into the following equivalent set of class methods:

def get_frequency(self):
    return float(self.interface.query('frequency?'))
def set_frequency(self, value):
    self.interface.write('frequency {:E}'.format(value))
@property
def frequency(self):
    return self.get_frequency()
@frequency.setter
def frequency(self, value):
    self.set_frequency(value)

Instruments with consistent command syntax (which number fewer than one might hope) lend themselves to extremely concise drivers. Using additional keyword arguments such as allowed_values, aliases, and value_map allows for more advanced commands to specified without the usual driver fluff. Full documentation can be found in the API reference.

Property Access

Property access gives us a convenient way of interacting with instrument values. In the following example we construct an instance of the Picosecond10070A class and fire off a number of pulses:

pspl = Picosecond10070A("GPIB0::24::INSTR")

pspl.amplitude = 0.944                  # Using setter
print("Trigger delay is: ", pspl.delay) # Using getter

for dur in 1e-9*np.arange(1, 11, 0.5):
    pspl.duration = dur
    pspl.trigger()
    time.sleep(0.05)

Properties present certain risks alongside their convenience: running instr.falter_slop = 18.0 will produce no errors (since it’s perfectly reasonable Python) despite the user having intended to set the filter_slope value. As such, we actually lock the class dictionary after parsing and intilization, and will produce errors informing you of your spelling creativities.