-
Notifications
You must be signed in to change notification settings - Fork 9
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.start();
// 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
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;
}
}
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.
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();
}
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());
...
}
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!");
}
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();
}
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;
}
}
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;
}
}
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;
}
}
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.start();
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 start() 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();
- Getting Started
- Object Orientation
- Choosing a Language
- Using JISA in Java
- Using JISA in Python
- Using JISA in Kotlin
- Exceptions
- Functions as Objects
- Instrument Basics
- SMUs
- Thermometers (and old TCs)
- PID and Temperature Controllers
- Lock-Ins
- Power Supplies
- Pre-Amplifiers
- Writing New Drivers