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

NAME

Lab::Measurement::Tutorial - Tutorial on using the Lab::Measurement package stack

Introduction

The Lab::Measurement package stack allows to perform test and measurement tasks with Perl scripts. It provides an interface several hardware driver backends. Dedicated instrument driver classes relieve the user from taking care for internal details and make measurements as easy as

  $voltage=$multimeter->get_voltage().

The Lab::Measurement software stack consists of several parts that are built on top of each other. This modularization allows support for a wide range of hardware on different operating systems. As hardware drivers vary in API details, each supported one is encapsulated into perl modules of types Lab::Bus and Lab::Connection. Normally you won't have to care about this; at most, your Instrument object (see below) gets different initialization parameters.

A typical measurement script is based on the high-level interface provided by the modules Lab::Instrument and Lab::Measurement. The former silently handles all the protocol overhead. You can write commands to an instrument and read the result. Drivers for specific devices are included, implementing their specific command syntax; more can easily be added to provide high-level functions. The latter includes tools for metadata handling (what was that amplifier setting in the measurement again?!), data plotting, and similar.

This tutorial will explain how to write measurement scripts. 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. 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 various ways of connecting measurement instruments to your control PC. We focus on the methods not so well-known to average PC users, i.e. VISA and GPIB programming. Usage of the higher level modules from the Lab::Instrument package requires almost no knowledge about VISA and GPIB at all, though.

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.

RS232

RS232 is the abbreviation for the serial port that used to be built into each PC. It provides a point-to-point connection to one instrument.

Architecture

A schematic view of the various software layers between your perl measurement script and the instrument hardware is depicted in the graphics http://www.labmeasurement.de/structure.png.

The lowermost layer is provided by the hardware driver library and its Perl binding. One option for this is under Linux the package LinuxGPIB, which comes wth its own Perl bindings module. Alternatively, National Instruments NI-VISA can be used. In that case, the module Lab::VISA is required to access the library functions from Perl; it makes the standard VISA calls available from within Perl programs. This layer is not part of the Lab::Measurement distribution, but must be installed separately.

Each hardware backend is encapsulated into a class of the Lab::Bus type. A Bus can be imagined as a cable, connecting your control computer with several measurement hardware components. On top of the Bus classes, classes of type Lab::Connection operate. Each connection, well, connects one measurement instrument with your script. Usually, the handling of Bus and Connection is transparent; as long as you dont want to add more backends or enhance the functionality, you will never have any need to directly address these levels.

The Lab::Instrument classes build on top and simplify the routine tasks of opening a connection to an instrument, sending and receiving messages. This is the level where usually customized measurement scripts access the protocol stack. Classes derived from Lab::Instrument as e.g. 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) can share common interfaces (e.g. Lab::Instrument::Source) to make interchangeability of similar instruments possible.

The highest abstraction layer is provided by the Lab::Measurement class, which contains methods for data and metadata handling, plotting and rudimentary keyboard control.

Using the Lab::Instrument class

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

  #!/usr/bin/perl
  
  use strict;
  use Lab::Instrument;
  
  ################################
  
  unless (@ARGV > 0) {
        print "Usage: $0 GPIB-address\n";
        exit;
  }
  
  my $gpib=$ARGV[0];
  
  print "Querying ID of instrument at GPIB address $gpib\n";
  
  my $i=new Lab::Instrument(
        connection_type=>'LinuxGPIB',
        gpib_address => $gpib,
        gpib_board=>0,
  );
  
  my $id=$i->query('*IDN?');
  
  print "Query result: \"$id\"\n";

This program opens a GPIB instrument for communication, sends the command *IDN? and reads out its response, the identification string of the instrument. All handling of GPIB boards, resource managers etc. is done within the Lab::Instrument class; we don't have to care about string lengths and cleaning up. Lab::Instrument does it for us. Now that's already quite nice, eh?

By only using Lab::Instrument you should already be able to do about everything that can be done with 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
  
  use strict;
  use Lab::Instrument::HP34401A;
  
  ################################
  
  unless (@ARGV > 0) {
        print "Usage: $0 GPIB-address\n";
        exit;
  }
  
  my $hp_gpib=$ARGV[0];
  
  print "Reading voltage from HP34401A at GPIB address $hp_gpib\n";
  
  my $hp=new Lab::Instrument::HP34401A(
        connection_type=>'LinuxGPIB',
        gpib_address => $hp_gpib,
        gpib_board=>0,
  );
  
  my $volt=$hp->$get_voltage_dc(10,0.00001);
  
  print "Result: $volt V\n";

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 get_voltage_dc() method that configures the multimeter for dc voltage measurement in the range given by the parameters, triggers one measurement, and returns the measured voltage value.

Next we show 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
  
  use strict;
  use Lab::Instrument::Yokogawa7651;
  
  unless (@ARGV > 0) {
        print "Usage: $0 GPIB-address [Target-voltage]\n";
        exit;
  }
  
  my ($gpib,$goto)=@ARGV;
  
  my $source=new Lab::Instrument::Yokogawa7651(
        connection_type=>'LinuxGPIB',
        gpib_address => $gpib,
        gpib_board=>0,
        gate_protect=>1,
        gp_max_unit_per_second=>0.05,
        gp_max_unit_per_step=>0.005
        gp_max_step_per_second=>10,
  );
  
  if (defined $goto) {
        $source->set_voltage($goto);
  } else {
        print $source->get_voltage();
  }

Here the gate_protect mechanism limits the step size of the voltage source to 0.005mV, and the sweep speed to at most 10 such steps per second. This is implemented automatically within the set_voltage($goto) command; after we have set the parameters in the initialization phase, we do not have to take care of it anymore.

Using the high-level Lab::Measurement and related classes

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 Lab::Measurement class together with the related Lab::Data:... classes 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 experimenter 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. 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 metadata

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.dat.

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

  use strict;
  use Lab::Instrument::Yokogawa7651;
  use Lab::Instrument::IPS12010;
  use Lab::Instrument::HP34401A;
  use Lab::Instrument::SR830;
  use Lab::Measurement;
  
  # measurement range and resolution
  my $Vbiasstart = -0.0036;     # V, after divider
  my $Vbiasstop = 0.0036;       # V, after divider
  my $Vbiasstep = 0.00002;      # V, after divider
  my $Bstart=0.1;               # T
  my $Bstop=0;                  # T
  my $Bstep=0.01;               # T
  
  # general measurement settings and constants
  my $Vbiasdivider = 0.01;      # <1, voltage divider value
  my $currentamp = 1e-9;        # A/V
  my $sample = "nanotube";
  my @starttime = localtime(time);
  my $startstring=sprintf("%04u-%02u-%02u_%02u-%02u-%02u",
                $starttime[5]+1900, $starttime[4]+1, $starttime[3],
                        $starttime[2], $starttime[1], $starttime[0]);
  my $title = "Bias versus magnetic field";
  my $filename = $startstring."_biasfield";
  
  # the bias voltage source
  my $YokBias=new Lab::Instrument::Yokogawa7651({
        'connection_type'=> 'LinuxGPIB',
        'gpib_board'    => 0,   'gpib_address'  => 3,
        'gate_protect'  => $Vbiasprotect,
        'gp_max_unit_per_second' => 0.05/$Vbiasdivider,
        'gp_max_step_per_second' => 10,
        'gp_max_unit_per_step' => 0.005/$Vbiasdivider,
        'fast_set' => 1,
  });
  
  # the lock-in: ac measurement         
  my $SRS = new Lab::Instrument::SR830({
        'connection_type'=> 'LinuxGPIB',
        'gpib_board'    => 0,   'gpib_address'  => 8,
  });
        
  # the multimeter: dc measurement
  my $HP = new Lab::Instrument::HP34401A({
        'connection_type'=> 'LinuxGPIB',
        'gpib_board'    => 0,   'gpib_address'  => 12,
  });
  
  # the superconducting magnet control
  my $magnet=new Lab::Instrument::IPS12010({
        'connection_type' => 'LinuxGPIB',
        'gpib_board'    => 0,   'gpib_address'  => 24,
  });
  
  # general comments for the log 
  my $comment=<<COMMENT;
  Bias sweeps versus magnetic field; gate voltage -3.74 V
  B from $Bstart to $Bstop step size $Bstep
  Bias voltage from $Vbiasstart to $Vbiasstop step size $Vbiasstep
  Current preamp $currentamp A/V
  SRS lock-in: integrate 100ms, freq 117.25Hz, sensit. 10mV
  COMMENT
  
  # the "measurement": things like filename, live plot, etc, 
  # plus all the metadata (data file columns, axes, plots, ...)
  my $measurement=new Lab::Measurement(
  sample          => $sample,        title           => $title,
  filename_base   => $filename,      description     => $comment,
  live_plot       => 'currentacx',   live_refresh    => '200',
  constants       => [
        {   'name'          => 'currentamp',
        'value'         => $currentamp,
        },
  ],
  columns         => [ # documentation of the data file columns
        {   'unit'          => 'T',   'label'         => 'B',
        'description'   => 'magnetic field perpendicular to nanotube',
        },
        {   'unit'          => 'V',   'label'         => 'Vbias',
        'description'   => "dc bias voltage",
        },
        {   'unit'          => 'A',   'label'         => 'Idc',
        'description'   => "measured dc current",
        },
        {   'unit'          => 'A',   'label'         => 'Iac,x',
        'description'   => "measured ac current, x component",
        },
        {   'unit'          => 'A',   'label'         => 'Iac,y',
        'description'   => "measured ac current, y component",
        },
  ],
  axes            => [ # possible axes for plotting, and their data columns
        {   'unit'          => 'T',   'label'         => 'B',
        'expression'    => '$C0',
        'description'   => 'magnetic field perpendicular to nanotube',
        },
        {   'unit'          => 'V',   'label'         => 'Vbias',
        'expression'    => '$C1',
        'description'   => 'dc bias voltage',
        },
        {   'unit'          => 'A',   'label'         => 'Idc',
        'expression'    => '$C2',
        'description'   => 'measured dc current',
        },
        {   'unit'          => 'I',   'label'         => 'Iac,x',
        'expression'    => '$C3',
        'description'   => 'measured ac current, x component',
        },
        {   'unit'          => 'I',   'label'         => 'Iac,y',
        'expression'    => '$C4',
        'description'   => 'measured ac current, y component',
        },
  ],
  plots           => { # plots that can be made using the axes above
        'currentdc'    => {
        'type'          => 'pm3d',
        'xaxis'         => 0,   'yaxis'         => 1,
        'cbaxis'        => 2,   'grid'          => 'xtics ytics',
        },
        'currentacx'    => {
        'type'          => 'pm3d',
        'xaxis'         => 0,   'yaxis'         => 1,
        'cbaxis'        => 3,   'grid'          => 'xtics ytics',
        },
  },
  );
  
  # correct the sign of the step sizes if required
  unless (($Bstop-$Bstart)/$Bstep > 0) { $Bstep = -$Bstep; }
  unless (($Vbiasstop-$Vbiasstart)/$Vbiasstep > 0) { $Vbiasstep=-$Vbiasstep; }
  my $Bstepsign=$Bstep/abs($Bstep);
  my $Vbiasstepsign=$Vbiasstep/abs($Vbiasstep);
  
  ## ENOUGH PREPARATION, NOW THE MEASUREMENT STARTS :) 
  
  # go to start field
  print "Ramping magnet to starting field... ";
  $magnet->set_field($Bstart);
  print " done!\n";
  
  # here you could eg. check the temperature
  
  # the outer measurement loop: magnetic field
  for (my $B=$Bstart; $Bstepsign*$B <= $Bstepsign*$Bstop; $B+=$Bstep)   {
  
        $measurement->start_block();
        
        # set the field 
        $magnet->set_field($B);
        
        # the inner measurement loop: bias voltage
        for (my $Vbias=$Vbiasstart; 
                $Vbiasstepsign*$Vbias<=$Vbiasstepsign*$Vbiasstop;
                $Vbias+=$Vbiasstep) {
  
        # set the bias voltage
        $YokBias->set_voltage($Vbias/$Vbiasdivider);
  
        # read dc signal from multimeter
        my $Vdc = $HP->get_value();
  
        # read the ac signal from the lock-in
        my ($Vacx,$Vacy)=$SRS->get_xy();
  
        # we multiply with (-1)*$currentamp (inverting amplifier)
        my $Idc = -$Vdc*$currentamp;
        my $Iacx=-$Vacx*$currentamp;
        my $Iacy=-$Vacy*$currentamp;
  
        # write the values into the data file
        $measurement->log_line($B, $Vbias, $Idc, $Iacx, $Iacy);
        }
  };
  
        # all done
        $measurement->finish_measurement();
        print "End of Measurement!\n";

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

 Copyright 2006 Daniel Schröer, 
           2012 Andreas K. Hüttel