The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Lab::VISA::Tutorial - Tutorial on using Lab::VISA and related packages

Introduction

Lab::VISA and its related packages allow to perform test and measurement tasks with Perl scripts. It provides an interface to National Instruments' NI-VISA library, making the standard VISA calls available from within Perl programs. Dedicated instrument driver classes relieve the user from taking care for internal details and make measurements as easy as

  $voltage=$multimeter->read_voltage().

The Lab::... software is divided into three parts. They are built on top of each other and provide increasing comfort. Your measurement scripts can be based on each of these stages.

The lowest level is Lab::VISA. It makes the NI-VISA library accessible from perl and therefor allows to make any standard VISA call.

The modules in the Lab::Instrument package make communication with instruments easier by silently handling the protocol involved.

Package Lab::Tools is the highest abstraction layer. These modules provide support for writing good measurement scripts. They offer means of saving data and related meta information to disk, plotting data etc.

This tutorial will explain how to write measurement scripts that build on any of these stages. However, this tutorial does not intend to teach the Perl language itself. Some introduction into VISA and GPIB terminology is given, but then some familarity also with these concepts is assumed. Not much is required. If you feel the need for more information on Perl or VISA/GPIB, please see the "References" section[1-6].

Measurement automation basics

This section provides a very brief introduction to the terms VISA and GPIB. For a more detailed explanation of the VISA and GPIB standards, the involved communication principles and the available commands for your specific instruments, please refer to the literature[1-3]. Usage of the higher level modules from the Lab::Instrument package requires almost no knowledge about VISA and GPIB at all.

VISA

Traditionally, test and measurement instruments can be connected and controlled via various standards and protocols. VISA, the Virtual Instrument Software Architecture[1,2], is an effort to provide a single standarised interface to communicate with instruments via several protocols. It was developed by the VXIplug&play Systems Alliance[4] and is currently maintained by the IVI foundation[5]. VISA can control VXI, GPIB, serial, or computer-based instruments and makes the appropriate driver calls depending on the type of instrument used. Hence, VISA is located in the application layer. The National Instruments NI-VISA library is one implementation of the VISA standard.

In one word: VISA tries to make it unimportant, how an instrument is connected physically.

GPIB

GPIB (IEEE488)[3] is a lower lying standard invented by Hewlett-Packard. It describes a way of connecting instruments. The standard is divided into the physical layer IEEE488.1 that defines cables and signals and the command layer IEEE488.2 that describes a syntax for messages between communicating instruments. SCPI (Standard Commands for Programmable Instruments) is an extension of IEEE488.2 and refines the available commands further, with the goal of obtaining a language that is independent of the exact model of the instruments in use. This could be very useful, as, in theory, it would allow you to exchange one instrument in your setup with a similar one from another manufacturer, without having to change your measurement software. In practise however, not many instruments support this standard, and even if, small differences make things a pain. As described below, the Lab::Instrument package follows another route to achieve interchangeability by providing common interfaces for similar instruments at a much higher level (e.g. the Lab::Instrument::Source interface).

In one word: GPIB tries to make communication with various instruments more similar.

Architecture

A schematic view of the various software layers between your perl measurement script and the instrument hardware looks like this:

 +-------------------------+ +---------------+ +------------------+
 |Lab::Instrument::HP34401A| |L::I::KnickS252| |L::I::Yokogawa7651|
 +-----------------------+-+ +----+----------+ +----+-------------+
                         |        |                 |
                         |     +--+-----------------+--+
                         |     |Lab::Instrument::Source|
                         |     +---+-------------------+
                         |         |
                      +--+---------+--+
                      |Lab::Instrument|
                      +-------+-------+
                              |
                         +----+----+
                         |Lab::VISA|
                         +----+----+
                              |
                      +-------+-------+
                      |NI-VISA Library|
                      +---+-------+---+
                          |       |
               +----------+-+   +-+----+
               |GPIB Library|   |  OS  |
               +----------+-+   +-+----+
                          |       |
                          |       |Serial connection
           GPIB connection|       |TCP/IP connection
                          |       |USB connection
                          |       |
                 +--------+-+   +-+--------+
                 |Instrument|   |Instrument|
                 +----------+   +----------+

The Lab::VISA module provides a perl binding for National Instruments' NI-VISA library. It makes the standard VISA calls available from within Perl programs.

The Lab::Instrument module builds on top of Lab::VISA and simplifies the routine tasks of opening VISA resources, sending and receiving messages.

The instrument classes like Lab::Instrument::KnickS252 are specialized modules for certain instruments. Most other measurement software packages would call this a virtual instruments or an instrument drivers. Each such class provides methods that are specific for one instrument. The Lab::Instrument::IPS120_10 class for example class is dedicated to a certain magnet power supply and therefore provides methods like set_target_field. Similar instruments (e.g. various voltage sources) however share common interfaces (e.g. Lab::Instrument::Source) to make interchangeability of similar instruments possible.

Using pure VISA calls

First we will see how to use the plain VISA interface and communicate with an instrument with standard VISA viRead and viWrite calls. It will show that this method is rather laborious. Later we will learn how Lab::Instrument makes life easier.

All the examples in the following sections can be found in the Tutorials directory of the Lab::VISA package.

  #!/usr/bin/perl
  
  # example1.pl
  
  use strict;
  use Lab::VISA;
  
  # Initialize VISA system and
  # Open default resource manager
  my ($status,$default_rm)=Lab::VISA::viOpenDefaultRM();
  if ($status != $Lab::VISA::VI_SUCCESS) {
      die "Cannot open resource manager: $status";
  }
  
  # Open one resource (an instrument)
  my $gpib=24;            # we want to open the instrument
  my $board=0;            # with GPIB address 24
                          # connected to GPIB board 0 in our computer
  my $resource_name=sprintf("GPIB%u::%u::INSTR",$board,$gpib);
  
  ($status, my $instr)=Lab::VISA::viOpen(
      $default_rm,        # the resource manager session
      $resource_name,     # a string describing the 
      $Lab::VISA::VI_NULL,# access mode (no special mode)
      $Lab::VISA::VI_NULL # time out for open (no time out)
  );
  if ($status != $Lab::VISA::VI_SUCCESS) {
      die "Cannot open instrument $resource_name. status: $status";
  }
  
  # We set a time out for communication with this instrument
  $status=Lab::VISA::viSetAttribute(
      $instr,             # the session identifier
      $Lab::VISA::VI_ATTR_TMO_VALUE,  # which attribute to modify
      3000                # the new value
  );
  if ($status != $Lab::VISA::VI_SUCCESS) {
      die "Error while setting timeout value: $status";
  }
  
  # Clear the instrument
  my $status=Lab::VISA::viClear($instr);
  if ($status != $Lab::VISA::VI_SUCCESS) {
      die "Error while clearing instrument: $status";
  }
  
  # Now we are going to send one command and read the result.
  
  # We send the simple SCPI command "*IDN?" which asks the instrument
  # to identify itself. Of course the instrument must support this
  # command, in order to make this example work.
  my $cmd="*IDN?";
  ($status, my $write_cnt)=Lab::VISA::viWrite(
      $instr,             # the session identifier 
      $cmd,               # the command to send
      length($cmd)        # the length of the command in bytes
  );
  if ($status != $Lab::VISA::VI_SUCCESS) {
      die "Error while writing: $status";
  }
  
  # Now we will read the instruments reply
  ($status,               # indicates if the operation was successful
   my $result,            # the answer string
   my $read_cnt)=         # the length of the answer in bytes
  Lab::VISA::viRead(
      $instr,             # the session identifier
      300                 # read 300 bytes
  );
  if ($status != $Lab::VISA::VI_SUCCESS) {
      die "Error while reading: $status";
  }
  # The result string will be 300 bytes long, but only $read_cnt
  # bytes are part of the answer. We cut away the rest.
  $result=substr($result,0,$read_cnt);
  
  print "$result\n";
  
  # As good citizens we'll cleanup now.
  # Close the instrument
  $status=Lab::VISA::viClose($instr);
  # And the resource manager
  $status=Lab::VISA::viClose($default_rm);
  
  __END__

First we have to open a resource manager. This manager can then provide us with a handle to one instrument. We try to open an instrument that is connected via GPIB to the GPIB board 0 in our computer and uses the GPIB address 24. This is specified by the resource name. We then ask the instrument for its identification string, read the answer and print it. We do so by sending the *IDN? command. This is a standard SCPI command, that all instruments that support the SCPI language, will understand. Agilent instruments do so for example.

We see that there is a lot of protocol overhead involved, that makes this very simple example a bit lengthy and ugly. These things should be factored out. The Lab::Instrument class can do all this dirty work for us, as we will learn in the next section.

Using the Lab::Instrument class

The Lab::Instrument class can do for us the routine work of connecting to certain instrument.

  #!/usr/bin/perl
  
  # example2.pl
  
  use strict;
  use Lab::Instrument;
  
  my $gpib=24;            # we want to open the instrument
  my $board=0;            # with GPIB address 24
                          # connected to GPIB board 0 in our computer
  
  # Create an instrument object
  my $instr=new Lab::Instrument($board,$gpib);
  
  my $cmd="*IDN?";
  
  # Query the instrument
  # Query is a combined Write and Read
  my $result=$instr->Query($cmd);
  
  print $result;
  
  __END__

This program achieves exactly the same as example1.pl, but with only two lines of code: one to open the instrument, one to query it. We don't have to care about resource managers and string lengths and cleaning up. Lab::Instrument does it for us. Now that's already quite nice, eh?

Let's see another example. This time we will send a great bunch of commands to an Agilent 81134A pulse generator, to set it up for pulse mode.

  #!/usr/bin/perl
  
  # example3.pl
  
  use strict;
  use Lab::Instrument;
  
  # Open instrument
  # We use the other form of the constructor here.
  my $instr=new Lab::Instrument({
      GPIB_board      => 0,
      GPIB_address    => 10
  });
  
  # Send a bunch of commands to configure instrument
  for ((
  # Protect the DUT
      ':OUTP:CENT OFF',       #disconnect channels
  
  # Set up the Instrument
      ':FUNC PATT',           #set mode to Pulse/Pattern
      ':PER 20 ns',           #set period to 20 ns
  
  # Set up Channel 1
      ':FUNC:MODE1 PULSE',    #set pattern mode to Pulse
      ':WIDT1 5 ns',          #set width to 5 ns
      ':VOLT1:AMPL 2.000 V',  #set ampl to 2 V
      ':VOLT1:OFFSET 1.5 V',  #set offset to 1.5 V
      ':OUTP1:POS ON',        #enable output channel 1
  
  # Generate the Signals
      ':OUTP:CENT ON',        #reconnect the channels
      ':OUTP0:SOUR PER',      #use trigger mode Pulse
      ':OUTP0 ON',            #enable trigger output
  )) {
      $self->{vi}->Write($_);
  }
  
  __END__

This example shows that Perl's great list handling makes it the ideal language for instrument control and data aquisistion tasks.

By only using Lab::Instrument you should already be able to do about everything that can be done the instruments in your lab.

Using Lab::Instrument::xxx virtual instruments

Many common tasks, like reading a voltage from a digital multimeter, require that a series of GPIB commands is sent to an instrument. These commands are different for similar instruments from different manufacturers.

The virtual instrument classes in the Lab::Instrument package attempt to hide these details from the user by providing high level methods like set_voltage($voltage) and get_voltage().

Additionally they provide an optional safety mechanism for voltage sources. This is used to protect sensitive samples which could be destoyed by sudden voltage changes. See the documentation of the Lab::Instrument::Source module for details.

  #!/usr/bin/perl
  
  # example4.pl
  
  use strict;
  use Lab::Instrument::HP34401A;
  
  my $gpib=24;            # we want to open the instrument
  my $board=0;            # with GPIB address 24
                          # connected to GPIB board 0 in our computer
  
  # Create an instrument object
  my $hp=new Lab::Instrument::HP34401A($board,$gpib);
  
  # Use the id method to query the instruments ID string
  my $result=$hp->id();
  
  print $result;
  
  __END__

This example show the usage of a dedicated virtual instrument class, namely Lab::Instrument::HP34401A, the driver for a Hewlett-Packard/Agilent 34401A digital multimeter. An instance of this class is created that is connected to one certain instrument. We use the id() method that returns the instrument's id string again.

Next we see an example on how to use the safety mechanism of Lab::Instrument::Source that is inherited by voltage sources like Lab::Instrument::Yokogawa7651.

  #!/usr/bin/perl
  
  # example5.pl

  use strict;
  use Lab::Instrument::Yokogawa7651;
  
  unless (@ARGV > 0) {
      print "Usage: $0 GPIB-address [goto_voltage]\n";
      exit;
  }
  
  my ($gpib,$goto)=@ARGV;
  
  my $source=new Lab::Instrument::Yokogawa7651(0,$gpib);
  
  if (defined $goto) {
      $source->sweep_to_voltage($goto);
  } else {
      print $source->get_voltage();
  }
  
  __END__

Using Lab::Tools

With the tools introduced so far you should be able to easily write short individual scripts for your measurement tasks. These scripts will probably serve as well as all other home grown solutions using LabView or whatever. The modules in the Lab::Tools package now provide additional tools to write better measurement scripts.

One main goal is to provide means to keep additional information stored along with the raw measured data. Additional information means all the notes that you would usually write down in your laboratory book, like date and time, settings of additional instruments, the environment temperature, the color of the shirt you were wearing while recording the data and everything else that might be of importance for a later interpretation of the data. In my experience, having to write these things in a book by hand is tedious and error-prone. It's the kind of job that computers were made for.

Another goal is to free the laborant from having to repeat himself all the time when the data is used for analysis or presentation. Let us assume that, for example, you are measuring a very small current with the help of a current amplifier. This current amplifier will output a voltage that is proportional to the original current, so in fact you will be measuring a voltage that can be converted to the original current by multiplying it with a certain factor. The last paragraph has shown that the Lab::Tools modules will help you to keep track of this additional information current amplifier constant of proportionality. But as long as the precise formula for this transformation is not stored together with the data, you will still find yourself repeatedly typing in the same expressions, whenever you work with the data. This is where the axis concept comes into play. Already at the time you are preparing your measurement script, you define an axis named current that stores the expression to calculate the current from the voltage. From there you work with the current-axis and will never have to care about the conversion again. And of course you can define many different axes. Read on!

The Meta data

The general concept is that a dataset is composed out of data and metadata, i.e. additional information about the data. This metadata is maintained by the Lab::Data::Meta class and is usually stored in a file dataset_filename.META, while the data is saved in dataset_filename.DATA.

The meta file is stored in YAML or XML format and contains a number of elements which are defined in Lab::Data::Meta. The most important ones are column, block, axis and plot. For the following discussion of these fields, let's assume a data file that looks like this:

 0.01   2.0   3
 0.01   2.1   3.4
 0.01   2.2   2.9
 
 0.02   2.0   1.7
 0.02   2.1   2.4
 0.02   2.2   2.2

This dataset shows an example where one quantity (third column) is measured in dependence of two others (first and second column). The data was recorded in two traces, where one input value is kept constant (1st column) and then for every setting of the other input value (2nd column) a datapoint is taken (3rd column). Then the the first input value is increased and the next trace is recorded.

column

The above example measurement has three columns. You will want to store additional information for each of these columns: What is being set or measured, what is the unit of the stored value etc. This information is stored in the column records of the meta file. More details on the available fields is given in the Lab::Data::Meta manpage.

block

The example data above was aquired in two traces or scans or sweeps, which are separated by an empty line in the data file now. Lab::Data::Meta adopts the Gnuplot[7] terminology and calls these blocks. Along with every block, additional information like the time the trace was started can be saved. Most of this is done automatically. See the Lab::Data::Meta and Lab::Measurement manpages.

axis

Usually you will not want to work with the raw data as it is stored in the columns of the file. For example you could want to plot the sum of two columns. Also you might want to display the data using another unit. Therefor you can define a new axis, that is defined as the sum of these two columns times any factor (which can be saved as a constant, see below) for the right unit. The expression amp * ($C1 + 10 * $C2) defines an axis as the sum of two columns multiplied with a constant amp. Additionally, axes have labels, ranges and such.

plot

With the plot element, default views on the data be defined. These views can then be plotted with a single command, using the Lab::Data::Plotter module and the script plotter.pl. Because all the necessary information is stored in the meta file, these plots will automatically contain the right axes, ranges, labels, units and any other information you wish! Plots can already be defined at the time the measurement script is written, and can also be added later. If you use the Lab::Measurement module, you can display any of these plots live, while the data is being aquired. Since this entire system can run on Linux, you can X-forward this graph to your remote desktop at the beach. Imagine the possibilities.

constant

This section of meta data can be used to store additional values that are important for the later interpretation of the raw data. Examples for such values could be amplification factors, voltage dividers etc. Constants have names that can be used in expressions of axis definitions.

The Lab::Measurement class

The Lab::Measurement class makes it easy to write a measurement script that takes advantage of the meta data system introduced above...

Examples

    #!/usr/bin/perl
    
    # example6.pl
    
    # Eine Spannungsquelle fahren, Leitfähigkeit (ohne Lock-In) messen
    
    use strict;
    use Lab::Instrument::KnickS252;
    use Lab::Instrument::HP34401A;
    use Time::HiRes qw/usleep/;
    use Lab::Measurement;
    
    ################################
    
    my $start_voltage   =-0.05;
    my $end_voltage     =-0.25;
    my $step            =-1e-3;
    
    my $knick_gpib      =4;
    my $hp_gpib         =24;
    
    my $v_sd            =-300e-3/1000;
    my $amp             =1e-9;    # Ithaco amplification
    
    my $R_Kontakt       =1089;
    
    my $sample          ="S5c (81059)";
    my $title           ="QPC links unten";
    my $comment         =<<COMMENT;
    Strom von 12 nach 14; V_{SD,DC}=$v_sd V; Lüftung an; Ca. 25mK.
    Ithaco: Amplification $amp, Supression 10e-10 off, Rise Time 0.3ms.
    Fahre Ghf4 (Yoko04)
    COMMENT
    
    ################################
    
    my $knick=new Lab::Instrument::KnickS252({
        'GPIB_board'    => 0,
        'GPIB_address'  => $knick_gpib,
        'gate_protect'  => 1,
    
        'gp_max_volt_per_second' => 0.002,
    });
    
    my $hp=new Lab::Instrument::HP34401A(0,$hp_gpib);
    
    my $measurement=new Lab::Measurement(
        sample          => $sample,
        title           => $title,
        filename_base   => 'qpctest',
        description     => $comment,
    
        live_plot       => 'QPC current',
        
        constants       => [
            {
                'name'          => 'G0',
                'value'         => '7.748091733e-5',
            },
            {
                'name'          => 'RKontakt',
                'value'         => $R_Kontakt,
            },
            {
                'name'          => 'V_SD',
                'value'         => $v_sd,
            },
            {
                'name'          => 'AMP',
                'value'         => $amp,
            },
        ],
        columns         => [
            {
                'unit'          => 'V',
                'label'         => 'Gate voltage',
                'description'   => 'Applied to gates via low path filter.',
            },
            {
                'unit'          => 'V',
                'label'         => 'Amplifier output',
                'description'   => "Voltage output by current amplifier set to $amp.",
            }
        ],
        axes            => [
            {
                'unit'          => 'V',
                'expression'    => '$C0',
                'label'         => 'V_{Gate}',
                'min'           => ($start_voltage < $end_voltage)
                                   ? $start_voltage
                                   : $end_voltage,
                'max'           => ($start_voltage < $end_voltage)
                                   ? $end_voltage
                                   : $start_voltage,
                'description'   => 'Gate voltage',
            },
            {
                'unit'          => 'A',
                'expression'    => "abs(\$C1)*AMP",
                'label'         => 'I_{QPC}',
                'description'   => 'QPC current',
            },
            {
                'unit'          => '2e^2/h',
                'expression'    => "(1/(V_SD/(-\$C2*AMP)-RKontakt))/G0",
                'label'         => "G_{QPC}",
                'description'   => "QPC conductance",
                'min'           => -0.1,
                'max'           => 7
            },
            
        ],
        plots           => {
            'QPC current'    => {
                'type'          => 'line',
                'xaxis'         => 0,
                'yaxis'         => 1,
                'grid'          => 'xtics ytics',
            },
            'QPC conductance'=> {
                'type'          => 'line',
                'xaxis'         => 0,
                'yaxis'         => 3,
                'grid'          => 'ytics',
            }
        },
    );
    
    $measurement->start_block();
    
    my $stepsign=$step/abs($step);
    for (my $volt=$start_voltage;
         $stepsign*$volt<=$stepsign*$end_voltage;
         $volt+=$step) {
        $knick->set_voltage($volt);
        usleep(500000);
        my $meas=$hp->read_voltage_dc(10,0.0001);
        $measurement->log_line($volt,$meas);
    }
    
    my $meta=$measurement->finish_measurement();
    

TODO: more examples

References

[1] NI-VISA User Manual (http://www.ni.com/pdf/manuals/370423a.pdf)
[2] NI-VISA Programmer Manual (http://www.ni.com/pdf/manuals/370132c.pdf)
[3] NI 488.2 User Manual (http://www.ni.com/pdf/manuals/370428c.pdf)
[4] http://www.vxipnp.org/
[5] http://www.ivifoundation.org/
[6] http://perldoc.perl.org/
[7] http://www.gnuplot.info/

AUTHOR/COPYRIGHT

This is $Id: Tutorial.pod 607 2010-04-11 21:12:09Z schroeer $

Copyright 2006 by Daniel Schröer.

1 POD Error

The following errors were encountered while parsing the POD:

Around line 531:

Non-ASCII character seen before =encoding in 'Leitfähigkeit'. Assuming CP1252