- Download MATLAB2016b (we are unsure about compatability with other versions of MATLAB)
- Add
modularControl
(and subfolders) to MATLAB path.
modularControl
is, as the name suggests, intended to be a modular and versitile solution to data acquisition in MATLAB.
There are two concepts that we first must introduce: behavior and identity.
- Behavior defines how something should behave, while
- Identity separates objects of the same behavior.
In analogy, behavior is like the profession of a person, while identity separates persons of the same profession. For instance, Dr. John behaves the same as Dr. Smith because they are both medical doctors, but does not have the same identity. Dr. John would not behave or be the same as Mr. Doe, a businessman.
The behavior of each mc<Classname>
is defined by the logic in the functions of the class. The identity, however is defined by what is given to the constructor of the class, which creates an object obj
based on the provided identity:
obj = mc<Classname>()
, no identity given, defaults toobj = mc<Classname>(mc<Classname>.defaultConfig())
wheredefaultConfig()
is astatic
function that returns the default config struct (see below);obj = mc<Classname>(config)
, uses the identity of the structconfig
. Fields ofconfig
might includeconfig.name
(i.e. the name of the identity), etc;obj = mc<Classname>('config.mat')
, uses the identity contained in the MATLAB file'config.mat'
(may be buggy)
Often there are other static
functions such as mc<Classname>.defaultConfig()
(e.g. mcaDAQ.piezoConfig()
) which conveniently define identity (in the form of a returned config
struct) so that the user does not have to correctly assemble a config
struct every time. Differences between config
s amount to simple differences in identity. For instance, config.chn
for a mcaDAQ
object, the DAQ channel that the object is connected to, could be 'ao0'
, 'ao1'
, and so on.
This separation of behavior and identity means that this code is inherently modular. mcAxis
is a class that generalizes the behavior of a 1D parameter space. The main function in mcAxis
is .goto(x)
, which tells the axis to goto that particular x
value. This function can be used on a variety of real objects that behave like a 1D parameter space: linear motion for piezos, wavelength for a tunable frequency laser, etc.
There are several parent classes that spawn a number of daughter subclasses in modular control. For clarity and organization, the daughter subclasses take the first three letters of the parent class as the prefix of thier classnames. The following is a list of classes that spawn daughters:
Class | This Class Abstractifies... | Subclass Prefix | Example Subclass |
---|---|---|---|
mcAxis |
...1D parameter spaces | mca |
mcaDAQ |
mcInput |
...measurement | mci |
mciDAQ |
mcGUI |
...MATLAB GUIs | mcg |
mcgDiamond |
Why was this done? Different code must be executed to interact with different devices. Attempting to contain the behavior of every axis inside a single mcAxis
class became difficult as the number of necessary behaviors increased. The switch
statement to deal with the different behaviors became unmanagably long. But, for compatability and modularity it was important to have all interactions be done through common classes and functions. Hence, mcAxis
, mcInput
, etc spawn a set of subclass mca
s, mci
s, etc that define the specific functionality. These subclasses, to reduce clutter, are stored in the subfolders of modularControl
labeled by parent name.
How is this done? Each parent function calls (after error-checking, etc) a capitalized version of that parent function.
Each custom subclass must 'fill in' functionality via that capitalized version.
For instance, mciDAQ
must define .Measure()
which is called by .measure()
, the method that the user calls. .measure()
is defined in the mcInput
superclass, along with an empty version of .Measure()
, which is 'filled in' by the subclass mciDAQ
. Most parents have a mc[]Template
to help with filling in the functionality; just replace the double-starred (**
) lines.
config.kind
is a rather-ambiguous structure in every mcAxis
and mcInput
. It was put in for organizational purposes. It contains the following fields:
config.kind.kind
, the programatic name of the kind (sorry, this was due to C++ habits);config.kind.name
, the explanatory name of the kind (e.g. for a later user);
config.kind.intUnits
, a string representing the units that the axis uses internally (e.g. for piezos, this is volts);config.kind.extUnits
, a string representing the units that the user should use (e.g. for piezos, this is microns);config.kind.int2extConv
, conversion from internal to external units (e.g. for piezos, 0V maps to -25um, 10V maps to 25um);config.kind.ext2intConv
, conversion from external to internal units, this should be the inverse ofint2extConv
, but is currently not error checked (check randomly in the future?);config.kind.intRange
, the range of the axis in external units (this is generated using the conversions);config.kind.extRange
, the range of the axis in external units (this is generated fromintRange
using the conversions);config.kind.base
, the value (in internal units) that the axis should seek at startup;
config.kind.extUnits
, the appropriate units (no internal units are neccessary here);config.kind.shouldNormalize
, whether or not the measurement should be divided by the time taken to measure (e.g. where absolute counts are meaningless unless the time taken to collect is present; this is currently only used withmciDAQ
devices);config.kind.sizeInput
, the expected size of the input (this allows other parts of the program to allocate space for themcInput
before the measurement has been taken; for numbers, this is set to[1 1]
; for a vector like a spectrum, this could be [512 1]).
It is meant to unify mcInstruments of similar, but not identical identity. For instance, all MadCity Labs piezos have the same config.kind
because they all have the same range and convert between units with the same conversions. The only difference is the special variables (dev
and chn
in this case).
The physical hardware uses internal units whereas the user uses external units. For instance, a piezo uses Volts internally but microns externally. The external units are defined via the anonymous function config.kind.int2extConv
and its inverse config.kind.ext2intConv
. For instance, for the piezos that we use in the diamond room, we convert between a 0 to 10 V range and a -25 to 25 um range. Thus,
config.kind.int2extConv = @(x)(5.*(5 - x));
config.kind.ext2intConv = @(x)((25 - x)./5);
It should be noted that the internal mcAxis
variables a.x
and a.xt
--- the current and target positions --- use internal units. The external current and target positions can be found via a.getX()
and a.getXt()
.
Suppose that we want to do an XY scan on the counter with the X piezo and the Y micrometer.
- Load the piezo:
- Let
configP = mcaDAQ.piezoConfig()
. This gives us the default configuration for a MadCity Piezo. - By default,
configP.dev
andconfigP.chn
are set to'Dev1'
and'ao0'
, respectively. Change these if neccessary. For instance, setconfigP.chn = 'ao1'
to access the piezo on the 2nd DAQ channel. - Set
configP.name
to a descriptive name in order to keep track of this axis later. e.g.configP.name = 'Piezo X'
- Set
piezo = mcaDAQ(configP)
which gives us amcaDAQ
object with the desiredconfig
. - Note that access to object pointed by
piezo
is not limited by access to the variablepiezo
. Every time an axis is initialized, it is registered withmcInstrumentHandler
for access via the rest of the program. - Note also that letting
piezo2 = mcaDAQ(configP)
will not make a new object. Instead, this will merely setpiezo2 = piezo
.mcInstrumentHandler
makes sure there are no duplicate axes. - Load the micrometer:
- Let
configM = mcaMicro.microConfig()
. This gives us the default configuration for a Newport Micrometer. - By default,
configM.port
is set to the USB port'COM6'
. Change this if neccessary. For instance, setconfigM.port = 'COM7'
to access the micrometer connected to USB port'COM7'
. - Set
configM.name
to a descriptive name. e.g.configP.name = 'Micro Y'
- Set
micro = mcaMicro(configM)
which gives us amcaMicro
object with the desiredconfig
. - Load the counter:
- Let
configI = mciDAQ.counterConfig()
. - By default,
configI.dev
andconfigI.chn
are set to'Dev1'
and'ctr1'
, respectively. Change these if neccessary. For instance, setconfigI.chn = 'ctr2'
to access the 2nd counter channel. - Set
configI.name
to a descriptive name. e.g.configI.name = 'Counter'
- Set
count = mciDAQ(configI)
which gives us amciDAQ
object with the desiredconfig
. - Note that the last three steps can be streamlined by a startup script.
mcDiamond
serves this purpose for the diamond microscope and load all of the pertinant axes and inputs. - Suppose that we want to do a 11x11 pixel scan from 10um to 20um with the x piezo and 20um to 30um with the y micrometer. We will use
mcData
. - Set
axes_ = {piezo, micro}
. This givesmcData
the axes we want to scan over. Note that it is also sufficient to setaxes_ = {configP, configM}
as long asconfigP.class = 'mcaDAQ'
andconfigM.class = 'mcaMicro'
. If you want to really be obscene,axes_ = {piezo, configM}
is also valid. - Set
scans = {linspace(10, 20, 11), linspace(20, 30, 11)}
. These vectors contain all of the points that we will scan over, with thei
th index of this cell array corresponding to the axis of thei
th index of the cell arrayaxes_
. Note that one can input pretty crazy vectors whose entries are not-neccessarily equally spaced (although this is not reccommended because the 2D imaging method assumes equal spacing; the 1D imaging method, however, should display correctly). - Set
inputs = {count}
. This givesmcData
the input that we want to measure at each point of the scan. Note specifically that moremcInput
s can be added as additional entries of the cell array (naturally). As with axes, using theconfig
instead of themcInput
object is sufficient. - Set
integrationTime = [time]
to the timetime
(in seconds) that we want to spend at each point.time = .09
sounds reasonable for ~1 second X scans. - Now call
data = mcData(axes_, scans, inputs, integrationTime)
. This gives anmcData
object that is ready to scan. - To scan, either
- Acquire in the command line with
data.aquire()
(typo, I know...). Note that this provides no visual input about the progress of the scan. It also blocks the MATLAB command line. The resulting data can be accessed afterward indata.d.data
. This will be a cell array with one entry (corresponding to the one input). This one entry will be a 11x11 numeric matrix with theij
th index corresponding to the result at pixel[i, j]
, i.e. the point[scans{1}(i), scans{2}(j)]
um. - Acquire the data visually with
mcDataViewer
. Useviewer = mcDataViewer(data)
. - The function
mcScan
is a GUI which makes amcData
structure without having to go through the command line as in step 5. RunmcScan
and simply select the appropriate axes/scans/etc using edit boxes and dropdown lists.
In all, we have
% 1. Load the piezo
configP = mcaDAQ.piezoConfig(); % Make the piezo config.
configP.chn = 'ao1';
configP.name = 'Piezo X';
piezo = mcaDAQ(configP); % Make the piezo object.
% 2. Load the micrometer
configM = mcaMicro.microConfig(); % Make the micrometer config.
configM.port = 'COM7';
configM.name = 'Micro Y';
micro = mcaMicro(configM); % Make the micrometer object.
% 3. Load the counter
configI = mciDAQ.counterConfig();
configI.chn = 'ctr2';
configI.name = 'Counter';
count = mciDAQ(configI);
% 5. Setup the mcData structure (which will aquire the data)
axes_ = {piezo, micro};
scans = {linspace(10, 20, 11), linspace(20, 30, 11)};
inputs = {count};
integrationTime = [.09];
data = mcData(axes_, scans, inputs, integrationTime);
% 6.1. Aquire the data programmatically
data.aquire();
disp(data.d.data); % Print the data in the console.
% 6.2. Aquire the data via GUI
data.reset(); % Reset the previous aquisition.
viewer = mcDataViewer(data);
- Make sure loading and saving configs/data is functional.
- Make sure that
mcData
works in all situations (e.g. different configurations of axes and inputs). - Polish
mcExperiment
stuff. - Commenting!
- 3D and Scatter modes for
mcDataViewer
. - Sine scan on all
mcAxes
(useful for alignment). - Fix current_position updating in mcWaypoints (currently disabled for performance).
- Streamline the grid-creation process.
- Fix cross-platform UI issues, especially in
mcScan
andmcData
(uitabgroup
issues). - Make tabbing in
mcUserInput
go to the next textbox, instead of button. - Make sure the
mcAxis
andmcInput
error-check properly. - Make
mcAxis
recognizeNaN
as the 'don't do anything' base. - Finish
uicontrol
registration to turn off controls when the assigned axis is in use. - Add exposure adjustment(/etc) controls to
mcVideo
. - Properly debug (e.g. PID loop settings) image feedback in
mcVideo
. - Make a GUI for loaded instruments (
uitable
?). - Add ungrouped axis controls to
mcUserInput
. - Fix mcData return to position when unpausing scan.