Skip to content

Measurements

William Wood edited this page Mar 21, 2019 · 12 revisions

Measurements

Defining a standard measurement is best done by creating a class to represent it. The idea is to end up being able to create the relevant measurement object, feed it parameters and set it off, something like this:

Java

// Connect to instruments
SMU smu = new K2450(new TCPIPAddress("192.168.0.5"));
TC  tc  = new ITC503(new GPIBAddress(0, 20));

// Conductivity measurement requires an smu 
// and a temperature controller
Conductivity cond = new Conductivity(smu, tc);

// Set measurement parameters
cond.startV(0.0).stopV(60.0).stepsV(61);

// Create a new ResultTable which will house our results
ResultTable results = cond.newResults();

// Do the measurement
cond.performMeasurement();

// Output the results to a file
results.output("file.csv");        

Python

# Connect to instruments
smu = K2450(new TCPIPAddress("192.168.0.5"))
tc  = ITC503(new GPIBAddress(0, 20))

# Conductivity measurement requires an smu 
# and a temperature controller
cond = Conductivity(smu, tc)

# Set measurement parameters
cond.startV(0.0).stopV(60.0).stepsV(61)

# Create a new ResultTable which will house our results
results = cond.newResults()

# Do the measurement
cond.performMeasurement()

# Output the results to a file
results.output("file.csv")

Doing it like this

Creating the Class and Constructor

The first thing you should do is create your new class and make it extend Measurement:

public class Conductivity extends Measurement {

}

Now we should add a constructor that should take the instruments needed for the measurement as parameters. In our case, we need an smu and temperature-controller:

import JISA.Experiment.*;
import JISA.Devices.*;

public class Conductivity extends Measurement {

    private SMU smu;
    private TC  tc;

    public Conductivity(SMU smu, TC tc) {
        this.smu = smu;
        this.tc  = tc;
    }

}

Measurement Parameters

You will likely also want to have some variable parameters for your measurement. In the case of our conductivity measurement, we want to be able to define the voltage range and number of steps we sweep over. Therefore, we should create class variables to hold these, as well as methods to set them:

public class Conductivity extends Measurement {

    private double minV = 0;
    private double maxV = 60;
    private int    numV = 61;
 
    public Conductivity(SMU smu, TC tc) {...}

    public Conductivity startV(double voltage) {
        minV = voltage;
        return this;
    }

    public Conductivity stopV(double voltage) {
        maxV = voltage;
        return this;
    }

    public Conductivity stepsV(int steps) {
        numV = steps;
        return this;
    }

}

Notice how we make each of them return a reference to the object by use of return this;. This means that since calling one of these on an object will return the same object we can then call another one immediately after. That is, we can chain the method calls like so:

measurement.startV(15)
           .stopV(-60)
           .stepsV(100);

This allows for easily configuring of the measurement in one line of code and in a way that's human-readable.

Methods to Implement

When extending the Measurement class you will be required to implement the following methods:

public abstract class Measurement {

    public abstract void run() throws Exception;

    public abstract void onInterrupt() throws Exception;

    public abstract void onFinish() throws Exception;

    public abstract String[] getColumns();

    public abstract String[] getUnits();

}

run()

In the run() method, you should put the code to perform your measurement. Results are handled internally in the Measurement class so to retrieve the relevant ResultTable object, just use getResults():

public void run() throws Exception {
 
    ResultTable results = getResults();    

}

To ensure that the experiment can be properly stopped mid-way, you should use the in-built sleep(...) method in Measurement to wait, not any general Thread.sleep(...) or Util.sleep(...) etc.

public void run() throws Exception {
    ...
    smu.setVoltage(v);
    sleep(500);
    results.addData(smu.getVoltage(), smu.getCurrent());
    ...
}

onInterrupt()

In the onInterrupt() method you should define what you want to happen when the measurement is manually stopped before completion (ie it's interrupted). Often there's nothing to put here as the onFinish() method is always run afterwards regardless of how the measurement stopped. However, it's conceivable that you may wish to have something special happen when interrupted, like outputting to a log or err stream:

public void onInterrupt() {
    Util.errLog.println("The measurement was interrupted!");
}

onFinish()

This method in called when the measurement has finished. It is ALWAYS run regardless of whether the experiment ended successfully, was interrupted or failed with an error. Here you should put code to make sure all instruments are returned to a safe state (ie turned off):

public void onFinish() throws Exception {
    smu.turnOff();
    powerSupply.turnOff();
}

getColumns()

This should return an array of String objects representing the columns to create in the measurement's ResultTable. For example, if we wanted the equivalent of:

ResultTable results = new ResultList("Time", "Voltage", "Current");

Then we'd have:

public String[] getColumns() {
    return new String[]{"Time", "Voltage", "Current"};
}

Ideally, this array should be stored as a constant of the class:

public class MyMeasurement extends Measurement {

    public static final String[] COLUMNS = {"Time", "Voltage", "Current"};

	....

    public String[] getColumns() {
        return COLUMNS;
    }

}

getUnits()

This is largely the same as getColumns() except it returns the units to use in the ResultTable instead of column names:

public class MyMeasurement extends Measurement {

    public static final String[] COLUMNS = {"Time", "Voltage", "Current"};
    public static final String[] UNITS   = {"s", "V", "A"};

	....

    public String[] getUnits() {
        return UNITS;
    }

}

All Ready

After implementing those methods, your measurement is now ready.

public class Conductivity extends Measurement {

    // Store column names and units as constants (good practice)
    public static final String[] COLUMNS = {"Temperature", "Voltage", "Current"};
    public static final String[] UNITS   = {"K", "V", "A"};
 
    // Create constants representing the column index of each quantity
    public static final int      COL_T   = 0; // Temperature is in column 0
    public static final int      COL_V   = 1; // Voltage is in column 1
    public static final int      COL_I   = 2; // Current is in column 2

    // Want to hold onto the instruments we're supplied with
    private SMU smu;
    private TC  tc;

    // Hold onto measurement parameters
    private double minV = 0;
    private double maxV = 60;
    private int    numV = 61;
 
    // Constructor requires instruments
    public Conductivity(SMU smu, TC tc) {
        this.smu = smu;
        this.tc  = tc;
    }

    // Chainable configure methods
    public Conductivity startV(double voltage) {
        minV = voltage;
        return this;
    }

    public Conductivity stopV(double voltage) {
        maxV = voltage;
        return this;
    }

    public Conductivity stepsV(int steps) {
        numV = steps;
        return this;
    }

    // The measurement itself
    public void run() throws Exception {

        if (smu == null || tc == null) {
            throw new Exception("Instrument(s) not configured.");
        }

        ResultTable results = getResults();

        double[] voltages = Util.makeLinearArray(minV, maxV, numV);        

        smu.setVoltage(minV);
		smu.turnOn();

        for (double v : voltages) {
            smu.setVoltage(v);
            sleep(500);
            results.addData(tc.getTemperature(), smu.getVoltage(), smu.getCurrent());
        }

    }
 
    // What to do if stopped?
    public void onInterrupt() {
        Util.errLog.println("Measurement interrupted!");
    }
    
    // What to always do afterwards (regardless of how it ended)
    public void onFinish() throws Exception {
        smu.turnOff();
    }

    // Return our constant array
    public String[] getColumns() {
        return COLUMNS;
    }

    // Return our constant array
    public String[] getUnits() {
        return UNITS;
    }

}

Using a Measurement Object

Now you can perform conductivity measurements simply by creating a Conductivity object, configuring it and calling performMeasurement() on it.

Conductivity measumrent   = new Conductivity(...);
ResultTable  results      = conductivity.newResults();

measumrent.startV(5).stopV(-60).stepsV(66);
measumrent.performMeasurement();

results.output("results.csv");

You can integrate a Measurement object into a GUI easily too:

Java

public class Main {

    static Fields         params      = null;
    static Field<Double>  minV        = null;
    static Field<Double>  maxV        = null;
    static Field<Integer> numV        = null;
    static Plot           plot        = null;
    static Grid           grid        = null;
    static ResultTable    results     = null;
    static Conductivity   measurement = null;
    static SMU            smu         = null;
    static TC             tc          = null;

    public static void main(String[] args) {
 
        GUI.startGUI();       

        try {
            smu = new K2450(new USBAddress(0x05e6, 0x2450, "54543"));
            tc  = new ITC503(new GPIBAddress(0,20));
        } catch (Exception e) {
            GUI.errorAlert("Error", "Connection Error", e.getMessage());
            System.exit(0);
        }

        params = new Fields("Voltages");
        minV   = params.addDoubleField("Start [V]");
        maxV   = params.addDoubleField("Stop [V]");
        numV   = params.addIntegerField("No. Steps");
        plot   = new Plot("Results", "Voltage [V]", "Current [A]");
        grid   = new Grid("Conductivity", params, plot);
        
        grid.addToolbarButton("Start", Main::start);
        grid.addToolbarButton("Stop", Main::stop);
 
        grid.setExitOnClose(true);

        grid.show();
        
    }

    public static void start() {
    
        measurement = new Conductivity(smu,tc);
        results     = measurement.newResults();
        
        measurement.startV(minV.get()).stopV(maxV.get()).stepsV(numV.get());
        
        // Clear any previous results from the plot
        plot.clear();
        plot.watchList(
            results, 
            Conductivity.COL_V, 
            Conductivity.COL_I, 
            "Data", 
            Color.RED
        );

	params.setFieldsDisabled(true);

        // Calls performMeasurement() on the measurement and automatically
        // uses dialogues to indicate completed/stopped/error
        GUI.runMeasurement(measurement);
        
        results.output("data.csv");

	params.setFieldsDisabled(false);

    }

    public static void stop() {
    
        if (measurement != null) {
            measurement.stop();
        }
        
    }

}

Python

params      = None;
minV        = None;
maxV        = None;
numV        = None;
plot        = None;
grid        = None;
results     = None;
measurement = None;
smu         = None;
tc          = None;

def start():

    global measurement, results
    
    measurement = Conductivity(smu, tc)
    results     = measurement.newResults()

    measurement.startV(minV.get()).stopV(maxV.get()).stepsV(numV.get())

    plot.clear()
    plot.watchList(
        results, 
        Conductivity.COL_V, 
        Conductivity.COL_I, 
        "Data", 
        Color.RED
    )

    params.setFieldsDisabled(True)

    GUI.runMeasurement(measurement)
        
    results.output("data.csv")

    params.setFieldsDisabled(False)

def stop():
    
    if measurement is not None:
        measurement.stop()


def main():

    global smu, tc, params, minV, maxV, numV, plot, grid    

    try:
        smu = K2450(USBAddress(0x05e6, 0x2450, "54543"))
        tc  = ITC503(GPIBAddress(0,20))
    except Exception as e:
        GUI.errorAlert("Error", "Connection Error", e.getMessage(), 600)
        quit()
    
    
    params = Fields("Voltages")
    minV   = params.addDoubleField("Start [V]")
    maxV   = params.addDoubleField("Stop [V]")
    numV   = params.addIntegerField("No. Steps")
    plot   = Plot("Results", "Voltage [V]", "Current [A]")
    grid   = Grid("Conductivity", [params, plot])

    grid.addToolbarButton("Start", start)
    grid.addToolbarButton("Stop", stop)
    
    grid.setExitOnClose(True)
    
    grid.show()

main();

Clone this wiki locally