pltSig help

pltSig

version 12Jul24
Copyright © 2024, Paul Mennen
Getting started: | ● Introduction | ● Default colors
 | ● Installation instructions | ● Workspace plotting
 | ● Experiment with plt5 | ● Release notes
Using the plt window: | ● Preliminaries | ● Cursoring | ● Menu box & menu bar
 | ● Selecting traces | ● Adding traces | ● Data editing
 | ● Zooming & panning | ● Right hand axis
Signal processing applications [sig folder]: | ● afilt | ● fseries | ● winplt
 | ● editz | ● psdZoom
 | ● erip | ● weight
Math & engineering topics [math folder]: | ● airspeed | ● curves | ● hermite
 | ● carlo | ● gauss | ● julia
 | ● circles12 | ● gpsLog | ● motion
plt programming examples [demo folder]: | ● programming examples
 | ● demoplt
 | ● pltSig.exe
plt programming reference: | ● Calling sequence and line styles | ● Cursor commands
 | ● Trace properties | ● GUI building with plt
 | ● Axis properties | ● Pseudo objects
 | ● Labels and figure properties | ● Auxiliary .m files & plt functions
 | ● Colors | ● MPP (Matlab Preprocessor)
 | ● Options


Introduction

pltSig version 12Jul24
Copyright © 2024, Paul Mennen
The pltSig Matlab toolbox is derived from an earlier toolbox called plt which is an alternative plotting interface to Matlab's native plot and plotyy routines. This alternative plotting interface is still the foundation of the toolbox but I changed the name to pltSig because the main focus of the toolbox has shifted from the plotting interface to the applications that depend on it. (Sometimes I still call the toolbox plt for conciseness as well as habit.)

The pltSig applications:
with practical or educational value in various topics of signal processing, math, & engineering

▪ afilt.m
▪ airspeed.m
▪ carlo.m
▪ circles12.m
▪ curves.m
▪ editz.m
▪ erip.m
▪ fseries.m
▪ gauss.m
classical analog filter design
aircraft performance modeling
Monte Carlo simulation
geometrical puzzle
classic plane curves
z-plane analysis
equal ripple digital filter design
synthesis of harmonic functions
summation of random variables
▪ gpsLog.m
▪ julia.m
▪ hermite.m
▪ motion.m
▪ pltmap.m
▪ psdZoom.m
▪ weight.m
▪ wfall.m
▪ winplt.m
GPS data analysis & simulation
Mandelbrot & Julia set fractals
Hermite polynomial interpolation
Brownian motion simulation
2D cubic interpolation & convolution
power spectral averaging & zoom
classic sound level weighting curves
clipping distortion effects
FFT windowing

These applications include capabilities and features not found elsewhere on the file exchange, and most of them merit an individual file exchange submission. However, I chose to combine them into a single toolbox because my goals go beyond the individual applications - primarily to promote a particular graphical interface style that makes applications easier to use. Some aspects of this style include:
  • A "flat" interface (i.e. all menus and controls to be visible at the same time).
  • The use of pseudo objects - collections of traditional Matlab objects combined to serve a specific purpose. The pseudo objects simplify application development and allow familiarity with one application to carry over to the others. The pseudo objects are designed to conserve screen area, allowing a flat design even when many controls are needed.
  • Including a way to adjust the size, position, and color of the screen elements directly in the application (i.e. without requiring opening an additional design tool).
  • The use of dark backgrounds. This allows higher color saturation, making color more useful for distinguishing traces and other screen elements. You can use traditional Matlab colors if you want, but the dark mode is the default to encourages its use.
  • Using mouse movements and clicks more smoothly and consistently to make it easier to pan or zoom plots, move cursors, and adjust control values.
  • The use of super legends (where legend entries also are controls for disabling/enabling traces and other functions).
  • The use of on-screen help text to make it easier to get started with a new application. Also the inclusion of a help button to provide quick access to the full documentation.
  • Providing consistent methods across applications for mouse & keyboard-driven data editing, metric prefixes, and cursoring features such as peak/valley finding, delta, rms, and average value readouts.

The pltSig utilities:

If you write Matlab programs, you will almost certainly find one or more of the included utilities useful. Most, but not all, are focused on the problems of GUI programming. Some of the more notable utilities are described below:

plt | An alternative to the native Matlab plot and plotyy routines. A common complaint programmers have about plot (and even more so with plotyy) is that to do anything beyond the mundane one needs to use obscure handle graphics commands that are only easy to find if you are already familiar with them. plt solves this problem by clearly explaining every option and parameter in one place, giving many examples of the use of every important parameter. There are also many other advantages to plt, to numerous to mention here. Features are added to plt regularly based on suggestions from users.
prin | This is what sprintf would have looked like if it was first written for Matlab instead of for the c library.
figpos | It is often difficult to size and position a figure window in a way that is ideal for all computers because of the wide disparities of screen resolutions and aspect ratios as well as different taskbar sizes and locations. These problems become even more difficult for applications requiring more than one figure window. The figpos routine makes solving such problems trivial, or at the very least, more manageable.
pltwater & pltImage | These routines allow you to easily create 3D waterfall and intensity plots, and like plt, they include many well-documented options.
colorpick | Creates a color palette that allows you to easily choose the color that you find most satisfying for a particular trace or screen element. Usually, if you are even given such a choice, you will be presented with a single palette including all possible color choices. The problem with that is that the color you want will occupy a microscopic portion of the palette, making the choice a frustrating trial-and-error process. colorpick is different because you can adjust the palette to focus on the range of colors that interest you. A large rectangle filled with the candidate color choice eliminates most of the trial-and-error repetition.
pp | A vector pretty printer which overcomes several of the annoyances of the Matlab disp function.

Requirements

  • This toolbox has been extensively tested and verified to run under all Matlab releases from 12.1 (ver 6.1) to R2023b under all versions of windows from Windows XP thru Windows 11. Brief testing has also been done under the Mac and other Unix-based platforms.
  • No toolboxes are required to use plt. However the following applications (in the sig folder) are enhanced when certain Matlab toolboxes are present:
    1. Normally the winplt.m application can display 16 different windows which is quite extensive and covers by far most of the applications of FFT windowing. However if you have the signal processing toolbox an additional 13 windows (defined in that toolbox) are available for display. Also winplt's user-defined FFT window capability does not require any toolboxes, so there is no reason to think of the signal processing toobox as a requirement.
    2. When using a Unix based operating system, the erip.m application must have the DSP system toolbox installed if you want to use erip to design IIR filters or non-linear phase FIR filters. This is because erip calls functions in that toolbox to design such filters. This limitation does not apply to Windows based systems because a compiled Windows application will be used instead of the functions in the DSP system toolbox if that toolbox is not installed. However, even for Unix based systems no toolboxes are required to design FIR filters which can satisfy most filtering requirements.
    3. afilt.m itself does not require any toolboxes, although a different version of that application (called aftiltALT.m) is included to demonstrate an alternative programming approach. This alternative version uses the signal processing toolbox to make that code more concise. Most users will use the standard version for designing and learning about the classical analog filters, and are unlikely to even be aware of the alternate version, so they won't run into this toolbox requirement.
  • If you don't have Matlab, you can still run all the applications listed above by running a Windows compiled application called pltSig.exe. (See the pltSig.exe section.) of the help file.
I'm interested in hearing about your problems and suggestions relating to this toolbox.
You can reach me at paul@mennen.org.
I hope using pltSig enhances your Matlab experience
and increases your knowledge about signal processing.


Installation instructions

  • If you are using Matlab R2014b or later, you may select "Download Toolbox" from the file exchange and all the installation details will be taken care of for you when you install the toolbox. (All the files and folders listed below will be installed.) If you have an older version of Matlab select "Download Zip" from the file exchange and then follow the instructions below.
  • Create a new folder called plt (or pltSig if you prefer) in any convenient place on your disk.
  • Extract the downloaded archive file (pltSig.zip) into this new folder. Your unzipper must preserve the directory structure. (Unzippers do this properly by default.) After the unzip operation the new folder will contain the files and folders shown below.
  • Add the following folders to your Matlab path:
    1. plt
    2. plt\util
    3. plt\demo
    4. plt\sig
    5. plt\math
  • You may delete the downloaded pltSig.zip if you prefer.

    plt.m
    pltinit.m
    cur.m         		Matlab code for the plt plotting package
    plt.htm
    doc\*.*         		Top-level html help file for the pltSig toolbox
    A folder containing all lower-level html files and images.
    plt.chm full plt help documentation (compiled from above plt.htm and doc\)
    util\Pebar.m
    util\Pquiv.m
    util\Pvbar.m
    util\pltwater.m
    util\pltt.m
    util\pltpub.m         		For displaying error bar plots
    For displaying vector fields (arrows)
    For displaying functions as vertical bars
    For displaying 3D waterfall plots
    For adding traces to an existing plt figure
    Calls plt with defaults oriented towards publication quality plotting
    util\arrange.m
    util\b64decode.m
    util\dealv.m
    util\funcstart.m
    util\inifile.m
    util\matplt.m
    util\uic.m         		Automatically generates object positions. Useful for initializing a GUI.
    Used to decode base64 encoded text files (such as MPP\MPP.b64 and sig\iir\iir.b64)
    Similar to the Matlab deal function, except for vector inputs
    Starts a function running and returns to the caller immediately
    Used by many of the applications and demos to create or read files in the plt\ini folder
    Creates a figure for plotting variables contained in a .mat file
    Like the Matlab uicontrol function but can create multiple controls
    util\figpos.m
    util\screencfg.m
    util\workingArea.m     		For positioning figure windows (called by plt)
    Called from figpos to determine screen layout (size, taskbar, etc).
    Called from screencfg to determine the screen working area.
    Used by screencfg.m if the automatic method fails
    util\prin.m
    util\Pftoa.m
    util\pp.m
    util\prin.pdf         		A powerful alternative to sprintf & fprintf. (Called by plt and its demo programs.)
    Called by prin. (Implements the additional floating point conversion formats.)
    Pretty printer for vectors and matrices
    A complete description of the prin, pp, and Pftoa functions.
    ini\pltColor1.mat
    ini\erip.txt
    ini\airspeed.txt
    ini\glider.jpg         		Color specification file: Rename this file to pltColor.mat to use Matlab's default colors.
    Saved filter specifications. (used by sig\erip.m)
    Aircraft configuration file. (used by math\airspeed.m)
    Used by demo\picplot.m if no picture input file is specified.
    ini\afilt.mat
    ini\demoplt.mat
    ini\editz.mat
    ini\erip.mat
    ini\gpsLog.csv
    ini\screencfg.txt
    ini\winplt.mat
    ini\workingArea.txt         		These files are not included with the toolbox, however, they are automatically created when you call plt or some of the included applications and demos.
    demo\demoplt.m
    demo\pltsig.m         		Automatically cycles through the 37 programs in the demo, sig, & math folders (in the order listed below). Like demoplt.m with a more minimal interface. Used to create pltSig.exe, the compiled version of demoplt.
    demo\plt5.m
    demo\plt50.m
    demo\pltn.m
    demo\vbar.m
    demo\wrkspace.m
    demo\movbar.m
    demo\trigplt.m
    demo\subplt.m
    demo\subplt8.m
    demo\subplt16.m
    demo\subplt20.m
    demo\picplot.m
    demo\pltmap.m
    demo\gui1.m
    demo\wfalltst.m
    demo\wfall.m
    demo\pub0.m
    demo\pub1.m
    demo\pub2.m
    demo\pub3.m
    demo\xChart.m         		Programs demonstrating the use of the plt plotting command and its various features. For descriptions, click here.
    sig\afilt.m
    sig\editz.m
    sig\erip.m
    sig\fseries.m
    sig\psdZoom.m
    sig\weight.m
    sig\winplt.m         		Signal processing applications.
    math\airspeed.m
    math\carlo.m
    math\circles12.m
    math\curves.m
    math\gauss.m
    math\gpsLog.m
    math\hermite.m
    math\julia.m
    math\motion.m
                 Applications related to various mathematics and engineering topics
    sig\afiltALT.m
    sig\afiltS.m
    sig\parks.m
    sig\iir\iir.b64         		Alternative version of sig\afilt.m (not run by demoplt)
    Simplified version of sig\afilt.m (not run by demoplt)
    Helper function used by sig\erip.m (Parks-McClellan design algorithm)
    Automatically decoded by erip to create the helper function sig\iir.exe
    ---------------------------- If you plan on making changes to plt.m or pltinit.m, you will need all the files listed below.
    Otherwise you may delete them since plt and the applications will run without them.
    (You also don't need these files to make changes to any of the other .m files.)
    MPP\MPP.CPP
    MPP\*.mi
    MPP\*.m         		The c source code for MPP, the Matlab Preprocessor (explained here)
    Example MPP input files
    Example MPP output files
    MPP\MPP.b64 MPP Executable for Windows/DOS. This text file is in base64 format because binary files are not allowed on the file exchange. To convert this file to the binary file MPP.exe type the following commands:
    >> cd(fileparts(which('plt')))
    >> cd MPP
    >> b64decode MPP.b64 .exe
    Or if you prefer you could use an online converter such as base64decode.org. The file size of the created exe file will be 65536 bytes if it has been decoded properly.
    source\plt.mi
    source\pltinit.mi
    source\cur.mi
    source\abrev.m
    source\pltcase.m
    source\pltdef.m
    license.txt         		True source code. Used to create plt.m by the preprocessor (see MPP.exe above)
    True source code. Used to create pltinit.m by the preprocessor
    True source code. Used to create cur.m by the preprocessor
    An include file referenced by plt.mi and pltinit.mi (GUI abbreviations)
    An include file referenced by plt.mi and pltinit.mi (switch case strings)
    An include file referenced by plt.mi and pltinit.mi (index definitions)
    Open source license

Notes for PC based systems:

If you type plt help or if you click on the Help tag in the menu box included in all the applications, then plt.chm is opened with the Windows help system hh.exe. (which usually resides in the C:\Windows folder) If hh.exe is missing or if you prefer not to use the chm compiled help file, just delete the chm file and plt will instead open plt.htm using your browser.

Notes for Unix based systems:

If you type plt help or if you click on the Help tag in the menu box included in all the applications, then plt.htm is opened in your browser. This file points to many other html files and images inside the doc folder. If your browser supports chm help files and you prefer to use it, you can do that by deleting or renaming the plt.htm file.


Experiment with plt5

Experiment with plt5.m

The easiest way to start learning about plt is to start the sample script plt5.m.
Just type plt5 at the command prompt.
(If you installed plt manually instead of using the toolbox and you did not add plt\demo to the path then you will have to cd to the demo folder before typing the plt5 command.)

A plot such as this will appear on your screen. This simple script plots five traces. Note that the last trace (Line 5) is plotted on the right hand axis. Experiment by trying the following:
• | Click on all the objects in the plt window including the trace IDs.
• | Try that again with the right mouse button (which usually does something different).
• | Click and drag on the x and y axes tick labels (again ... try both left and right buttons)
• | Left or right click and drag in the plot area. (On and off the traces do different things).
• | Hold down both mouse buttons in the plot area and drag (creates an expansion box).

Don't be alarmed that this figure window looks so different from what you are used to with Matlab's native plot. Under the hood, plt and plot use the same fundamental objects. The dark plot background and the missing menu and toolbar are the starkest visual differences between the two. But you will soon learn how easy it is to change the color scheme back to what you are used to and also some reasons why you may not want to do that (at least for data exploration). Because of plt's rich feature set, the menu bar and toolbar are less useful but rest assured that they are easy to bring up if you want them. (Just click on the "Menu" tag to get the Menu Bar and go to the View menu to enable the toolbar.) You can also choose to have the Menu Bar appear by default.

After experimenting with plt5, expand on what you have learned by:
  • Using plt with some data you are currently working with. Type plt(x,y) just as you would with Matlab's plot routine.
  • Use the plt workspace plotter by typing plt with no arguments. Then you can interactively select the variables you wish to plot.
  • Run the other programming examples. (demoplt.m sequences through all the examples.)
  • Explore the topics in "Using the plt window" and "Programming with plt " from the plt help home page.


Default colors

The first thing most users notice when running plt for the first time is that the traces are plotted on a black background. In fact, this can be shocking at first because it is so different from the traditional Windows and Matlab standard color scheme that you may have grown used to. Rest assured however that you are not forced into any color scheme. The next section explains why you may not want to change the defaults, and the section after that explains how this is easily done if you prefer to ignore that advice.

The advantage of the default plt color scheme

The primary virtue of plt's default black plotting background is that you can distinguish far more traces based on color alone when compared with Matlab's default white plotting background.

Why is this? Consider the green trace for example. The plt default uses [0 1 0] for the green trace as you would expect. However, with the standard Matlab color scheme, the green trace is not [0 1 0] because that is too bright and yields low contrast against the white background. So instead they use [0 .5 0]. But this means that the green is less saturated making it more difficult to distinguish the green from say a black or dark grey. Similar problems happen with some other colors. This is why the Matlab default trace color order only includes seven different colors. Once you define an 8th trace it cycles back around and uses the same color as the first trace. Especially with the thin traces commonly used, seven is about the maximum number of colors most people can distinguish and even that is not easy. However, with a black background and when using plt's carefully chosen default trace colors it is not difficult to distinguish at least three times as many traces based on color alone.

To see that this is true, open the pltn example with 20 traces enabled (i.e. type pltn(20) at the command prompt). Now see if you can match up all 20 traces with the respective trace labels in the TraceID box. If you aren't seriously color blind you probably will find this task easy. Now use the edit box below the TraceID box to change the number of lines to 40. (This picture shows the 40 trace colors that will be used.) If you have sharp color vision you still probably can identify all 40 traces just by the trace color. You will find that if you switch to a white plotting background, you will be able to identify far fewer traces based on the trace color no matter what trace color sequence you choose. Matlab's standard plot routine doesn't have features that encourage the use of so many traces, and so you probably haven't noticed this problem with the white plot background. However, plt was designed to work well with many dozens of traces and you will likely take advantage of this capability soon ... and in the process you will come to appreciate plt's default color scheme.

Configuring plt to use Matlab default colors (color specification files)

When you type a command into the command window such as plt(x,y) the data specified will be plotted using plt's default colors (i.e. dark background). However if even after reading the previous section you would rather it plot the data using Matlab's default colors, the easiest way to do this is to rename the file pltColor1.mat in the plt folder to pltColor.mat. This is a "color specification file" whose contents are described in the section below. Every time you enter a plt command from the command window, plt will look for this file (pltColor.mat in the plt folder) and will use the specified colors if the file exists. This particular file (until you change it with the methods described below) specifies colors that are the same as Matlab's default color selections.

If you call plt from a Matlab script or function file then plt will not use the pltColor.mat file; however, it will look for a different color specification file. Suppose you write a Matlab script called FooPlot.m that contains a call to plt. Then plt will look for a color specification file called FooPlotColor.mat. This file must be located in the same folder that contains FooPlot.m. So to make FooPlot use the default colors, you could copy pltColor1.mat to FooPlot.m. This can also be accomplished by including the 'ColorDef',0 parameter in the plt argument list, however, the color specification file method is better if you want to allow the user to easily modify the program's colors from its graphical interface.

There is one other special color specification file that you can use named pltColorAll.mat. If this file exists in the plt folder it will be used by plt no matter which script or function it is called from and even when plt is entered from the command line. However the colors specified by pltColorAll.mat may be overridden by several methods:
  • A color file whose name is derived from the name of the script as explained above will take precedence over the pltColorAll.mat file
  • If a color specification file is included in the plt command line (via the 'ColorFile' parameter, of course that is the file that will be used. If the 'ColorFile' parameter includes a null argument (i.e. [] or '', then plt will ignore all color specification files, thereby reverting to the usual plt defaults. Equivalent to the 'ColorFile' parameter with the null argument is to include the string 'IgnoreColorfile' in the argument to the 'Options' parameter.
  • Even when a color specification file is being used, any particular color characteristic may be overridden by the specific plt parameter that controls that feature. All these parameters are defined in the Colors section.

Creating or modifying a color specification file

Start by opening any plt figure. It will be easier if you choose a plt figure that is already using colors that are close to what you want. To edit one of the trace colors:
  • Frist click on the trace that you want to change color.
  • Then right-click on the y-cursor edit box
  • Select "Properties" from the popup menu by left-clicking on it.
  • The edit box in the lower left corner of the small figure that appears contains the color triple for the selected trace. Simply edit the color triple with the value you have in mind. As soon as you hit enter, the color of the selected trace and its associated TraceID label will be changed to the color you entered.
  • Or if you don't know the color triple that you want, right-click on the color triple edit box and a "Color Pick" palette will appear allowing you to choose the color you want by changing the sliders and then clicking on one of the 100 colors visible in the palette. Learn more about this palette in the description of the Color Pick pseudo object in the pseudo object section.
  • Repeat steps one and two, but right click instead of left-clicking on "Properties". A small figure will appear that will allow you to edit the figure colors.
  • Select the figure element you want to change with the popup menu.
  • As before you can change the color by entering the color triple or by using the color palette.
  • After all the colors have been adjusted to your satisfaction, enable the top menu bar by clicking on the "Menu" tag in the MenuBox, then click on the last menu (plt). Select "Save figure colors". You will see a message box that tells you the name and location of the file that was saved. (If your script was called FooPlot, the file will be FooPlotColor.mat. If you called plt from the command line the file will be called pltColor.mat.) If the color file had already existed, it will be overwritten without warning.
Once the color file is created, you may rename it to pltColorAll.mat if you want it to apply to all your script and function files. Although it is usually easier to edit the colors by the method described above, you can also manually edit the colors in the Matlab command window. For example, if you type:

clear; cd plt; load pltColor; who;

you will see the following list of variables from the file:

cTRACE - Trace colors (an N x 3 matrix, where N is the number of traces in the TraceID box)
cFIGbk - Figure background color
cPLTbk - Plot background color
cXYax - Axis border color
cXYlbl - Axis label color
cDELTA - Delta cursor color

After editing the variables you want to modify, type save pltColor to make the changes permanent.

The menus used to edit these colors are also described in the Menu box and menu bar section. There you will also find that there are other ways of accessing those menus which perhaps you will find more convenient.


Workspace plotting

Starting the workspace plotter

The workspace plotter is a fast way to plot the variables in your current workspace. No more errors from mistyping those long variable names. Just type plt (with no arguments) at the command prompt and a window will appear such as the one shown to the left. (This window was generated with the included wrkspace.m script example which creates the variables listed and then calls plt with no arguments.) All the variables in your workspace (except for scalars and strings) will be listed in the workspace plotter figure. The size of the variable (row, column) appears right after the variable name. If your workspace contains many variables, the variable list may appear in several columns. If your workspace includes 1x1 structures with vector fields (such as the bottom two variables in this figure), then these fields will also appear in the workspace plotter figure using the usual structure notation (struct.field)

Choosing an x-vector

The first thing you should do is select the x-vector that you want to plot along the x-axis. As you can see from the instructions (in green) at the top of the figure, you should do this by clicking on the desired x-vector using the right1 mouse button. You may click on any of the variables shown in white. Note that in this example, the variable called long_variable_name is grayed out. This is because only row or column vectors can be x-vectors. Since long_variable_name has three rows of 400 elements it can't be selected as an x-vector.

1 Actually in this initial situation you may also select the x-vector with a left click. Usually, a left click is used for selecting y-vectors, but since you can't do that without at least one selected x-vector it assumes that you are choosing an x-vector in either case. For consistency, you still might want to stick with the right click for selecting the x-vector - a habit that probably will make workspace plotting easier.

Suppose you choose to plot the 400 element row vector sec along the x axis. After you click on that vector, it turns red for identification and visibility and the tag ←x is placed after the variable name indicating this is the chosen x vector as shown to the left.

Choosing a y-vector

Next, you must choose the array (or arrays) to plot along the y axis. The plot routine requires that one of the dimensions of the y array match the length of the x vector. So in this example, the y arrays must have either 400 rows or 400 columns. In this example, five of the variables do not meet this condition, so they are grayed out and you will not be allowed to select them.

Suppose you then click on these 3 array names: long_variable_name, psvb1, and s.psvb4. As you click on them, the names will turn yellow and the tag ←y is placed after the variable names to indicate that these arrays are to be plotted along the y axis.

If you then click on the "Plot" button plt will create a plot containing five traces. The first 3 traces will be the 3 rows of the array long_variable_name. The fourth trace will contain the data from psvb3 and the last trace will contain the data from s.psvb4. (Note that the order of the traces will be according to the order that the variables appear in the list and not on the order that you clicked on them.) By default, a maximum of 7 characters are used in the TraceID box which means that some characters may be removed to make it fit. Note that the last character is always included and underscores are removed to save space. Also note that for arrays with more than one row or column a row or column index is attached to the end of the name.

Since a single x vector is being used, the x vector name (sec) is used as the x-axis label. In this example, the y-axis is labeled with Y axis for lack of anything better. If you had selected just a single y-axis variable for plotting, that variable name would be used as the y-axis label.

Starting from the previous situation, suppose the numbers in psvb1 were far bigger than the other selected y variables. Then the other traces would be too small when plotted on the same y-axis scale. One way to solve this problem is to plot psvb1 on the right hand axis and leave the other variables on the left side. To do this, double click on psvb1. Note that its color changes to orange and the ←y changes to ←yR to indicate that this variable will be plotted on the right hand axis. You may select as many traces as you want for either the left or right hand axes. To change a ←yR (orange) to a ←y (yellow) single click a few times on the variable name. (The exact number of clicks depends somewhat on the situation.)

So far we have only selected a single x vector. Although this will be sufficient most of the time, the workspace plotter allows multiple x vectors to be selected. Suppose you now right-click on seconds and vb2rep. (Since they are grayed out, they won't accept a left click, but in this situation, they will accept a right click). These two variables will then turn red to indicate that they are to be used as x vectors. They will also be marked with ←x2 and ←x3 respectively and the ←x marking on sec will change to ←x1. The digits after the "x" make it easier to know which x vector you have selected for each y vector. Also the first two variables (b1catb3 and b2catb4), which were grayed out before, now turn white because these 800-element row vectors now can be plotted versus either x2 or x3.

Suppose you then click once on both b1catb3 and b2catb4. They will both turn yellow and be marked with ←y2 indicating that they should be plotted with respect to x2 (seconds). In this case, there is more than one choice of x vectors, so if you click on b2catb4 again, its mark changes from ←y2 to ←y3 as shown in the figure to the left. Since there are no other possible x vectors to choose from, if you click on b2catb4 one more time the ←y3 mark will disappear and the variable name will change back to white, indicating that it is no longer selected.

Every time you click the "Plot" button, the workspace plotter will create a new figure window containing the plot you specified by the various ←x and ←y tags. When you have created many plots, pressing the "CloseAll" button is a convenient way to close all of these figure windows (although the workspace browser window itself remains open). If you have many long variable names, you may not be happy with TraceIDs of only 7 characters. In that case, you can specify longer TraceID's tags by typing a command such as TraceIDlen=17; before calling the workspace plotter. (Try this before typing wrkspace). However, you will notice one problem. The longer trace names will not fit in the space reserved for the TraceID box and the characters will run into the main plot axis. You could solve this problem by using a plt option variable, which is any variable containing the characters pltopt in the variable name. So for example the variables pltopt, pltopt2, another_pltoption would all be recognized as plt option variables whereas variables pltOpt2 and another_plt_option would not be so recognized because it must contain the string "pltopt" exactly (including case). An example of how a pltopt variable can help follows:

Before starting wrkspace.m, try typing the following two lines at the command prompt:

TraceIDlen=17;
pltopt = {'xy', [0 .24 .12 .74 .86; -1 .01 .83 .2 .15]};

The first row of the xy parameter gives new coordinate locations for the plot (both left & right axes) and the second row gives new coordinates for the TraceID box. These coordinates can be generated easily by moving/resizing the objects with the mouse. (To see how look at the manual section GUI building with plt).

If you wanted to make the two commands above permanent for all workspace plotting, create a file on your Matlab path named pltdef.m which contain those two lines. Below are more details about how to use pltdef.m

As an alternative to the 'xy' parameter used above you could use the 'AxisPos' parameter as follows:

pltopt = {'AxisPos',[1.8 1 .86 1 2.8]};

This would increase the width of the TraceID box by a factor of 2.8 while letting plt choose the height of the TraceID box appropriately (an advantage over the 'xy' command). Unfortunately, the AxisPos parameters can't be determined automatically with the GUI building tools. (The "1.8" tells plt to make the blank space to the left of the axis 80% bigger to make more room for the TraceIDs and the ".86" tells plt to make the plot width 86% of the former size so that the plot doesn't run off the right edge.)

To see a list of all the possible commands you can insert into pltopt variables, see these sections of the manual
  • Trace properties.htm
  • Axis properties.htm
  • Labels and figure properties.htm
  • Colors
  • Options

pltdef.m

If a file named pltdef.m exists on your Matlab path then that file will be run before the workspace plotter is opened. This file may contain any Matlab commands, and are usually used to define workspace plotter defaults and variables.

The workspace plotter looks for any variable that contains the characters pltopt anywhere in its name. If it finds any such variables it will use the parameters they contain as arguments to plt when you press the "Plot" button. For example suppose pltdef.m contains these two lines:

pltopt1 = {'Options','Menu','Title','This is a plot title'};
pltopt2 = {'FigName','Workspace plotter'};

Then any plot created by the workspace plotter will have its figure menu bar enabled (from Options), its axis label will be set to 'Frame data' and its figure name will be set to 'Workspace plotter'.

pltdef.m may also include other variables or commands unrelated to plotting options. For example, suppose pltdef.m included these three lines:

circleY = exp((0:.04:2)*pi*1j); % 50 point unit circle
circleX = real(circleY);
circleY = imag(circleY);

Then whenever you started the workspace plotter you would see circleX and circleY in the workspace variables list. This would allow you to add a unit circle to your plot by selecting these variables from the list (x vs. y) This would be quite useful if you were often plotting z-plane poles and zeros, Nyquist data, or other data that lives in the complex plane.


pltSig release notes

Version

12Jul24
  • The toolbox has now been tested with Matlab version R2023b and one incompatibility with that version was fixed.
  • Added @ZVAL to the list of string replacement tokens used with the Xstring/Ystring/Zstring parameters. Also added a version of the cursor get command that returns the z value of the active trace. These changes are described in the Cursor commands section.
  • The changes above were used in the curves.m application so that for curves defined parametricaly, the independent variable (t) at the cursor location appears to the right of the X cursor readout. This was implemented using the Xstring parameter.
  • Other improvements where made to the curves.m application as well, including adding a new curve (bringing the total to 43 curves), adding screen comments to many of the curves, making the figure window larger and other cosmetic improvements.
  • Fixed a bug in the erip.m application that caused it not to run properly when it is installed in a folder path containing spaces (e.g. "MATLAB Add-Ons"). There were also other minor improvements and bug fixes to this application as well as impovements to the help file.

14Sep23
  • Previously the erip was limited to linear phase FIR filter designs if the DSP System Toolbox was not installed. (erip uses functions in that toolbox when designing IIR filters or nonlinear phase FIR filters.) However, as of this version, erip can call an alternative c-based compiled Windows application allowing it to design IIR and nonlinear phase FIR filters without the DSP System Toolbox. This application (called iir.exe) is supplied as a base64 encoded text file since binary files are not allowed in the file exchange. For Windows systems, when erip is started for the first time the text file is automatically decoded to create the executable. Currently the iir.exe application is only available for Windows, so if you are using a Unix-based OS, you will still need the DSP System Toolbox for designing IIR filters. (Note: This doesn't diminish the value of The DSP System Toolbox since it contains many other useful tools and functions. Also, note that the DSP System Toolbox was introduced in R2011a. Before that, the functions used by erip for IIR filter design were in the Filter Design Toolbox.)
  • In the erip.m application, left clicking on the fvtool button opens Matlab's fvtool utility allowing you to display items that are not shown in the other erip figures (step & impulse response, unwrapped phase, phase delay, and group delay). As of this release, you can now also right click on this button which displays those items using the easier-to-use plt plotting function. This is also useful since you may not have the fvtool program in your Matlab installation since it is part of the DSP System Toolbox. (In the case where you don't have fvtool available, left clicking the fvtool button has the same effect as the right click.)
  • Added the utility function b64decode.m which decodes text files encoded using the standard base64 format. See details in the Auxiliary functions section. This function is used automatically by erip as mentioned above and also may be used to create MPP.exe from the included base64 encoded text file if you are interested in using the included Matlab Preprocessor.
  • Added the utility function matplt.m which allows you to easily plot the variables contained in a .mat file. (Similar to the workspace plotter).
    See details in the Auxiliary functions section.
  • Added a new pltSig.exe section to the help file. pltSig.exe is a compiled version of demoplt.m that allows non-Matlab users to run all the applications included in the pltSig toolbox. The pltSig.exe Windows application is not included in the pltSig toobox, but is available on my website here: pltSigEXE.zip (21 Mbytes). To run it, simply unzip the archive to a folder of your choosing and double click on pltSig.exe
  • Added four string arguments (reset,full,width,height) to the figpos.m utility function. Read about the use of these new arguments in the Auxiliary functions section.
  • Improved and dramtically simplified the utility function screencfg.m used by figpos.m to aid in figure window positioning. The screencfg helper function taskbarSZ.m is no longer needed and has been deleted. See details of the screencfg.m function in the Auxiliary functions section.
  • Enhanced the image pseudo object by adding an option for direct scaling and also by providing the ability to add custom colormaps.
  • The pltmap.m demo program used to create the image using cubic interpolation. That is still the default on startup, but now you may also select nearest neighbor interpolation or the image can be created using 2D convolution with a selectable kernel size and shape.
  • Added a load button in the gpsLog.m application so you can load a new log file after the application has started.
  • Fixed a bug in erip.m and editz.m that caused the phase to be calculated improperly.
  • afilt.m was calculating the correct elliptical filter for even order filters but incorrectly for odd order filters. This bug has been fixed. Also the Save tag was added to the menu box to make it easier to save the results to a .mat file as well as a .txt file if desired. The documentation was improved to show how the results in the .mat file can be used.
  • Fixed a recently introduced bug in plt('close')
  • Since the plt.m function has been growing larger, to improve readability, it has been split into two parts (plt.m and cur.m).
  • Fixed additional bugs related to Windows 11.
  • Other minor bug fixes.

04Jul23
  • Fixed an incompatibility with Windows 11. (Previously I had tested only up to Windows 10).

01Jul23
  • The first official release of the pltSig toolbox.

04Apr23

The pltSig toolbox evolved from the plt toolbox. If you would like to see the release notes from the plt toolbox, you can find them on my website here: Old release notes.


Preliminaries

To make it easier to talk about the features of plt, let's first define some terms:

Note that the plt figure window can be moved, shrunk, expanded, stretched, maximized, or minimized at will using the usual Windows controls. If you shrink the window much smaller than the default size, some of the graphic elements may start to overlap.


Selecting traces

If more than one y-vector argument was given to plt, a trace selection box will appear in the upper left corner of the plt window. Note: when using sub-plots, only the main plot (lower left) includes a trace selection box. (Note: sometimes I call this the TraceID box).

In the example shown to the left, plt has been given five y-vectors to plot. The name of each trace and the colors used to plot them are also given. Usually, you should give each trace a more informative name, but in this case, no trace names were specified on the command line, so plt just named each trace with a number. The trace IDs associated with traces 2 and 3 are shown in italics, indicating that they are currently disabled (not visible). The other three trace IDs are shown in a bold upright font, indicating that those traces are enabled in the plot area.

Below is a list of the ways you can control which traces are displayed. If you are plotting just a few traces, the first bullet should cover all that you need to know. However since plt can plot up to 99 traces, the remaining tricks may come in handy:
  • When you left-click on one of the trace names in the trace selection box, the associated trace is toggled on or off.
  • To view a single trace all by itself, right-click on the trace name of interest. That trace will be enabled and all the others will be disabled.
  • Double click on any of the trace names to enable all traces at once.

The default trace color order used by plt (shown in this figure) is quite different from Matlab's usual default in that it allows you to distinguish many traces based on color alone. Since the color used for the trace IDs match the color of the corresponding trace, you can easily identify each trace by name for plots with a dozen or so traces and perhaps even many times that amount depending on the acuity of your color perception. Read more about this in the Default colors section If you have any doubt about the name of a particular trace, simply click on the trace, and its trace ID will appear in the "cursor ID" tag just to the left of the y-axis cursor readout. (See "Cursoring" on the next page). Another way to verify the name of a trace is to click on the name of the trace in the trace ID box. As mentioned above, the corresponding trace will become invisible and then restored when you click on the trace ID a second time. To see the list of RGB triples used to generate this default color order, see the description of the TRACEc parameter. Note that only the colors for traces 1 thru 40 are defined by this array. The defaults for lines 41 to 80 are the same as the colors listed above for lines 1 to 40 except that they are 26% dimmer. The defaults for the lines 81 to 99 are again 26% dimmer than the trace colors for lines 41 to 59.

If the plot uses a right-hand axis, plt indicates which traces are plotted on the right-hand axis by adding some shading to the background of the trace ID. For instance, in this example, traces 1,3,4, and 5 are plotted on the right-hand axis. The use of italics to indicate inactive traces and the methods for enabling/disabling traces are the same no matter which axes are used. (Traces 2 and 4 are disabled in this example.)

If the TraceMK argument was used, then the line types are also shown in the trace selection box. An example of this is shown here which used 'TraceMK',.6 in the plt argument list. The .6 indicates that the first 60% of the width is used for the trace names and the remaining width is used to show the line types. See the Trace Properties section for more detail on the TraceMK parameter.

When the line types are included in the trace selection box, you may enable/disable traces by clicking on the trace name as described above, or by clicking on the line type to the right of the trace name. In this example traces 3,4 and 5 have been disabled. The trace names for the disabled traces are shown in italics as usual and the line types are also grayed out (actually blue/green) to make it clear that these traces are not currently visible in the plot area.

Since left or right clicking on the line types serves the same purpose as left/right clicking on the trace names, you can dispense with the trace names altogether if you like. (This mode is selected by specifying assigning less than 25% of the width of the trace names, i.e. a TraceMK parameter of less than .25). In the example to the left, the TraceMK parameter was 0.1 and note that the first trace has been disabled.


Zooming and panning

Panning

The simplest way to pan the x or y axis is to click on one of the axis tick labels (actually anywhere outside the plot area will work) and drag it until the part of the display you wish to view is visible. Sometimes you may want to pan both the x and y axes at the same time. Instead of doing separate pans on each axis, you can do both at the same time by clicking anywhere in the plot area (but NOT on any of the traces) and dragging that point until the desired view is achieved. Yet another way to pan the x axis is to use the optional x-axis cursor slider that is described in the next section (cursoring) One panning issue you should be aware of is that its performance will suffer when plotting very large arrays. For example, try the following command:

plt(humps((1:1e6)/1e6)'*(1:4));

This will plot 4 traces each of which contains a million points. (These vectors are far bigger than what you will usually want to plot.) The display update rate while panning this plot on my 2011-era desktop computer is about 3 times per second, which is noticeably jerky but certainly usable. The update rate while panning this plot on my somewhat newer 2020 desktop computer is faster (perhaps around 10 times per second), so the jerkyness is much less noticeable. Still, if you want to plot even more data than this it may make sense to decimate it before plotting. Your eye won't know the difference once are plotting more than a couple of hundred points per trace, so you won't be missing anything unless you zoom in dramatically.

Zooming

Try the same thing as with panning, except drag with the right mouse button instead of the left. You will find that dragging toward the origin compresses the axis (for zooming out) and dragging away from the origin expands the axis (for zooming in). As with panning you can zoom both axes at once by a right click and drag in the plot area. (Unlike panning you don't have to worry about whether you are on a trace or not. The same thing will happen in either case.)

Often getting the desired view requires two mouse movements. The first, with a right click and drag to expand or contract the axis (or axes) and the second, with a left click, to re-center the display. You may find that this is the most convenient method, or perhaps you will like one of the seven other methods described below.

The expansion box

If the portion of the graph that you want to zoom in on is completely visible on the graph, the fastest way to display the desired area is to draw an expansion box. This is the way you will likely use most often, so you should try all four ways to draw the expansion box that are listed below to see which ones you like best.
  1. Position the mouse in the plot area over one corner of the area you wish to zoom in on. Then double click the left mouse button, but don't release the button after the second click. Hold the mouse button down while dragging the mouse toward the opposite corner of the desired zoom area. A yellow box will be drawn, which will be stretched or contracted as you drag the mouse around. When you let go of the mouse, the display will look similar to the picture to the left.
  2. A method that requires less coordination is to press and hold the keyboard shift key, then click and drag with the mouse until the expansion box is the size you want.
  3. If your mouse has a middle button, you can click and drag with the middle button. (Note that the mouse scroll wheel often also functions as a middle button.)
  4. Position the mouse in the plot area over one corner of the area you wish to zoom in on. Then click both mouse buttons at the same time, holding them both down while you drag to create the expansion box. [Method 4 apparently is not supported with the new graphics engine introduced with Matlab 2014b, so for that version or newer use one of the other methods.].
  5. And finally you can left-click the grey "x" label in front of the x-cursor edit box. (Actually using the grey "y" label in front of the y-cursor edit box does the same thing.) This draws the expansion box covering the same area as the current axis limits and then zooms the display out by about 20% so that you can see the expansion box. This method makes more sense when you which to make small changes to the x or y axis limits or when you are planning to type in the new limits numerically.

If plt was called with the MotionZoom or the MotionZup parameter, the function specified with that parameter can cause additional text, plots, or other visual effects to appear and be modified as you adjust the size of the expansion box. (See Mouse Motion Functions in the Cursor commands section)

Accepting or canceling the limits indicated by the expansion box

If you are happy with the expansion box you have drawn using any of the five methods described above, both the x and y-cursor edit boxes double up and contain the limits of the expansion box (as shown in this figure). To accept the current limits shown, simply LEFT click anywhere in the plot area. It does not matter whether you click inside or outside the zoom box, however, if you have a very small zoom box then you will have to click outside the zoom box. This is because your click won't be recognized if it is too close to the zoom box.

If you are not happy with the expansion box, you can modify it using one of the methods mentioned below ... or simply RIGHT click anywhere inside the plot area to remove the expansion box and start again.

If you want to adjust the expansion box size or position before accepting it, use one of the methods described below.

Adjusting the expansion box

The most precise way of setting the expansion box limits is to simply type them in. For example, suppose you want to change the x-limits shown in the figure above (7.1866 to 7.9650) to the values 7.1 to 7.9. Simply highlight the lower limit x limit (7.1866) by dragging the mouse over it and then type in the desired value (7.1). Then press "tab" which will accept that value and automatically highlight the next value (upper x-limit). Then after entering the upper limit, press tab again to highlight the y-axis lower limit ... or if you don't want to edit the y limits as well, hit enter instead of tab. As soon as you hit enter or tab each time you will see the edit box change in the plot area to reflect the entered values. Note that the limits are shown in increasing order, however, you are not restricted by that convention. (i.e. entering the limits 4,3 draws the same expansion box as if you entered 3,4). Although this method is by far the most precise way to adjust the expansion box, it is usually more convenient to do this using the mouse as described below:
  • Adjusting the expansion box size
    Simply click and drag on any of the four corners of the expansion box. The corner you clicked on will follow the mouse while the diagonally opposite corner will remain fixed. Note that the mouse behaves in the same way as when the expansion box was first drawn.
  • Adjusting the expansion box position (preserving its size)
    Click on the midpoint of any edge of the expansion box and simply drag the expansion box to its desired location. The expansion box size or shape does not change during this operation.
When adjusting the expansion box size or position in this way, you should click reasonably close to the corners or the edge midpoints. If you click too far away from these points then as mentioned above, the click indicates that the limits indicated by the expansion box should be accepted as the new axis limits, and of course, the expansion box is cleared after the new limits are set.

Alternate method
In earlier versions of plt (before the above method was devised) a different method was used to adjust the expansion box with the mouse. Although it allows you to adjust a single edge at a time (as well as move the position while preserving size), you will probably find this older method less natural. Nevertheless I did not remove this method in case you became used to it with older versions of plt. Newer users will likely prefer the method described above, but for completeness, the older method is described below:
  • Right-click anywhere near the middle of the lower x limit edit box (the 7.1866 number in this figure) but don't release the mouse button. As you are holding the mouse button down, drag the mouse to the left or the right. (You don't have to remain inside the edit box, or even inside the figure window for that matter). As you drag to the left you will see the 7.1866 number decreasing and also the left edge of the expansion box will move to the left. As you drag the mouse to the right of center, the number will start increasing and the expansion box moves accordingly. The farther you drag the mouse from the center of the edit box, the faster the left edge of the zoom box moves. Any vertical movement of the mouse in this situation is ignored.
  • Right-click anywhere near the middle of the upper x limit edit box (the 7.9650 number in this figure), hold the mouse down, and drag left or right. This time the right side of the edit box moves similarly to that described above.
  • Right-click near the middle of the lower y limit edit box (the 2.2735 number) to adjust the lower edge of the expansion box similarly except now only the vertical movement of the mouse matters. Any horizontal movement is ignored.
  • Right-click near the middle of upper y limit edit box (the 5.4595 number) to adjust the upper edge of the expansion box. Again, any horizontal movement is ignored.
  • Note that the mouse methods for adjusting the four edges described so far alter the size of the expansion box. There is one final method to describe that doesn't change the size of the expansion box, but only its position. Right-click on the left or right edge of any of the four edit boxes (x/y lower & upper limits), leaving the mouse button down while dragging the mouse as before. (You have to be close to one of these edges for this to work.) This time, as you drag, the position of the expansion box moves in the same direction as the mouse offset from the original clicked position. The farther the mouse is moved from this original position the faster the expansion box position changes. Unlike the previous four situations, here both the horizontal and vertical movements of the mouse have an effect, so you can even move the box's position diagonally if you choose. Note that the behavior is the same no matter which of the four edit boxes you use to initiate the action. The size of the expansion box will not change during this movement with one exception. If you bump the expansion box into one of the edges the box becomes smaller because the trailing edge will continue to move even after the leading edge has hit the wall.
Note that when right-click dragging the mouse using the alternate method, the mouse pointer does not have to stay inside the figure window and the movement speed increases as you move farther outside the figure window. For this reason, you may not want to position the figure window too close to the bottom screen edge since the mouse movement is already limited in that direction.

Lens mode

Once the expansion box is created, we can choose to accept the new limits or cancel them as we discussed above. However there is a third choice that wasn't mentioned before, and that is to enable "lens mode". To do this, RIGHT click on the marker button in the cursor button group. (This button is labeled with the single character "o"). To indicate that we are in lens mode, the button label "o" will become smaller. Right clicking on this button again will cancel the lens mode and the button label will return to its original size.

Usually, you will first create an expansion box and then enter the lens mode. As soon as you do this, a new figure will be created just to the right of the current figure that shows the portion of the data inside the expansion box. (This is the same portion of the data that would be seen if you accepted the new limits indicated by the expansion box by left clicking inside the expansion box.) If you enter lens mode when an expansion box is not visible, the lens figure will not be created until you create an expansion box. (When using this order, you might want to drag open the expansion box slowly, so the lens figure can keep up with the changing expansion box size.)

Although the lens mode takes more screen area because of the new lens figure created, it has the important advantage that you can see both the expanded and the un-expanded views at the same time. Even more importantly, as you move or resize the expansion box (by any of the methods mentioned above) the lens figure is automatically updated to show the data inside the changing expansion box. This gives you the feel of moving a magnifying glass around the plot. It's a little bit like the magnifying glass provided by the Windows operating system, but also fundamentally different. The Windows magnifier just increases the size of the pixels but you can't see any more than if you used a real magnifying glass in front of your screen. That is it won't show your data with any more resolution. However, the lens mode does increase the resolution. So for example, if you are plotting a trace with many thousands of points, there will be details in the data that you can't see in the unexpanded view, even with the windows magnifier. However, with lens mode, you will be able to see these details. The smaller you make the expansion window, the higher the resolution that will be available in the lens figure.

When plt is called more than once for the same figure window, then there may be two different cursor button groups. You can see an example of this by running the plt50.m demo program. The lower plot has ten traces and if you right click on the marker button ("o") near the lower left corner of that plot, a lens figure will be created showing the data of those ten traces that is inside the expansion box you created in the lower plot. The upper plot has 40 traces, and if you right click on the marker button near that plot, a separate lens figure will be created showing the data from those 40 traces that is inside the expansion box you created in the upper plot. These two lens figures will be overlapping, but you can move one of them if you like, so that you can see both lens figures at the same time.

In most of the example programs that have more than one plot in the figure window, these plots were created using the subplot argument. In that case, the lens figure only shows the data in the main (lower) plot. A possible enhancement would be to allow the lens figure to also show data in the subplots, which I will probably do if I get requests for this feature.

Note that the lens figure also works with image data. Try running the picplot.m demo program, then create an expansion box inside the image and right click on the cursor marker button. Then use the mouse to move the expansion box around the image and see that the lens figure updates appropriately.

Auto scaling

If plt is called without any 'xlim' or 'ylim' arguments, both axes are initially auto-scaled to show the entire data range. At any later time, you can auto-scale the x-axis by right-clicking on the grey "x" label in front of the x-cursor edit box. Right clicking on the grey "y" label is similar for auto-scaling the y-axis, although there is one difference. The difference is that the y-axis is scaled to ensure that the data associated with the active trace is visible. There is an alternate way to auto-scale that picks display limits to ensure that all the traces are visible instead of just the active trace. (See "Expansion history" below).

Expansion history

Whenever you change the x or y axis limits by any of the above methods, the previous limits are stored in an expansion history list. You can cycle through these stored limits by left-clicking on the XY↔ tag in the menu box. (See "Menu box" below). This list is 4 elements deep, so when you zoom or pan the fifth time the oldest display limits fall off the bottom of the stack. Assuming the expansion history list is full (which is usual) clicking on the XY↔ tag four times in a row will show you the last four display limits. On the fifth click, plt will auto-scale both the x and y axes in a way that ensures that all the data for all traces falls inside the display area. On the sixth click, plt goes back to using the axis limits stored in the expansion history list. Although you can auto-scale by clicking on the XY↔ tag a suitable number of times can be cumbersome since you usually don't know where you are in the rotation. For this reason a faster way to auto-scale is provided ... simply right click once on the XY↔ tag.

Doubling or halving the display area

Left-clicking the Zout tag in the menu box (see "Menu box" below) expands each axis by 40% which increases the display area by 1.42 (1.96) i.e. approximately doubling the display area. Right-clicking zooms in, halving the display area. In both directions, the center point of the display remains in the center after the zoom operation.

xView slider

The black horizontal bar with the short gray segment that appears above the plot is called the xView slider. It provides yet another way of panning and zooming the x-axis and is particularly useful when you want to view a small segment of a long data set. The whole bar represents the entire data set and the gray segment represents the portion of the data currently in view. If 10% of the data is currently in view, then the length of the gray segment will be 1/10 the length of the whole bar. Similarly, the position of the gray segment within the bar represents the position of the displayed data relative to the whole data set.

To bring up the xView slider, first right-click on the Ycursor edit box. This will bring up the Yedit popup menu shown here. Then select the third item in this menu (xView slider) and the slider will appear. This is a toggle, so selecting it again will make the xView slider disappear.

If you brought up this popup menu and then decided not to make a selection, just click on the black "X" in the lower right corner of the popup.

If you wanted to the xView slider to appear when your program starts up, you can include the string xView in the 'Options' parameter. Also you can enable or disable the xView slider from the command line or in a program with the command plt click Yedit 3; or its functional form plt('click','Yedit',3);

Moving the gray segment left or right is as easy and natural as you would expect. Simply click on the gray segment and drag it left or right. The plot underneath will update as you are sliding allowing you to easily search for the data portion that interests you. You can also make the gray segment larger so that a larger portion of the data is displayed. To do this simply click in the black area to the left of the gray segment and the left edge of the gray segment will immediately be extended to the point where you clicked. (Similar for the right edge of course.) But notice that this method won't work if you want to make the gray segment smaller. So how do we do it? Simple, just click in the black area, hold down the mouse, and drag. The edge that you selected will follow the mouse allowing you to place it wherever you want. (An alternate method of making the gray segment smaller is to right-click inside the gray segment, but the first method I mentioned is usually easier.) And finally, there is one more trick you can do with the gray segment. Double clicking on it expands the gray segment to fill the entire black bar (i.e. it resets the x-axis limits to cover the full extent of the x data). This is somewhat similar to right-clicking on the menubox XY↔ tag except that the XYrotate tag affects both the x and y axis whereas the xView slider never affects the y axis. (Double clicking on the gray segment a second time undoes the effect of the first double click.)

Notice that when the x-axis is zoomed or panned by any of the other methods provided, the xView slider will automatically be updated so that the gray segment properly represents the visible portion of the data.

The appearance of the xView cursor is probably suitable for most situations, but you can modify its appearance by using the xvProps figure application data. This is best illustrated with an example. Suppose we follow the call to plt with the expression:

setappdata(gcf,'xvProps', ...
{'color' 'red' '+color' 'blue' '+' [0 -.01 0 .02]});

The cell array consists of property name/value pairs. If the property name does not have the "+" prefix the property is applied to the short gray segment, so the first property pair above changes the gray segment into a red segment. If the property name does include a "+" prefix then the property is applied to the long horizontal black bar (which actually is an axis). So the second property pair changes the black bar into a blue one. Any axis property name may be used. The last property pair is a special case since it has the prefix without a property name. The meaning of this special case is that the value specified is to be added to the current position value for the horizontal bar (axis). So what this example does is to move the (blue) horizontal bar down by 1% of the figure height and to make the bar thicker by 2% of the figure height. (Note that you could also specify the position in absolute terms by replacing the '+' with '+pos')


Cursoring

Selecting the active trace

Simply click on the trace you wish to cursor. The cursor will move to the trace you clicked on and that trace will become active. The x and y-cursor edit boxes will change to the color of the new active trace and those edit boxes will be updated to contain the current x and y locations of the cursor. Although the color of the edit box will often be enough to let you know which trace is active it sometimes may be difficult to tell at a glance if there are many traces plotted. For this reason, a cursor ID tag is displayed just to the left of the y-cursor edit box. This will be especially helpful if you assigned meaningful trace names in the plt calling sequence. This cursor ID tag is also in the same color as the active trace.

Dragging the cursor

Click on a trace, hold down the mouse button, and drag the cursor left or right. The cursor will follow the mouse as you are dragging and the edit boxes will continually update with the current cursor location. Make sure you click directly on the trace. If you are not close enough to the trace when you click the mouse button, dragging the mouse will pan the display instead of moving the cursor. Once you start to drag the cursor, however, the cursor will follow the mouse x position even if the mouse moves far away from the line. This is the easiest and most natural way to move the cursor, and probably the only way you will use for small data sets. The other ways of moving the cursor (described below) become more attractive as the data set gets larger (perhaps over 500 points or so).

Typing in a cursor location

If you type a new value into the x-cursor edit box, the cursor will move to the ordered pair (x,y) on the active trace, where x is equal to, or as close as possible to the value you typed in. Likewise, if you type a new value into the y-cursor edit box, the cursor will move to the ordered pair (x,y) on the active trace, where y is equal to, or as close as possible to the value you typed in. In some cases, there may be more than one position on the active trace that meets the above condition. If that happens, the cursor will move to the first such position.

Clicking on the x-axis label

The advantage of this method is that it is very easy to move the cursor by the smallest amount possible (i.e. one data element) even when the data density is very high. Simply click on the x-axis label (for example "Milli-seconds" in the plot shown in the above Preliminaries section) and the cursor will move forward by one data element. And a right click will move in the reverse direction. This method is especially useful when the data order is not clear from the plot (unconnected markers for instance). If you are viewing the entire data set (i.e. the x axis is not zoomed in) then continuing to click after the cursor has reached the left or right edge will have no effect. However, if you are zoomed in, then the cursor will continue to move to the next data element beyond the edge of the display, but then the display will pan so that the cursor remains at the display edge.

A useful feature of the x-axis label is that if after the left or right click you hold down the mouse button, after a short delay (called the repeat delay) the cursor will continue to step left or right at the "repeat rate". When the x-axis is zoomed in, this provides a pleasing and smooth way to pan the display left or right. The default repeat rate is 0.03 seconds (i.e. 33Hz) but you like to pan slower or faster you can change the repeat rate. For example, to change the repeat rate to 0.01 seconds (i.e. 100Hz) use the command:

setappdata(findobj(gcf,'string','ABC'),'repeat',0.01)
(where "ABC" is the current x-axis label string).

The use of the 'repeat' application data property is explained more fully in the description of the edit pseudo object in the Pseudo objects section. You will see from that section that it is also possible to change the default repeat delay (which is 0.4 seconds)

The Cursor button group

In the lower-left corner of the display you will find these four cursor buttons.

The up and down arrow buttons on the left are the peak and valley finder buttons respectively. When you click on the ↑ button (peak finder), the cursor will move to the largest peak in the current display range of the active trace. The next click on the up arrow will move the cursor to the second-largest peak and so on. Moving the cursor by clicking on the trace or by using the cursor slider will reset the peak finder so that the next time you click on the up arrow the cursor again moves to the largest peak in the display range. An alternate way to reset the peak finder is to right click on the peak finder button. The ↓ button (valley finder) works the same as the peak finder except that the cursor moves to successive local minima.

Left clicking on the marker button (o) will toggle the line styles used for all traces in the figure (including subplots). The first click will change the style from lines to markers. The 2nd click will change the style to show both lines and markers, and the 3rd click will restore the traces to the original lines-only style.

Right clicking on the marker button will turn on the "lens" (magnifying) mode. To indicate that we are in lens mode, the button label "o" will become smaller. Right clicking on this button again will cancel the lens mode and the button label will return to its original size. To learn what the lens mode does read about the Lens mode in the zooming and pannings section.

Left clicking on the last button (Δ) enables the delta cursor mode (described below) while right clicking on the Δ button puts the figure into repositioning mode. In repositioning mode, all the figure elements will no longer perform their usual function but you will be able to drag the figure elements to new positions and sizes. The repositioning mode is described in more detail in several parts of the GUI building with plt section including here, here, and here

Delta cursors

Delta cursors are useful for measuring horizontal or vertical distances between two points on a trace. First, move the cursor to the desired reference point on the trace. Then press the Delta (∆) cursor button. The Delta button will turn black to indicate that we are in Delta cursor mode and the reference location will be marked with a large red plus (about 4 times bigger than the normal cursor). Next, click on a different point on the same trace (or drag the cursor to the desired location). The view will then look similar to the picture shown here. (You can also click on a point on a different trace -- the delta cursor will still work as expected.) You can read the current location of the cursor (the small yellow plus) in the usual way - i.e. the edit boxes just to the right of the grey x/y labels. In this case, the cursor x location is 88.0875 µseconds and the y location is 2.63571. The other two edit boxes show how far away the cursor is from the marked reference. In this example, it shows that the cursor at a location 30.0375 µseconds later than and 0.923871 units higher than the marked reference. Simply click on the delta cursor button again, and the reference mark will disappear, the delta cursor button returns to its normal color, and the usual cursor operation will return.

Average, RMS, Slope, & Distance readout

When you click on the cursor ID tag (Line 4 in this example) the ID tag changes to Avg and the edit box then shows the average y-value of all the points of the active trace that are within the current display x limits.

When you click on the ID tag a second time, the Avg changes to RMS, and the edit box then shows the rms value of the active trace. As with Avg, the rms calculation only includes points that are within the currently displayed x-axis limits. (In case you are not familiar with this commonly used form of averaging - rms stands for "root mean square" and is calculated by taking the square root of the average of the squares of the data values).

When you click on the ID tag a third time, the RMS changes to y/x and the value in the y-cursor edit box changes to show the ratio of the y and x cursor values at the current cursor position (i.e. the slope of the line from the origin to the cursor). If delta cursors were enabled, an additional edit box appears to the right of the y-cursor edit box containing ΔY/ΔX, which is the slope of the line connecting the marked position to the current cursor. You can use this to estimate the slope of a trace at a particular point.

When you click on the ID tag a fourth time, the y/x changes to √x2+y2 and the value in the y-cursor edit box changes to show distance from the origin to the cursor position (i.e. the magnitude of x+yi). If delta cursors were enabled, an additional edit box appears to the right of the y-cursor edit box showing the distance from the marked position to the current cursor position (i.e. the magnitude of ΔX+ΔYi).

Clicking the ID tag a fifth time (or clicking in the plot area) restores the cursor readout to show the usual Y value and the ID tag again shows the name of the cursored trace.

Dual cursor

The usual cursor can show the value of only one of the visible traces on the plot. With a dual cursor, two such values can be shown at the same time. This is especially useful when displaying two tightly linked values (for example, the magnitude and phase of a complex quantity). You specify a dual cursor for the Nth trace by including 'DualCur',N in the parameter list. Then when you move the cursor, two y-value readouts are updated. The first one (the active trace) in the usual y value readout location (immediately to the right of the "y:" cursor label) and immediately to the right of that the second value (trace N) will appear. In the plot area, in addition to the normal cursor on the active trace, you will see the dual cursor on trace N (where N is the number specified as the DualCur argument). Just as with the regular cursor, the dual cursor will be shaped like a plus for a trace on the left axis or a circle for a trace on the right axis.

This example was created by including 'DualCur',5 in the parameter list so that the fifth trace (blue in this example) is always cursored along with the active trace. In this picture, Line 1 (green) has been selected as the active trace (by clicking on it) and so its y value is shown (also in green) in the usual y-readout location. As you move the active cursor back and forth along the x-axis, the dual cursor on trace 5 (the circle) tracks the back-and-forth movement so that it always remains vertically aligned with the main cursor on trace 1.

Instead of specifying a particular trace for the dual cursor, it is also possible to specify a particular offset so that the trace for the dual cursor can depend on which trace has the active cursor. For example, suppose I supplied the parameters 'DualCur',-4 . Then plt would add 4 to the line number associated with the active cursor and make that the dual cursor. (For example, if trace 3 was the active trace, trace 7 would be the dual trace.) If adding 4 pointed to a trace number larger than the number of defined traces, then the 4 would be subtracted from the active trace number to obtain the dual cursor trace.

Normally you set the DualCur parameter in the initial call to plt, however, if you can also interactively adjust this parameter by using the "Set dual cursor" submenu under the plt menu. The offset method of specifying the dual cursor (i.e. negative values of DualCur can't be selected using this submenu. (See details in the Menu Box section under the Menu Bar heading)

A complication happens when you are using the dual cursor and delta cursor features at the same time because the same auxiliary y readout location is used for both features. To resolve this conflict plt disables the dual cursor while delta cursors are being used.

The editz.m and afilt.m demo programs demonstrate the use of both the positive & negative DualCur parameter (respectively).

multiCursor

Usually, the single or dual cursors mentioned above are sufficient, but sometimes what you want is to cursor all the traces simultaneously. For the common situation where all the traces share the same x vector, the multiCursor shown to the right is effective. (If the x vectors for each trace are different, the multiCursor is unlikely to be very useful.)

To enable the multiCursor, first right-click on the y-cursor edit box, which will bring up the Yedit popup menu shown to the left. Then click on the 2nd menu item ("multiCursor") and a vertical line will appear on the plot at the current cursor location. Also, a circular marker will appear at all points where this vertical line crosses one of the traces. A string will also appear to the right of each of these markers showing the Y-value associated with the marker. All the methods of moving the cursor mentioned above will also move the multiCursor. If you want the multiCursor to follow the mouse as you drag it back and forth, you must first click on one of the traces (but not on the multiCursor) and then drag the mouse while holding down the mouse button.

The "multiCursor" menu item is a toggle, meaning that when you select it a second time the multiCursor will be disabled.

Usually, the multiCursor is enabled from the Yedit popup menu as shown above, however, if you wanted to the multiCursor to appear when your program starts up, you can include the string multiCur in the 'Options' parameter.

If you wanted to enable or disable the multiCursor from the command line or in a program use the command plt click Yedit 2; or its functional form plt('click','Yedit',2); (Equivalent to selecting the 2nd item in the popup shown above.)

The appearance of the multiCursor elements is probably suitable for most situations, but you can modify the appearance of the multiCursor by using the mcProps figure application data. This is best illustrated with an example. Suppose we follow the call to plt with the expression:

setappdata(gcf,'mcProps', ...
{'|vis','off','+markersize',12, ...
'fontsize',14,'tag',' \\leftarrow %4w'});

The cell array consists of property name/value pairs. The property names may have a prefix. So the first property ('visible') has the vertical bar prefix which signifies that the property should be applied to the vertical dotted line that is usually shown with the multiCursor. (So this name/value pair makes that dotted line invisible.) The next property ('markersize') has the plus sign prefix which indicates that this property should be applied to the markers. The next two properties (fontsize & tag) have no prefix and this indicates that the properties should be applied to the text objects associated with each marker. The tag property is a special case in that this is where the multiCursor formatting string is stored. So in this example, the text next to each marker will be a left arrow (tex character) followed by a four-character floating point string (using the w formatting code). One variation is that any of the values for the markers or text objects may be cell arrays. As an example, suppose there were 3 traces and you wanted the first trace to have a larger cursor text than the last two traces. Then you could replace the "14" above with {14;10;10}. Note that this cell array must be a column vector and its length must be equal to the number of traces.

If you need to read out values for all the traces at once but don't want the clutter of the many cursors on the plot, there are alternatives to the multiCursor. One of these is to bring up the "Cursor Data Window" by clicking on the Data tag in the MenuBox (described here). Yet another alternative is to make double use of the TraceID box to display cursor values as well as the TraceIDs. This can be using the 'Options','tidcuR' parameter which is described in the Options section. The fseries.m and pub2.m examples demonstrate how this can be done.

X-axis cursor slider

If the string 'Slider' or 'S' appear in the 'Options' argument then this slider is inserted just below the cursor button group discussed above. The X-axis cursor slider has two different functions depending on whether the x axis has been expanded as follows:
The X axis is not expanded or is expanded by less than a factor of two. | The X-axis cursor slider moves the cursor left or right by 1% of the data length when you click on the left or right slider arrows and left or right by 5% of the data length when you click in the trough area of the slider to the left or right of the slider button. The slider button itself is not used in this mode and remains fixed in the middle of the slider.
The X axis has been expanded by more than a factor of two | The X-axis cursor slider makes it easy to quickly scroll through the data set or to pan to a particular location. Clicking on the left or right arrows moves the view window left or right by 10% of the visible range i.e. currently visible data will be scrolled out of view after 10 clicks. (Holding the mouse button down over these arrows results in a pleasing continuous scrolling effect.) Clicking in the trough area to the left or right of the slider button moves the view window left or right by 100% of the visible range (i.e. currently visible data will be scrolled out of view for every click). Note that the slider button moves to show the relative position of the data currently in view. Also, you can do a quick pan by grabbing the slider button with the mouse and dragging it left or right. Dragging it as far as possible to the left or right causes the plot to pan to the beginning or end of the data and dragging it near the center of the slider pans the plot to show data near the middle of the data set. Note that all three panning methods (arrow, trough, button) keep the amount of data displayed constant (i.e. the difference between the upper and lower x-axis limits doesn't change). The data cursor is kept in the middle of the x-axis during this type of panning. (Note that in this situation the X-axis cursor slider performs a similar function as the xView slider described in the in the Zooming and panning section. Although the xView slider is more versatile in this role it does require some sacrifice in the vertical space available for the plot.)

Most of the cursor movement and display panning capabilities of this slider (as well as the xView slider) are also achieved using the x-axis label features described above. So you should experiment with these three methods to figure out which ones work best for any particular plot. One disadvantage of the x-axis label method over either of the slider methods is that without a dedicated graphical element for these functions, it's easy to forget that these panning and cursor movement capabilities exist.

Subplot cursors

Each subplot has its own cursor marker but the x-axis readout edit box is shared with the other subplots in the same column. The y-axis readout edit box is also shared with the other subplots in the same column when using the independent subplot mode. (In the linked mode, however, each cursor has its own y-axis edit box.) When using subplots the "Average, RMS, Slope, & Distance" modes mentioned above are not supported. Also, the following cursor features are supported only on the main (lower left) plot:
  • delta cursors
  • x-axis slider
  • xView slider
  • multiCursor
As noted above, there are two subplot modes (set using the SubPlot parameter). With the linked mode, when you move a cursor, all the other cursors for subplots in the same column will move to the same x-axis location. (The cursors in other columns will not be affected.) Also when you change the x-axis limits of a plot (via any method including zooming and panning) then all the subplots in that column will be adjusted so they have the same x-axis limits. On the other hand, when using the independent mode, changing the cursor or axis limits in one subplot will never affect any of the other subplots.

Cursor data window

In addition to all the ways to extract data from a plot, there is one final method that is best when you want to see the values for all the traces at several x positions at once. To do this, simply click on the Data tag of the MenuBox and the cursor data window will appear. A complete description of this window and an example of what it looks like can be found in the Menu Box section.

Right-clicking on the y-axis label

When you right-click on the y-axis label, the menu box and all cursor objects disappear. This is useful for making screen captures of the plots since these objects are used for data exploration and are normally just a distraction in a hardcopy. Right-clicking again causes the objects to reappear. (The plt hideCur; command has the same effect.)

Right-clicking on the Cursor ID Tag

Left-clicking on the Cursor ID Tag has a quite different function described above, but right clicking on this tag is used when you would like to change the name of the currently selected trace. (The name that was specified in the TraceID parameter.) Granted, this need may be rare, but it is here if you need it.

After you have clicked on the trace that you want to change, its name will appear in the Cursor ID Tag. In this example the trace name is "err-constant" and we will change this name to "ErrorOffset". Click on the Y cursor edit box and enter the desired new name. It may be instinct to press "Enter" at this point but be careful not to do that.

Now right-click on Cursor ID Tag. As soon as you do this, you will see the name of the trace change, both in the Cursor ID Tag and in the TraceID box.


Adding traces

From the command window, the most common way to start plt is with a command such as:

plt(x,y)

where x and y contain the data that you want to plot. Of course, this is the same as you would have done before being exposed to plt, except that you would have spelled it "plot". This command opens a new axis in a new figure window containing just the single trace defined by the x,y parameter pair. There are many ways to call plt so that multiple traces are defined, and most of these methods are also shared with the native Matlab plot command. These methods are reviewed in the Calling sequence section.

Where plt and plot diverge more noticeably is how you add traces to a figure that has already been created. With plot this is done using the hold on and hold off commands that I think you will recognize as cumbersome after learning how it is done with plt. The more traces you add, the more you will recognize the advantage of the active legend automatically provided by plt, both for identifying the traces and controlling which traces are visible. (Soon you may begin to wonder how you managed without plt.)

Assuming a single trace was defined using the plt command mentioned above, the command:

pltt(x1,y1,'TraceName')

will cause a new trace defined by the x1,y1 pair to appear on the plot. Also, a second entry will appear in the TraceID box with the trace name specified in the call to pltt. If you are feeling lazy, you could omit the trace name in the argument list, in which case a default (incrementing) trace name will be used. The more traces you add, the more you will realize that you should have specified the trace names all along.

When you type the plt command in the command window, space will be reserved in the TraceID box for eight additional traces (beyond those defined in the plt command itself) to be added using the pltt command. This is usually enough, but if you go beyond this limit, pltt will overwrite the trace data and TraceID of the previously added trace to make room for the new trace being added. If you anticipate needing to add more than eight traces, you can allocate more space in the TraceID box using the + parameter in the plt argument list. (The + is the last parameter described in the Trace properties section.)

You can also specify the data with a single argument: pltt(d) ... or pltt(d,'TraceName') if you want to specify the trace name.

If d is real this is equivalent to the command: pltt(1:length(d),d)

and if d is complex it's equivalent to the command pltt(real(d),imag(d))

Usually, you will probably find it most convenient to add just one trace at a time, however pltt also provides a few ways to add more than one trace at a time. (This is especially useful inside script files). For example, both of these commands:

pltt(x,{y1 y2 y3},{'new1' 'new2' 'new3'});
pltt(x,[y1;y2;y3],{'new1' 'new2' 'new3'});

are equivalent to the three separate commands:

pltt(x,y1,'new1');
pltt(x,y2,'new2');
pltt(x,y3,'new3');

Note that in the 2nd form above, it was assumed that y1,y2,y3 were row vectors. (If they were column vectors they would need to be delimited with spaces or commas in place of the semicolons.)

As before, the data may be specified with a single argument, so:

pltt({d1 d2 d3},{'new1' 'new2' 'new3'});
pltt([d1;d2;d3],{'new1' 'new2' 'new3'});

are equivalent to the three separate commands:

pltt(d1,'new1');
pltt(d2,'new2');
pltt(d3,'new3');

And one last form to consider for when you are adding traces with differing x data. The command:

pltt({x1 x2 x3},{y1 y2 y3},{'new1' 'new2' 'new3'});

is equivalent to these three commands:

pltt(x1,y1,'new1');
pltt(x2,y2,'new2');
pltt(x3,y3,'new3');

In all the commands above with a cell array of trace names, that parameter may be omitted in which case the default trace names will be used. Or the cell array of trace names may be replaced with a single character string in which case all the traces defined by that command will be given the same trace name (useful in a few unusual situations).

It's unusual to need to delete a trace because it is easier to simply click on the trace name in the TraceID box to make the unneeded trace invisible. However, in rare situations, you may want to delete the trace from the plot so that you can later add a new trace in its place (with pltt). You can delete a trace using pltt. As an example, the command:

pltt(-3);

will remove the trace associated with the third trace name in the TraceID box. Later a trace added by a command such as pltt(x1,y1,'new') will be placed in the vacated third position of the TraceID box (assuming that is the first free open slot). Attempting to delete a trace that has already been deleted will have no effect. You can delete any trace regardless of whether it was created with the original plt command or added later with the pltt command. However pltt can't be used (either for adding or removing traces) unless plt was called from the command line or if the '+',n parameter was used in the plt argument list. (n may be zero if you don't need to add more traces than were originally defined by the plt command.)


Right-hand axis

Enabling

You specify which traces should appear on the right-hand axis with the 'Right' parameter. For example, if you included 'Right',[1 4:2:10 17] in the parameter list, then plt would put trace numbers 1,4,6,8,10, and 17 on the right axis and all other traces on the left axis. A slight shading is used behind the Trace IDs associated with the right-hand axis so you can tell at a glance which traces belong to which axis. (You may disable that shading if you prefer. To see how to do that, look at the details of the TraceID parameter in the Trace properties section here.) You can also tell which axis a trace is on by the shape of its cursor ('+' for left axis and 'o' for the right axis). You can optionally specify the label or the limits for the right-hand axis by using the 'LabelYR' or 'YlimR' parameters respectively or by using cell array inputs with the 'LabelY' or 'Ylim' parameters. Note that if you enabled metric prefixes on the y-axis, this applies only to the left-hand axis. The right-hand axis uses standard scaling.

Cursoring

Cursoring the traces on the right or left-hand axes is identical except for the shape of the cursor - a '+' for traces on the left axis and a 'o' for traces on the right axis. Different cursor shapes are used for the dual cursor.

Panning and zooming

The following controls affect both the main and auxiliary axes simultaneously:
  • LinX/LogX (menu box)
  • LinY/LogY (menu box)
  • Panning the x-axis
  • Zooming the x-axis
Panning and zooming the y-axis (which includes zooming with an expansion box and left/right clicking on the "zout" tag) is also normally done simultaneously on the right and left-hand axes. This is called the "linked" mode. Sometimes however it is more convenient to adjust the left and right axes separately (i.e "unlinked"). To unlink the axes, simply click on the right-hand axis label. The label will then appear between two divide signs as shown in this picture (i.e. the axes are "divided"). Click on the label again and the divide signs will disappear indicating that the axes are again linked.

Normally the axes are linked when plt initializes. However if you want plt to start in unlinked mode, include the parameter 'AxisLink',0 in the plt argument list. Including 'AxisLink',1 tells plt to start in linked mode, although you will rarely do that since the linked mode is the default anyway.

Regardless of the linked/unlinked status you can pan or zoom the right hand axis by right or left clicking on or near one of the right-hand axis tick labels (i.e. the 20,40,60,80, or 100 in this picture) and dragging them to the desired position. (As before left click/drag is for panning and right click/drag is for zooming).


Menu box

By default, the menu box contains the 8 items shown in this figure. Some or all of these items may be missing if they were specifically excluded by using the 'Options' argument in the calling sequence. The name of each menu box item is chosen to identify the action when you left click on the item. The action corresponding to a right click is less obvious, but a quick look at the plt menu (see below) will remind you what these actions are.

You can use the 'addtag' parameter to add additional items to the bottom of the menu box. This parameter is described in the Options section.

Details about each menu box item follow

Help

On Windows systems when you left or right click on the Help tag, plt will display the file plt.chm. On Unix and other systems, the browser will be opened to display the file plt.htm (since these systems don't support the chm file format). The left click behavior may be modified by including the 'HelpFile' parameter on the command line and the right click behavior may be modified by including the 'HelpFileR' parameter. Those parameters are described here.

LinX

Left-clicking this tag changes the x-axis scale from linear to logarithmic. The name of the tag itself also toggles between LinX and LogX so that it the tag name always matches the current x-axis scaling type. Left-clicking again toggles it back to LinX.

Right-clicking this tag swaps the x and y data for all the traces which has the effect of displaying the inverse function of the original display. This swapping works best when only a single axis is being used. For multiple axes (i.e. with a right hand axis or with subplots) the effect might not quite be what you expect.

LinY

The y-axis scale changes from linear to logarithmic. This tag also changes to LogY so that it always matches the current scaling of the y-axis. Clicking again toggles it back to LinY.

Right-clicking on this tag opens the plt HardCopy dialog box which looks like this:

This dialog box was developed for older versions of Matlab which did not have much flexibility in creating hardcopy files. Now it is mostly obsolete since you can accomplish pretty much the same things from the File menu in the menu bar. But this dialog box remains since there are a few things it can do that the File menu can't (such as creating a postscript or embedded postscript file for example).

Grid

Left-clicking on the Grid tag alternates between no grids (tick marks only) and full grids on both x and y axes. By default, the grids are solid dim grey lines. (This may be modified by the GRIDc and GridStyle parameters.) Grid lines with high contrast colors and brightness (such as what you get with the native Matlab plot command) make it more difficult to observe the main data traces.

Assuming the grid style has not been modified with the GRIDc or GridStyle parameters, right-clicking on the Grid tag will alternate between the following two grid modes:
  •  Color = [.13 .13 .13],  LineStyle = '-'
  •  Color = [.26 .26 .26],  LineStyle = ':'

Zout

Each left click of the Zout (Zoom out) tag expands the x and y axes by 40% (20% at each end). This approximately doubles the area of the Cartesian plane displayed within the axis limits. (1.42 = 1.96). Right-clicking on the Zout reverses the effect of a left click (i.e. zooms in).

XY↔

Each click on the XY↔ (XYrotate) tag cycles the x and y axis limits to the next display expansion stored in a history list. (The axis limits from the last four zooms or pans are saved in this list). After all the display limits in the history list are used, the next click autoscales both axes so that all the data is displayed. The next click again uses the first display expansion in the history list. Right-clicking on this tag skips the rotation through the history list and directly autoscales the axes to show the full x and y extent of the trace data.

Mark

When you left-click on the Mark tag, a square marker is placed at the current cursor location and a marker string is added to the plot containing the x and y coordinates of the cursor location. The text string will be the same color as the active trace. If the text label is not positioned where you want it, click on the label and drag it to the location you want.

If you right-click on the marker string (which is (7.675, 2.95902) in this example), a new dialog box will appear which allows you to change any or all of the properties of both the trace marker (with the left popup and edit box) and the marker string (with the right popup and edit box). The two pictures below show the Marker and String properties respectively that you can edit.

Note that you can have many of these Edit Marker dialog boxes open at the same time - as many as one per text string (or even more, although there is probably little benefit to that). All these dialog boxes will be deleted automatically if the main plot window is deleted.

Once you select one of the 8 marker properties (on the left), or one of the 10 string properties (on the right, the current value of the property appears in the respective edit box. To change the property value, click in the edit box, and type in a new value.

Note that both the Marker and the String popups have Delete as one of the options. This is useful if you want to add a marker without a string or a string without a marker. You may delete all the markers you have added this way by selecting Delete and then typing all into the edit box below the popup. The same trick works for deleting all the strings added to the figure via the Mark tag. If you want to delete all the markers and all the strings at once click on the Delete all cursor annotations button at the bottom of this figure. First, the button will change its name to click again if you are sure and then you can click on the button again to delete the annotations. If you clicked on that button by mistake, simply close the dialog box before clicking on the button a second time.

Note that when you select the color property for the marker or the string, the property value is a set of three numbers corresponding to red, green, and blue respectively. Each number is an intensity value and must range from zero (off) to 1.0 (full intensity). You may change the color by entering the desired rgb values. When you press <Enter> the new value will be accepted and you will see the marker or string change to the new color. However, since it is often difficult to predict exactly what these colors look like, plt provides an easier way to select new colors. Instead of left-clicking on the rgb triple, use a right click. Then a new color selection box will appear that allows you to use sliders to choose the rgb values and to see the resulting color instantly. Scroll down to the bottom of this page to read about the use of the color selection box (also called a ColorPick pseudo object).

Data

It's easier for our brains to understand (or modify) data sets graphically, especially for large data sets. This is why Matlab includes a plot command and why I developed the plt toolbox. However, occasionally it is useful to view (or modify) the data numerically, which is the purpose of the Data tag. Left-clicking this tag opens a Cursor Data Window (see below) which allows you to view and edit the plotted data numerically.

An additional use of the Data tag is to toggle the menu bar on or off. You do this by right clicking on the Data tag. (See the description of the menu bar below). Note that the initial state of the menu bar is off unless the (unless 'options','Menu' appears in the argument list).

To experiment with the Cursor Data Window, let's begin by typing pltn(3) at the command prompt. This will bring up a plot containing 3 traces. Next click on the Data tag in the Menu box, which will bring up the window you see here.

By default, there will be 25 lines in the cursor data window (24 data lines plus the first line showing the column labels). There might be fewer than 25 lines if the main figure is very short. If you want to see fewer or more lines, simply use the mouse to resize the height of the cursor data window.

The background color of the cursor data window will be the same as the background color of the plot (black in this example). If you were to click on the Data tag in the subplt8.m demo program (which uses a white plot background) then the cursor data window will also have a white background color.

Plots created using the pltpub call don't have a menu box (by default) so you can't open a cursor data window by clicking on the Data tag since it is not there. But a data window is still sometimes useful for publication-style plots so an alternate way of opening one is provided. Since the menu bar is turned on by default by pltpub you can click on the plt menu (the last menu on the menu bar) and then click on "Cursor Data Window" (or press the "c" key).

The cursor data window is not usually big enough to show all the plotted data. Only a subset of the data near the current cursor position is shown. The first column shows the index corresponding to the data in the remaining column, so in this example, the 201st data element is shown at the top and the 224th data element is shown at the bottom. The cursor position is indicated by a minus sign in front of the index, so in this example, the cursor is located on the 201st data element. The cursor position is almost always shown at the top of the figure (as it is here), except when the cursor position is near the end of the data set. So, for example, if the cursor is moved to the right end of the trace, the bottom row would have the negative index number. (This is done so that we always see as many data elements as we have room for in the cursor data window.) Try clicking around at various points of the traces and note that the data shown in the cursor data window always follows the cursor.)

If you click on the Data tag again, a second data window will appear. This can be useful (especially when editing the data) so that you can see and modify two different portions of the data at the same time. Only the 2nd cursor data window will follow the cursor while the 1st data window will continue to show the same indices. If you want the first data window to follow the cursor, then resize the data window. (The cursor will follow the last data window to be resized). You can open as many data cursor windows as you like, although much beyond two is rarely useful.

The second column (with heading "X") contains the X data and the remaining columns show the Y data for the three traces active on the plot. The colors used for the Y data are the same colors used for the plot traces. The cursor happened to be on the first trace when the data window was opened, so the X and ID01 column headings are highlighted to indicate that these represent the X and Y values of the cursored trace. If you click on the 2nd trace (ID02) on the plot, you will see that the X and ID02 column headings will be highlighted. There is only one X column in this example because the three traces happen to have the same X vector, but this will not always be the case.

You may have noticed the small pink box in the lower right corner of the cursor data window. This is used to save the data shown in the cursor data window to variables in the base workspace. This is useful when you want to perform some computation on the data shown in the window. (Perhaps computing the sum of the difference between two columns for example). If plt was called from the command window or from a script, you would already have this data in the base workspace, but if plt was called from a function (as in this example, from function pltn) the data would not be available in the base workspace.

As soon as you click on the pink box, the box changes to the blue(ish) color shown here. The color of the box is of no consequence other than to give you some visual feedback that you have successfully clicked on the box and that the workspace variables were created. If you click on the box a second time it will change back to the original color and the workspace variables will be updated to reflect the new data in the cursor data window (which may be different if you have moved the cursor, resized the window, or edited some data values, for example).

After clicking on this box, if you type who into the command window, you will see:

Your variables are:
XYindex xID01 xID02 xID03 yID01 yID02 yID03

The variables that start with "x" or "y" contain the x or y data for each of the named columns. In this example the length of xID01 (and the other variables) will be 24, although this will change if you stretch or shrink the height of the cursor data window. The first variable "XYindex" will contain the vector [201 224] indicating that the indices from 201 to 224 have been saved.

If you right click on the box instead of left clicking, you will see the same variables in the base workspace, but the lengths of these variables will be 400. (i.e. the entire trace data is saved instead of just the data within the specified indices). An alternate way of getting the full length variables into the workspace is to right click on the "Data" tag (which makes the menu bar visible) and then select the "plt save" menu option. This will create a file named "plt.plt" (or a different file name if you choose to enter one). Then you can type load plt.plt -mat and you will see two new variables called xdat and ydat in your workspace. In this example both of those variables will be length 3 cell arrays, where each element of the cell array contains the data for the ID01, ID02, and ID03 variables respectively.

In this example, the data window was created from a plot containing four traces. The first three traces happen to have the same X vector and the fourth trace has a different X vector. The cursor was on the 3rd trace (serp) when the data window was opened, so the first X column and the serp column have their headings highlighted to indicate that the cursor was on that trace. If you click on the last trace (sawtooth) then the last two column headings would be highlighted. If all four traces happened to have different X vectors, then the data window would have had 9 columns:

index X humps X bell2 X serp X sawtooth

If you click on the pink box in the lower right corner, you will find that these variables have been added to the base workspaces:

XYindex xhumps xbell2 xserp xsawtooth yhumps ybell2 yserp ysawtooth

The cursor data window only displays data for traces that are visible at the time you click on the Data tag. In this example, we have run plt5.m and then disabled all the traces except Line 3. I have zoomed the display to show the small portion of the data between x values of 3.1 and 3.9 and also have clicked the marker button (o) in the cursor button group (the 4 buttons at the lower left corner of the figure). This allows us to see the individual data elements more clearly. Then after clicking the Data tag, the display will look something like this.

Note that the cursor is on the data value with x = 3.2 and y = 3.589224. We can see these two numbers in the cursor readout edit boxes of the plt5 window as well as the top data line of the cursor data window (index = 97).

Suppose now we decide we want to modify the Y value of the data element at x = 3.3 (index 101) from its given value of 3.706158 to a new value of 3.9. To do this, we right click on the number in the cursor data window (3.706158) and then hit the backspace key until the number is gone and then type in the number 3.9 followed by the <Enter> key. As soon as you hit the Enter key, the display will look like this.

Note that the trace in the plot has been automatically updated so that it shows the new value (3.9) that we entered in the cursor data window. Also, note that the heading for the first column in the data window has changed from "index" to "index1". This indicates that we have made just one edit using the cursor data window.

If you simply wanted to make the original value bigger but were not so concerned with an exact value, you could simply left click on the number (3.706158) in the data window somewhere near the last few digits of the number (i.e. near the 1 or the 5 or the 8). After the mouse click that data element would be increased by 2 percent and would then show a value of 3.780281. Left click on this same spot again, and the data element would increase by another two percent to a value of 3.855887. In fact, you can also hold down the mouse key, and the data value will continue to increase by two percent (with a repeat rate of 33 times per second). As you continue to hold the mouse button down you will see the data element in the plot continue to rise until you let go of the mouse button. If you wanted finer control, you could change the default 2 percent change amount to a smaller amount and you can also change the repeat rate of 33 times per second to a faster or slower rate. To learn how to do either of those things, go to the The pseudo edit object section of the manual and read the portion titled "Keyboard and mouse behavior" near the bottom of the page. Note that if instead of increasing this value, you wanted to decrease it by two percent, then you would click closer to the first few digits of the number (i.e. near the 3 or the 7 or the 0). Normally you would want to avoid clicking right in the middle of the number (near the 6 in this example) because then it would unpredictably either increase or decrease the value.

Now let's modify another y value of this same trace at index 113. Right click on the current value (3.76692), backspace to remove it and type in a new value of 3.92 and press <Enter>. The display will now look like this. As before the new value of 3.92 appears in the cursor data window and the plot has been updated, now showing both edited values. The first column heading now shows "index2" to indicate that 2 edits have been made to the original data. Also, a new graphical element has appeared in the data window ... a vertical grey bar. This will appear whenever two successive edits have been made to the same trace. Notice that the ends of the vertical bar extend from index 101 to index 113 (the two positions that have been edited). The purpose of this vertical bar (called the range bar) is to allow editing a range of points.

Suppose for a moment that the 2nd point we wanted to edit was index 124 (instead of 113). Notice that we can't see index 124 in the cursor data window which means that we would have to move the cursor before editing the desired Y value. After editing that value, the range bar will appear, but the ends of the range bar can't be positioned next to the indices that have been edited as before since both indices are not visible in the data window at the same time. In that case, the range bar will extend to the full height of the data window.

Let's now click on this range bar and observe that the display will look like the plot below.

Note that all the y values between 101 and 113 have now been edited in such a way that the points form a straight line. Of course, if you wanted all these values to contain the same number (so the line created would be horizontal), you would simply edit both endpoints to contain that same number. Note that the first column heading now shows "index3" indicating that 3 editing operations have been performed (even though more than 3 data elements have been modified).

Now suppose you wanted to Undo some or all of these edits. To do this, click once on the first column heading and note that the column heading has changed back to "index2" and the plot now looks like the display above. Click on it again and the plot looks like the display above that, and click on it a 3rd time and the plot looks like the display above that (i.e. back to the unedited data). The column heading with then show "index", verifying that the data displayed is the same as it was before any editing has been performed. If you wanted to undo all the edits without having to click once per edit, right click on the first column heading. In this example, that would have changed "index3" back to "index" undoing all the edit operations at once. There is essentially no limit to the size of the undo buffer (limited only by the size of your computer's memory).

Once we have undid all 3 edit operations, let's now experiment with the Redo feature. Click once on the second column heading (which will always be "X") and notice that the first edit has reappeared. After the 3rd click, all 3 edits will reappear and the display will again look as shown here (with the straight line between x = 3.3 and 3.6). As you might guess, right clicking on the second column heading will redo all the remaining edits that have been undone. (In this example, right clicking the second column heading has the same effect of left clicking it 3 times since 3 edits have been undone.)

So far we have only shown that you can alter trace Y values, but actually, you can similarly alter X values. For example, let's go to index 101 and right click on the X value in the 2nd column. Backspace to remove the old value (which is 3.3) and replace it with the new value 3.5. Notice that the point that was at (3.3, 3.706158) has been moved to (3.5, 3.706158). (The Y value was not changed.) In this example, the X vector for trace 3 was modified, but the X vector for the other traces was not changed because those traces were disabled. If we were to open a cursor data window when say (for example) the first 3 traces were enabled and the last 2 traces were not, then when we edit an X value, the X vector for traces 1,2, and 3 would be modified since the first column represents the X data for all 3 traces. Also, the edit count indicated by the first column heading would then increase by three indicating that edits to 3 different traces were performed. Clicking once on the column heading (the undo function) would undo this change for all three traces, and the edit count would decrease by three. On the other hand, if each trace of a plot has a different X vector, then editing an X value only modifies one trace as you would expect and the edit count only increases by one.

Another important feature of the cursor data window is that there is an alternate method of moving the cursor when a data window is open. Simply click on one of the index values in the first column and the cursor will be moved to that index. In this figure, for example, clicking on the "98" will move the cursor to index 98, essentially advancing the cursor by one point, which will move all the values in the data window up one line. (Note that the effect of this is the same as clicking on the X label (Micro-seconds) of the plot.) If instead you click on the index at the bottom of the column (118) it will move the cursor to index 118, effectively moving the cursor forward by one page of the data window. The exception to this is that if you click on the current cursor index (-97 in this example) the cursor will move to index 1 (the beginning of the trace). If you want to move the cursor faster than this, right click on the index value (for example "107" in this picture shown here), backspace to remove the current value, and type in a new cursor index. After pressing <Enter>, the cursor will be moved to the index specified (which normally will be shown at the top of the data window). To move the cursor to the end of the trace, enter a large number such as "1e9" or "inf". More often, you will find it more convenient to move the cursor by clicking on the desired position on the trace, but if the cursor controls have been removed (such as for a publication-style plot created by pltpub.m), then this alternate method for moving the cursor will be the only method available.

The last feature of the cursor data window to describe is its ability to add or remove points from the traces.

Run the plt5 demo, disable all the traces except for Line2 and Line4, and zoom the x and y axis to include the small portion of the plot shown here. Click on the "o" button in the 4 button group at the lower left corner of the figure. This enables the "marker" view of the data so we can better see each data element. Next, click the cursor on one of the points at x= 7.35, and then click on the Data tag to open the cursor data window. Now suppose we want to insert four additional points into the plot between the points at x = 7.4 (index 265) and x = 7.425 (index 266). To do this right click on index 265 in the cursor data window, backspace to remove the index number, and type in 5i. (You can think of the "i" as meaning insert.).

Then after pressing <Enter> you will see the figures shown below.

As you can see, 5 new points have been added after the point at x = 7.4 for both traces. The first column heading of the data window has changed from "index" to "index2" indicating that edits were made to two different lines.

Originally these traces contained 400 points, but if you move the cursor to near the end of the trace you will be able to see that the last index value is now 405 verifying that 5 points have been added. The three lines that were disabled (1,3,5) were not modified and still have their original 400 points. If you click on the "index2" column heading, this modification to both Line2 and Line4 will be undone and the figure will again look like it does above.

Of course, if you want to insert points in just one of the traces, all you have to do is enable only that one trace, then open a cursor data window and insert the points in the position desired.

Note that if the x spacing between points was uniform after an insert modification is done, the spacing is no longer uniform. An exception to this is if you do an insert operation on the last point of a trace. In that case, we are not inserting the new points between two existing points, but merely extending the data set to include some new points at the end. In this case, the x spacing of the new points is set to be the same as the x position between the last two points of the trace (before any points were added). This means that if the trace had uniform spacing before the insert operation, it will still have uniform spacing afterward.

Let's enable only the first 3 traces and zoom the display to the portion of the data shown here. Now suppose we want to remove the four consecutive points on each of these three traces starting at x = 1.5. To do this, open a cursor data window and right click on the index associated with x = 1.5 (index 29 in this example). Backspace to remove the index number and type in the value "-4i". (You might want to think of this as inserting negative 4 points.)

Then after pressing <Enter> you will see the figures shown below.

Note that 4 points starting at x = 1.5 have been removed from all three enable traces. If you move the cursor to near the end of the traces you could verify that these three traces now include only 396 points. The disabled traces (Line4 and Line5) will still include 400 points as before. The first column heading now shows "index3" indicating that 3 lines have been modified. If you click on the "index3" (the undo feature), all three of these edits will be undone and the figure will again look like the figure above.

You might question the value of the cursor data window since you could of course use the familiar interface of an Excel spreadsheet to edit your data. But there are several advantages of the cursor data window that would be difficult to replicate using a spreadsheet. One of these is the synchronization of the text colors used in the data window with the plot trace colors. Another is the instant updating of the plot (including the execution of any cursor callback functions defined for the plot) whenever a data point is edited in the cursor data window. Also, when you are editing a data point, instead of typing in a new number, you can type in a Matlab expression, even including Matlab function calls that you define yourself. The value of the Matlab expression will become the new data point value.

Note that all the data editing operations mentioned in this section can also be done without opening a cursor data window by using the graphical editing methods shown in the 2d data editing section. Usually, that method is easier if you wish to edit the data graphically, whereas the cursor data window is easier if you want to edit the data numerically.

Menu Bar

In most cases you won't need the menu bar when using plt, so by default, the plt figure is opened without a menu bar. However, if you find that you need the menu bar (perhaps to create a hard copy of the figure from the File menu for example), right click on the Data tag in the Menu box and the menu bar will appear. Also if 'Options','Menu' was included in the plt argument list, then the figure will be opened with the menu bar.

The first eight menus in the menu bar (File through Help) are the same menus that will appear in any figure created by Matlab. However, the last menu (plt) was added by plt and contains items useful only for figures that were created by plt. Usually, you will open the plt menu by simply clicking on it, but you can also open it by pressing the ALT key (at which point the accelerator key associated with each menu is underlined) and then pressing the "p" key.

Either way, this is what the opened plt menu looks like. Normally you will select one of these 13 submenus by simply clicking on the desired one, but this can be also done with the keyboard. Notice that there is an underlined character in parenthesis after each submenu name. This is the accelerator key. For example, to open the Cursor Data Window, you can just press the "c" key. Note that some of these submenus have some blue text after them. These are directions for selecting the submenu action without using the menu bar. Eventually, you will probably remember these directions, which will save you time since you won't have to first turn on the menu bar. For example, the Cursor Data Window can be opened from this menu, or by clicking on the Data tag in the menu box (note the menubox Data tag after the Cursor Data Window submenu.

These 13 submenus are described below:

1. plt Help

Opens the plt help system at the top level. For Windows-based systems, the compiled help file (plt.chm) is opened. If you prefer to use your browser instead of the Windows chm renderer, rename or delete the chm file. Then the file plt.htm (in the root plt folder) will be opened in the browser. plt.htm links to many other html and image files in the plt\doc folder. Non-Windows operating systems don't have a .chm reader, so they always use the browser to access the help system. This is similar to clicking on the Help tag in the menu box, however, that tag is often redirected to a subtopic of the help system instead of starting at the top level.

2. plt Save

Saves the current figure so that it can be opened later (see plt open below). A dialog box opens allowing you to choose the name of the file. A .plt extension is used for these files although they are ordinary .mat files. This menu item is equivalent to typing plt save at the Matlab command prompt. Also at the command prompt, you may type plt save filename which avoids the file dialog box by specifying the file directly and of course the functional form plt('save','filename') works also.

3. plt Open

Opens a dialog box that allows you to select a .plt figure file that was saved with the plt save menu. The new window should look and behave the same as the original plt window. (Note that if the original plt window was created with a function that provided additional plotting features to the plt window, those features will not be available after opening the figure.) This menu item is equivalent to typing plt open at the Matlab command prompt. Also at the command prompt, you may type plt open filename which avoids the file dialog box by specifying the file directly. A new window is opened containing the data that was saved.

4. plt Properties

The 4th plt submenu opens a dialog box used to edit line, cursor, or figure properties. It will usually be more convenient to open this dialog box without using the menu bar as follows. First RIGHT click on the Yaxis edit box. This will open the following popup menu:

Then clicking on the first option in this menu (Properties) will open the properties dialog box. There are two advantages of using this method to open the dialog box:
● | The first advantage is that you don't have to enable the menu bar (which will usually start out hidden).
● | The second advantage is only apparent when the traces are distributed among two or more axes. This will happen when either the subplot parameter or the Fig parameter has been used. Usually, there will be a separate Yaxis edit box for each axis containing traces. Right click on the Yaxis edit box associated with the traces or cursors you want to modify. (The dialog box only edits traces or cursors from a single axis.) When using the menu bar to open the properties dialog box you must be careful to first click on the axis containing the traces or cursors you want to modify. Otherwise, the wrong dialog box may be opened.

For example, suppose we are running the demo program subplt.m (shown here), which has 3 traces on the main (lower) plot and two more traces, each on the two subplots. Now RIGHT click on the Yedit box associated with the lower plot. That's the one to the right of the "y:" label with a green background - matching the trace color of the active cursor which is on the first trace (with the trace name "Square").

Then in the menu that opens, click on "Properties" and this dialog box will appear.

The popup menu at the top will be set to the trace that had the cursor on it, and indicates that we are prepared to edit properties of that trace as well as its cursor. If you want to edit a different trace, click on this top popup menu and you will see the choices as follows:

Note that the three trace names associated with this axis are in this list as well as "ALL LINES" which you can choose if you want to make a change that affects all the lines. (For example, when you are changing the linewidth, often you will want to change all the lines at once.)

If instead of starting by right clicking on the green Yaxis edit box, we clicked on the red one right next to it (which you can tell by its color is associated with the middle plot), then the top popup menu in the properties dialog would have been set to "Sweep" (the trace name for the trace in that axis). Also, there would be only one selection in that popup menu since that axis contains only a single trace.

Once we have selected the trace we want to modify, you can click on the popup under the Line properties: label and you can select one of these 8 line properties that you wish to modify. (See figure to the right). Or you can click on the popup under the Cursor properties: label and you can select from the same set of properties that were available for the lines. (See figure to the left).

After selecting on of these properties, the current value for this property will appear in the edit box underneath the popup menu, and then you can change the property value by clicking inside the edit box and typing in the desired change. As soon as you hit Enter, the change you made will be immediately apparent in the main figure.

You could conceivably edit the line data this way by selecting the Xdata or Ydata line property, although this isn't very practical unless the line contains no more than a few points. For longer data sets there are easier ways to edit the line data. (See the Data editing section.)

The popup menu at the bottom allows you to edit other properties that aren't directly related to the traces, such as the figure and axis colors. The eight properties that you can edit from this popup menu are shown here. As before, when you make a change in the edit box below this popup, you will immediately see the effect of that change.

When you select a color property from any of the three popup menus, you will see a color triple in the associated edit box (i.e. three numbers between 0 and 1.) If you know the color triple for the color you want to select, you can left click inside the edit box and type in the new numbers. However, it is rare to know the color triple you want and more often you won't even be sure of the color you want. So instead you can RIGHT click inside the edit box and you will be presented with a color selection box. Read "The Color Selection box" section at the bottom of this page to learn how to use it to choose the color you want.

None of the changes you make from any of these three edit boxes are permanent. So after you close the figure all of the changes are lost. Starting the program again will show the original properties that were in effect before you made any of these edits. Generally, you will use the Properties dialog box to experiment with the various properties to produce a visually appealing figure window for the application. Then the program will be edited to include the property values we have modified with the dialog box. However, there is a way to make the color scheme permanent without editing the application itself. Simply click on the Save color scheme button. For the example, we have been using above (subplt.m, clicking on that button will create a file called subpltColor.mat which contains all the properties in the bottom popup menu as well as the colors of all the traces (reflecting any changes to these colors that may have been made from the Line properties popup menu. When subplt.m is restarted, it will see this new file and use the colors that it specifies. If you want to return to the original default colors defined by the application, simply delete or rename its associated .mat color file. The color selection files created using one application can be used in another application by renaming the color file. These color files are described in more detail in the Default colors section. Also, an application can override this behavior by using the 'ColorFile' parameter which is described in the Colors section.

5.) Cursor Data Window Rclick Menu

This submenu opens a cursor data window which is described above under the right-click action of the Menubox Menu tag.

6.) Swap X/Y axes Rclick LinX

This submenu performs the action described above under the right-click action of the Menubox LinX tag.

7.) Hardcopy Rclick LinY

This submenu menu opens a dialog box used for printing and creating screen captures of plt figures. The use of this dialog box is described above in the description of right click action of the menubox LinY tag.

8.) Hide/Show cursor controls Rclick Yaxis label

Before making a screen capture of the figure window, you may want to use this option to reduce clutter on the hardcopy.

9.) Hide/Show TraceID box

Usually, the TraceID box will be useful on a hardcopy, but sometimes it is not needed and you may want to remove it for the same reason mentioned above for the cursor controls.

10.) Toggle magnifying lens mode Rclick "o"

You can read about the lens mode in the Zooming and panning section here.

11.) Set dual cursor

The dual cursor mode allows you to simultaneously cursor two traces on the same plot. Usually this is set up using the 'DualCur' parameter. (see Dual Cursor). However, you may also use this menu to set the dual cursor interactively. Simply put the cursor on the trace that you want as the dual cursor (by clicking on it) and then select this submenu. After that, the Dual Cursor will become active on the selected trace. This submenu acts as a toggle, which means that if the dual cursor was already enabled, it will be disabled.

12.) Toggle Reposition mode Rclick Delta

The reposition mode is a key feature of plt's GUI building tool set which allows you to move and resize screen objects with the mouse. This submenu toggles between the normal GUI mode and the reposition mode and back. The repositioning mode is described in more detail in several parts of the GUI building with plt section including here, here, and here

13.) SnapTo Grid size Delta+Rclick Delta

This submenu brings up a small auxiliary figure titled SnapTo containing two sliders, one for controlling the x step size and the other for controlling the y step size of the GUI object repositioning mode. More details about the SnapTo auxiliary figure can be found in the GUi building with plt section here

The Color Selection box

When using the "Edit Marker" or "Properties" dialog boxes described above, if you right-click on an edit box containing an rgb triple, this color selection box will appear.

As with the rgb triples, the three sliders represent the intensity values (except in percent) and will initially be set to the same values that were in the edit box. You can move the sliders (or type in new values) to give the proportion of each color that you want. Only integer values between 0 and 100 are accepted giving you more than a million different colors (101 cubed). As you move the sliders, the color of the marker, line, or figure element selected is continually adjusted to reflect the slider settings. The color of the large rectangular patch to the right of the red slider (called the "current color patch") is also adjusted at the same time which makes it easier especially for the smaller screen elements.

To make it easier to find the most pleasing color, the 11 by 11 grid of colors is also updated every time a slider is moved. What this grid shows you are the colors that result when the intensity value of the active slider is mixed with 1 of 11 different intensity values of the two inactive sliders. The active slider (i.e. the slider that was last moved) is shown highlighted in yellow.

So for example, in the figure above, the bottom slider (blue) is active and happens to be set to 0%. This means that all 121 squares of the grid are made up of colors containing no blue. Each row of the grid contains a different intensity percentage of red (0% for the bottom row, 10% for the next row, and 100% for the top row). Likewise, each column of the grid contains a different intensity percentage of green (0% for the left column and 100% for the right column). Thus, the upper right square in this example then would be yellow (rgb = [1 1 0]).

When you see a color in the grid that you like, just click on it. The screen element selected as well as the current color patch will instantly change to be the same as the color you clicked on. Of course, the two inactive sliders move to show the intensity values of the color you just selected. If you can't find a better color, you can revert to the color in effect when you right-clicked on the rgb triple by clicking on the current color patch.

All this may sound somewhat complicated, however, it is far easier to do than to explain. Generally, you can pick any of the three sliders, move it around a bit, and you will quickly see the color you want in the grid. By the way, clicking on the slider trough area moves the slider by 10%, so if you want to limit yourself to the 113 colors formed with the intensities 0,10,20,30,...100%, then you can see all 1,331 such colors by moving one of the sliders to 0 and then slowly clicking 10 times in the trough area of that slider. For more details on the color selection box, see The color pick pseudo object section.


Data editing

You can modify any data plotted with plt, either one point at a time or over a specified range. If your data was plotted as an intensity map using the image pseudo object, use the methods described in the second section below (3d data editing). Otherwise, use the methods described in the first section (2d data editing).

2d data editing

There are three methods of 2d data editing, each with different advantages.

The first method is to use the Cursor Data Window which is the best method when you want to make many edits to several different traces. The strength of this method is that you can see the results of the changes for many points and many different traces at the same time as well as its multi-level undo/redo capability. Its only weakness is that you can't drag points to new locations with the mouse (i.e. it is only keyboard-driven). How to use the Cursor Data Window is explained in detail in the Menu box section here.

The second method is to use the built-in line property editor. This method is conceptually the simplest but is only practical for making minor data changes to lines containing just a few points.

To start the line property editor, first click on the line you want to edit, then RIGHT click on the Ycursor edit box. (This is the edit box closest to the lower right corner of the figure.) This will open the popup menu you see here.

Then choose the first selection from this popup (Properties) to open the properties editor.

Next, click on the popup menu under the Line properties heading. As shown here, this will give you a choice of line properties. Then click on "Xdata", "Ydata", or "Zdata" depending on which coordinate you would like to modify. Then the X,Y, or Z line data (whichever you chose) will appear in the edit box below the popup. Since the edit box is small, you will only be able to see the first two or three values, but you can use the left/right keys to scroll to the data element you want to change. Then you can use the usual keyboard entry methods to enter the new value or values you want, which will be accepted when you press the enter key. You can't add or remove points using this method, i.e. you can only edit points that are already there. (As I mentioned before ... simple, but primitive).

The bottom popup menu of the properties editor is used to modify other miscillaneous figure properties as described here.

The third method of 2D data editing is the graphical method, which primarily depends on using the mouse to move, add, or remove data points. (This method allows keyboard entry as well, but you probably won't use that method often.) As before, the first step is to click on the trace containing the data you want to edit, followed by a right click on the Ycursor edit box which again brings up the popup shown below.

Above, we have already discussed the first menu (the Properties editor).

The next two choices (unrelated to data editing) are multiCursor which toggles the multiCursor mode (described here in the Cursoring section) followed by xView slider which toggles the xView slider (described in the here in the Zooming and panning section).

The fourth menu choice (Undo) will reverse the previous edit operation that was done using the remaining options in this popup. If the previous operation was a range edit (described below) then all the data points within the selected range will revert to their previous value. This is a single-level undo (i.e. you can't undo multiple edit operations). If no edits have been made, the Undo selection will have no effect other than reverting the edit cursor (if it's enabled) back to the standard cursor shape.

The remaining six selections in this popup contain the actual data editing commands. The descriptions below are written for completeness rather than brevity so don't be scared off. The commands are intuitive, so you may be better off skipping the descriptions at first in favor of experimentation. The six editing commands are divided into these two types:

Modify | The modify commands change the x or y coordinates (or both) of a single data point by moving the edit cursor with the mouse. After a modify command, the lengths of the x and y vectors normally don't change ... however, there are two exceptions to this. The first is called the "delete mode" which is activated by dragging the edit cursor to any point outside the axis boundary. The point dragged will be removed causing the lengths of the x and y vectors to decrease by one. The 2nd exception is called the "insert mode". The insert mode is activated by dragging the edit cursor with the right mouse button instead of the usual left button. With the insert mode, the lengths of the x and y vectors will both increase by one.
Range | With the range commands the length of the x and y vectors remain the same. The range commands allow you to modify many consecutive data elements at once. All the data points between the current cursor location and the location modified during the previous data editing command are modified so that all these points lie on a straight line connecting the two end locations. For this to work, the previously edited point and the current cursor location must both lie on the same line. If this is not true, then the Range commands behave just like the Modify commands described above. You can recognize whether the range operation or the modify operation is in progress based on the shape of the edit cursor (See cursor shapes table below).

The six commands are also split into three categories which are identified by the arrows next to each command as follows:

Modify↑↓
Range↑↓
These are perhaps the two most commonly used of the data editing menus. As soon as you select the editing mode, the regular data cursor disappears and is replaced by an editing cursor with a different shape. (See data editing cursor shapes below). Then you can grab the edit cursor with the mouse and drag it to the desired location. However, you will only be able to move the cursor up and down (i.e. only the y coordinate is allowed to change). This is useful because in many data sets, the independent variable (x) represents a specifically chosen set that you want to keep fixed. As soon as you release the mouse button (after dragging the edit cursor to its new location) the edit command will take effect, the edit cursor will disappear, and the normal data cursor will reappear. The cursor then reverts to its usual data exploration function, and to edit another data point you must right click yet again on the Ycursor edit box and select the desired data edit operation. This back-and-forth operation (which I will call the "normal editing mode" is convenient when you just have a few data points to modify, but can become cumbersome when you want to edit many data points in succession.

In that situation, you can switch to "persistent editing mode". (The persistent editing mode applies only to the three Modify selections and does not apply to any of the Range selections). To enable the persistent editing mode for Modify up/down, bring up the data editing menu as usual, but then instead of left clicking on the Modify up/down selection, click on it with the right mouse button. The label in front of the Ycursor edit box (gray) normally contains the letter "y" (as shown to the left) but this label will switch to the delta symbol (also shown to the left) which indicates that you are now in persistent editing mode. Once in this persistent mode, you can continue to modify as many points on the graph as you want (including insert and delete operations) without having to open the data editing menu each time. The only drawback is that you have to forgo the usual data exploration features of the cursor, however, you can restore the default mode by double RIGHT clicking on the Ycursor edit box (at which point the Ycursor label changes from the delta back to the "y").

Modify↔
Range↔
These two menu options behave identically to the two commands described above except for the fact that with the mouse you can only drag the cursor left or right (i.e. only the x coordinate is allowed to change). It is far more common to want to change the y coordinate (using the selections above) or to use the selections below to have the freedom to move the point anywhere (modifying both coordinates), however, the left/right option is included for completeness. You can also select this option with the persistent editing mode by right clicking on the Modify left/right option. The gray "y" label of the Ycursor editbox changes to a ">" greater than sign (as shown at the left) to indicate the persistent mode is enabled. Again, you can restore the normal editing mode by double RIGHT clicking on the Ycursor edit box, which will cause the label to revert to "y" indicating the normal editing mode.

Modify
Range
When using these last two menu options you can use the mouse to drag the edit cursor anywhere (i.e. you can change both the x and y coordinates of the point. You can select this option with the persistent editing mode by right clicking on the Modify option. The gray "y" label of the Ycursor editbox changes to a solid diamond symbol (as shown at the left) to indicate the persistent mode is enabled. Again, you can restore the normal editing mode by double RIGHT clicking on the Ycursor edit box, which will cause the label to revert to "y" indicating the normal editing mode.

The usual data cursor is a plus sign or a small circle, but once you select one of the six editing modes, the data cursor changes to one of the six edit cursors shown in this table. (Although a single edit cursor shape would suffice, these six cursor shapes make it clear which editing mode you are using.)

Whenever you see one of these six data cursors, in addition to dragging the cursor with the mouse, you also have the opportunity to type a new x or y value into the x or y cursor edit boxes. This allows you to enter values more precisely than what is possible with a mouse movement. In all six modes, you may enter a new value in either the x or y edit boxes. (So, for example with the Modify up/down selection, you can still type in a new value into the x edit box, thus bypassing the "y-only" restriction of the mouse movement.) With the ↑↓ and ↔ editing modes, data editing is terminated as soon as you type a value into the x or y edit box. However, for the unrestricted modes, (i.e. Modify or Range without the arrows) data editing isn't terminated until you click on one of the data traces with the mouse. This allows you to enter both x and y values as well as to experiment with different entries before accepting any particular value.

When dragging a data edit cursor around, plt attempts to limit the allowed positions so that the x and y values can be represented in as few digits as possible while still allowing the cursor to be positioned at every pixel in the axis. If you find that you are not able to position the edit cursor at the exact desired spot, you can increase the number of possible positions by increasing the size of the figure, or by zooming the axis in the vicinity of the edit cursor. Another strategy is to simply type the desired value for the x and/or y positions of the edit cursor into the respective edit boxes as described in the previous paragraph. On the other side of the spectrum, sometimes you may want to further limit the allowable positions of the edit cursor to make it easy to place it at cardinal points. You can do this by typing a complex number into the Y edit box.

For example, if you enter the number 20i into the Y edit box, the allowed positions as you drag the edit cursor will be approximately 20 pixels apart in both the x and y directions. This is equivalent to typing the command setappdata(ax,'eRes',[20 20]); where ax is the axis containing the edit cursor. Typing 1i into the Y edit box would return the edit cursor resolution to the default value of 1 pixel.

As another example, suppose we enter the number 3+10i into the Y edit box. This will set the x edit resolution to 3 pixels and the y edit resolution to 10 pixels which also can be accomplished by typing the command setappdata(ax,'eRes',[3 10]);. (Sometimes it is useful to have different resolutions in the x and y directions.)

Note that after typing a complex number into the Y edit box, the message "eRes set" will appear in the edit box to notify you that the new edit resolution has been accepted.

Data Editing programming features

Although programming features and techniques are generally discussed in the "Programming with plt section, the following information is included here because it relates closely with the data editing user interface features shown above.

If you don't like the default size that plt chooses for the six data edit cursors, (shown in the table above) you can change this with a command such as: setappdata(gcf,'CurEdit',14). The new size will be used the next time a cursor edit mode is selected.

If you want to prevent a line from being edited, set the line's tag property to 'NoEdit'. With that line property set, any attempt to edit that line will be ignored.

If plt was called using the MotionEdit parameter, the function specified with that parameter will be called continuously as you drag the edit cursor around. This function may be used to create or modify text, plots or other gui objects on the screen. The sig\editz.m, sig\editz.m, and demo\hermite.m programs all demonstrate the use of the MotionEdit parameter. (Also see Mouse Motion Functions)

If you want to save the altered data (to a file for example) you have to get the data from the 'xdata' and 'ydata' properties of the line handle. (Remember the line handles are returned by the plt call.) When the user modifies any data using these data editing functions, plt executes the user-specified move cursor callback. (See the description of the 'set','moveCB' function here.) The callback routine or any other part of your application can use the 'NewData' application property of the current figure window to determine if data has been modified by one of the data edit commands. For example:

index = getappdata(gcf,'NewData'); % returns the index where xdata/ydata was modified
if index % is there any new data?
DataWasModifiedAction(index); % yes, process the new data
setappdata(gcf,'NewData',0); % indicate that the data was already processed
end;

Note that the NewData property only gets set after a data edit operation is complete, unlike the MotionEdit function which gets called as you are dragging a cursored value to its new location. There is one additional figure application data variable called Dedit that may also be useful for an application using the data editing feature. The command getappdata(gcf,'Dedit') will return the following 9 element cell array:

'Dedit' figure application data (cell array)
1 | The CursorID associated with the edited trace
2 | A number from 1 to 6 which identifies which data edit command is being used (in the order that they appear in the popup): 1: Range 2: Range Left/Right 3: Range Up/Down 4: Modify 5: Modify Left/Right 6: Modify Up/Down
3 | The handle of the cursor object associated with the modified data.
4,5 | For internal use. (This saves the cursor marker shape and size so that the normal cursor can be restored when the data edit operation is complete.)
6 | For internal use. (This saves the edited trace x/y data so that it can be restored by the undo function (right clicking on the "Cancel" option)
7,8 | For internal use. (This saves the position index and trace number of the previously edited trace - information is needed for the modify range operations.)
9 | The position index of the cursor into the edited trace
10 | The edit cursor resolution in the horizontal direction (in pixels)
11 | The edit cursor resolution in the vertical direction (in pixels)

After performing a data edit operation you will likely want to perform another data edit using the same data edit command. Thus it would be nice if you could initiate another data edit operation without having to again select one of the nine data editing operations. In fact, there is a way to do this - simply right-click on the Xcursor edit box. This behaves similarly to what happens when you right-click on the Ycursor edit box except you don't get the menu of data editing choices, since it will use your previous selection. There has to be a default for this operation in case the user right-clicks on the Xcursor edit box before doing any other data edit operation. The default is "Modify" since this is the most commonly used operation. It is rare to want to change this default, although you can do so by changing the second element of the Dedit application variable as described above.

If you create a figure with a plt command that includes the parameter 'MotionEdit','funcname', then while you are modifying a data value by using the data editing feature, the function funcname(a) will be continually called as the mouse is moved (i.e. for as long as the mouse button is held down). The parameter "a" is a nine-element cell array containing information related to the edited trace (as described in the table above). Alternatively, you can define the MotionEdit function after the call to plt with the following command:

setappdata(gca,'MotionEdit',{@funcHandle param1});

The application editz.m shows how the MotionEdit function can be used to reposition the plotted poles and zeros. Note that allowing the plot to be updated as the data is being edited has an impressive advantage for that application.

The hermite.m example also demonstrates the use of the MotionEdit parameter to update the polynomial interpolation of a vector field in real time.

In addition, you may also define functions that get called when a data edit operation begins or terminates. These two functions (respectively) may be defined as follows:

setappdata(gcf,'EditStart',@StartFuncName);
setappdata(gcf,'EditStop', @StopFuncName);

Usually, you enter one of the data editing modes by selecting one of the entries in the Yedit popup menu with the mouse, however, this can be initiated from a program as well. For example, the command plt click Yedit 6; or its functional form plt('click','Yedit',6); puts the cursor into the "modify up/down" mode, since that is the 6th selection in the menu shown above. (Note that the "6" refers to the sixth entry in the Yedit popup menu. The full list of commands similar to this is shown in the table below:

plt xleft EDIT 1; | Enter data editing (using last used editing mode)
plt xleft EDIT 2; | Open up Yedit popup menu
plt xleft EDIT 5; | Exit data editing mode
plt xleft Yedit 4; | Cancel data editing mode
plt xleft Yedit 5; | Enter data edit mode (Range)
plt xleft Yedit 6; | Enter data edit mode (Range left/right)
plt xleft Yedit 7; | Enter data edit mode (Range up/down)
plt xleft Yedit 8; | Enter data edit mode (Modify)
plt xleft Yedit 9; | Enter data edit mode (Modify left/right)
plt xleft Yedit 10; | Enter data edit mode (Modify up/down)
plt xright Yedit 8; | Enter persistent data edit mode (Modify)
plt xright Yedit 9; | Enter persistent data edit mode (Modify left/right)
plt xright Yedit 10; | Enter persistent data edit mode (Modify up/down)

3d data editing

When the data has been plotted using plt('image',...) (i.e. the image pseudo object) you can use the method described below to edit the data in the plotted intensity map. You can practice using these methods with these programs that use the image pseudo object:
  • demo\picplot.m
  • demo\pltmap.m
  • math\julia.m

Modifying a single pixel at a time

To edit a single pixel, first expand the axis so that each individual pixel near the region you want to modify is easily visible. If you don't know how to do that, review the Zooming and panning section. Next, click on the pixel that you want to edit. In this example, we have clicked on the pixel at (x,y) coordinates (37,42). The cursor marker (an asterisk symbol) will always appear in the middle of the pixel. Its current Z value (i.e. intensity) will appear below the graph after the "Z:" label. In this example (using the picplot.m demo in its grayscale mode) the Z value for this pixel is 223.66. Suppose we want to change this value to 4.4. Right click on the 223.66 number and then press the backspace key until the number is erased and then type "4.4" and press enter. (Instead of the backspace key, you could also use the down arrow key. That's harder to remember, but it's faster since you only have to press the down arrow key once to erase the entire number.)

The figure will now look like this. Notice that the chosen pixel has changed to black and the Z value readout shows the new pixel value. Also, note the cursor color has also changed. (This is because plt ensures that the contrast between the cursor and the pixel color is as high as possible.) Notice that a new symbol has appeared (a left arrow) just to the left of the Z: label. This is the undo symbol. If you now click on the undo symbol, the Z value will revert to 223.66 (the value the pixel held before editing). Since this was the only data edit we have done, the left arrow symbol will disappear indicating that there is nothing left to undo. There is essentially no limit (beyond the amount of memory allocated to Matlab) to how many undo operations are allowed. If you make 100 edits, the left arrow will remain visible until you click on it 100 times as each of the edits are reversed. Modifying the same pixel twice in a row is treated like a single edit. For example, if we modified the 4.4 value again (to say 5.5) without changing a different pixel in between) then pressing undo will not change it back to 4.4 but rather back to 223.66 (its original value).

In addition to typing the new Z values as we just described, if you want to change the intensity value more gradually you can left click just to the right or left side of the current Z value to increase or decrease the Z value by 1. You can also hold down the mouse button after clicking and the Z value will continue to increase or decrease until you release the mouse button. The pixel value will slowly change its color as you are holding down the mouse button and the cursor marker will also change as necessary to maintain adequate contrast with the surrounding pixel color.

Modifying a rectangular region

In addition to editing a single point, we can fill a rectangular region with a constant value. We start by placing the cursor on any corner of the region we would like to fill. In this example, I have chosen to cursor the upper right corner of the fill region at the (x,y) coordinates (6.377,8.102). The Z value at this point happens to be 6.3951 as you can see by the Z readout at the bottom of the figure. For this particular color map (jet) this Z value corresponds to the orange pixel you see in the cursored square. (Note: You can replicate this figure by running the pltmap.m demo program and then moving the resolution slider all the way down to its lowest setting of 25 pixels).

Next, we enter the desired fill value (zero in this example) by right clicking on the current Z value (6.3951), entering backspaces followed by a zero and then pressing enter. This is the same process described above for editing a single pixel, and indeed only the one pixel chosen has been modified as shown here.

Next, we cursor the opposite corner of the desired rectangle. In this example, I have cursored the point (3.188,2.209) which will be the lower left of the fill region. As you can see, the Z value of this point is 6.3627 (which is similar to but not identical to the original Z value of the upper right corner pixel).

To complete the fill operation, right click on the yellow Z: text string which is located at the bottom of the figure between the left arrow symbol and the zero character that indicates the fill value for the region. As soon as you do that, the selected region will be filled and the figure will look like this one. This may feel like two edit operations, but it is recorded as a single edit operation which means that clicking on the left arrow will restore the entire fill region to contain the values that it had before the rectangular fill operation began. If no other pixels were edited, the left arrow symbol will disappear indicating that there is nothing left to undo. The undo buffer may contain a mixture of single point and rectangular fill edit operations and they will be undone one item at a time (in the reverse order that they were entered) until the left arrow disappears and the plot is returned to its original configuration.


afilt.m version 09Sep23
(Classical analog filters)

Author: Paul Mennen
Email:  paul@mennen.org

Overview

Displays the frequency response for the five "classical" analog filters:

Butterworth | Designed to be as flat as possible in the passband without passband ripples. An Nth-order Butterworth filter will roll off at 6N dB per octave (or 20N dB per decade). This rolloff isn't as fast as with the Chebyshev or Elliptic filters, but the phase for those filters will be more nonlinear. First described in 1930 by Stephen Butterworth
Bessel | This filter is designed to have the best phase linearity possible in the passband. The phase delay and group delay will be nearly flat in the passband so this will be the best choice if it is important to preserve the wave shape of the passband signals. The step response has some overshoot, but much less than with the other filter types. The price to pay for this linearity is that it has a noticeably slower rolloff and thus less stopband attenuation. Friedrich Bessel developed the mathematical theory which was then applied to the filter design problem by W.E. Thomson in 1949.
Chebyshev type 1 | This filter gets its name because they are derived from Chebyshev polynomials. It achieves a steeper rolloff than the Butterworth filter by allowing ripples in the passband.
Chebyshev type 2 | This filter also achieves a steeper rolloff, but it does this by allowing ripples in the stop band. (i.e. no ripples in the passband). Also sometimes known as an inverse Chebyshev filter.
Elliptic | This filter type achieves even a steeper rolloff than the Chebyshev filters by allowing ripples in both the passband and the stopband. Also sometimes known as a Cauer filter (after the German mathematician Wilhelm Cauer).

Although these 5 filters are analog filters by design, some people design digital filters by choosing one of these filter types and then transforming them from the continuous-time representation in the s-plane to the discrete-time representation in the z-plane, usually using the bilinear transform. This is done because the designers are familiar with these analog filters, but it is generally misguided since there are better design techniques for digital filters. (See the erip.m application for example.)

This application comes in 3 flavors:
  1. afiltS.m - A simplified version appropriate for study by a programmer learning GUI design.
  2. afilt.m - The full version.
  3. afiltALT.m - An alternate version to explore another method of displaying the phase information
All 3 are included in the sig folder, but only the second one is run by demoplt.

1. afiltS.m

By keeping the goals modest with few frills we can create an application with just a few dozen lines of code. The goals and the design method for this application are described here in the GUI building with plt section.

afiltS first creates a prototype filter for each of the five filter types. This prototype filter is always low pass and always uses a cutoff frequency of 1 radian/sec regardless of the settings of the frequency sliders. Then the prototype filter is transformed to high pass, band pass, or stop band if one of those types was selected as well as translated to the frequencies selected by the frequency sliders.

Note that the lower limit on the frequency sliders is .01 radians/sec and the upper limit is 10. However, you can enter any value you want for these sliders by typing the number into the slider edit box. That number will be used even if it is less than .01 or greater than 10. The X axis limits will be set appropriately so that the cutoff frequency is somewhat close to the middle of the plot.

When you LEFT click on the No. of Points popup (the 200) in this default setup, the popup will expand showing you the choices available as shown here. If you want to change the number of points without opening the popup, instead RIGHT click on the popup and it will change to 400. Another right click and it will change to 800, and then to 1600, and then cycles back to the first options (100). Clicking on the black "X" closes the popup without making a new selection.

When you RIGHT click on band pass (just to the right of the # of points popup) the popup will expand showing you the choices available as shown here. If you don't want to open the popup, LEFT click instead and it will cycle thru the four options as you click. Notice that the use of the right vs. left mouse clicks are the reverse of what is used for the number of points popup described above. Notice in the code where this popup is created there is the argument 'swap' and the end of the call, and that argument enables the reversed use of the left and right clicks. Whether you use the regular or the swapped usage is largely a matter of taste, but the swapped style is often used when there are only a few choices and you want to be able to easily see the results of every choice. Usually, popups are created with a label (as with the number of points popup above) since the number alone would have no meaning. But for this popup, a label isn't necessary because the choices make the meaning self-evident. To find out more details about the popup pseudo objects, refer to the Pseudo objects section. There you can also read about the edit pseudo object, which is used here for the filter order (just to the left of the number of Decades popup.

2. afilt.m

To create this application, I started with afiltS.m above and made the following enhancements:
  1. Elliptic transition ratio (shown with multi-line text string)
  2. Selecting colors
  3. Phase display
  4. Pole/zero plot
  5. Display of phase delay and group delay
  6. Saving/restoring the GUI state using a configuration file
  7. Adding temporary user help message
The motivation and some of the coding techniques behind these seven enhancements are discussed in the here in the GUI building with plt section.

The text on the plots (in orange and yellow) provides some basic user help. Those help messages disappear after any parameter is modified. This prevents the help text from being a distraction when using the application.

With the default settings, the prototype checkbox will be checked which means the poles and zeros displayed in the pole/zero plot will be for a low pass filter with a 1 radian per second cutoff frequency. (These prototype filters are calculated as a first step in deriving the filters with the specified frequencies of all four configurations (band pass, high pass, passband, and stopband). If you uncheck the prototype checkbox, then the poles and zeros of the filter in its requested configuration is displayed which also enables the linking of cursors with the main plot. (i.e. clicking on a trace in the main plot will move the cursor to the corresponding position of the imaginary axis of the s plane.)

With the default parameters (as shown above), select "low pass" from the popup, and then left click the phase delay button. You will see a figure similar to this one, which shows the phase delay for each of the five filter types.

Notice that the Bessel function has almost perfect flatness up to the cutoff frequency (0.1 radians/sec). This makes it good at preserving the shape of the passband signals. The Butterworth filter is also reasonably linear in the passband whereas the other filters are not very linear. If this non-linearity is not important, then you can take advantage of the much sharper rolloff of the magnitude response beyond the cutoff frequency.

If instead of left clicking on the phase delay button you right click on it, then a similar figure to this will appear except that it will show group delay instead of phase delay. Note that the Bessel filter also has a nearly constant group delay in the passband.

When you close the afilt figure window, a file called afilt.mat will be written to the plt\ini folder which contains the numerator and denominator polynomials associated with the current display, the current values of all the afilt controls as well as the current figure size and position. The next time you restart afilt, it will use the settings in afilt.mat so that the figure looks the same as when you last closed it. (Delete the afilt.mat file if you want to return to the default settings.)

If you want to save the afilt.mat file without exiting the application, click on the Save tag at the bottom of the menu box. If you right click on the Save tag, in addition to saving the afilt.mat file it will also create a file called afilt.txt (also in the plt\ini folder) that contains the numerator and denominator polynomials in text form (in order of decreasing powers of s). For example, if you start afilt with the default settings, switch to the low pass filter type, and then right click on the Save tag, the text file created will look like this:

----- Butter -----
num:                     0                     0                     0                     0                     0  1.000000000000000e-5
den:  1.000000000000000000  0.323606797749978914  .0523606797749978856  5.236067977499788e-3  3.236067977499789e-4  9.999999999999999e-6

----- Bessel -----
num:                     0                     0                     0                     0                     0  9.999999999999999e-6
den:  1.000000000000000000  0.381070120534927559  .0677667371567686472  6.886367652423625e-3  3.936283427035349e-4  9.999999999999996e-6

----- Cheby1 -----
num:                     0                     0                     0                     0                     0  8.172251697369242e-7
den:  1.000000000000000000  .0706460568061092337  .0149954326711260084  6.934769584958417e-4  4.593491210648848e-5  8.172251697369246e-7

----- Cheby2 -----
num:                     0  5.000250018751560e-3  6.92180711223233e-21  2.000100007500624e-4  1.55762265652227e-21  1.600080006000499e-6
den:  1.000000000000000000  0.214920041464015166  .0230828108613219812  1.550053632488997e-3  6.572941232619946e-5  1.600080006000500e-6

----- Elliptic -----
num:                     0  4.314287472988311e-3  -1.3272994986220e-21  1.794044791760442e-4  -3.6138205971008e-23  1.686430764421909e-6
den:  1.000000000000000000  .0697394438160997276  .0169373877260952863  8.336743397671178e-4  6.689503003586831e-5  1.686430764421909e-6

You can use the standard Matlab tools to verify that the frequency response curves shown by afilt are correct as follows:
>> load E:\mcode\plt\ini\afilt.mat % Load the afilt.mat file. The ini folder isn't usually
                                   % on the path, so you need to specify the full path
>> load(inifile('afilt'))          % Or, use this alternate way of specifying the plt\ini folder
>> figure;                         % This is needed if the afilt figure is still open. Otherwise freqs will
                                   % display its results inside the afilt figure which doesn't work well.
>> freqs(num{1},den{1});           % Display the Butterworth frequency response.
>> freqs(num{2},den{2});           % Display the Bessel frequency response.
>> freqs(num{3},den{3});           % Display the Chebychev type 1 frequency response.
>> freqs(num{4},den{4});           % Display the Chebychev type 2 frequency response.
>> freqs(num{5},den{5});           % Display the Elliptic frequency response.
>> [n,d] = bilinear(num{5},den{5},.5);  % In this example, we create a digital elliptic filter from the analog filter
>> freqz(n,d);                          % and display its frequency response using freqz or fvtool. However digital
>> fvtool(n,d);                         % filters are more easily designed using the erip.m application.

3. afiltALT.m

This is an alternate version of afilt.m. It was also created by enhancing afiltS.m, except that only enhancements number 1 and 3 listed above are implemented. This alternate version is included for two reasons:

● A different method of displaying the phase information is used. Only 5 traces are used instead of 10. This means that each trace has to display both magnitude and phase information. You may find that this method of displaying two different functions on a single trace useful in applications of your own design.

● Instead of computing the frequency response inside the application, this version calls on the butter( ), besself( ); cheby1( ); cheby2( ); and ellip( ) functions in the signal processing toolbox. Comparing the results of afilt.m and afiltALT.m allows you to verify that the frequency response calculations done inside afilt and afiltS are equivalent to these five functions. Of course, that means that you can't run this if you don't have the signal processing toolbox installed.

Copyright © 2024
Paul Mennen


editz.m version 01Jul23
(Tinkering in the z plane)

Author: Paul Mennen
Email:  paul@mennen.org

Overview

Although you could design digital filters with this application, there are hundreds of other tools better suited to that task. On the other hand, editz is more focused on helping you learn to think in the z-plane or develop an intuition about how the roots of the transfer function affect the frequency response. The key feature of editz that will help you develop this intuition is the ability to grab hold of a pole or zero with the mouse and slide it around the z-plane while watching how this affects the frequency response. As soon as you used editz for the first time you will quickly recognize the value of this instantaneous feedback.

To benefit from this application, you will need at least a rudimentary understanding transfer functions and their depiction in the z-plane. There are hundreds of books that can help you with that, but if you are a beginner in this area, one recommendation I can make is Sitler's Notes - one of the first papers ever written discussing the z-plane for system analysis. Although this paper was never officially published, you can find it on my website (www.mennen.org) in the section titled "Signal processing papers".

Starting

Begin by typing editz in the Matlab command window. When you start editz for the first time, it will initialize the transfer function to:

Note that the numerator polynomial is 5th order and the denominator polynomial is 4th order so there will be 5 zeros and 4 poles. (Two of those zeros and two of those poles are below the x-axis, so they are not shown on the pole/zero plot.) There isn't anything particularly special about this transfer function ... it was just chosen so that you will have something more interesting to start with than a flat transfer function. After the first time, editz will start up showing the transfer function that was in effect the last time you pressed the "save" button.

This is the figure window you will see after starting editz with the above default transfer function:

The top plot (Transfer function)

The top plot shows the dB magnitude (green trace) and wrapped phase (purple trace) of the transfer function. The position of the cursor shown above is at x = 0.25 (i.e. one-quarter of the sampling frequency) which represents an angular frequency of pi/2 (or 90 degrees) which is shown just to the left of the x cursor readout. The y cursor readout shows that the magnitude is 3.37658 dB below the peak value (or 67.8 percent of the peak value, as shown below the traceID box to the left of the plot.) The y cursor readout (in purple) also shows that the wrapped phase is 135 degrees. Of course, these cursor readouts will change as you slide the cursor left or right. You can move the cursor one position right or left by clicking on the x-axis label ("Fraction of sample rate") with the left or right mouse button, and you can hold the mouse down to see the cursor slide slowly left or right. If you would like to see the magnitude or phase trace alone, click on "Mag" or "Phase" in the traceID box to the left of the plot.

The bottom plot (Pole/Zero plot)

The blue help text in the middle of the pole/zero plot tells you how to use the mouse to move/add/remove roots (i.e a pole or zero). This text disappears as soon as you click anywhere on the pole/zero plot or if you click on any of the buttons to add or remove a root. If you want to see the help message again, just click on the green "quick help" tag just below the TraceID box.

Try clicking on any root.

Notice that a diamond shape marker appears on top of the root indicating that you can now move this root to a new location by dragging it with the mouse. Observe that while you are dragging the root to a new location the top plot is being continuously updated to reflect the transfer function magnitude and phase associated with the pole/zero plot. (This is the signature feature of editz.) When you release the mouse button, the diamond marker will disappear but will reappear as soon as you click on another root that you wish to move. Note that when you are moving a root, you are actually moving a pair of roots (complex conjugate pairs) unless the root is on the x axis. This ensures that the polynomial coefficients are always real.

Often when you are moving a zero, you may want to place the zero on the unit circle since this results in a symmetric numerator polynomial. To make this easier, there is a "snap to" region shown in green around the unit circle. When you move the zero inside this snap to region, the zero jumps to the nearest position that is exactly on the unit circle. If you want to change the thickness of the snap to region, adjust the "Tolerance" slider in the far lower left corner of the figure. (To disable the snap to feature, simply move the tolerance slider to 0.)

Likewise, when you are moving either a zero or a pole, often you will want to place it on the real axis. To make this easier, as you move the root inside the green tolerance band along the X axis, the root will snap to the real axis. Note that as you snap a root onto the real axis, the number of coefficients in the corresponding polynomial will decrease by one since the root no longer has a corresponding complex conjugate location. (You will also see the number of zeros or poles change in the TraceID box just to the left of the Yaxis label.) Note that as you are moving the root, the x/y cursor readout shows the real and imaginary components of the root respectively and the polar representation (i.e. magnitude/phase) is shown in parenthesis to the right of the x cursor readout - (0.743, 172.04 degrees) in this example.

To add a new pole or zero, click on the "new pole" or "new zero" button. The new root will appear at (0,0) and then you can drag it to the desired location. To remove a root, click on it and then click on the "delete p/z" button. You can undo the delete operation by right clicking on the "delete p/z" button. (This is a multi-level undo, so you can delete all the roots and then restore them all by continuing to right click on the button.) Click on the "save" button to save the current transfer function. (It will be stored in a file called editz.mat in the same folder as editz.m.) Click the "load" button to restore the transfer function stored in the file. When you start editz, the initial transfer function will be loaded from this file. If you want to start editz using the default transfer function mentioned above, simply delete the editz.mat file.

The coefficient strip

The dark blue strip between the two plots shows the coefficients of the transfer function numerator and denominator polynomials (num(z) and den(z)). The highest-order term is on the left. You will see these coefficients change whenever you move roots around using the pole/zero plot, add or remove roots, or hit one of the quantization buttons. You can also edit these coefficients by typing in new values. Just right click on any coefficient, backspace to remove the old value and type in a new value. When you press "enter", the new value will be accepted and the transfer function and pole/zero plots will be updated to reflect the new transfer function. You can also increase or decrease the coefficient value by a small amount (.001 by default) by left clicking on it. If you click closer to the right edge of the number, the coefficient will increase and if you click closer to the left edge it will decrease. It's also interesting to hold down the mouse button while the mouse is over a coefficient. What will happen then is that the coefficient will slowly increase or decrease, while you watch the roots move slowly in the lower plot, and the magnitude and phase response changes in the upper plot. (You can adjust the update rate if you like.) The text color for the coefficients alternates between purple and yellow every 5 values. (i.e. the first 5 are purple, the next 5 are yellow, then back to purple, etc.) This makes it easier to see at a glance how many coefficients there are. There is only enough room to display the first 12 coefficients of each polynomial with the default figure size, but if you add enough roots to go beyond that you can stretch the figure width if you need to see all the coefficients.

Quantization

While (or after) you are dragging a root of the transfer function in the pole/zero plot, the associated polynomial is saved in its full precision (i.e. 64-bit floating point). Although only 5 decimal places are displayed in the coefficient strip, the full precision is available in the base workspace variables as well as in the editz.mat file if the save button is pressed. However, suppose we then click on the "den quant: 100" with the mouse. (It doesn't matter whether you click on the number or the label.) Then all the coefficients in the denominator polynomial will be rounded to two digits after the decimal point. (i.e. a coefficient value of 1.6388 would be rounded to 1.64). But that's only the default quantization. Suppose for example that you wanted to round the denominator coefficients to 4 digits after the decimal point. To do this, right click on "den quant: 100" (either the number or the label), then backspace to remove the "100" and then enter "10^4". Or suppose you want to round to 6 binary digits after the binary point. Then you would enter "2^6". This quantization feature has been included in editz because when a digital filter is realized the filter coefficients will become quantized to a level that depends on the implementation. So it is useful to get a feel for how this quantization affects the root locations and the frequency response.

As mentioned in the previous section, when you click on one of the coefficients in the coefficient strip, that coefficient will increase or decrease by .001. But that is only the default. Suppose you want the coefficients to change by .0001 instead. To do that, left or right click on the "num incr: 1000" or "den incr: 1000" string, backspace and enter "10^4". Or enter something like "2^4" to cause the increment/decrement operation to change the values by 1/16. Instead of incrementing by a fixed amount, you can also choose to increment by a fixed percentage. A negative entry indicates this. For example, if you want the denominator coefficients to increment/decrement by 0.1% every time you click on them, then click on the "den incr: 1000" string and enter "-0.1".

It is sometimes desirable to keep the numerator polynomial symmetric (i.e. it reads the same left to right or right to left). This keeps all the zeros on the unit circle or in pairs at distances r and 1/r. To ensure that the numerator remains symmetric as you increment or decrement a numerator coefficient, put a check in the "Symmetric" check box. (Even with this checked, you can still end up with an unsymmetrical numerator by dragging a zero off the unit circle in the pole/zero plot.) With the coefficient strip example above, with Symmetric checked, modifying the 2nd numerator coefficient (4.3) will also modify the 5th coefficient and visa versa. Try continuously decreasing the value starting from 4.3 by holding the mouse button down near the left edge of either the 2nd or the 5th coefficient. As you do this notice that two of the zeros on the unit circle move towards each other, then collide and split apart in a radial direction towards and away from the origin. (This will be a familiar behavior if you have studied root locus analysis.) Note that the symmetry checkbox does not affect the denominator polynomial since there is no particular advantage to a symmetric denominator.

At the bottom of this group is the "DC gain" readout which shows the value that the step response will eventually settle to. It is computed by adding up all the numerator coefficients and dividing that result by the sum of the denominator coefficients. This readout is always up to date since it is changes even while you are dragging a root on the pole/zero plot.

Base workspace variables

The current numerator and denominator polynomials are copied to the base workspace using the variable names pn and pd respectively. This allows you to type commands at the command prompt to do further analysis or editing of the current filter. For example, you could display the step response by typing this into the command window:

in = ones(1,40);
step = filter(pn,pd,in);
plt(step);

Or to produce a step response using vertical bars, replace the last line with this slightly more complex plt command:

plt(Pvbar(in,0,step),
'title','step response');

Or you could plot the group delay with a command such as this:

plt((0:511)/1024,grpdelay(pn,pd),'title','group delay');

Note that this command will only work if you have the signal processing toolbox since the grpdelay function is defined in that toolbox.

Although you could compute the zeros and poles from the numerator and denominator polynomials for convenience they are also copied to the base workspace using the variable names z and p respectively. (Only the roots in the upper half plane are included.) Here is an example of using the z workspace variable:

Although editz provides a way to quantize the polynomial coefficients it doesn't provide a way to quantize root locations. Suppose after editing the numerator polynomial in the coefficient strip you wanted to move the zeros to the nearest multiple of 5 degrees. Running the following script from the command window would perform that function:
plt xleft EDIT 5;                            % exit data editing mode
[t r] = cart2pol(real(z),imag(z));           % convert zeros to polar (theta,rho)
c = 36/pi; [x y] = pol2cart(round(c*t)/c,r); % round angle to nearest 5 degrees
h = getappdata(gcf,'Lhandles');              % list of lines [Mag Phase Zeros Poles Circle]
set(h(3),'x',x,'y',y);                       % save quantized data to the zeros line
a = get(gcf,'ResizeFcn'); feval(a{1});       % update the frequency response plot
Note that the z workspace variable is used to retrieve the current zero locations but it is not sufficient to merely modify that variable. You actually have to modify the line data so that the plot gets updated. Also without the last line, the frequency response plot would not reflect the new locations of the zeros.

Help

Clicking on the "Help" tag in the menu box to the left of the pole/zero plot will bring up the document you are currently reading. Notice that in the navigation pane on the left, you can navigate to the plt help content. (plt is the plotting tool used by editz). Of particular importance is the section "Using the plt window" which will help you get the most from using this application. If you are interested in creating new commands similar to the ones described above for step response and group delay, you may also want to look at the section "Programming with plt".

After using editz for a while, most people guess that an application like this would involve thousands of lines of code. So you may be surprised to learn that editz.m consists of less than 200 lines of code. The main reason the code is so small is that the plt toolbox includes many features needed to create complex plotting-based graphical interfaces.

If your questions are not answered by reading the help documentation, don't hesitate to contact the author. My email address appears at the top of this document and can also be found at the top of any of the included .m files.

Copyright © 2024
Paul Mennen


erip.m version 12Jul24
(A tool for the design of optimal fir and iir digital filters)

Author: Paul Mennen
Email:  paul@mennen.org

Overview

erip allows you to design optimal (equal ripple) linear phase FIR filters using the famous Parks-McClellan design algorithm which is based on the Remez exchange algorithm. Parks-McClellan is usually implemented using the firpm.m routine which is part of the Signal Processing Toolbox. However erip does not use firpm to avoid requiring any toolboxes. Instead erip uses the parks.m routine which is included in the pltSig toolbox. parks.m uses a different implementation of the Parks-McClellan algorithm that is somewhat simplified but produces similar results. It also runs faster than firpm; in fact about twice as fast for filter orders in the 35 to 70 range, with an even greater speed advantage for longer filters.

erip also allows you to design optimal IIR filters as well as non-linear phase FIR filters, but only if your copy of erip is registered.

If you attempt to design an IIR filter (or a non-linear phase FIR filter), then if you haven't yet registered erip you will see this dialog box requesting that you register. Although registering is free, I still apologize for this extra step. I'm asking you to register so I can judge the level of interest in erip to see if it is worthwhile to continue to add features and improvements to this application.

After entering the registration number you have received, the erip windows will close and you will see this message box. Assuming you have entered the number correctly, your copy of erip has been registered. After restarting erip you will be able to use it to design IIR filters and you will never see either of these messages again.

check mark indicates IIR capabilities | Windows OS | non-Windows OS (i.e. Unix based)
erip is NOT registered |  |
erip is registered The DSP Toolbox is installed |  |
erip is registered The DSP Toolbox is not installed |  |
The checkmarks in this table show you when you can use erip to design IIR filters (as well as non-linear phase FIR filters).

When the DSP Toolbox is installed the iirlpnorm.m design algorithm is used to design IIR filters and firlpnorm.m is used to design non-linear phase FIR filters. Both of those routines are included with The DSP System Toolbox. However if you do not have that toolbox installed, erip will use an alternative design algorithm which has been compiled into a Windows application called iir.exe. Since executables are not allowed in Matlab toolboxes, iir.exe will be automatically created the first time it is needed from the included plt\sig\iir\iir.b64 base64 encoded file.

The checkbox in the lower right corner of the table is missing because iir.exe can't run in a Linux environment.

The Mathworks also has a tool (called filterDesigner) that serves the same purpose as erip. The most important differences between these tools is described in the table below.

filterDesigner

erip
This is part of the DSP System Toolbox, so you must have that toolbox installed to run filterDesigner Also, if you want to design an FIR filter, the signal processing toolbox is also required (because it uses the firpm.m (Parks-McClellan) function which is part of that toolbox. | This is part of the pltSig toolbox and you don't need any other toolboxes to run it. (Note the limitation mentioned above when using Unix-based operating systems.)
filterDesigner is primarily for the design of filters that fit into one of the 4 standard types (lowpass, highpass, bandpass, and bandstop). It is also capable of designing filters that don't fit into one of those categories (i.e. arbitrary magnitude response), but to do so you must specify the desired response by typing in the values of 4 vectors (Frequency, Edges, Magnitude, & Weight) but that process is anything but intuitive. Once you enter those vectors, filterDesigner doesn't produce any graphical representation of the shape that you specified making it difficult to know if you entered these vectors correctly. | erip allows you to enter a magnitude response graphically which is usually much easier. You may also enter these values numerically, but the response is always shown graphically as well so there will never be confusion about the design specification.
For the four standard filter types as well as when using an arbitrary magnitude response, you must specify the response in linear units. However, the frequency response of the filter that results from your specifications can't be displayed in linear units (it's always in dB). This makes it difficult to see how well the design spec was met, and for arbitrary magnitude specifications, it offers little feedback about whether the 4 vectors were typed in properly. | erip allows you to display the magnitude response of the design result in linear or logarithmic units (or both simultaneously). As an example of how important this is, look at the design result of the differentiator example with the linear result (trace 2) turned off and observe how difficult it is to see how faithfully the design result follows the specification as the filter order is varied.
A pole/zero editor is included which can be a powerful tool to help you gain an understanding of how the pole/zero locations affect the frequency response. However, the pole/zero plot occupies a small fraction of the figure window making the unit circle so small it is somewhat frustrating to use. Also, you can't precisely drag a zero onto the unit circle since mouse control doesn't offer enough precision for such a task. (Being exactly on the unit circle is often important because it allows the polynomial to be symmetric.) | erip displays the pole/zero editor in a separate resizable figure window, providing a much more useable editor. Additionally, erip displays only the top half of the unit circle, allowing this half circle to fill the figure window. This again makes the radius larger, making the editor more comfortable to use. And finally, erip provides an adjustable "snap-to" region around the unit circle making it easy to move a zero to the unit circle. erip provides another snap-to region around the x-axis allowing you to drag a root exactly onto the x-axis, turning a complex conjugate pair of roots into just a single root (another impossible task with filterDesigner).
A window is provided allowing you to view the filter coefficients, but it is so small that even with simple filters you need to use the scroll bar to see all the coefficients. Also, it is awkward that you can't view the coefficient window at the same time as the magnitude response or the pole/zero locations. So for example, after changing a filter design parameter, you can see how this affects the coefficients, the magnitude response, or the pole/zero positions, but you can't see all three (or even two) of these effects at the same time. | erip has separate figure windows for showing the filter coefficients, the magnitude/phase response, and the pole/zero locations. So after a design change, you can see the effects in all three windows simultaneously. Also, the coefficient window shows the coefficients more compactly, and the window is large enough to show all the coefficients without scrolling for even quite complex filters. Another advantage of erip's coefficient window is that it allows you to constrain the coefficients to fixed point precision with any desired number of bits.

You can learn a lot about gui programing using the plt toolbox by studying the erip.m source code, especially for learning how to integrate multiple figure windows and dealing with complex user interfaces and mouse interactions. However at 516 lines of code (not counting comment lines) erip is the longest and most complex of all the toolbox applications so it's certainly not one to start with if you are relatively new to gui programming. Instead you should begin with the programs and explanations in the GUI building with plt section and then move on to some of the other simpler applications.

Graphical interface

To start the erip graphical interface, simply type erip at the Matlab command prompt. This opens three windows similar to these:

Click on this thumbnail to see the full-resolution screen capture. The largest figure (upper left) could be thought of as the main erip figure since you will use it to define the characteristics of the filter design, and it also displays the frequency response of the resulting design. The figure on the upper right displays the locations of the poles and zeros of the resulting design and also allows you to move these root locations to improve the design or to gain an understanding of how the frequency response depends on the root locations. The wide (but not very tall) figure at the bottom is used to display the numerator and denominator polynomials of the resulting design and also allows you to modify these coefficients to see how this affects the frequency response and root locations.

The size of these figure windows depends on your computer's screen resolution. The coefficient window (bottom) will 1800 pixels wide if you have a display with at least that many horizontal pixels. For displays with less than 1800 horizontal pixels, the coefficient window will fill the entire width of the screen, and the two figures above it will be sized to match. The screen capture to the left was done on a system with 1400 pixels of available horizontal space. If your screen has fewer than around 1200 pixels horizontally, the figure sizes may be too small to be usable.

Coefficient figure

This figure displays the coefficients of the numerator and denominator polynomials. The top line of numbers is the numerator polynomial and the bottom line is the denominator. The first number starting at the left is the constant term (z0) followed by z-1, z-2, etc. In this example, both polynomials are displayed in a single line, but with more complex filters, they may extend to multiple lines - up to 2 lines for the denominator polynomial and up to 3 lines for the numerator. (And up to 5 lines may be displayed for the numerator for FIR filters.) If you change the width of the coefficient figure, each polynomial may occupy fewer or more lines of text. You may need to expand the width to the full width of the screen so that all the coefficients of a high-order filter are displayed cleanly. The color used to display each coefficient alternates between white and orange every 5 numbers to make it easier to identify specific coefficients. For example, we can quickly tell that the -152.44 near the end of the denominator polynomial is the 10th coefficient (z^-9) because it is the last number in yellow (the end of the 2nd group of five).

In this example, the top line of coefficients begins with these red characters: b(z)06 = 2-8 * where b(z) is the traditional letter used to represent the numerator polynomial of a transfer function. The 06 superscript indicates that this is a 6th-order polynomial (i.e. has 7 coefficients), and the 2-8 * indicates that you have to multiply all the numbers to the right by 2-8 to get the actual polynomial coefficients. This multiplier is chosen in an attempt to allow the filter coefficients to be displayed without using exponents which in turn allows the coefficients to be shown with higher precision in the small space allocated on the screen for these coefficients. Note that the exponent will not always be -8 as in this example but will be chosen to produce the most readable coefficient representations. Clicking on the b(z)06 = 2-8 * will change it to b(z)06 = 10-3 *. Again the -3 exponent will vary since it is chosen based on the particular coefficients being displayed. The numbers shown for the numerator polynomial are now all different than before but once you apply the multiplier shown you will find that the resulting coefficients haven't changed. Finally, clicking on these characters a third time will change it to b(z)06 = indicating that there is no multiplier at all (i.e. the numbers shown are the actual filter coefficients). Usually, the decimal multiplier will be the most convenient, although if you are planning a hardware implementation, then the binary multiplier will often be the best.

In this example, the second line of coefficients begins with these red characters: a(z)11 = 2-8 * where a(z) is the traditional letter used to represent the denominator polynomial of a transfer function. As before the 11 superscript indicates that this is an 11th-order polynomial (i.e. 12 coefficients). As with the numerator polynomial, clicking on this text toggles between the 3 modes (binary multiplied, decimal multiplier, and no multiplier. Note that the constant term (z0) of the denominator polynomial is always one since that is necessary for the IIR filter implementation.

You may quantize the numerator polynomial by typing a number into the edit box labeled "Quantize b". For example, if you want to quantize the coefficients to the nearest hundredth, type in a value of 0.01 into the edit box. To round the coefficients to the nearest integer, type a value of 1 into the edit box. In this example you would then see the numbers 2 4 7 7 6 3 1, however, note that the polynomial coefficients themselves are not integers because of the 2-8 multiplier. If the number you want to quantize by is already in the edit box, you don't have to enter it again because clicking on the "Quantize b" label will have the same effect. You can also change the value of any individual coefficient. So in this example, after quantizing by 1, if you click on the second coefficient (the "4") it will either increment or decrement by 1. (Clicking slightly to the right of the 4 increments and clicking slightly to the left of the 4 decrements.) Note that the amount that the coefficient increments or decrements is always the same as the value in the quantize edit box. You could also change this value independent of the quantize value. Simply right click on the "4", backspace to remove the 4 and then type in "3.9" (for example). As soon as you change a coefficient, either by typing in a new value, incrementing, decrementing, or quantizing - the pole/zero plot will update to show the new root locations and the main figure will update to show the new magnitude and phase response. You won't often design filters by fiddling with individual coefficients, but still, it can be educational to observe the effects of such changes. Any time you want to return to the coeffect values you had before any quantizing or coefficient editing, simply hit the "Run" button in the main figure. Of course, the same interactions are allowed with the denominator polynomial and the "Quantize a" edit box. The effects of entering a quantize value are temporary. For example, if you move one of the roots on the pole/zero plot, the filter coefficients will be updated as a result and again be stored with full 64-bit precision. So if you want the coefficients to continue to be quantized you would have to click on the appropriate "Quantize a/b" label after such changes.

If you check the "symmetric" checkbox, this will affect what happens when editing the coefficients of the numerator polynomial. For example, if you change the value of the first coefficient, the last coefficient will also change to the same value. Or if you change the value of the last coefficient, the value of the first coefficient will also change to the same value. A similar pairing will happen with the second coefficient and the next to last coefficient, etc. While the symmetric checkbox is checked, after any value is modified, the first half of the numerator polynomial is flipped and then copied to the second half of the polynomial ensuring that the whole polynomial is symmetric, not just the coefficient being edited. A point of confusion is that you might expect that checking the symmetric checkbox insures the design algorithm will produce a symmetric numerator polynomial. However that is not the case. As described below, the design algorithm insures a symmetric numerator polynomial only when the Pz parameter (number of poles) is set to -1. Also even with a symmetric design, if you drag a zero that is on the unit circle to any position that is not on the unit circle or the x axis, the numerator polynomial will become unsymmetrical. It may help to understand an interesting mathematical property of the roots of a symmetric polynomial, which is that if there is a complex root that is not on the unit circle, then there will also be another root at the same angle with its reciprocal magnitude. For example, if there is a root at 30 degrees with a magnitude of 0.8 there will also be a root at 30 degrees with a magnitude of 1.25. Thus you could say such roots occur in pairs - or quadruples if you are counting the complex conjugates of these roots (below the x axis). It is easy to verify this mathematical property by examining the pole/zero plot while editing the coefficients or conversely by viewing the coefficients while moving the zero locations on the pole/zero plot.

The final control to discuss in filter coefficient window is the DC gain edit box. After the run button in the main window is pressed, after a root is moved in the pole/zero plot, and after a coefficient is modified in the coefficient window, the DC gain edit box is updated to show the transfer function evaluated at z = 1 (i.e. the sum of the numerator coefficients divided by the sum of the denominator coefficients. This value will usually be close to 1.0 for a low pass filter but will be very small for a high pass filter. You can also edit the value in the DC gain edit box. Say for example the DC gain edit box contains the value 1.002. Suppose then we triple this value by typing the number 3.006 into the edit box. After doing so, you will see that all the coefficients of the numerator polynomial have tripled.

Pole/Zero plot

The zeros (o's) on this plot always show the roots of the b(z) polynomial depicted in the coefficient window and the poles (x's) show the roots of the a(z) polynomial. Each root shown on the x-axis, represents just a single root, whereas each complex root (i.e. not on the x-axis) represents a pair of roots. The other root of the pair is the complex conjugate. These complex roots must appear in pairs since the filter coefficients are limited to the real numbers. The complex conjugates are not shown on the plot to make the plot bigger, less cluttered, and easier to read. For example, suppose we click on the pole shown in this figure that is the one that is closest to the unit circle. The x and y edit boxes below the plot show that the pole is located at .262145 + .914289i (in rectangular coordinates) and right next to the x edit box you can see that in polar coordinates this pole has a magnitude of .951 at an angle of 74 degrees. The complex conjugate root is at .262145 - .914289i but this is not shown on the plot.

In the example shown here there are 11 poles. As you can see, 3 of these poles are real. (The fat green horizontal line is the x axis). The remaining 8 poles are complex and only four of those (above the real axis) are shown on the pole/zero plot. The other 4 poles are the complex conjugates which are below the x axis and are not shown on the pole/zero plot. This is done to save space on the plot, but of course you must assume that those other four poles are there. In this example there are six zeros (all complex) and only the three that are above the x axis are depicted.

But actually for a filter to be causal, there must be the same number of poles and zeros, so with this example, five extra zeros are needed at the origin. These extra zeros are not shown on the pole-zero plot since this would be difficult to depict since they are all on top of each other. But you should assume they are part of the z-plane representation. Whether these extra zeros are there or not will not change the magnitude response of the filter, but they do affect the phase response. The phase response shown in the main figure is calculated assuming these extra zeros are there. Suppose instead of 11 poles and 6 zeros, it was the other way around ... i.e. 11 zeroes and 6 poles. Then there would be 5 extra poles all at the origin to insure that the filter was causal (again not depicted on the pole-zero chart).

Note that when you clicked on the pole, a diamond shape cursor appeared around the pole. That cursor shape alerts you to the fact that you can now drag this pole around using the mouse. While you are dragging the pole, the complex conjugate pole is also being moved and the a(z) polynomial display in the coefficient window is being updated and the magnitude and phase frequency response is being updated in the main window. If you move the pole onto the x-axis, the order of the a(z) polynomial decreases by one since the pole no longer represents a complex-conjugate pair. If you later move the pole off the x-axis, the polynomial degree increases by one. Of course, moving a zero works the same way except that the b(z) polynomial is updated as you drag the zero.

When you click on the "new zero" or "new pole" button, a new root will be added at z = 0 (the origin of the unit circle) and then you can drag it to the desired location. To delete a root, simply click on the root and then left click on the "delete p/z" button. Then if you right click on the "delete p/z" button, it will undo that operation (i.e. restore the deleted root at its previous location). Continuing to right click on that button will restore more roots if you have deleted several. You probably won't often design filters by positioning the roots on the pole/zero plot in this manner, but you may still find it useful to gain an understanding of how the root positions affect the frequency response.

As mentioned before, dragging a root onto the x-axis has a special significance. However, a mouse operation like this doesn't have the precision to ensure that the imaginary component is exactly zero. To solve this problem there is a tolerance band around the x axis (shown with green shading) where the root will snap to the real axis whenever you drag the root inside the tolerance band. The default width of the tolerance band is usually acceptable, but you can adjust this width using the slider in the lower-left corner of the figure. (Moving the slider to zero disables the snap-to feature.) Also, we have mentioned before the significance of moving a zero onto the unit circle. (It results in a symmetric numerator polynomial, as well as linear phase if it is an FIR filter.) Again a snap-to region is necessary to give us the ability to move the zero exactly on the unit circle, and the green region around the unit circle shows this snap-to region. The slider mentioned before also controls the width of this snap-to region.

Main figure

The main figure allows you to specify the characteristics of the filter you want to design as well as to view the frequency response of the result. The white dashed line in the lower plot defines the desired filter magnitude response (linear) and you can modify that line graphically or numerically in this figure. The units for this specification are on the left edge of the plot. The thick green & grey line at the top edge of the lower plot shows the location of the transition bands if any (grey = don't care, green = do care). The upper plot (thin green trace) depicts the weighting curve and is also part of the filter specification. It defines how much emphasis to put on minimizing the error in each region. As with the white dashed line, you can modify the weighting curve graphically or numerically. (Both methods are described later.)

After clicking the Run button, the yellow trace of the lower plot will show the magnitude response (linear) of the resulting design, and ideally will closely follow the white dashed line in the do-care regions. The blue trace shows the magnitude response in logarithmic units (dB) which are shown on the right edge of the graph.

The gray box near the lower right corner of the figure is where you set the parameters of the filter design. To change any of these numbers, right click and then type in a new value. Pz, the last parameter on the right specifies the number of poles. In this example, 11 poles have been selected which means the denominator polynomial will have 12 coefficients. Note that the pole/zero plot only shows 7 poles. That's because 4 of the poles are complex, and the reflections of these poles below the x-axis (the complex conjugates) are not shown. In addition to changing Pz by right clicking and typing in a new value, you can also change its value using the increment/decrement method. Click near the right side of the current value (11) or in the space just to the right of the number and its value will change to 12 (i.e. increment). To change the value to 10 (i.e. decrement), click on the left side of the number. If you enter Pz = 0, then the denominator polynomial is simply 1 resulting in an FIR filter (finite impulse response). No special symmetries are forced on the numerator polynomial which implies that the filter will in general have a non-linear phase. If you want the resulting filter to have linear phase, enter Pz = -1. Of course, there are still no poles, but using -1 is just a way of specifying linear phase without needing an additional parameter. Normally in this case, even symmetry will be used, meaning the first and last numerator polynomial coefficient will be the same, and also the second and next to last coefficient will be identical, etc. (Specifying odd symmetry will be discussed later.) Note that on Unix-based operating systems, if you don't have the DSP System Toolbox installed, Pz will be automatically set to -1 because, without that toolbox, we must depend on the included parks.m filter design tool which designs linear phase FIR filters.

Often we do want to design a linear phase FIR filter, but sometimes allowing non-linear phase (i.e allowing a non-symmetric numerator polynomial) can be an advantage. As an example of this, select the Shape22 filter design (the last selection in the Saved Filters popup discussed below). Note that Pz is set to 0 indicating that this is an FIR filter but that non-symmetric polynomials are allowed. Now click on the left side of the "0" which will change it to a -1, now specifying that the polynomial must be symmetric. Notice how the filter shape (yellow trace) no longer follows the desired filter shape as closely. We could create a symmetric FIR filter that does as well as the original non-symmetric one, but we would have to approximately double the filter order.

Nz specifies the number of zeros. In this example, 6 zeros were selected, so the numerator polynomial has 7 coefficients. You can increment or decrement it in the same way described above. A new filter design will be calculated each time you modify Nz or Pz (as if you had pressed the Run button). This makes it easier to develop a feel for how the filter response is affected by the filter order.

The next parameter, Radius, applies only to IIR filters and specifies how close to the unit circle the poles can be placed. With the default value of 1, the poles are unconstrained and can be placed anywhere inside the unit circle. If for example, you change the Radius value to 0.97, then all the poles will be positioned inside a circle of radius 0.97 centered at the origin. This can be useful to ensure stability, especially when dealing with finite word length effects.

The remaining two parameters (Fs and Grid) are not really filter parameters, but they do affect the way the results are displayed. In this example, Grid is set to 400 (the default value) which means that the frequency response is calculated at 400 evenly spaced points between zero and the Nyquist frequency. (This is almost always sufficient). Fs specifies the sampling frequency. The x-axis limits of the frequency response plot will always be set to 0 and Fs/2. If you set Fs to 1 (as in this example), then the x-axis shows angular frequency.

To analyze the passband performance, you may have to zoom in many times before you can see the passband ripple (especially for high order filters). In that situation it can be difficult to return to a scaling allowing a view of the entire frequency response. To make that easy, you can click on the yellow defLim tag at the bottom of the menu box. This will reset the axis limits to the default values.

Saved Filters popup
As you are working on your filter designs, you may save any current filter specification by clicking on the "Save" button which adds the current filter spec to a file called erip.txt in the plt\ini\ folder. This file is initialized after installation to contain the specification of 10 different filters, so initially after clicking on the saved filter name Default just below the x-axis label (Frequency) a popup similar to this one will appear showing the names of all the saved filters stored in the file. These 10 filter specs were chosen to show you a variety of filter types. It's not expected that any of these 10 filters will be useful as is for one of your designs since the specified shapes are somewhat arbitrary, so at best they can be starting points for your own designs. But when starting a new design, you will find it easier if you start by selecting one of these 10 filter shapes which looks most similar to the filter you wish to create. If you delete the erip.txt file (perhaps accidentally or intentionally) then this popup will contain only the single item Default whose filter shape and parameters are defined in the erip.m source code.

When you select one of the filter specifications from this popup, the current filter shape and parameter set will be overwritten with the values associated with the name of the item in the popup list. For this reason, if there was anything you wanted to save from the current filter spec. you should always hit the save button before opening this popup. If you open this popup inadvertently, you can close it again without taking any action by clicking on the black "X" in the lower right corner of the popup menu.

In general, filter specification isn't affected by the name you choose to represent that specification, but there are two exceptions to that. If the name chosen begins with the three letters Dif or Hil (case sensitive) then the FIR design algorithm (parks.m) will be used to design the filter, and what's more, that design algorithm will be directed to produce an anti-symmetric filter. This means that the first and the last filter coefficients are negatives of each other, and the same for the second and next to last coefficient ... and so on. The same design algorithm is used for differentiators and Hilbert transformers. The only difference is that for the Hilbert transformer, the weighting function specified by the top graph is used directly whereas for the differentiator that weighting function is first divided by the angular frequency before being sent to the filter design algorithm. This causes the lower frequencies to be weighted much more strongly than the higher frequencies, which produces better-performing differentiators.

The data saved for each filter in this drop down list include:
  • The filter shape (i.e. the white dashed trace of the main figure's lower plot)
  • The weighting curve (i.e. the green trace of the main figure's upper plot)
  • The filter name (displayed below the x-axis label)
  • The values in the shaded parameter box below the x-axis (includes the filter name, Fs, Grid, Radius, Nz, Pz)
  • The current settings of the Quantize b and Quantize a edit boxes in the coefficient figure
  • The currently displayed coefficients in the coefficient figure. Usually these values are the same as what you will see after clicking on the Run button. However if the coefficients have been modified after the run (using the quantization controls or by editing the coefficients manually) then the modified coefficients are saved. You can see an example of this with the default erip.txt file when selecting the second filter in the list (LowPass). The coefficients were modified after the run button was pressed first by quantizing the result and then by directly editing the quantized coefficient values to reduce the errors caused by the quantization. You can examine the differences between the design algorithm results and the edited results by alternately clicking on the Run and Reload buttons.

Button Group
This group of buttons appears in the upper left corner of the figure window.
  • The Run button causes the design algorithm to execute (parks.m or iirlpnorm.m as appropriate) which will then update the magnitude and phase response as well as the coefficient display and the pole/zero plot (if those to figures are enabled). You don't have to press the Run button after changing the filter order (Nz or Pz) since changes to those two parameters will also immediately initiate the execution of the design algorithm.
  • When you are satisfied that the current design is worth saving so that you can recall it later, press the Save button. You will be prompted for a name to identify this filter specification. This is the name that will be added to the popup list described above. It's best to enter a unique name that you will later recognize, but if you leave the name field blank, erip will make up a name for you. For example, if the current filter name is "HighPass", then if you don't specify a new name, erip will use "HighPass-A". If the current name is already "HighPass-A", then it will use "HighPass-B", and then "HighPass-C", etc. Although it may lead to later confusion, you are allowed to use the same name for two different filter specifications in which case this name will occur twice in the saved filters popup. Sometimes, instead of creating a new name, you will want to overwrite the current filter spec. To do this, instead of left clicking on the Save button, use a right click. Doing this accidentally has the potential to overwrite a filter spec that you wanted to keep. Also when you select a new filter from the Saved Filters popup described above, and equivalent of a right click on the Save button is executed which will overwrite filter specifications in the file with any changes you have made. Usually this is what you intend, but sometimes not. To protect against these eventualities, whenever erip rewrites the erip.txt file (which contains all the filter specs and their names), it also back up the old erip.txt file using the name eripBAK.txt. This is only a single-level backup, so it will only help you if you recognize the problem right away. For this reason, you should create your own backups of the erip.txt file with different file names, perhaps numbered or including the date so you have a history of the work you have been doing.
  • The Saved Filter popup may get a bit unwieldy with more than a few dozen entries, so you probably will want to delete the filter specifications that you are no longer working on. This can be done by selecting the unneeded filter, and then clicking on the Remove button.
  • Sometimes when editing a filter specification you may find you want to revert to the previously saved filter shape. One way to do this is to simply exit erip and restart it. A faster way however is to click on the Reload button.
  • The main window is the only window you need to have open. The other two windows (Pole/Zero plot and the coefficient window) are optional. To remove the Pole/Zero plot window, click on the pzPlot button and to remove the coefficient window, click on the CoefFig button. Click on either button again to re-enable the respective figure window.
  • Left clicking on the fvtool button opens Matlab's filter visualization tool using the coefficients currently shown in the coefficient window. You may find fvtool useful because it graphs some information that is not shown in the previously shown erip figures, primarily:
    1. impulse response
    2. step response
    3. unwrapped phase
    4. group delay
    5. phase delay
    If you right click on the fvtool button the two windows shown at the right will appear displaying these same 5 items. (These are just thumbnails. Click on them to see the full image.) You may prefer the right click action, because these figures use the same cursoring, zooming, and panning methods that you have become familiar with from the other erip figures.) fvtool is part of the signal processing toolbox, so if that toolbox is not installed, left clicking on the fvtool button behave the same as the right click (i.e. the two figures shown at the right will appear).

Editing the desired filter shape
Starting from the default filter shape, suppose we want to make the notch in the middle of the passband deeper as well as add a flat region in that notch. The first step is to enable only the specification trace (the thick dashed white line). You could do this by clicking on "mag" in the TraceID box (turning the font to italic indicating the trace has been disabled) followed by clicking on "mag dB" to also disable that trace. A slightly faster way is to right click on "spec" in the TraceID box. That tells the plotting routine to enable the spec trace while disabling all others. When erip sees that the spec trace is the only one enabled in the lower plot, it assumes you want to edit the spec curve so it enters a special editing mode. First, let's click on the breakpoint in the middle of the notch. With the normal cursor mode, a "+" shaped cursor would appear at this breakpoint, but because we are in edit mode, the shape of the cursor is a diamond as you can see in this screenshot. The diamond indicates that we can now drag that point to anywhere we want in the coordinate system of the lower plot.

Suppose I drag this point to (0.05, 0.7). When I release the mouse button after dragging the point, the plot will now look like this (to the right). Notice the cursor has changed back to the regular (+ sign) cursor. However if you decide you didn't get it right and you want to move this point again, simply click on the plus sign and it will change back to the diamond which then can be dragged to a new point. However, this time let's right click on the plus sign cursor instead. As before, we will see the diamond cursor, but this time it will do something different. It will leave the point (.05, .7) where it is, and add a new point where ever it is we drag the diamond cursor to.

In this example, we drag the diamond cursor to (.15,.7), thus creating the desired flat region inside the passband notch. Notice the frequency shape is now defined by 6 points at x = [0 .05 .15 .2 .25 .5] whereas before the right click edit operation it was defined with only 5 points.

The thick green line at the top edge of the plot shows the "care" and "don't care" regions. Note that there are 6 square boxes on the green line at the 6 x coordinates defining the frequency shape (two endpoints and four interior breakpoints). After any edit operation that changes the number of defining x coordinates (such as our last edit operation), the entire region from the smallest x coordinate to the largest will be set to green, indicating all regions are "care" regions. If we now press the "Run" button, the design algorithm will do its best to match every dashed line segment. But suppose we don't need the filter to match the steep decline between 0.2 and 0.25 (i.e. a transition region).

So we simply click on the green bar anywhere between those two x coordinates, and that portion of the green bar will turn to gray as you see here, indicating that this is a "don't care" region. Note that it doesn't make sense to have two successive don't care regions. (In fact, erip won't let you do that.) Instead, simply remove the unneeded breakpoint in the middle of your intended don't care region. (To remove a point, drag it outside the plot area.) Likewise, you can't select the first or last region as a don't care region. Instead, just start the filter spec at the beginning of the first care region and end the filter spec at the end of the last care region. (Select the "Shape22" or "HilbertTransformer" filter shapes from the popup to see examples showing that the filter spec does not have to begin at 0 or end at Fs/2.)

Editing the desired filter weight
Editing the filter weighting function is similar to editing the filter shape. As before, first you have to enable only the spec trace on the lower plot. (This enables the editing mode for both the filter shape and the weighting function). Click on a weighting point you want to move. You will see the usual plus sign cursor change to a triangle shape. The triangle shape indicates that you will be able to drag the point to a new location but only vertically. Even if you try, erip will not allow you to move the point horizontally. (Remember that the editing cursor for the filter shape was a diamond, which indicates that you can move the point both horizontally and vertically). Also, erip won't allow you to remove the point from the graph. If you try, erip will move the point back onto the plot. This is because the weighting function must have the same x-coordinates as the filter shape.

At this point we have done enough work that it would be a good idea to hit the save button, assuming this is something we want to continue working with or modifying further. When it asked for a name, we could simply click "OK" without entering a name. Since we have been modifying the shape called "Default", erip will name the new shape "Default-A". If we continue to make incremental changes, we can follow up by right clicking on the save button, which will update the "Default-A" specification without adding yet another entry to the popup.

We've covered everything you need to know about how to graphically edit the filter shape and weighting function, but if you want to learn more about how to edit the plot data graphically, go to the 2d data editing section.

Editing the filter shape & weighting function numerically
Suppose while looking at the filter shape above (with the flat spot in the notch that we added) you click on the "Data" tag in the menu box (which is near the lower left corner of the figure). This figure window will appear showing the x and y coordinates of both the spec and Weight traces. Unlike with the graphical editing technique, you don't have to disable the mag and mag dB traces first. The data editing window will look the same whether those traces are enabled or disabled. (This window is also sometimes referred to as the "Cursor data window".)

Now suppose we want to make the transition band wider, ending at 0.3 instead of 0.25. Simply right click on the "0.25" in the X column, then backspace to get rid of the "25", type in a "3" and hit enter. (Another way is to hit the down arrow key which removes the "0.25" entirely and then you can type in ".3".) You could even type in a Matlab expression such as 3/10 or even round(pi)/10 and the result would be the same. As soon as you hit the enter key you will see the graphs of the spec and weight traces updated (as well as the don't care regions) to show that the x-coordinate has changed. Yet another way you could do this is to do a regular left click on the "0.25", but slightly to the right of center (or just to the right of the number). You will see that the 0.25 increased by a small amount (2 percent). But you can hold down the left click while this value slowly increases. You can watch the graph changing until you see what you like. Or of course, you can slowly decrease the value by clicking slightly to the left of center (or just to the left of the number).

Now suppose you want to make the flat spot in the middle of the notch deeper (.6 instead of .7). Right click on the first 0.7 under the "spec" column heading and change it to 0.6 and then do the same for the second 0.7 right below it. Again as you make these changes you will see the graph being updated every time you hit the Enter key. (Of course, you can use the move slowly left click method as well.)

Now suppose you want to increase the weight in the stop band from 10 to 15. Go to the two 10s in the weight column and change each of them to 15 using the methods already described. Note that the y-axis limits are automatically changed from [0 10] to [0 15] so you can see the whole trace. This is an advantage of the numerical editing method because, with the graphical method, you would have had to increase the upper y-axis limit manually (by right clicking on the y-axis and dragging down) before you were able to drag the "10" values up to "15".

To see more information about using the cursor data window, go to the Menu box section, and scroll down a page or two until you see the "data" heading. There it describes the use of the data cursor window in detail. There you will learn how to add and remove points which you will often need to do while using erip. You will also learn about the multi-level undo/redo feature (another advantage over graphical editing which only has a single-level undo, and no redo feature.) There you will also learn how to change the increment amount (to a different percentage or a fixed value) for the slower left click editing method.

Both the graphical and numerical editing methods described here have their relative strengths and weaknesses, but if you take the time to learn how to use both of them you will find that these editing methods are useful in many of the other applications in this toolbox. Also both these editing techniques with automatically be available to any application you may create using the plt plotting function.

The erip.txt file format
If you are spending a lot of time designing one or more filters, the erip.txt file is important since this is where your work will be saved when you click on the Save button or when you select a new filter type from the saved filter popup. Just like when you are editing a program or document, you should make regular backups of the erip.txt file using different file names so you can look at the history of your work, and to make sure you don't accidentally overwrite some work that you meant to keep. You don't normally need to know the file format of the erip.txt file but for completeness, this format is described below.

The first two lines of the erip.txt file will look something like this:
Fnum: 3
 Pos: 13 143 949 620    977 143 825 620    13 13 1790 92
The first line (filter number) indicates which filter is currently being viewed. In this case, since Fnum is 3 when you start erip it will start up showing the 3rd filter specification defined in the erip.txt file.

The next line shows the positions of the 3 figure windows that make up the erip application (The filter spec window, the pole/zero plot, and the filter coefficient window in that order). The one situation where you may want to edit the erip.txt file yourself (i.e. with a text editor) is when you want to reset these three window locations to the defaults. This is especially useful when you want to transfer the erip.txt file from one computer to another computer that has a different screen resolution, since in that situation these coordinates may be inappropriate. To do this, remove all 12 of the numbers on the Pos: line and replace them with a single zero so that the 2nd line of the file looks like this:

Pos: 0

The next 11 lines of the erip.txt file will contain the first filter specification and will look something like this:

******HighPass********************************
  Fs: 1000
   f: 0 200 350 500
 des: 0 0 1 1
  wt: 10 10 1 1
tran: 2
grid: 400
 rad: 1
   b: .0282127542 -.068055835 .0904799506 -.067314998 .0274030873
   a: 1.000000000 1.853755775 1.885164302 0.969992730 0.224312164
 Qba: 0.1 0.1
Line 1 | Name | The name of the filter design (ignoring the asterisks) as it appears in the saved filters popup.
Line 2 | Fs | The sample rate.
Line 3 | f | A list of frequency breakpoints for defining the desired filter shape and weighting function. This must be an increasing sequence of numbers between 0 and Fs/2.
Line 4 | des | The desired filter shape. It must be the same size as f. plt(f,des) plots the desired filter shape.
Line 5 | wt | The filter shape weighting function. (Higher weights indicate a region of more importance). plt(f,wt) plots the filter shape weighting function.
Line 6 | tran | Identifies which bands are transition regions. In this example, the value "2" indicates that the band between frequencies f(2) and f(3) is a transition region. This can contain multiple elements when the filter specification includes multiple transition bands.
Line 7 | grid | The value of the grid pseudo edit box (grid density)
Line 8 | rad | The value of the radius edit box (which limits how close poles can approach the unit circle)
Line 9 | b | The numerator polynomial (May be different than the optimal solution after clicking "run", if the values have been quantized or edited numerically or if the zeros have been moved on the pole/zero chart.)
Line 10 | a | The denominator polynomial (May be different than the optimal solution after clicking "run", if the values have been quantized or edited numerically or if the poles have been moved on the pole/zero chart.)
Line 11 | Qba | The two values from the Qb and Qa edit boxes (numerator and denominator quantization values respectively)

The next 11 lines will contain the second filter specification and continues in that pattern to include all the filters you have defined via the save button. Note that the number of lines in erip.txt will always be two more than a multiple of 11.

You can edit values in the erip.txt file with a text editor if you like, and these changes will take effect when you restart erip or when you click on the Reload button. However, usually it is easier and safer to make changes using the graphical or numerical methods described above as well as by entering new values into the various edit boxes.

Copyright © 2024
Paul Mennen


fseries.m version 01Jul23
(Fourier series)

Author: Paul Mennen
Email:  paul@mennen.org

Overview

In 1807 the French mathematician Joseph Fourier showed that any periodic function can be written as the sum of infinitely many harmonically related sine and cosine waves. This implies that the periodic function can be approximated by a finite sum and that this approximation improves as more terms are added. Later the German mathematician Peter Dirichlet generalized and formalized these results and proved that the infinite series converges. This is a fundamental result that has been extended in many ways that are essential to most areas of signal processing. There are several ways of writing the Fourier series, but one simple way is:

where f(x) is any (piecewise continuous) function that is periodic between -π and π.

The coefficients an and bn may be computed using these integrals:

Your calculus skills must be sharp to use these formulas to compute the an and bn coefficients, but at least for common periodic functions such as the ones used with fseries.m many people have already done this and we can easily look up the results. When you do that, notice that for most simple functions either the an or bn will all be zero which means only one of the sums in the expression for f(x) above is needed.

This program demonstrates the principles of the Fourier series by approximating these seven periodic functions:

A square wave can be approximated by using only the odd harmonics. This trace is the result of summing together 8 terms of the series (i.e. n = 1,3,5,7,9,11,13,15). Notice that the segments that should be flat for a perfect square wave have ripples. These ripples get smaller as we add more terms to the series. Also, note that the rising and falling edges have overshoot (known as the Gibbs phenomenon). The duration of the overshoot (but not the amplitude) gets smaller as we add more terms to the series.

The triangle wave is also approximated with only the odd harmonics. This is also the sum of the first 8 non-zero terms of the series (as are the remaining periodic functions plotted below). This approximation doesn't exhibit an overshoot, but the corners are rounded. These corners become sharper as we add more terms to the series.

The sawtooth waveform requires all the harmonics. This shows the sum of the first 8 terms (n = 1,2,3,4,5,6,7,8).

The pulse (or non-symmetrical square wave) also requires all the harmonics. Here I have chosen the positive portion of the pulse to be 1/6 of the total period.

The quadratic function also requires all the harmonics. Once we make it periodic it doesn't look much like the quadratic function we normally think about, but the usual quadratic (without the repetition) is not periodic and so it can't be represented by a Fourier series.

The absolute value of the sine function is sometimes referred to as a full wave rectified sine wave since this is the signal you get when you pass an AC signal through a full wave rectifier. This function only requires the even harmonics (i.e. n = 2,4,6,8,10,12,14,16).

This function consists of only the positive portion of a sine wave (the maximum value of sin(x) and zero). This is the signal we get when you pass an AC signal through a half-wave rectifier. Unlike all the previous functions, this one is actually the sum of 9 harmonics (n = 1,2,4,6,8,10,12,14,16). The first one (n=1) is a sine function and the remaining eight (even harmonics) are cosine functions.

Here is what the fseries figure window looks like after startup (i.e. with its default settings). The first of seven waveforms is selected (square) showing two cycles of the periodic function with 100 points per cycle (i.e. 200 points total).
• | The first trace (in green) is the fundamental frequency with trace ID "1st".
• | The second trace (in purple) has trace ID "3rd" and is the sum of the fundamental frequency plus the 3rd harmonic.
• | The third trace (in cyan) has trace ID "5th and is the sum of the fundamental and the 3rd and 5th harmonics.
...
• | The last trace (in white) has trace ID "15th" and is the sum of all the odd harmonics between 1 and 15. It is rendered with a thicker trace so that the best approximation of the square wave is more obvious.

Right click on the Waveform popup to cycle thru all seven periodic functions - or left click on the popup to choose the function you want from a menu. You can also adjust the number of cycles displayed as well as the number of points plotted per cycle. The number of points plotted for each trace is the product of the number of cycles and the number of points per cycle so it ranges from a minimum of 25 points (1 cycle) to a maximum of 25600 points (16 cycles with 1600 points/cycle).

When you click on one of the eight traces, the x and y values of the clicked position appear below the plot in the x and y edit boxes. However, sometimes it is useful to be able to see the y values for all the traces at the same time. The usual way of doing this is to enable the multi-cursor. You can do this by right clicking on the y edit box, and selecting the multiCursor option. You might want to try that here but you will find that the multiCursor is not very useful for this particular application. That's because the 8 traces are all quite similar which means that plt will be placing 8 cursors very close to each other. The cursor values will overlap making them quite difficult to read. But fortunately, plt has a couple of other ways of accomplishing this. The one used here is called the traceid cursor. Note that 'tidcuR' is included in the plt Options list. This enables the traceid cursor which causes the y values of all 8 traces to be displayed in the TraceID box. Try clicking and dragging the cursor left or right and notice that all 8 trace IDs change as you do that. (Also try moving the cursor by left or right clicking on the X axis label ("Cycles") and holding the mouse button down.) Finally, there is one more way of viewing the data for all the traces at once. Click on the "Data" tag in the menu box near the lower left corner of the figure. That will create a new figure that shows 24 successive values for al 8 traces at once starting at the current cursor position. As you move the cursor by either of the methods just mentioned, the new data window will be automatically updated so it always shows the data starting at the cursor location. Also if you want to see more than 24 successive values, you can make the data window taller.

For this figure, I have selected the Sawtooth function and disabled all the traces except the 1st, 2nd, and 8th. Now let's click on the "start" button and observe that the plot starts moving. The amplitude of the selected waveform will vary periodically at a speed that is related to the value entered for the "Speed" parameter (which has been set to 32 in this example). The speed parameter doesn't change how often the display is updated, but rather how much the amplitude changes between every plot update. Below the plot, we see the yellow text "156.50 updates/sec" which tells us how often the plot is updating. There is no pause between display updates, so this number gives us a measure of how fast the computer can update the display. You can use the number to compare the performance of different computers, operating systems, and Matlab versions. The display will continue to update forever (modulating the waveform amplitude in the process) ... until you click on the start button again (which actually will have been renamed to "stop"). This feature of the application (moving plots) complicate the code noticeably, so you may be wondering why this feature was added to the application (especially since the moving plots don't allow us to learn any more about the Fourier series this application is meant to study). The two reasons for the moving plots are one, that it makes the application more visually appealing, and two (and more importantly), it demonstrates some of the techniques involved in creating real-time plots, which are important for many signal processing applications.

Notice that the start/stop button doesn't look like the typical uicontrol button. That's because we have used a pseudo popup control for this, using a mode of the pseudo popup referred to as a "super button". We could have used a standard uicontrol button, but the programming for the pseudo popup control is simpler.

The second new control is the "Speed" popup which is right below the Waveform popup. The speed popup doesn't change the display update rate (although it may seem to). Instead, it changes how much the amplitude is changed at each display update.

The number of display updates per second is calculated and displayed below the plot. This is done every 200 display updates. (It would be a waste of time doing this every update and would also be changing so fast it would be difficult to read.) Every time the updates per second is calculated, the color of the characters alternates between yellow and green to make it obvious that a new value was calculated. When experimenting with the coding of the display loop, you can use this readout to decide if a change improves or degrades the efficiency of the loop. You can also use it to judge the speed differences between various computers, operating systems, and Matlab versions.

Notice that the position coordinates for all the controls have been grouped in one place (lines 26-29). This makes it easy to make adjustments to the GUI with the plt move function. How to do this is described in the GUI building with plt section.

It's easy to make a plot move. Simply create a loop that modifies the 'xData' and/or 'yData' properties of a line (or set of lines). Complications arise when trying to make this idea practical in a typical application. One needs a way of starting and stopping the plot motion. Also one usually wants to change what is happening in the plot when various controls are modified - even while the display loop is in progress. Also, it would be nice for the user to be able to close the figure window while the plot is moving without generating any error messages (Errors will occur if the display loop tries to modify data of a line that no longer exists because the figure was closed.) A final requirement is that the display update should be as quick as possible (i.e. the display loop should be as concise and as efficient as possible). These requirements require some care in how we construct the display loop. This application shows one way of managing those requirements.

The callback function (gCB) responds to changes in any of the controls and also includes the display loop, so, of course, this function is the one that requires the most thought. The callback function behaves quite differently depending on whether the display is static (i.e. in the stopped condition) or dynamic (in the running condition with moving plots). If the display is static, the main display loop (consisting of the 31 lines between line 56 and 88) is run exactly once. On the other hand, if you make any change to one of the controls while the display is dynamic, then the callback function doesn't do anything. (It is called, but it returns right away on line 49.) Instead, we are depending on the main loop which is still running. Eventually, the main loop will recognize that one of the parameters has changed and take appropriate action (by executing the code between lines 61 and 82).

As I mentioned, the display loop consists of 31 lines - however only the last 3 lines (85-87) are executed every time thru the loop. This is done to make the update rate as high as possible. Those 3 lines are sufficient to update the trace values and change the amplitude. The rest of the loop is run only once every 200 display updates and is responsible for detecting that the user has changed a parameter and making the appropriate adjustments based on that change. This is why when you change a parameter while the display is running you may notice a slight delay before the display is affected by that change.

Sometimes it is useful to have a program enter its dynamic (i.e. moving image) state immediately after startup without the need to hit a start button. To provide this option for this application, we add an input argument so that fseries(0) starts the program in its static (non-running) state and fseries(1) starts the program in its dynamic state. Zero is the default option, so calling fseries with no arguments starts in the static state. Also, any character string is treated as nonzero, so something like fseries('go') starts in its dynamic state. One remaining conundrum is how to start the plots moving when the input argument is nonzero. The first idea is as follows:

plt('pop',S.go,'index',go+1);

So when the input argument (go) is zero, the index parameter for the start/stop superbutton is set to 1 (the index for the static state) and when the input argument is 1, the index parameter is set to 2 (the index for the running state). However, this doesn't actually enter the running state because setting the index in this way does not call the pseudo popup's callback function. However, if we use the negative of the index we want, then the callback is called. This leads us to what works (which is also shown in line 42 of the program:

plt('pop',S.go,'index',-go-1); % start running if go is 1

So you may be wondering why this line is commented out in the code. Although that line would work, it does have one drawback. For this command to complete, the callback has to terminate. However since the callback is essentially an infinite loop, it will never terminate (unless we hit the stop button). This means after typing fseries(1) in the command window and pressing the Enter key the >> that we normally see in the command window telling us that we can enter another Matlab command does not appear. This is somewhat inconvenient, since we may want to use the command window for other calculations or to start other applications while fseries.m is still running. The solution to this conundrum is to use the following line, which appears in line 43 of our program:

funcStart({@plt 'pop' S.go 'index' -go-1});

This has the same effect as line 42 except that now the command prompt returns immediately after typing fseries(1) into the command window. More details about the funcStart auxiliary function can be found in the Auxiliary functions section.

Copyright © 2024
Paul Mennen


psdZoom.m version 01Jul23
(Power Spectral Density measurements and Frequency Translation)

Author: Paul Mennen
Email:  paul@mennen.org

Overview

Demonstrates how to compute and display power spectra on fabricated data as well as how to use frequency translation (also known as "zoom") to see more detail (i.e. higher frequency resolution) in a narrow frequency band.

As an introduction to spectral analysis, let's consider the design of a 40kHz spectrum analyzer (i.e. frequency components of the input signal can be measured in the 0 to 40kHz range). Also, let's assume that our spectrum analyzer with have a dynamic range of 70dB (i.e. we are interested in measurements as low as 70dB below the peak signal).

Front end

The front end consists of the analog signal conditioning portion of the analyzer as well as the ADC (analog-to-digital converter). Here is the block diagram for the front end:

Analog
input 		→
Programmable
gain stages
→
Anti-aliasing
filter
→
12-bit A/D
converter
→ microprocessor

Because of our 70dB dynamic range requirement, all three blocks must have low noise as well as be linear to within 0.03%. Otherwise, spurious harmonics will appear above the -70dB level that you would mistakenly believe are real signals from the input. This level of linearity is modest, allowing the use of relatively inexpensive components. However, an experienced analog designer would be needed since relatively simple mistakes can easily exceed that threshold. 70dB is sufficient for many applications, but higher quality instrumentation would usually have an 80dB dynamic range, or more - requiring even more experience and care for the front-end design.

For special-purpose applications, the programmable gain section may not be needed, but for more general-purpose instrumentation, the gain needs to be variable to accommodate a wide range of input signal levels. Older designs often used relays to switch the various gain stages in and out, but newer designs usually use MOSFETs as switches, although care must be used to achieve the desired linearity.

Before designing the anti-aliasing filter we must decide on a sampling rate for the ADC. The smallest sampling rate that can be used to represent the data accurately is twice the analysis bandwidth. (This is known as the Nyquist frequency). So in this case we need to sample at at least 80kHz. However 80kHz isn't possible because that would require a "brick wall filter", i.e. a filter that passes all the frequencies from 0 to 40kHz and rejects all the frequencies above 40kHz. So we need to pick a higher sampling rate. Such a choice is a compromise. A higher sampling rate makes the AA filter design easier and less expensive, but a lower sampling rate saves processing time and may also allow the use of a somewhat less expensive ADC.

For this design, we have chosen a sampling rate of 102.4 kHz (i.e. a Nyquist frequency of 51.2 kHz. This means that in the range between 40 kHz and 51.2 kHz, the response of the AA filter has no impact on the measurements. (That range is known as the transition band. The ratio of these two frequencies (40/51.2) is 78.125% is sometimes called the front-end efficiency because 78.125% of the data computed by the FFT will be useable - i.e. there will be 100 good alias-free lines from an FFT with 128 outputs (i.e. an FFT with 256 real input points or 128 complex input points). The fact that the sampling rate is a multiple of a power of two is no accident. Since the simplest FFT has an input length that is a power of two, such a choice simplifies the design and the programming.

The frequency response of the AA filter for this application will look something like this:

This is an 8th-order elliptic filter design that meets the 70dB stopband requirement. The passband ripple is ±.05 dB which represents a limitation of the spectrum analyzer amplitude accuracy. (The passband frequency response of the gain stage, the ADC, and the digital filter used for the zoom analysis, all affect the amplitude accuracy as well.) If you wanted a smaller passband ripple or a higher dynamic range, you would either need to increase the filter order or relax the front-end efficiency (or both).

For higher frequency designs, the filter would generally be constructed using passive components (capacitors and inductors) but in this frequency range, a carefully designed solid state (op-amp) filter could achieve this specification with a lower manufacturing cost than the passive design.

The psdZoom application can't easily simulate these 3 front-end blocks, so for this program, we create simulated data that represents the output of the ADC. In fact, this is not a particularly accurate simulation, because the linearity and flatness of the simulated data buffer far exceeds what would be possible with a real-world front end. Never-the-less this application can help you understand many of the computational methods and design choices involved.

Simulated input data

Since software can't include a real front end, we simulate what the digital output of the front end would look like when the analog input consists of the sum of random noise with three sinewaves at various frequencies and amplitudes. This input can be adjusted using these controls which are to the left of the upper plot in the psdZoom figure window.

The sine wave frequencies are in Hz, and you may enter any value between 0 and 40,000Hz. To change a frequency, right click on it and press the down arrow to erase the previous value and type in the new value. Entering a sine wave amplitude of .001, 1, or 1000 will result in a nominal -60, 0, and 60 dB output respectively. Enter an amplitude of zero to disable any of the sinusoids.

Uniform random noise is generated which is then shaped by an iir digital filter. The denominator is fixed, but you can adjust the three numerator coefficients. The exact shape of the random noise added to the 3 sinusoids is not important to the simulation, but you can adjust those values if you like. You can enter numbers by right clicking as described for the sine wave frequencies, or you can left click just to the right or left sides of the number to increment or decrement (respectively) by 1. Use smaller numbers if you want less noise, or enter three zeros to disable the noise component entirely.

At the start and also whenever any of the above 9 parameters are changed, a new input data buffer is computed consisting of 222 points (approximately 4 million points) representing exactly 40.96 seconds of data at the front-end sampling rate (102.4 kHz). Once the entire 41 seconds of data has been analyzed, we start reusing the same data again from the beginning of the buffer. For a more realistic simulation we should recompute the input buffer rather than reusing it, but this buffer is long enough that you probably won't notice that it is being reused. On a slow computer (especially with older Matlab versions) you will notice a delay when you change one of these input parameters as it computes the 4 million points, but on a fast computer you may barely notice the delay.

Baseband processing (0 - 40 kHz)

Simulated
front end
(222 points) 		→
get next frame
(128 - 4096 points)
→
FFT

→
→
Hanning
(optional)

→
→
Compute
magnitude
& convert to dB

→
Averaging
(exponential)
→ lower
plot

The lower plot always displays the full 0-40kHz frequency range of the input signals. The input from the front end is processed in chunks (called frames) of 128, 256, 512, 1024, 2048, or 4096 points. The reason we choose frame sizes that are a power of two is that this makes FFT algorithm simpler and more efficient.

The frame is then sent to the FFT. The input to the FFT in the baseband case is real, however in Matlab, the FFT input is always complex, so we set the imaginary components of the FFT input to zero. For a complex input FFT, the number of output points is the same as the number of input points. (The imaginary parts of the output in general will not be zero.) In the block diagram, a single arrow represents a real signal and a double arrow represents a complex signal.

To understand the FFT output let's consider a specific example where the frame size is 512 points. With our 102.4kHz sample rate, these 512 points represent 5 milliseconds of data which means there will be 200 frames of data generated every second. (We say that the frame rate is 200Hz.) The first output of the FFT (which we will call bin 0, or the DC term) will be the average value of the input, so this value will be large if there is a DC offset in the input. The second term (bin 1) is the lowest frequency term. Suppose the input consisted of a pure sinusoid with a frequency of exactly 200Hz. This means that one period of this sinusoid fills the frame exactly. In that case, bin 1 will contain a nonzero value, and all the other bins will be zero. Whether this non-zero output falls into the real or imaginary part of the bin or some combination of both depends on the phase of the input (i.e. where the sinusoid starts). If the input consisted of a sine wave of exactly 400Hz, 600Hz, 800Hz, etc. then it would be bin 2,3,4 respectively which would contain the non-zero term. Extrapolating you can see that bin 256 (the 257th output since we started counting at zero) would correspond to an input frequency of 200*256 = 51.2 kHz which corresponds to the Nyquist frequency. (This bin is often called the Fmax term which would be the only non-zero term if the input consisted of a repeated pattern of alternating ones and minus ones.) We discard the remaining 255 terms (the "negative frequency" terms) since they add no new information, and in fact, will be the complex conjugate of the positive frequency terms. In a hardware implementation, or a non-Matlab implementation, most often a real input FFT is used which means that the negative frequency terms are not computed or stored. This is more efficient than using the complex input FFT. We shouldn't feel bad about using the Matlab FFT however. Even though it computes and stores the negative frequency terms it does recognize that the imaginary components of the input are zero and adapts the algorithm for that situation. The Matlab FFT is so well optimized, you would be hard-pressed to beat it by implementing a real input FFT algorithm.

Even with a sinusoid input, the examples above where all the outputs are zero except for a single bin won't happen with data from a real front end partly because the input will include some noise but also because the input frequency will not usually be an exact multiple of 200Hz (again using our 512 frame size example). The FFT bin that is closest to the input frequency will contain the largest value, but the sine wave energy will also spread out (leak) into all the neighboring bins. This somewhat inconvenient effect is referred to as "leakage". When we can get the desired frequency information from the measurement despite the leakage we usually use the boxcar window (i.e. no window at all) but when the leakage obscures the measurement a window (such as Hanning) is used to reduce the leakage. These 257 complex outputs are fed into the Hanning block which implements the Hanning window using its simple 3-point convolution kernel. We could have chosen to implement the Hanning window by multiplying the time domain signal by the window shape (in which case the order of the FFT and Hanning blocks above would be reversed). Which method is more efficient depends on the implementation details and the type of window chosen. Hanning is the most popular window to use, but a more sophisticated spectrum analyzer would usually offer several different window choices. To learn more about other window types that may be used, read the documentation for the winplt.m application here.

Before the data is sent to the magnitude computation, it is reduced in size again, from 257 points to 201 points (with our 512 frame size example) covering the frequencies between 0 (DC) and 40 kHz. The reason the bins beyond 40kHz (up to 51.2 kHz) are not displayed is that this is the transition region of the front-end anti-aliasing filter, so this data is not reliable (i.e. may contain aliases above the 70dB dynamic range of the analyzer). The arc-tangent of the ratio between the imaginary and real parts of the FFT output represents the phase information for each bin but this information is not displayed because, for a real-world input, it is nearly impossible to make sense of this information. (However, the phase is quite important for multi-channel measurements.) Ignoring the phase, the remaining useful information is contained in the magnitude of the complex result (the square root of x2 + y2) where x and y are the real and imaginary parts respectively. This gives us energy units, although if we skip the square root operation we would get power units. Sometimes the spectrum is displayed in these linear units (energy or power) but more often we want a logarithmic display so we can see a wide dynamic range. The decibel (dB) is a convenient unit for this, which is 10 times the log of the power unit output or 20 times the log of the energy unit output.

And lastly, we send the magnitude data to the averaging unit. Additive averaging is a common method where we choose a specific number of frames to average together. Intermediate results are usually displayed while these frames are accumulated, and once the required number of frames has been accumulated the display stops showing the final result. The other common method (which is used here) is called exponential averaging which unlike additive averaging continues forever (or until you hit the stop button). As an example, suppose we select 64 as the averaging parameter. The first output of the averager will be the same as the input to the averager, but after that, the averager output (which I will call p) will be computed as:

p = P/64 + (63/64)p

where P is the output from the magnitude computation. Suppose you change the amplitude value of one of the input sine waves from 1 to zero. You won't see the peak representing that frequency disappear immediately, but rather it will slowly decay until it is no longer visible. The disadvantage of using a larger averaging parameter (512 for example) is that this decay will be slower and you will have to wait longer before the output represents the new input. The advantage however is that the display will be smoother and more accurate. If you set the parameter to 1 (i.e. no averaging) there would be no delay (other than the frame time) but the output will be much noisier. It's easy to experiment with the averaging parameter to arrive at a setting that gives you the desired compromise between smoothness and response time.

Zoom processing

As you will see in some of the examples below, it is often useful to focus the spectral analysis to within a narrow band of interest so we can see more detail in that band. Zoom processing (sometimes called "frequency translation" is an effective way to do this. This type of analysis is somewhat more complicated than the baseband processing discussed above, but we can understand how it works from the following block diagram:

Simulated
front end
(222 points) 		→
× e-jωt

→
→
Decimate
by 2n
filter

→
→
get next
frame
 64 - 2048 points

→
→
FFT

→
→
Same last 3
boxes as above
Hanning
Magnitude
Averaging
→ upper
plot

The first block is the frequency translater (or shifter). It shifts the frequency content of the input signal by the amount "ω" by multiplying the input signal by the complex exponential e-jωt which also can be written as cos(ωt) + sin(ωt)j. Suppose for example we want to focus the analysis on a region around 14kHz. The sine and cosine waves we would use for the translator would be the ones that would have all their energy in bin 70 of our 512-point FFT. (i.e. the sine wave would cycle 70 times within a 512-point frame.

Then we want to lower the sample rate to focus on a small region around 14kHz (in this example). Suppose we want to zoom in by a factor of 8 (i.e. to have 8 bins of frequency information for every baseband bin. To do this we would need to lower the sample rate by a factor of 16. (The extra factor of 2 is because the data is now complex, essentially doubling the effective data rate.) Your first instinct might be that we can just save every 16th data point while discarding the remaining 15. That will indeed increase the frequency resolution, but aliases from other parts of the baseband spectrum will leak into the band we are focusing on rendering the result useless. So before decimating the data, we must use a low pass filter on the data before decimating. This is similar to the reasoning behind the front-end anti-aliasing filter, but this time we need a digital filter instead of an analog one. Since the entire baseband bandwidth is Fs/2 we want our filter to pass the frequencies up to Fs/32 and reject all the signals after that. Of course, such a brick wall filter is unrealizable, but since we are using only 200 out of the 256 FFT bins our pass band only has to go out to (256-56)/256 * (Fs/32) = .78125 * Fs/32 = .0244 * Fs.. And the stop band must begin at (256+56)/256 * (Fs/32) which is about .0381 * Fs.

To give us an idea of the sharpness of this transition band, let's type:

plt([0 .0244 .0381 .5],[1 1 0 0],'Linewidth',2)

which gives us this figure showing the desired filter shape. Notice how steep the roll-off is in the transition band. This makes designing the filter a challenge, and this challenge grows increasingly more difficult as you approach the maximum decimation rate of the analyzer, which for this program is a decimate by 1024. (Although I have shown the stopband extending all the way from .0381 to .5, there are actually 16 don't care regions inside that range. However, it is difficult to take advantage of these don't care regions because they are narrow and become even narrower with higher decimation rates.)

To solve this problem, we will use a much simpler decimate-by-two filter and run it multiple times to achieve higher decimation rates. So to achieve the maximum decimate by 1024 we will run the input data thru the filter 10 times. After each time running the filter, we will discard every other output point (i.e. decimate by two).

To achieve our desired 78.125% efficiency (100 good lines out of 128) the decimate-by-two filter must have a stopband that passband that goes out to .78125 * (Fs/4) = .1953125 * Fs . The stop band must have the same width as the passband which means that the stop band begins at .3046875 * Fs . To maintain the expected 70dB dynamic range, the stop band ripples must be under 70 dB. The erip application was used to design this filter and the result is shown here. Even though we need to run the decimate-by-two filter multiple times, this still turns out to be less computationally intensive than a single-pass filter.

This filter has 7 zeros and 5 poles (i.e. 8 numerator and 6 denominator coefficients) and is implemented in psdZoom.m with the following Matlab statement:

filter([.0161 .0717 .164 .24 .24 .164 .0717 .0161],...
[ 1 -1.104 1.546 -.802 .398 -.052],data);

This filter has a stopband attenuation of 70.9dB, exceeding the 70dB requirement slightly. The passband ripples of this filter are ±.02dB. (You can verify this by expanding the display in the passband region using either the erip or editz applications.) This is better than the flatness of the front-end AA filter which is desirable so that we don't degrade the amplitude accuracy too much further. Unlike the analog AA filter, designing the digital decimating filter for more dynamic range or better amplitude accuracy doesn't require more skill from the designer, but will require a higher order filter with more computational demand.

It works perfectly well to use the filter described above for every filter pass. However, it turns out that we can speed up the filtering somewhat by using different filters for some passes. For example, consider the decimate by 4 case which requires two passes thru the decimate-by-two filter. For the first pass, half of the passband won't be used because its output will again be decimated. So for the first pass, the passband only has to go out to .09765625 instead of the .1953125 required for the second (and last) pass. And the stopband also can be half the size. This means we can use a simpler filter (only 7 roots - counting both poles and zeros) compared with the 12 roots for the first pass as compared with the 12 roots required for the last pass. This simpler filter is implemented with the following command:

filter([.0338 .1478 .2724 .263 .1328 .028], [1 -.59 .468],data);

This filter has a stopband rejection of 80 dB and a passband ripple of ±.0035 dB. Again you can use the editz or erip application to verify these performance characteristics.

Let's take this one step further, by considering the decimate by 8 case. For the same reason as before, the first pass can use an even simpler filter shown here (25 lines out of 128), with the second pass using the 50-line filter shown above, and the last pass using the 100-line filter above that. This filter is implemented with the following command:

filter([.06 .266 .4375 .296 .0223 -.0624 -.0202],1,data);

Which yields a stopband rejection of 77.5 dB and a passband ripple of ±.0046 dB. For the decimate by 16 case and above, we could use even simpler filters, but for simplicity, I chose to use this 6th-order FIR filter for all passes except for the last two.

In most implementations, you will have one filter for the real part and a second identical one for the imaginary part. However, since complex numbers are the native Matlab data type, here we only need one call to filt for each decimate-by-two stage.

The complex data from the decimating filters is then processed in frames as in the baseband case. When the frame size popup is set to 512 (for example) the baseband frame size is 512 real points, while the frame size for zoom processing is set to 256 complex points. (An FFT with 256 complex inputs yields the same number of outputs as an FFT with 512 real inputs.) The two halves of the FFT output are swapped (using fftshift) so that the FFT DC-term (now representing 14kHz in our example) is in the middle of the FFT output.

Consider the case where the zoom factor is set to one. This means the decimating filter will be run just once. If you set the zoom frequency to 20kHz you will see that the zoomed spectrum is identical to the baseband spectrum, except for perhaps some barely noticeable effects due to imperfections in the decimate-by-2 filter. (With a different center frequency, the spectrum will be a shifted version of the baseband spectrum.) So a zoom factor of one is not useful since you have nothing to show for the extra work of frequency shifting and filtering. However, once you select the higher zoom factors, we will see in the examples below that you will get an advantage from these extra computations.

The psdZoom figure window

From the baseband spectrum in the lower plot, you might think that the input consists entirely of shaped random noise. However, looking more carefully we can notice an artifact near 14kHz. So we set the zoom center frequency at 14kHz and set the zoom factor to 8 to increase the resolution in that region to 25Hz (compared to the 200Hz baseband resolution). Now we can clearly see that this artifact consisted of two sinusoids at approximately 13.6 and 14.2 kHz.

In the baseband spectrum, the noise signal was enough to nearly completely obscure the sine waves. The reason we can see them in the zoomed spectrum is that the noise in each bin of the baseband spectrum is now spread out into 8 bins in the zoomed spectrum. This makes the noise power low enough in each bin that the sine wave signal can easily rise above the noise.

Now, to be able to measure the frequencies of the sine waves more accurately, let's increase the resolution by another factor of 4 (by selecting a zoom factor of 32). Now we can see that the largest sine wave is closer to 13.606 kHz. (Click anywhere in the upper plot to select this trace, and then click on the up arrow (peak finder) button near the lower left corner of the figure. That will move the cursor directly to this peak and you can read the frequency in the respective x-axis edit box.)

But notice that now we can see that the input includes yet another sinusoid, this one at about 14.23 kHz. (Click in the up arrow peak finder two more times and it will land on this 3rd highest peak.) The reason we can see the third sine wave here but not with the lower zoom factor is the same as before. We are again spreading out the noise power into more bins, so there is even less noise per bin than before. The noise has not been eliminated. It is still the same total noise power as before. If we were to compute the noise power between two frequencies by summing the noise power over many bins, we would get the same answer regardless of the zoom factor (provided we have enough resolution to pick the band edges accurately in each measurement).

If we wanted to measure the frequency of any of these peaks more accurately, we can continue to increase the zoom factor which in turn increases the resolution of the frequency measurements. We will have to adjust the zoom center frequency so that the sine wave of interest remains in the zoomed region.

In this example, we have kept the same settings as before except the frame size has been increased to 1024. The inputs are the same as well except for the third sinusoid whose frequency was changed slightly and its amplitude was increased by a factor of 5000. The leakage from this large sine wave is so large that it obscures everything else in the upper zoomed plot. In the lower plot, we also see mostly the large sine wave, but we can see that there is some noise source as well, but it's not big enough to say anything about its shape or character.

It appears that our biggest problem with this measurement is the leakage, so here we turn on the Hanning window to help deal with that problem. The Hanning window has made a dramatic difference. In the baseband plot, we can now see the large sine wave as well as the shape of the underlying noise. We can't see the other two sine waves in the baseband display, but we now can see all the sine wave signals in the zoomed plot ... thanks to the leakage reduction afforded by the Hanning window.

This measurement is the same as the one shown in the previous figure, except that instead of using the "Hanning" popup choice as above, here we have chosen "hannP" which is short for "Hanning with power correction". You may notice that the noise level here is a few dB lower than in the figure above. If you were to try to estimate the noise power within a particular frequency range you would find that the previous spectrum would yield an incorrect answer, but we would get a good estimate using the power-corrected Hanning window. So why wouldn't we always use the power correction? To understand why, look at the amplitude of the first peak in the spectrum (near 13.6 kHz). The figure above shows this peak at about 20dB (the correct result) whereas here we see a value that is a few dB less than that. So apparently we can only get accurate amplitude measurements for amplitude or noise, but not both at the same time. This is one of the drawbacks of using windowing, but it is something we put up with because the benefits are often overwhelming as seen in this example.

Here we show a measurement of an input consisting of 3 large sine waves (0 dB) at 6 ,8, and 10 kHz with a small amount of noise added in. The "disable aaFilt" checkbox has been checked. This causes the digital filter that precedes the zoom processing decimation to be bypassed. Suppose we now zoom in by a factor of 4 on the frequencies above 10kHz to see if there are any signals in that region. Now we see two large peaks at 15.6 and 17.6 kHz. These additional peaks are clearly spurious because they are so large that we would also see them in the baseband spectrum, but we do not. However, in some cases, the aliases are much smaller and we would have no way of knowing if the peaks represent real signals or not. Omitting these filters renders the zoomed spectrum useless, which is why this option (to disable the filters) is not provided on any real spectrum analyzer. It is included here for demonstration purposes. Likewise, if you omitted the analog AA filter of the front end (or designed, built, or tested it improperly) even the baseband spectrum would be rendered useless.

After unchecking the checkbox, we will see this result for the zoomed spectrum, which agrees with what we would expect to see given the chosen simulated input parameters.

Sometimes we would like to start the analysis at DC but we are only interested in a small portion of the full 40kHz spectrum (i.e. we want a lower bandwidth baseband). In this example, we are looking at a signal consisting of 3 sine waves all near 100Hz. So a maximum analysis frequency of 150 or 200 Hz would be sufficient. Choosing a zoom factor of 256 gives us a bandwidth of 40,000/256 = 156.25 Hz. Then we set the zoom frequency to half of that (78.125Hz) so that our analysis window starts at 0 Hz.

Instead of using zoom processing, a more common way of generating a lower baseband spectrum is to simply filter and decimate the original data from the front end. The real to complex FFT is usually somewhat less efficient (to generate the same number of outputs) but this is balanced by the fact that we don't need to multiply by the complex exponential making the computational load for both methods is similar.

Here the simulated input consists of three sinusoids with frequencies of 30.4kHz, 34.8kHz, and 0Hz all with an amplitude of 1 volt with some noise mixed in. Since the sinusoids are actually cosine functions which begin at one (at t = 0), the zero-frequency cosine wave is effectively just a 1-volt DC term. In the zoomed plot, both of these frequencies are multiples of the bin width, so they appear as a single peak at 0dB (because the boxcar window was selected). Note that the bin width (ΔF) is shown to the left of each plot. In the baseband plot, the 30.4kHz peak is a multiple of the bin width so it appears as a single 0dB peak, but the 34.8kHz signal is not. Since it is a multiple of half the bin width, its energy is split mostly between two adjacent bins, although some of its energy also spreads to neighboring bins.

In both the upper and lower plots, the DC term also appears as a single peak at 6dB, not at 0dB as you might first guess. If you have studied calculus, you have probably once proved that the average value of a squared sine wave is one-half which is why the DC term is twice the other two terms. Since the 0dB reference is somewhat arbitrary, we could have chosen to represent the DC term as 0dB and the other two terms as -6 dB. Of course, it is important to specify what reference is being used.

You probably have noticed that this display includes a circle marker for each FFT output bin. This is especially useful when a small frame size has been selected (as in this example). To add these markers to the lower plot, first, click anywhere on the lower trace and then click the button labeled "o" in the 4-button group near the lower left corner. This will add the markers but remove the lines connecting the markers. If you want to see both the markers and the line (as shown here) click again on the "o" button, and click a third time to return to the original presentation. You can do the same thing for the upper plot. Just click anywhere on the upper trace before clicking on the "o" button.

One interesting design challenge, no matter if the front end is real or simulated, is how to set the Y-axis limits. Because the amplitudes can vary widely, setting these limits to a fixed value is not workable. One approach is to provide a set of knobs so the user can easily set the desired limits. This is not usually too much of an imposition, but it is tempting to try to relieve the user of this responsibility by setting the limits automatically.

At first, this seems like a trivial task. The ideal limits would allow the spectrum to have its maximum near the upper limit and the minumum near the lower limit with perhaps some headroom (perhaps 5 or 10 percent of the plot height) so that the trace doesn't touch the upper or lower limit. So we can simply search for the largest and smallest value and set the limits to give the desired headroom.

However, after trying that simplistic approach, we find that it doesn't work well. That's because the spectrum is often noisy (especially with little or no averaging) so every display update will change the Yaxis limits, sometimes by a dramatic amount. Especially with a rapid display update rate, the wildly updating axis limits will make the plot look so jittery that it is difficult to see what is happening in the plot.

The solution to this problem used here is to calculate the ideal display limits as described above, but instead of setting the axis limits to this ideal setting right away, we slowly adjust the limits so that they move toward the ideal limits at a prescribed rate. This prescribed rate is a compromise. A fast rate is better because then when the input changes, the display stabilizes quickly on the appropriate limits for the new input. But a slow rate is better because it reduces the jitter we observe when the limits are adjusted rapidly. The compromise I chose was to use a relatively fast time constant (one second) when the limits need to be widened because the current spectrum is exceeding the current limits. The somewhat faster rate was chosen in this situation because it is undesirable to not be able to see the part of the spectrum that is beyond the upper or lower axis limit. On the other hand, when the limits need to be narrowed because the spectrum doesn't span enough of the plot height, the problem is less urgent since we can at least see the whole spectrum even though the detail may not be as good as desired. So in that case we use a slower (four-second) time constant. You may want to experiment by dramatically increasing and decreasing the amplitude of one of the sinusoidal inputs while the display is continuing to update, so you can observe how these two different time constants behave.

In addition to describing how to use the psdZoom application, I have tried to give you an introduction to the concepts needed to design an instrument that displays the spectrum corresponding to an analog input signal. If you are mathematically inclined, there are many books that can help you explore these ideas further. However if you don't have the background or patience for all those equations, you can go a little farther in some of these concepts by viewing a talk that I prepared years ago. You can find that talk here

Copyright © 2024
Paul Mennen


Below are the slides I had prepared for a talk about
FFT analysis that may help you understand some
of the topics related to the psdZoom application.

Some practical aspects of data acquisition

Speaker: Paul Mennen
  • Sampled data systems
  • Anti-alias filtering
  • A/D conversion
  • Averaging
  • Windowing
  • Frequency translation
  • Signal generation
  • FFT Analyzers

First I'll talk about sampled data systems in general, then more detail on the remaining topics. At the end, I'll talk about the analyzers that employ these ideas. Since I have built and supported these analyzers, I've been exposed to many of the practical concerns and problems that are not often found in books. I would like to share some of those with you today.

A sampled data system includes the acquisition section or the signal generation section or both.

Acquisition
• | Signal Conditioning: Preamps, bias, AA filters, etc.
• | A/D: Discretize signal in both time & amplitude
• | Signal Processing: DFT and more

Generation
• | Signal Processing: data from external source or from acquisition
• | D/A: Convert signal to analog form (zero order hold)
• | Signal Conditioning: Smoothing filters, buffers, power amps, etc

I've heard people say that they will be able to tell an alias when they see one, so it's not worth the expense to have AA filters.

Of course, it is not possible to tell. But if you are not convinced, consider this transfer function of an electrical network:

• | Blue trace: with AA filters (accurate). Substantial notch at 3.3kHz.
• | Red trace: without AA filters. Not only is the notch not nearly as deep, but it shifted frequency as well. The error at the high end is nearly 20dB.

Obviously, without AA filters, you would have no way to know that this transfer function estimate was bogus. Often the lack of AA filtering will cause a jagged appearance to the transfer function and/or low coherence. Not always, however. In this case, averaging smoothed out the erroneous estimate, and the coherence was near 1.

Note: A transfer function is the ratio of the power of the system output to the power of the system excitation measured at each frequency.

Here is an example of a typical analog AA filter for use with a conventional A/D converter.

These are the important characteristics of AA filters.

• | Passband cutoff frequency:This parameter defines the bandwidth of the analysis. In this example, we have chosen a bandwidth of 20kHz. This means that we will be able to analyze signals in the DC to 20kHz range. (This covers most audio and vibration applications.)
• | Passband ripple: This example shows several dB of ripple, which is larger than you would normally want in a high-quality front end. The ripple in the passband degrades the amplitude accuracy spec. To reduce this ripple (all other things being equal) we would need to use a larger order filter.
• | Stopband rejection: About -80dB in this example. More is better. Dynamic range can never be better than the amount of stopband rejection.
• | Efficiency: The 3rd and last important parameter relates to how steeply the response transitions from the pass band to the stop band. Usually specified in terms of roll-off in dB/octave or dB/decade. A more useful way of specifying this parameter I will call "efficiency".

Since we decided that we want to analyze signals up to 20 kHz, the Nyquist theorem tells us the sample rate must be 40 kHz or higher. Suppose we choose a sample rate of 40 kHz. Now imagine an input signal consisting solely of a 20.1 kHz sine wave. When sampled at 40 kHz, the resulting samples will look the same as if the input came from a 0.1 kHz sine wave - i.e. an "alias". (Convince yourself of this by trying it.) So to prevent this alias (and others) the analog anti-aliasing filter would need a stop band that starts at 20kHz. But the filter should be flat in the passband (up to 20kHz), so the filter response would have to go straight down from one to zero at 20kHz (referred to as "a brick wall filter"). It is impossible to build a brick wall filter, which means we actually have to choose a sample rate that is higher than 40 kHz. As we increase the sample rate, designing and manufacturing the AA filter gets easier, but we also don't want to go higher than necessary since that increases the processing time required for the same analysis.

In this example, the sample rate was chosen to be 51.2 kHz.

You might wonder why I didn't choose some nice round number such as 50 kHz for example. The reason has to do with the frame size which is almost always chosen to be a power of two (since it simplifies the FFT). The frequency resolution (aka FFT bin width) is the sample rate divided by the frame size. Suppose we had chosen 50 kHz for our sample rate and 512 (29) as our frame size. Then the bin width would be 97.65625 Hz. This would work of course, but 100 Hz would be much more convenient (the bin width with a 51.2 kHz sample rate).

Now consider an input signal consisting of a 31.1 kHz sine wave. When sampled at 51.2 kHz, this produces an alias at 20.1 kHz (51.2 - 31.1). However, this is not a problem since we have decided that we are only trying to analyze signals up to 20 kHz. From this calculation, it is apparent that the stop band of the analog AA filter must begin at 31.2 kHz so that no aliases appear anywhere in our 20 kHz bandwidth.

With the filter response shown here, we see that the stop band (above 31.2 kHz) always has an attenuation of 80dB or more, which tells us that any measurement that shows a signal within the 20kHz bandwidth that is above 80dB must be a real signal, and not an alias of some higher frequency input.

The efficiency of the AA filter is the ratio of the analysis bandwidth (20 kHz) over the Nyquist rate (25.6 kHz) which in this case is 78.125%. With higher efficiencies, we get to use a larger fraction of the Nyquist range but the roll-off between the passband and stopband must be steeper (i.e the transition band must be narrower).

AA filtering has its drawbacks as well. If you are making purely time domain measurements, such as estimating time delay or overshoot, then the AA filter makes the job more difficult.

Here is the step response of the AA filter shown previously. The filter overshoot would make it difficult to estimate the overshoot of the system being measured.

For reference, the blue line is the passband magnitude response of the AA filter. The green line is the filter's phase response. Notice that it is not a straight line. This non-linear phase, which is characteristic of efficient AA filters also distort the time history of the signal being measured (but does not affect spectral measurements).

For these reasons, many dynamic signal analyzers provide the ability to bypass the AA-filtering. But when you do, beware. As many of you know from using digital oscilloscopes, occasionally you may completely misinterpret the display as an unsuspected alias comes home to roost.

One problem for an instrument designer is that the AA filter passband cutoff frequency must change depending on the bandwidth required for each specific measurement.

Traditionally this was solved by building the filter with components that can be switched in value using relays or FET switches. For high-performance AA filters this is a difficult design job and an even more difficult manufacturing problem.

Another approach is called a switched capacitor filter, where the passband frequency can be adjusted by controlling a clock rate. However, SCFs are usually limited to lower-quality sample data systems because of their high noise and modest achievable levels of stop-band rejection.

The more modern approach, used when high performance is required, is called a fixed sample rate system. An analog AA filter is used with a fixed cutoff frequency corresponding to the highest bandwidth of interest. If lower sample rates are required, digital decimating low pass AA filters are used to reduce the sample rate. Usually, this filtering is designed using multiple stages of decimate by 2 and decimate by 5 filters. This is more efficient computationally than computing the entire filter in a single stage. Each filter down the chain gets easier because the sample rates get lower and lower.

We could simplify this design noticeably by cascading only the decimate by 2 filters. So instead of the bandwidths shown in this figure, we would get bandwidths of 20kHz, 10kHz, 5kHz, 2.5kHz 1.25kHz, 625Hz, 312.5Hz, 156.25Hz, 78.125Hz, 39.0625Hz, 19.53125Hz, etc. This doesn't prevent you from making all the same measurements, but most users prefer the "nicer" bandwidth and bin width values, justifying the extra design effort for including the decimate by 5 digital filters.

Moving one step down the chain: A/D conversion.

This leads to an important spec of data acquisition systems: Dynamic range

Assume we connect up an ultra-pure sine wave to the input. Theoretically, the power spectrum should show only one peak. Actually, we will see other spurious signals as well. The size of these spurs determines the dynamic range of the acquisition system.

There is no one commonly accepted definition of dynamic range, but this is the one I find most useful.

Quantization noise is a measure of the amplitude error introduced by the A/D because its output can take on only a finite number of values.

Suppose the A/D has b bits. Then the A/D can take on 2b different values. Thus the step size and the quantization noise (in terms of its standard deviation) is proportional to 2-b

So if the noise is proportional to 2-b, the SNR is proportional to 2b.

Taking the log of both sides we get: SNR = about 6 dB per bit plus this constant C to account for the proportionality.

Most books take many pages to derive this formula. Most of the complications have to do with figuring out what the constant C is. However, this constant is often ignored since it is negligible once we are up to a handful of bits. Also, different answers are obtained depending on the assumptions made.

This equation is often interpreted as meaning that the acquisition dynamic range is simply 6 dB times the number of A/D bits. Wrong! The dynamic range can be much worse because of other errors in the system. Also, the dynamic range can be much better since the averaging effects of the FFT and the digital filters reduce the effects of the quantization noise.

So if quantization noise is not the primary limit to dynamic range, what is? The two most likely limitations to the dynamic range are the A/D linearity and the linearity of the signal conditioning circuits.

A/D linearity: The curve in black is for an ideal 4-bit converter. The curve in red shows a realizable converter (exaggerated). Each step of the ideal transfer function is exactly the same size. Such an ideal converter (if you could build such a thing) would pose no limitation to dynamic range. The waviness of the red curve represents the nonlinear behavior. Note in this example there are also two missing codes.

The nonlinearities of the pre-amplifiers or analog AA filters are just as likely to limit the dynamic range. The dynamic range of the measurement can't be considered greater than the amount of rejection in the AA-filter stopband.

Noise from the power supply or digital circuits can leak into the signal conditioning circuits causing spurs that limit dynamic range. Similarly, noise terms can come from external sources such as a CRT monitor sitting on top of the equipment.

The FFT assumes that the signal is uniformly sampled. Clock jitter (usually a problem only with external sampling) violates this assumption and has similar effects to the other forms of non-linearities.

With modern sample and hold circuits, the A/D aperture error is usually small enough not to be a factor.

Moving down the chain again: Averaging.

There are two types of averaging used in a dynamic signal analyzer: Time and frequency domain.

Here is an example of time domain averaging. These five traces are instantaneous (i.e. not averaged) time histories of a repeating signal. When we add them up point-by-point (i.e. column-wise) the result is seen to be much smoother.

For this to work, we must have a stable trigger so that the picture doesn't jump around in the time axis. Without a trigger, the time domain average (the signal and the noise) will eventually average out to zero.

For frequency domain averaging, the idea is similar except that for each time history, we first compute an instantaneous PSD. These five traces are instantaneous PSDs from 5 consecutive frames. Adding them up column-wise as before yields the averaged trace below. Note that the variance of the average result is much less than the instantaneous PSDs. (I actually used 50 averages to get this degree of variance reduction.)

Triggering is not required. Note from the look of the time history (shown in red), that a stable trigger would be impossible with this type of data.

Here is an example that starkly shows the contrast between the two averaging types.

The left side is a PSD computed using frequency domain averaging. It shows a single tone buried in a high level of noise.

The right side is a PSD computed from the result of a time domain average (on the same signal as before). Note that the noise level is much lower since it has been averaged out. This reveals the existence of another tone in the signal (which is the 3rd harmonic). Remember this only works if you have a stable trigger. Also, signal components that are not harmonically related to the fundamental may also average out to zero since they are not synchronously related to the trigger event.

So, more averaging in the frequency domain does not tend to reduce the noise level. It just measures the amount of noise more accurately. However with time domain averaging, the more averaging, the lower the noise.

For both time and frequency averaging types, I have been implicitly talking about additive averaging. That is, as shown in this formula, we add the ith PSD, as i goes from 1 to n (the number of averages) and then divide by n to normalize the result. The second equation is a recursive way of computing the same thing, with the advantage being that the result of each step is correctly normalized so we can view the average as it is being computed. Additive averaging is primarily useful for stationary signals, i.e. signals that don't change their frequency or amplitude characteristics during the time we are viewing them.

If the signal is non-stationary, then additive averaging is not very useful. However, if we still need some degree of variance reduction, we can use exponential averaging to accomplish this while still being able to track changes in the input signal. In this formula, λ (from 0 to 1) controls the tradeoff between the variance reduction provided and the ability to track changes in the input. As λ approaches 0, the old data is weighted less, and the tracking ability approaches that of an instantaneous (non-averaged) measurement. As λ approaches 1, the old data is weighted more resulting in better variance reduction and a longer time constant. [note: the time constant is 1/(1-λ) frames]

Peak hold does not really fit the conventional idea of averaging, but it is convenient to classify it here. Basically, the maximum of the input and previous average is saved, with this determination made separately at each frequency bin. This is most useful when you are trying to determine whether a certain event has occurred over a long measurement time.

A time history is shown at the upper left. The blue trace on the plot to the right shows the magnitude of the DFT of this time history. The green trace on the same plot is actually a more accurate representation of the signal's frequency content. The tone near 16 kHz and the other lumps in the PSD are masked in the DFT by a problem referred to as leakage. The reason is that the blue trace represents the frequency content of the time history repeated an infinite number of times (the periodicity assumptions). Note that the time history begins at a negative voltage but ends at a positive voltage. So when the frame is repeated and placed immediately following, there is a sharp transition from this positive value to the negative one. This sharp transition spreads (or leaks) energy into every bin of the DFT, and obscures the true nature of the frequency content of the signal if we could have sampled it for longer.

Windowing helps solve this problem. Below the time history is the shape of perhaps the most popular window, called Hanning. It is simply a raised cosine function. It is one in the middle and tapers to zero at both ends. If we multiply the time history by this Hanning window, we get the signal in green. Now when we repeat this new signal, no transitions occur at the boundary. The green frequency trace is the magnitude of the DFT of this windowed signal.

One problem with windowing is that it reduces the total energy in the signal which, as we will see, is difficult to account for.

To illustrate one of the drawbacks of windowing, I fabricated a signal consisting of a 1.5 kHz sine wave (at -20dBV) superimposed with a narrow band noise source. The PSD is computed using a boxcar window (i.e. no windowing) and displayed on the left. Notice that the sine wave and the noise source show up at the same amplitude (-20dBV).

Now we apply the Hanning window (in the frequency domain this can be implemented as a convolution with [-.25 .5 -.25]). The result is shown in the middle figure. Now that the leakage has been reduced by the windowing, we can see that the signal also contains a small 1.1kHz tone. (With the boxcar window, that tone was buried by leakage). The power loss due to this window is 3/8 (sum of the squares of the kernel), so we have corrected the plot for this power loss by multiplying by 8/3. Notice that the amplitude of the noise source is the same as before. However, when we cursor the peak at the sine wave, we see an amplitude of -21.7 dB. (1.7 dB smaller than before.)

So, what if we wanted to measure the amplitudes of sinusoidal data? Well, then we can apply the amplitude correction factor instead (the reciprocal of the square of the center term = 4). With this correction factor, we see the display on the right. Now when we cursor the sine wave peak we get the correct amplitude of -20 dBV. But then the amplitude of the noise reads -18.3 dB (again a 1.7 dB error).

One correction factor cannot make both numbers right. The windowing has distorted the data in a non-linear way causing the DFT to change shape. (Explain intuitively by showing how Hanning spreads the power of the sine wave out into the neighboring bins.) This same effect is different for the random portion since the neighboring bins are already at roughly the same level.

The plot on the left is a PSD of a signal containing 2 sine waves separated by 75 Hz. This is 7.5 FFT lines since the bin width is 10Hz.

If we had used the boxcar window (i.e. no windowing) the peak would have been much broader than even the purple trace. So the Hanning window does provide considerable leakage protection but not enough to detect the smaller sine wave. The Blackman-Harris window easily shows the second sine wave. (Blackman-Harris is actually a family of windows, this one known as the minimum 4-term Blackman-Harris).

The plot on the right is the Fourier transform of the window shape. From this, we can see how much leakage protection we get from these two windows. The smaller the side lobes, the smaller the leakage. For example, on the Hanning window curve, at 7.5 bins we see that the side lobe is at about -70dB. But this is the same amplitude as the smaller sine wave, so it is still masked by the leakage. The Blackman-Harris window on the other hand shows about 100 dB of leakage suppression at that position.

The price you pay for the smaller side lobes is the wider main lobe which decreases the frequency resolution of the measurement. (This is the other main drawback of windowing in general. The boxcar window provides the best frequency resolution in cases where the leakage doesn't obscure the measurement.)

One difficulty we encounter with FFT analysis is known as scalloping loss. This is not an easy phenomenon to explain without a lot of heavy math, so in an attempt to make the problem easier to visualize, I fabricated a signal consisting of the sum of 11 equal amplitude sine waves at the frequencies listed here (from 100 Hz up to 1710 Hz). If we sample 256 points of this signal at a 5120 Hz sample rate, our FFT line spacing is Δf = 20 Hz. So dividing by Δf we get the bin numbers shown here. The important part is the fractional component shown in blue. Note that the first component, 100 Hz is exactly on a line (bin 5) and the last component, 1710 is exactly between two lines (bin 85.5), and all the components in between slowly shift from these two extremes as can be seen by looking at the fractional part of the bin number.

Now the PSD (no windowing) is displayed in the upper left. The high leakage is especially noticeable near 2 kHz. Otherwise, the display looks like a good representation of the signal, with one peak for each sine wave component. However, if we expand the amplitude scale (near -10 dB) we see that the amplitudes are not the same for all components as you might expect. The first term (100Hz) is actually correct. The others are in error (up to 3.9 dB). This is the error we refer to as scalloping loss.

When we repeat the experiment with the Hanning window, we see that the leakage is much reduced, but the scalloping loss is still significant (a bit less at 1.4 dB).

Why do we get this scalloping loss? The answer is easily seen from the Fourier transform of the window shapes (shown in the leftmost plot for Boxcar and Hanning). The red dotted lines show the extent of the center bin. The amount of droop within these dotted lines represents the scalloping loss. You can see that Hanning shows less droop than the boxcar.

Now in the center plot, let's look at the transform of two windows that don't have an appreciable scalloping loss. The first one (blue) I'll call the Flat201 window, although it is sometimes called the Potter 201 or the Potter 3-term Flattop window. The second one (green) I'll call the Flat401, and likewise is also known as the Potter 401 or the Potter 5-term Flattop window. Note: Ron Potter (1932-2020) was an engineer at HP who pioneered HP's early development of FFT analyzers.

At the right, we have expanded the center bin so we can measure the scalloping loss. For the Flat201 window the scalloping loss is .018 dB, but by choosing the amplitude correction factor to be in the middle of this range we can say that the error due to scalloping loss is +/- .009 dB. And likewise, for the Flat401, the scalloping loss error is +/- .0033 dB. For either window, these errors are small enough that we can essentially claim victory over the scalloping loss.

But there are still reasons to choose one or the other of these flattop windows (not to mention many other flattop window variations). Notice that Flat401 has more than 80dB of leakage suppression whereas the Flat201 only has about 40dB. So Flat401 is superior when we need this level of leakage suppression. However, this comes at the cost of a much wider main center lobe which reduces the frequency resolution of the measurement. (As you might expect, the Flat301 is a compromise about midway between those two choices.)

In the literature, you will see mention of "the" flattop window which implicitly implies that there is only one flattop window, but this is far from the truth. In fact, there are an infinite variety of flattop windows.

Now here is the proof.

We again compute the PSD of the signal containing the 11 sine waves, but now using these two Flattop windows. The lower plots show the same expanded view as before, and there is no scalloping loss visible. If we expanded the y-axis enough in the vicinity of the peak amplitude we would eventually see some scalloping loss, however, before we got to that point we would probably see bigger errors in the system (such as the ripples in the front end anti-aliasing filter for example).

Suppose in addition to the 11 sine waves that make up our signal we add 2 more low amplitude signals, the first, a 980 Hz sinusoid at -50dB and the second, a 1900 Hz sinusoid at -65dB. (These plots do not depict these extra imagined signals.) With the Flat201 we would be able to see the 980 Hz sinusoid (between two of our peaks), but not the second one because the leakage in that range would obscure that signal. However, with the Flat401 it would be reversed. We wouldn't be able to see the 980 Hz sinusoid because the main lobe is so wide there isn't enough space between the peaks to see that signal. However, the leakage is low enough at the high end of the range that you would be able to detect the 1900 Hz signal.

This demonstrates that you may not be satisfied if only a single flattop window was available.

Frequency translation is an important data acquisition technique. It's also often called zoom. (In the graphics world, zoom refers to display expansion. But frequency translation is not a display expansion.)

To those of you familiar with radio theory, it is similar to heterodyning. The samples from the A/D converter (xn) are multiplied by a complex phasor (e-jω) resulting in these two sequences (real and imaginary parts). Each of these sequences is then fed through a decimate by D low pass filter. The zoom factor (or expansion factor) is D/2. Each of these ÷D blocks represent the whole cascaded chain of decimate by 2 and 5 blocks shown before. Since two of these blocks are required, the filtering operation for frequency translation requires twice the processing horsepower as for baseband.

Since the FFT can handle complex-valued inputs, we can then feed the result of the filtering directly to the FFT and we can compute all the usual FFT-based functions (auto & cross spectra, transfer functions, etc.)

Multiplying by the complex exponential essentially twists or frequency shifts the data so that the center frequency moves down to DC. (The twisted signal is complex-valued since it is no longer symmetric around DC.) Then the filtering increases the frequency resolution by discarding the frequencies outside the band of interest.

Because of the averaging effect of the digital filters, we can often detect signals using zoom that otherwise would be buried in the noise.

For example, the upper left plot is a baseband PSD of what looks like pure random noise.

In the lower-left plot we have zoomed around 18 kHz by a factor of 10 (the resolution is improved from 100 Hz to 10 Hz). Now we can barely see that there is a sine wave buried in the noise.

Now zooming by another factor of 10 (resolution of 1 Hz) we can more clearly see the sine wave.

In the upper left is a baseband measurement with a resolution of 50 Hz. The signal looks like it has primarily one component near 5 kHz. The shape is a little broad to be a pure sine wave, but it is difficult to tell whether it is sinusoidal or random.

An increase in resolution by a factor of 10 (lower left) shows that the peak is actually a band of noise. Also, we see side bands that were not evident from the baseband display. We can also see a small glitch near 5 kHz that indicates there might be something irregular there.

So we increase the resolution again by a factor of 5 (upper right). Now we can see that this little glitch actually was two sine waves near 5.05 kHz. If we zoom by another factor of 20 (lower right) we can now see these two tones clearly.

Note that this extra resolution comes at a price. The price is acquisition time. The acquisition time is equal to the reciprocal of the resolution. So for the baseband measurement (res = 50Hz) the acquisition time is 20mS. Thus we could do 50 averages in only 1 second. The lower right plot has δf=.05Hz, so the acquisition time is 20 seconds and our 50 averages would take almost 17 minutes. This is a fact of physics; the fastest analyzer in the world can't do it any faster.

Signal generation often gets slighted in discussions of sampled data systems. However, it is just as important and just as difficult to do well. Many of the technical challenges are similar, although some are unique (e.g. the sequence generation for random noise and chirps)

The most useful output signal is the ubiquitous sine wave, the basis function for all spectral analysis. For example, to measure the distortion of a network at a specific frequency, we need to excite the network with a sine wave that is as pure as possible. The purity of the sine wave limits the levels of distortion that can be measured. In general, the dynamic range of the signal generation system should be similar to the dynamic range of the acquisition system. The sine wave can also be stepped in frequency to make transfer function measurements using the classic swept-sine technique. The other periodic functions have more occasional uses, usually for time domain measurements.

Broadband functions, the other class of output functions, are used to excite a whole band of frequencies at once. These functions allow network measurements to be made much faster than using sine waves.

Other than the sequence generation, the components of a signal generation system are similar to a data acquisition system. The interpolating digital filters are used so that the D/A and its smoothing filter can run at one fixed sample rate.

The interpolated signal can then be frequency translated to concentrate the energy of the output in a narrow band. It is common to frequency- translate the output when the acquisition system is also frequency-translating.

The D/A converts the digital numbers into a series of analog steps, each step holding its value constant for the sample period (called a zero-order hold).

An analog smoothing filter is used to smooth out these steps. The characteristics and quality measures of this filter are identical to that of the AA filters. Often the exact same filter design can be used. The D/A zero-order hold introduces a sin(x)/x frequency error which is usually compensated for with a simple one-pole filter.

Finally, the filtered output is amplified or attenuated to the desired output amplitude and buffered to yield the desired driving characteristics.

Of the sequence generators shown in the previous diagram, the most challenging one is random noise. Can a deterministic device such as a DSP chip generate a purely random number sequence? (i.e. a sequence that does not repeat) ?

No it can't. However, we can generate sequences that look random at least over a short time period. These are called pseudo-random sequences. There are many algorithms for computing pseudo-random sequences, both simple and complex. Complexity does not ensure a long sequence, however. The literature is full of examples of extraordinarily complex algorithms that yield extremely poor results.

My favorite because of its simplicity and well-researched properties in known as the linear congruential sequence. We start with an arbitrary number (R) known as a random seed. We multiply by constant a, add another constant c, and then divide by a third constant m and save the remainder from this division (R') as the next number in our random sequence. Since the remainder will always be between 0 and m, the random numbers generated are also in this range. If the sequence covers all such values, then it is called a maximal length sequence. Choosing the three constants according to these two simple constraints has been proven to be sufficient to ensure a maximal length sequence. Thus we can make our sequence as long as desired by picking a large value for m. Choosing m to be a power of 2 simplifies the arithmetic. m = 216 is good enough for some applications. 232 is much better, but requires a 32 by 32-bit multiply which may require multiple precision arithmetic in some implementations.

The name Dynamic Signal Analyzers was coined by HP, one of the early makers of such equipment. Other manufacturers have sometimes used this fairly descriptive name, although FFT analyzers or Fourier analyzers is also common. They are designed to analyze signals and systems covering a broad range of fields and applications, usually involving something that moves (mechanical) with some applications involving purely electrical systems (e.g. telecommunications).

The measurements are usually divided into these three categories.

For time domain measurements, the sophistication of a dynamic signal analyzer is not usually required. A digital scope would suffice. However, these functions are useful, since it often means that you can leave the scope behind.

Most spectral analysis problems can be accomplished with a single input channel, and draw on the power of the FFT to display the time histories in the frequency domain.

Network measurements refer to measuring the input/output characteristics of a system usually to compute a frequency response function (FRF) or a transfer function (mathematical system model). Network measurements usually require a signal generator to excite the system under test, and always require two input channels, one to measure the excitation and one to measure the system response.

Dynamic signal analyzers cover such a diverse range of applications that it is somewhat difficult to categorize them. However at least 90% of the time I find that the customer's application can fit into one of the following categories.


weight.m version 26Jun23
(Sound Level Weighting Filters)

Author: Paul Mennen
Email:  paul@mennen.org

Overview

Displays the frequency response for four filters used in sound level measurements (as defined by IEC 651). These four filters are:

A weighting | This was developed as an equal-loudness filter. This means that if you sweep a constant amplitude sine wave through this filter, the output will appear to be of uniform loudness to those with normal hearing. Logically, minimizing the A-weighted sound level should also minimize the danger to people exposed to the sound. Newer research suggests that other weightings could do better in this regard, but never-the-less A-weighting is still the universally accepted standard for these safety measurements.
C weighting | C weighting is often used when measuring peak amplitudes of short-duration sounds.
B weighting | B weighting is a compromise, falling between the curves for A and C weighting, but is rarely used anymore and should be considered obsolete.
D weighting | D weighting is sometimes used for sound level measurements of military jet engines and has few (if any) civilian uses.

Typing weight at the command prompt will produce a figure similar to this:

  • The primary plot is in the middle, showing the magnitude of all four weightings in dB with a logarithmic frequency axis.
  • The lower plot is the same except the magnitude is displayed in linear units. The D-weighted curve is clipped by default so that you can more easily see the shape of the other curves. You can easily rescale the Y axis if you like to see the entire D-weighted curve.
  • The upper plot shows the phase of these filters. This plot was included as a whimsical curiosity since the phase is really of no consequence because of the way these filters are used.

    Since there is some overlapping of these curves, it is easier to see the shapes by looking at one weighting function at a time. You can do this by RIGHT clicking on one of the trace IDs in the black TraceID box near the lower left corner of the figure. (More information on the use of the TraceID box is given here.) To alert the first-time user of this feature, a two-line help text (in orange characters) was placed at the lower edge of the figure. (The HelpText pseudo object makes this easy.) So as not to be a distraction, this help text is removed as soon as you click in the plot area (to move the cursor) or when you left or right click on any of the four items in the TraceID box.

    The primary purpose of this application is mentioned above, but an important secondary purpose is to highlight some of the programming techniques used:

    The first line of this program defines a logarithmically spaced set of frequency points between 10 and 20,000 Hz.

    The second line identifies the 87th point of that vector as corresponding to 1000Hz which is used as the reference amplitude. (All the weighting curves have a value of 1 at this reference frequency).

    The next four lines define the transfer function of the four weighting functions as shown here. The 4 poles of the C weighting filter are also used by the A and B weightings, so the computations for HA and HB can be simplified by referring to HC in the calculations.

    The transfer function is evaluated along the imaginary axis at the points defined by the frequency vector. The magnitude and phase are then computed, with the magnitude normalized to 1 at 1000Hz.

    And finally, plt can be called to display the results of these computations. Notice the inclusion of the 'SubPlot',[30 50 20] parameter in the plt call. This tells plot to allocate 30%, 50%, and 20% of the plot area to the lower, middle, and upper plots respectively. The lower plot is referred to as the main plot and the remaining plots are called subplots. Usually (and by default) plt assigns only one trace to each subplot and puts all the remaining traces on the main plot. So in this case, since there are 12 traces, it would put the first 10 traces on the lower plot, trace 11 on the middle plot and trace 12 on the upper plot. One reason that plt assigns traces in that way is that it makes it easy to define a logical cursoring scheme that allows you to cursor all the traces (i.e. Each subplot trace has its own Y cursor readout and then one more Y cursor readout is used for the remaining traces depending on which trace in the main plot was clicked on last.)

    Of course, that wouldn't be very useful for this application. Fortunately, there is a way of overriding these default trace assignments. We follow the SubPlot parameter with 'SubTrace',[4 4 4] which tells plt to assign 4 traces to each plot. For plots intended for publication, that's all there is to it, but in this case, we are using plt more for data exploration. It would be difficult to predefine a cursoring scheme that would be appropriate for all situations involving the SubTrace parameter, so it's pretty much left up to the application to define the desired cursor behavior. That is the purpose of the last 4 lines of the weight function as well as the cursor callback function (curCB).

    Note that there are three Y cursor edit boxes. These edit boxes (left to right) are created for the lower, middle, and upper plots respectively. These are created by plt as they would be for any plot including a SubPlot parameter, however here we have repurposed them. The left-most edit box displays the cursor for the lower plot (linear Magnitude) in the same way, it would for any plot created by plt, so no modifications are required for that one. The second edit box is used to read out the dB Magnitude from the middle plot. The characters "dB" are added to the edit box to make it clear that this is for the middle plot. The third edit box displays the Phase (top plot) and a degree symbol is added at the end to make this obvious.

    Normally, clicking on an element in the TraceID box will only enable or disable one of the traces in the main (lower) plot. However here we want the TraceID box to control all three plots. This is the purpose of the TraceID callback function (tidCB). This function ensures that the three plots are synchronized with respect to which weighting functions are being displayed. So for example, if the A and C weighting functions are the only ones currently enabled in the lower plot, the same will be true for the other two plots.

    Both the curCB and tidCB functions include the command plt('HelpText','off'); so that the help text gets turned off after any user action, insuring that the help text does not become a distraction.

    Copyright © 2024
    Paul Mennen


winplt.m version 01Jul23
(A tool for the analysis of FFT windows)

Author: Paul Mennen
Email:  paul@mennen.org

Overview

Struggling with Matlab's FFT window display tool (wintool), I found it cumbersome and limited. I wanted a way to quickly change window parameters and see the effect on the time and frequency shapes and the most common window measures (scalloping and processing loss, frequency resolution, and equivalent noise bandwidth). I couldn't modify wintool for my taste since most of the code was hidden (pcode). So I wrote winplt.m to create a more usable gui for displaying windows. winplt displays traces showing the time and frequency domain shapes of 31 different FFT windows and also is a tool for designing your own windows by adjusting the kernel coefficients with a slider. You can also use winplt's command line interface to return the window time shapes for use in your Matlab programs.

While working with this application, you may find the IEEE paper on Windows for Harmonic Analysis (by Harris) useful. This is the most cited reference on FFT windows and includes descriptions of most of the windows plotted by winplt. For your convenience, you can get this paper from my website (www.mennen.org) in the section called "Signal processing papers".

Most treatments of FFT windows (such as the Harris paper mentioned above) are very mathematical. You will need all that math to understand how to compute the various characteristic window metrics, but if you want to understand the basic ideas without the many pages of mind-numbing equations, click here where you can view the portion of a talk I gave years ago that discusses windowing in a more accessable way.

Winplt is also a great example of gui programing using the plt plotting tool, however, if you are just getting started with gui programming I would recommend starting first with some of the shorter examples in the plt\demo folder.

Thirteen of the FFT windows computed by winplt are defined by Matlab functions contained in the signal processing toolbox. If that toolbox is not installed you will see a warning that the function is not defined. (A boxcar window is returned in that case). The remaining FFT windows are defined using a convolution kernel and will work correctly without any toolboxes installed.

If you know of some FFT windows I have not included, feel free to let me know about them, and I will add them to the next release. Or if you want to keep them secret, you will find it easy to add them to winplt yourself. Also, feel free to suggest new features. (You can always reach me at the email address shown above).

Clicking on the Help tag (near the lower left corner of the figure) opens the plt help file starting at topic with details about the winplt user interface (i.e. the topic you are now reading). Navigate to the Using the plt window section for instructions on display zooming/panning, enabling traces, and using the cursors.

Command-line interface

winplt(id,points)
The first parameter (id) refers to one of the 31 windows listed below, although the last two are user-defined windows and can only be used from the gui interface. A column vector is returned (of length points) containing the amplitude-corrected window time shape. Amplitude correction means that the average value is one, i.e.:
    sum(winplt(id,points)) = points

winplt(id,points,1)
Same as above except that the window is scaled using power correction. This means that the average value of the square of the window is one, i.e.:
    sum(winplt(id,points,1).^2) = points

winplt(id,points,pwr,opt)
id:     An index specifying one of the FFT windows
points: The number of points in the time window to be generated
pwr:    0 = amplitude correction, 1 = power correction (default = 0)
opt:    optional window parameter (used only for windows 1 thru 5 and 11)
This is the most general form of the winplt call. The return value is a column vector to be consistent with the Matlab window functions. Window id's 14 and 15 both return the same Blackman window using different implementations (a good check that the code is correct).

[name, kernel] = winplt(id)
Returns the name of the window associated with the specified id number (0 to 31). The second output argument (if given) will contain the convolution kernel. For windows not defined by a kernel the Matlab function used to compute it is returned, along with the window parameters.

winplt(-2)
Returns a cell array containing all names of the defined windows.

winplt(-1)
Displays a list of all windows and their id codes, which will look as follows:

    ID 00: Boxcar
    ID 01: Hanning/Rife Vincent
    ID 02: Chebyshev
    ID 03: Kaiser
    ID 04: Gaussian
    ID 05: Tukey
    ID 06: Bartlett
    ID 07: Modified Bartlett-Hanning
    ID 08: Bohman
    ID 09: Nuttall
    ID 10: Parzen
    ID 11: Taylor
    ID 12: Hamming
    ID 13: Exact Blackman
    ID 14: Blackman
    ID 15: Blackman (Matlab)
    ID 16: Blackman Harris (Matlab)
    ID 17: Blackman Harris 61dB
    ID 18: Blackman Harris 67dB
    ID 19: Blackman Harris 74dB
    ID 20: B-Harris 92dB (min 4term)
    ID 21: Potter 210
    ID 22: Potter 310
    ID 23: FlatTop - Potter 3 term
    ID 24: FlatTop - Potter 4 term
    ID 25: FlatTop - Potter 5 term
    ID 26: FlatTop - Matlab 5 term
    ID 27: FlatTop - small coef. 5 term
    ID 28: FlatTop - Mennen 6 term
    ID 29: FlatTop - Mennen 7 term
    ID 30: adjust kernel
    ID 31: user

 Note: The windows listed above in red are defined by the functions in the Signal Processing Toolbox. If that toolbox is not installed then selecting one of those windows will show the boxcar window instead of the window shape you would expect. (Also a warning message appears in the command window indicating that the selected function is not defined.)

Graphical interface

To start the winplt graphical interface, simply type winplt at the Matlab command prompt (i.e. with no arguments). This opens a window similar to this:

The solid green line is the frequency shape of the selected window and the dashed green line is the time domain view.

When you click on the Help tag on PC Windows-based systems the plt.chm help file will be opened pointed at the section of that manual devoted to describing this winplt.m application. Right clicking on the help tag will open the same plt.chm file at the top level which would be more useful for finding instructions for zooming or panning the display, enabling traces, using the cursors, etc. With other operating systems, the plt.htm file will open in the browser allowing you to navigate to the winplt section or any other section of interest.

After you have zoomed into a portion of the plot to look at some detail, you often will want to return to the original axis limits. You can do this by clicking on the last tag in the menu box defLim (default limits).

Use the popup control (the large yellow text just above the plot) to select one of 31 different windows - including most windows mentioned in the literature and a few of my own. Left clicking on this control brings up the menu shown here with the currently selected window highlighted. Simply click on the window you want to look at. Alternatively, you may right click on this control and the window selection will change to the next window in the sequence (ordered by the window ID). When you select a window defined by a convolution kernel, that kernel appears in the far upper right corner of the figure window.

Use the number of bins slider to control how many fft bins to plot on either side of the center.

For these window types (as well as Rife Vincent below), an additional slider appears to the left of the # of bins slider. Note that the slider name as well as the upper and lower slider limits change depending on the window selected. As soon as you move the slider, the plot will be updated to reflect the changed parameter.

Window ID 01 (Hanning/Rife Vincent) is unique in that the parameter slider controls the number of times that the Hanning window is applied. So select 1 to get the traditional Hanning window (the most popular of all windows), and select 5 to get the Hanning window applied 5 times (which is sometimes referred to as the Rife Vincent 5 window). Note that if you set the parameter to zero, the Hanning window is applied zero times which is equivalent to the boxcar or rectangular window (ID=0). In the example shown in this picture, the Rife Vincent 3 window was selected. Note that the Rife Vincent 3 kernel is shown as:
[-.05 .3 -.75  1 -.75 .3 -.05] If we divide by the 1st term and take the absolute value we get:
[  1   6   15  20  15  6   1 ] which you may recognize as the 6th row of Pascal's triangle.

Whenever a new window is selected, or when any window parameters are changed, these four window characteristics are computed and displayed in the upper left corner of the plotting area. Green text is used to indicate that the data is associated with the first two traces (also in green):
  1. Scalloping loss (dB): A measure of the uncertainty of the amplitude measurement due to the non-flatness of the frequency shape. It's computed as the difference between the maximum and minimum values of the frequency response between -.5 and +.5 bins.
  2. 6dB bandwidth (bins): A measure of frequency resolution. Indicates how precisely one can estimate the frequency of an input tone. A window with a wider main lobe will have a larger 6dB bandwidth and less certainty in the frequency measurement.
  3. Equivalent Noise Bandwidth (bins): With white noise as an input, the enbw is the width of an ideal rectangular filter which would accumulate the same noise power as that obtained from the output of the window. The processing loss is equal to the square root of the enbw. For example, if enbw = 2 bins, processing loss = 3dB.
  4. Worst Case Processing loss (dB): This is the sum of the processing loss in dB (10*log10(enbw)) and the scalloping loss.

Winplt remembers the previous two frequency and time shapes displayed. These stored traces make it easy for you to compare two or three different window types, or to compare up to three windows of the same type but using a different parameter value. By default, only the first two traces are enabled showing you the frequency and time shapes respectively of the currently selected window. The next two traces (shown in blue on the plot as well as in the TraceID box) show the window that was selected previously to the current one. Note that the TraceIDs are in italics indicating that the trace is disabled. Simply click on the TraceID to change it to a bold font and enable the trace on the display. (You can't do this until you have selected at least one other window, since otherwise there is no meaning to "previously selected".) Likewise, the last two traces (shown in red) show the "previous previous" window. If you don't know what that means, read the example situation below:

Suppose you select these three windows, in this order, using the popup control:
  • 12: Hamming
  • 25: FlatTop - Potter 5 term
  • 21: Potter 210
The trace ID box will then have trace labels as shown in the above picture. The first trace will show the frequency shape of the Potter 210 window (since that is the window currently selected) and its trace ID will be "Freq21". The third trace will have an ID of "Freq25" and if enabled will show the Potter 5-term FlatTop window frequency shape since that was the shape previously shown. The fifth trace will have an ID of "Freq12" and if enabled will show the Hamming frequency shape since that was the shape "previously, previously" shown. The 2nd, 4th, and 6th traces will display the time traces associated with the frequency plots just mentioned. If you need to compare more than 3 different windows at once, the easiest option is to start multiple copies of the winplt application.

Note that when either of the blue traces is enabled (Freq25/Time25 in the above example) the four window characteristics of the previous window are also displayed on the right side of the graph. This is shown in blue to remind you that it is associated with the trace of the same color (i.e. traces 3 and 4). This second set of text strings for the previous window makes it easy to compare these characteristics between two windows. And again if you need to quickly compare these numbers for more than two windows, just open multiple copies of winplt.

On the right side of the graph (above the right axis label) is a checkbox called "power corrected". When checked (as it is by default) the time trace is power corrected. When this checkbox is unchecked, the time trace is amplitude corrected. The meanings of power and amplitude correction are described above in the command-line interface section.

The simplest way to experiment with your own window designs is to first select one of the kernel-based windows that most resemble the window you want. (Those are the windows that have the kernel displayed above the window name, including IDs 1,12,13,14, and 17 through 28). Then when you switch to ID 30 (adjust kernel) you can modify the selected kernel element by using the slider labeled "Parameter". (The selected kernel element is indicated by bold text and is surrounded by parentheses). At first, the last (rightmost) kernel element will be selected as shown in this figure. To select a different kernel element simply click anywhere on the kernel list and the selected element will move one position to the left (in a circular fashion). If you want to enter a particular number for the selected kernel element, just type that number into the parameter slider edit box. Usually however, you will want to use the slider arrows to adjust the value. Clicking on the slider right / left arrows will increase / decrease (respectively) the kernel value by one part in 2000 (i.e. by 0.05%). You can change the increment factor by entering a value of 100 or greater into the slider edit box. For example to set the increment factor to 1 part in 10,000 (i.e. 0.01%) just enter "1e4" into the parameter slider. Clicking in the trough area of the parameter slider changes the kernel element by 10 times as much as for the slider arrows. Note that the frequency and time domain plots instantly update every time the selected kernel element is modified. This instant feedback may allow you to get a feel for how you want to modify the kernel, but even with this feedback, it is difficult for all but the shortest kernels.

If you right click anywhere on the kernel, instead of moving left as you get with the left click you will see this popup selection. The first two selections (Move right/left) are not often useful since it is faster to just use the left click method. The next two choices add or remove a kernel element respectively. There are two ways to save the kernel as you currently have modified it. The first method Save simply saves the kernel to temporary memory (which will be lost when you close winplt.m. (Selecting Revert restores the kernel from this temporary memory). To save the kernel more permanently, select the choice Write winplt.mat which will save the kernel in the same folder containing winplt.m with the indicated file name. This kernel is reloaded when you click on the Load winplt.mat selection. You will find both methods of saving the current kernel useful as you explore the effects of modifying each element while searching for the desired characteristics. If you opened this menu inadvertently, you can close it by selecting the last choice (-- cancel--) or the black "x".

To display your own window design based on a convolution kernel, the selection above (ID=30) is usually the easiest. But if you don't want to be limited to that design technique, you can display your own window designed in a completely general way by selecting the last window type in the popup control (ID 31: user). The current user function string will then appear directly above the popup control. If you haven't entered such a string yet, the default userwin string is shown which is simply userwin(points,param). If you don't have a function named userwin on your Matlab path, then you will also get the undefined function error message shown in this picture. Any other errors encountered when winplt is computing the user defined window will also appear in this location.

The easiest way to display your own window definition is to create a Matlab file called userwin.m containing a function of your own design called userwin with two input arguments and a single output argument. The first argument is the number of time points to return and the second argument is a window parameter. (It's easiest to include the 2nd argument in the calling sequence even if you don't need a window parameter. That way there is no need to modify the userwin string.) And of course, the output argument is the returned time window which normally will be a vector of the length specified by the first input argument.

An example of a possible userwin.m file might look like this:
function out = userwin(points,param)
  out = winplt(21,points)*(100-param) + winplt(22,points)*(100+param);
If the parameter slider is all the way to the left (-100) then this user window would display the same thing as window ID 21 (Potter 210). If the parameter slider is all the way to the right (+100) then the user window would display the same thing as window ID 22 (Potter 310). If the slider is in between then the window generated would be a weighted mixture of those two windows. (The parameter set to zero would generate an even mixture of the two windows.) Note that you don't have to worry about normalizing the window amplitude.

If you want to modify the user function string, just click on it. The string will change colors to indicate "edit mode" and an underline cursor will appear. The left/right arrow keys and the delete/backspace keys will work as expected as you are editing the string.

An obvious change to this user function string would be to change the function name from "userwin" to something else. This would be useful if you were experimenting with more than one window definition. Another possibility for the userwin string is to enter the computation itself, avoiding the need to write a separate .m file). This is practical for reasonably simple definitions. For example, to display a triangular window, enter this as the userwin string:

[1:2:points points:-2:1]

You don't have to worry about whether the result is a row or a column since winplt will convert it to a column either way. You don't even have to get the size correct since winplt will truncate or zero pad it to make it the correct length (i.e. "points").

Consider this user string which shows another example of combining two windows:

chebwin(points,param) + winplt(28,points)

In response, winplt will compute a new window which is the sum of the Chebyshev window and the Mennen 6 term FlatTop window (ID 28). Note that the parameter slider value will be used in the Chebyshev portion since it includes the string "param" which gets replaced with the current value of the slider. Note that the string "points" must be used to indicate the number of points in the returned window function. The adjustable parameter can be used in many other ways in the userwin function. One example is to use it as a weighting between two predefined window functions as in this user string:

winplt(21,points)*(100-param) + winplt(22,points)*(100+param)

This will create a user window identical to the one described in the userwin.m example above (in red). You could also use the user window to display one of the standard winplt window selections, but with a different choice of parameters. For example, suppose you want to show the Taylor window with nbar=8 instead of the nbar=5 used by window ID 11. Note that taylorwin (from the signal processing toolbox) is defined as follows: function w = taylorwin(points,nbar,dBsidelobes). So suppose we enter the following user string:

taylorwin(points,8,-abs(param))

Winplt will then show the Taylor window with the nbar parameter set to 8. The sidelobe level will be controlled by the parameter slider. Since taylorwin expects the sidelobe level expressed in dB as a negative number, the negative absolute value was used to avoid the error message that would otherwise result when the parameter slider is moved to a positive number. If instead of changing the sidelobe level, you wanted to see the effect of changing the nbar parameter with the sidelobe level set to -90dB you would enter:

taylorwin(points,param,-90)

Notes

  1. Matlab's window functions return windows with a normalized scaling (i.e. with a max value of one), although that type of scaling has little use in spectral analysis. Using winplt, you can compute a normalized window as follows:
              w = winplt(id,points);
              w = w/max(w);
  2. You will notice that for some of the flat top windows, the sum(winplt(id,points)) is slightly less than points. This is on purpose, and is done to minimize the scalloping loss. The scaling for the power corrected windows is always exact however.

    3. Copyright © 2024
    Paul Mennen


airspeed.m version 01Jul23
(Aircraft performance, the power required equation, & regression analysis)

The purpose of this application is threefold:
  • To allow you to create useful performance charts for the aircraft that you fly.
  • As an educational tool to enable you to better understand some aerodynamic concepts.
  • To show how easy it is to perform linear and non-linear regression calculations and to show how useful these calculations are in a variety of settings.

Linear and non-linear regression

There are three situations related to this application where it will be necessary to estimate the parameters of an equation to produce the best fit to a data set. This is called curve-fitting, or regression analysis.
  • The first of these situations is the need to create a model for the relative air density as a function of altitude. (This is needed because air density is one of the variables in the power required equation.) We will start by talking about how to create this air density model.
  • The second situation is the need to estimate true airspeed from a collection of GPS position coordinates. How this can be done is discussed below. We will need several estimates using this method to compute the drag parameters (the third situation is discussed below)
  • The third situation is the need to estimate the two drag parameters from a set of performance measurements. (How to do this is described later). We will need these parameters to derive the performance curves which are the basis for this application.
Here is a script designed to create a non-linear air density model and create the two plots shown below:
% Plots the residuals for two possible altitude models to see which one is better --------------
  d = [0    1      2      3      4      5      6      7      8      9    10;     % Altitude (km)
       1 .90748 .82162 .74214 .66868 .60091 .53853 .48123 .42871 .38069 .33690]; % density ratio
  % The data above is from the 1976 US Standard Atmosphere, US Government Printing Office:
  % https://ntrs.nasa.gov/api/citations/19770009539/downloads/19770009539.pdf
  % starting on page 67 of the pdf document (last column of the table)
  ktf = 1000/(25.4 * 12);  % convert km to thousands of feet
  a = d(1,:) * ktf;        % altitude in thousands of feet
  s = d(2,:);              % density ratio
  alt = (0:.01:10) * ktf;  % x axis for extrapolated values (every 10 meters)
  k = 1:100:1001;          % indices into alt for every 1000 meters
  tid = {'Calc' 'True' 'Resid'};  xlbl = 'Thousands of feet';
  % The density ratio must decay to 0 at infinite altitude and is defined to be 1.0 at sea level.
  % The simplest model satisfying these conditions is the single parameter model: e ^ (-alt/p)
  p = fminsearch(@(p,a,s) norm(exp(-a/p) - s),1,[],a,s);
  t = prin('sigma = e ^ (-alt/%6w)',p);   % show the model in the figure name
  S = exp(-alt/p);                        % extrapolate density ratio from model
  plt(alt,S,a,s,a,(S(k)-s)*100,'Ylabel',{'Density ratio' 'residual (%)'},...
     'FigName',t,'Xlabel',xlbl,'Styles','-ox','MarkerSize',12,'TraceID',tid);
  % This two-parameter model also satisfies those conditions: (1-alt/b) ^ c --------------------
  p = fminsearch(@(p,a,s) norm((1-a/p(1)).^p(2) - s),[100 1],[],a,s);
  t = prin('sigma = (1 - alt/%6w) ^ %6w',p); % show the model in the figure name
  S = (1-alt/p(1)) .^ p(2);                  % extrapolate density ratio from model
  plt(alt,S,a,s,a,(S(k)-s)*1e6,'Ylabel',{'Density ratio' 'residual (ppm)'},...
     'FigName',t,'Xlabel',xlbl,'Styles','-ox','MarkerSize',12,'TraceID',tid);
The first fminsearch above estimates the single parameter for this simple model:

σ = e-alt/p where alt is the altitude in thousands of feet.

Matlab's fminsearch function uses the Nelder-Mead method for finding the minimum of a non-linear multidimensional function, although in this first case, the function is one-dimensional. It estimates the single parameter p to be 31.429 which you can see in the title bar.

The fminsearch is followed by a plt command which produces the figure you see here. Note that the model (green trace) goes through (more or less) the true data represented by the purple circles. So it isn't a terrible fit, although the worst case residual which occurs at 10km altitude (32,808 feet) is 1.5 percent - a noticeable error. More telling is that the residuals appear to follow a distinctive pattern (a quadratic). Patterns in the residuals usually indicate that a better model is possible.

(A positive residual indicates the calculated density ratio is larger than the true value.)

The previous model is the most obvious candidate for a function that starts at one and eventually decays to zero, but it is not the only choice. The next most obvious equation that meets those requirements is:

σ = (1 - alt/b)c where alt is the altitude in thousands of feet.

The second fminsearch in the code above finds the best fit for the two parameters (b & c) in this alternative model.

The plt command following the fminsearch produces the figure shown here.

This is a multi-dimensional search for p = [b c] and the best fit values for these two parameters can be found in the title bar.

The worst case residual which occurs at 1km altitude (3,281 feet) is about 15 parts per million which is three orders of magnitude better than the first model calculated above. This is clearly the preferred model to use and is more than accurate enough to use for generating the aircraft performance curves.

The power required equation

The next thing we must wrap our brains around to be able to understand this application is the meaning of the power required equation:
Pr = c σ v3 +
k
σ v

where: | Pr | is the Power Required to maintain level flight
 | v | is the true airspeed
 | σ | is the air density
 | c | is the parasitic drag coefficient
 | k | is the induced drag coefficient

There are two drag forces that we must overcome to maintain flight. The first drag force is called parasitic drag and can be thought of as the force required to push the air out of the way. This is related to the size and shape of the aircraft's frontal area - both factors which affect c, the parasitic drag coefficient in the first term of the Pr equation. To understand why this term is proportional to velocity cubed, consider the force required to push an object thru a fluid or gas. If the object is moving very slowly, the fluid can move smoothly out of the way (laminar flow) and this drag force will grow linearly with the velocity (i.e. if the velocity doubles, the drag force also doubles). However, if the object moves more quickly, the fluid can't flow smoothly around the object and the flow becomes turbulent. This increases the pressure in front of the object by an amount that is also proportional to velocity making the drag force grow in proportion to the square of velocity. (This is not a proof, but at least a plausibility argument that parasitic drag is related to v2).

Now the work required to push the object thru the fluid a distance (Δd) is the drag force times the distance. (W = F Δd). Power is the work expended per unit time (W/Δt) so the power required is FΔd/Δt, or Fv. So if the force is proportional to v2, the power will be proportional to v3. Since it will require more power to push a thicker fluid out of the way it is also plausible that the power will be proportional to the air density σ, justifying the first term in the Pr equation.

The second drag force is known as induced drag and arises from the requirement that the wings direct the air mass that it is running into downward at a rate fast enough to provide enough lift to keep the aircraft at a steady altitude. Intuitively this becomes more difficult and less efficient when the airplane is moving through the air more slowly and when the air is less dense. This is evidence that the induced drag will be inversely proportional to both velocity and air density (although again doesn't constitute a proof). A complete proof of this equation would require a more solid grounding in fluid dynamics than I can offer.

Aircraft parameters

All the performance curves and characteristics shown in this application are derived from just five parameters:
  • BSFC     The engine efficiency (brake specific fuel consumption - in pounds of fuel per hp)
  • hpMax    The maximum engine power (in horsepower)
  • Fuel     The maximum usable fuel (in gallons)
  • c        The parasitic drag coefficient
  • k        The induced drag coefficient
The first three parameters are easily found in the airplane operating handbook or engine manual. However the drag coefficients are not published by the manufacturers, so we will have to calculate these two coefficients from airspeed measurements made while flying. (See the section Making measurements for the aircraft configuration file below.)

When this application starts, it looks for these measurements as well as the first three parameters listed above in a text file called airspeed.txt in the plt\ini folder. Unless you have modified it, this file will have the following contents:
Aircraft configuration file: N3946Q: Cessna 185 with IO550

BSFC      : .40067
hpMax     : 300
Fuel (gal): 76

 alt     tas     gph
-----   -----   -----
 4000    71.4    5.7
16000    62.2    7.8
16000   113.4    7.8
 4000   124.6    10
20000   155.0    12
 8000   150.0    14
12000   160.4    15
You can edit this file to contain the parameters and measurements from your aircraft of interest, or you can save the file with a different name if you want to analyze more than one aircraft. You shouldn't modify the formatting of this parameter file, so restrict your modifications to the numbers in the file, as well as the text in the first line which contains the aircraft type and identifier. The three headings for the measurements stand for altitude (in feet), true airspeed (in knots), and gallons per hour (fuel flow).

The parameters and measurements shown above are associated with the airplane I own - a Cessna 185 with a turbo-normalized IO550 engine. It's a good idea to include the aircraft N number in the ID field as I have done here. Otherwise, you might lead someone to assume that two airplanes of the same type will have the same performance characteristics. That is rarely true since most aircraft have been modified since leaving the factory. For instance, the airplane used in this example has aftermarket wheel fairings, flap gap seals, and other aerodynamic modifications installed as well as additional antennas and strobe lights all of which affect drag.

Note that seven measurements have been included in the supplied configuration file, although you can include as many measurements as you like. (Some tips on how to make these measurements are given near the end of this section.) More measurements generally will give you a more accurate estimate of the two drag coefficients. Theoretically, just two measurements made at different altitudes or power settings are sufficient to estimate the two drag coefficients. However, that isn't likely to result in a good estimate. The residuals from the regression analysis will give you an indication of how confident you should be with the estimates of the drag coefficients. (This is explained in the section below titled "Residuals".

The True Airspeed Chart

If you have not edited the aircraft configuration file (plt\ini\airspeed.txt), then when you start airspeed.m you will see this figure (the True Airspeed chart) along with another figure (the Efficiency & Range chart shown below). Note that the aircraft identifier from the first line of the config file is put into the text block in the upper right corner of the figure. Also, the 3 parameters in the config file are copied to the BSFC, hpMAx, and Usable fuel edit boxes above the plot. The next thing the application does it to do a regression (again using fminsearch) to estimate the two drag coefficients to best fit the alt/tas/gps data in the config file. These coefficients are then put into the two drag coefficient edit boxes next to the other parameters. Then the application extrapolates the airspeed curves you see here for 9 different altitudes ranging from 0 to 32,000 feet. (Ignore the circle markers for now, as I will explain their meaning later.)

Since the power required equation is so central to this application, the equation along with a description of the variables is shown in the lower right corner of the plot. If it becomes distracting, right click on the Help tag in the menu box to make it vanish, or reappear. (Left click on that tag to see the document you are now reading.)

Although you can experiment to see the effects of changing a parameter by editing the config file, it's faster to edit any of the 5 parameters directly in the airspeed chart. To change a parameter by a small amount, simply click on the parameter value. Clicking slightly to the right of the center of the number will cause the number to increase and clicking slightly to the left of center will decrease the number. To make a larger change, right click on the number, press the down arrow key to erase the number, and then type in the desired value.
As soon as you change the value of any of the 5 parameters, the string (modified) is added to the end of the aircraft ID field to indicate that the plots no longer correspond to the data in the config file. To restore the parameters and plots, left click on this text box, and the data will be read in again from the text file and the modified string will be removed. Or if you want to read in a different aircraft configuration file, right click on the text box and a file selection box will appear allowing you to select the file you want. After selecting the file, the text box will show the aircraft identifier in that file and the five edit boxes above the plot will be updated to match the parameters in the selected file.

Note that there are two x-axes, one on the top and one on the bottom. Percent power can be computed from the fuel flow and visa versa (using BSFC and hpMax) but having both of these axes can be convenient. The default x axis limits, as seen here are from 50 to 85 percent power since most of your cruise flying will be in that range. Also, the y axis limits have been expanded to show only the front side of the power curve for the same reason. If you want to see the entire curve, (front and back side of the power curve, from minimum cruise power to 100%) click on the xy toggle tag (XY ←→) in the menu box or use any of the panning or zooming methods described here.

In case you aren't that familiar with the concept of the back side of the power curve, let's pan this display so that we see more of the slow and low power portion of this airspeed chart as we see in this figure. (I've also disabled all the traces except the 8,000 ft curve and cropped off part of the chart.) Suppose we are cruising along at 8000 feet happily at 60% power. If you put the cursor on that point (where the blue arrow is) you will see that you will true out at 140.3 knots with a fuel burn of 12 gph. I haven't discussed the efficiency chart yet, but we will be able to see from that chart that we will be traveling 11.69 nm per gallon of fuel.

Now suppose you start getting worried that you might not have enough fuel to reach your destination. Your reaction to this concern (if you have been properly trained) will be to pull back on the power somewhat and as you do this you will notice that your airspeed decreases as expected (assuming of course you retrim to maintain 8000 feet). But how far can you go? This chart will tell us. Let's drag the cursor to the farthest left that it can go (as shown in this figure). It is a little tricky to find this point on the curve, but it helps to move the cursor slowly back and forth by right or left clicking on the Xaxis label (Percent power) and stop when the percent power readout (after the "x" label) or the gph readout is the smallest you can get. (This is where the yellow "+" cursor is in this figure.) Here we can see that the fuel flow will be just a hair over 6gph and your airspeed will be 75.9 knots, and by looking at the efficiency chart we will see that the efficiency is now 12.55 nm per gallon which means that we can now travel 7.4 percent farther than before. This speed is known as the minimum power speed, or also known as Vme, the maximum endurance speed (i.e. the speed where we can stay at 8000 feet for the longest time before running out of fuel). If you try to reduce the fuel flow any more, you will find it impossible to maintain your 8000 ft altitude.

But is this the speed that will maximize the chance to make it to our destination? The answer is no. Consider the point on this curve shown by the green arrow at 99.9 knots and 34.5% power (6.9 gph). That will give us an efficiency of 14.49 nm/gal meaning we could travel an impressive 24% farther than we could have at our original 60% power setting. This turns out to be the best we can do. (i.e. flying either slower or faster will reduce our range). This speed is called Vmr, the maximum range speed (which will vary with altitude) and also sometimes is called the best L/D (lift to drag ratio). But how can we tell from this chart that this is the maximum range? This is somewhat difficult, but if we pan the display so that we can see the origin (i.e. zero on both axes), then if we draw a line from the origin so that it is tangent to this curve, that line will touch the curve at this exact point. Can you figure out why this works? (Consider that we are looking for the highest ratio of speed over power.) Fortunately, we will see that there is a much easier way to find this point when we later discuss the efficiency chart.

Although Vmr and Vme both vary with altitude, there is an interesting relationship between them. An easy-to-remember rough rule of thumb is that Vme is about 3/4 of Vmr. To be more precise, the exact value of this ratio is the fourth root of 1/3 or about .7598. The Vme/Vmr ratio using the numbers in the paragraphs above is 75.9/99.9 = .7596. As you can see, this is very close to the fourth root of 1/3 but not quite. The Vme/Vmr ratio however is exact for any aircraft at any altitude (not an approximation). The reason we didn't get exactly the fourth root of 1/3 here is because of the limited resolution of the airspeed plot (0.1 knots in this case). If we were to increase the resolution, the computed ratio will get closer to the true value. (Increasing the plot resolution would be pointless however since the resolution already exceeds the accuracy for our speed measurements.) The value of this speed ratio can be computed from the Pr equation. If you are interested in this calculation, click on the thumbnail on the left.

Now suppose we want to fly even slower than 75.9 knots (Vme), say 55 knots for example. Is this even possible? According to this chart, the answer is yes. Put the cursor on the curve at 55 knots (near the red arrow) and it will show you that you would have to increase the power setting to 6.837gph (13% higher than at the maximum endurance speed). Perhaps because it is somewhat counterintuitive to have to add power to go slower, this portion of the curve is known as the back side of the power curve. There would be little occasion to use such a slow speed during cruise, but one example might be to minimize the disruption in the cockpit when you needed to open a window or a door (perhaps to allow a parachutist to deplane, or to dump the ashes of a friend or relative over the ocean). Of course, during the takeoff and landing phases of flight, the use of the back side of the power curve will be much more important. So why did we have to increase power to go slower? The answer is that at such slow speeds, the induced drag becomes dominant. Remember from our power required curve, the induced drag is inversely proportional to velocity. So even though the parasitic drag continues to drop as we fly slower, in this portion of the curve that drop is outweighed by the increase in induced drag.

For the performance curves shown above in the airspeed chart, it is convenient to have power on the x axis since that is the variable we can control, with airspeed on the y axis as the consequence of that power choice. However the airspeed is the independent variable for the Pr equation, so it is also useful to view these curves as functions with airspeed on the x axis, and in fact this is the way you usually find the Pr curves depicted in aerodynamics textbooks. To view the curves that way, you can swap the x and y axes by RIGHT clicking on the LinX tag in the menu box. This produces a display similar to what is shown here. Even in the swapped view, the cursor will remain properly synchronized with the cursor in the Efficiency & Range chart discussed below. One advantage of this swapped view is that it is quite easy to find Vme (minimum power) by simply clicking on the valley finder cursor button (down arrow). As we described before, finding Vme in the original view involved some back-and-forth cursor movement while looking for the smallest power value. Once you have the cursor on Vme (as in this screenshot - for the 8,000 ft curve) you can right click on the LinX tag a second time to swap the x and y axes back to the original configuration, and the cursor will still remain on Vme.

The Efficiency & Range chart

This is the default view for the Efficiency and Range chart. The lower and upper X axis is Percent power and Fuel flow respectively, just like the Airspeed chart, although here the default limits show the full power range, from the minimum possible power up to 100%. The Y axis is nm per gallon (a measure of efficiency) and we have Range (also a measure of efficiency) on the alternate axis on the right side of the graph.

Suppose we are planning a flight where we expect to cruise at 8000 feet at 70% power. We can place the cursor at that point as shown in this figure. Next to the x cursor readout, we can see that we will be burning 14 gallons of fuel per hour. The y cursor readout shows that we will be traveling 10.72 nautical miles for every gallon burned (assuming no wind) and next to that, we can see that we will have a range of 814.82 nm. Of course, we should not plan on going that far since we need to observe the reserve requirements of the FARs, not to mention some fuel will be used up for takeoff and climbout. (The fuel required to climb can mostly be recovered in the decent with wise power management.) We can't see what the airspeed will be on this chart, but if we look over at the airspeed chart, we see right away that the TAS will be 150.2 knots. We don't even have to move the cursor on the airspeed chart to see this because the cursors are linked between the two charts. Moving the cursor to 70% power on the efficiency chart will automatically move the cursor to 70% power on the airspeed chart (and vice versa).

Near the upper right corner, we see the yellow text "Lift portion: 16.37% which means that 16.37% of the power used at the cursored position (70% power, 8000 feet) is used to generate lift and the remaining 83.63% is used to push the air out of the way. Note that if we move the cursor to a higher altitude curve, the lift portion increases. This is because it requires less work to push the thinner air out of the way, so a higher percentage of the total will be due to the induced drag. Slow down to the maximum range point and you will see the lift portion increase to around 50% (depending on altitude) and increase again to around 75% at the maximum endurance point. By the time you are well into the back side of the power curve, you will see that the lift portion exceeds 90%.

Suppose you want to know what the maximum range airspeed is for this altitude. Simply click on the peak finder button (The up arrow in the 4 button group in the lower left corner.) The cursor will jump right to the 6.9gph point (with 14.49 nm/gal) that we found before using the speed chart. (Notice how much easier this is, using the efficiency chart). You will also see that the range will be 1101.2 nm and that the airspeed required to achieve this range is 100 knots (by looking at the linked cursor in the airspeed chart.

The traces are pretty close together in this maximum range region, so let's zoom the display so we can see this area more clearly. The cursor is at the peak of the 8000 ft curve (after hitting the peak finder button). Notice that the peaks of the other curves are at the same efficiency level (14.49 nm/gal and 1101.2 nm range), although this is achieved at different airspeeds. For example, click on the blue 20,000 ft curve and click again on the peak finder button. The range is still 1101.2 nm as before but now we will be flying at a higher airspeed (121.4 knots) and a higher power setting (8.38 gph), so it will take us significantly less time to reach our destination. This shows the advantage of a higher altitude, but the higher altitude may not always be practical. (The best altitude to use is usually a combination of factors including efficiency, terrain, availability of supplemental oxygen, winds, clouds, icing, and other weather conditions.) This chart shows us the efficiency advantage of the lower power settings in a dramatic way, yet not many pilots fully appreciate this. Even with this knowledge, few pilots will have the patience to fly that slowly unless forced to by dwindling fuel reserves.

Here I have expanded the display somewhat near the low end of the power range and placed the cursor on the maximum range point on the 8,000-foot curve. As before, finding the exact minimum fuel flow point requires moving the cursor slowly back and forth while looking for the smallest fuel flow or percent power. (Left and right clicking on the X-axis label is a convenient way of moving the cursor back and forth one index at a time.) If you look above at the partial screen capture of the airspeed chart (the one with the blue, green, and red arrows), the cursor position in that plot corresponds to the cursored position shown here. And since the cursors are linked, finding this point on one curve will automatically place the cursor at the corresponding point on the other curve. Notice that the maximum range points for all the other altitude curves occur at the same efficiency (12.56 nm/gal). As with the other chart, the portion of the curve below the cursored position is the back side of the power curve where you progress to lower airspeeds, higher power settings, lower range, and lower fuel efficiencies.

The aircraft used to create the supplied default aircraft configuration file (N3946Q) has a turbonormalizer. A turbonormalizer compresses the air from the air intake so that the air that is sent to the engine has the same density as sea-level air, even when flying at altitude. This allows the engine to produce full power even as you climb into thinner air. However no turbo has infinite capacity, so if you climb high enough, the maximum power produced will fall below 100%. For simplicity, this application does not model that limitation, and all the curves extend up to 100% power for all altitudes. Even where 100% power is possible, that doesn't mean you can fly at full power for long. In fact with large engines such as the IO550 you can rarely operate at more than 80% power for long without overheating the cylinders.

But what about aircraft that don't have the benefit of having a turbo installed? Engines without such equipment are called "normally aspirated" because the engine breathes the same ambient air that the pilot and passengers breathe (assuming the cabin is not pressurized). The engine needs oxygen to support combustion and so it doesn't perform as well breathing the thinner air just as the pilot also doesn't perform as well breathing the thin air. (BTW, most airplanes in the general aviation fleet have normally aspirated engines as well as unpressurized cockpits.) Now we are finally in a position to explain the circle markers on the traces in both the airspeed and efficiency charts.

Consider, for example, flying at 16,000 feet on a standard day. Looking at our two-parameter altitude model we can see that the air density ratio at that altitude is 0.608. This means that a normally aspirated engine operating at that altitude can only generate 60.8 percent of its rated power. Notice that the red circle on the red (16,000 ft) trace is exactly at that value (60.8%) and so a normally aspirated airplane can't operate to the right of that circle when flying at 16,000 feet.

Notice that there is no orange circle on the orange (28,000 ft) trace. (At first, it may look like the white circle for the 24,000 ft trace also passes thru the orange trace, but if you look at the expanded view above, you can see that the white circle is not actually on the orange trace. (Even if it was, that would not have any significance.) From our altitude model, we see that the air density ratio at 24,000 feet is 0.4025. So a normally aspirated engine at that altitude could produce no more than 40.25% power. However, from the efficiency plot, we can see that 42.27% power is the minimum power setting compatible with level flight (the maximum endurance setting). So that tells us we wouldn't have enough power to maintain that altitude, not to mention to climb up to that altitude in the first place. (Of course, none of these limitations apply to the turbonormalized airplane used in this example.)

Drag components

As we discussed above, the Lift portion text near the upper right corner of the plot, displays the percentage of power that is used to create lift at the altitude and airspeed corresponding to the cursor position.

Suppose we have the cursor somewhere on the 8,000 ft curve and then we click on the Lift portion text. That will bring up the plot you see here showing Pr vs. Airspeed for the 8,000 ft curve (in green - trace #1). Also the value for the first term of the Pr curve (parasitic) is plotted in purple, and the value for the second term (induced) is plotted in cyan.

Although these drag component curves don't have much use in determining your aircraft's performance, they can help you understand some of the theoretical considerations, as well as give you an appreciation for how insignificant the parasitic drag is on the back side of the power curve. Remarkably the two component curves cross each other at precisely Vmr (max range). Click on the equation thumbnail above to see a proof of this fact.

Residuals

After reading the aircraft configuration file, fminsearch is used to estimate the parasitic and induced drag coefficients so that the power required curve comes as close as possible (in the least squares sense) to the measurements included in the configuration file. Using this estimate for the drag coefficients the average difference between the power required calculated from the power required curve and the power required specified in the configuration file measurements is computed and displayed in the upper left corner of the Efficiency plot as shown here. A small value (as we have here) indicates that the measurements in the configuration file are consistent. The regression error is not useful if only two measurements were included. In that case, the two data points will always be perfectly consistent since two drag coefficients can be chosen to match the two measurements exactly producing a regression error or zero (or nearly so).

Especially when the regression error is not small, it can be useful to look at the individual residuals for each measurement point. To do this, simply click on the residual text on the screen and a figure such as this will appear.

The X axis is the measurement number, so the leftmost point is the residual for the first measurement in the file and the rightmost point is the residual for the last measurement. The first three columns of the table shown in the upper right corner of the plot are the same as the 3 columns of data included in the configuration file. The fourth column is the residual associated with each measurement, so it shows the same information as the plotted trace. (In most cases it is easier to interpret the plotted residuals instead of the numerical values shown in the table.) Here the largest residual is the sixth measurement, but the residual is not much larger than some of the other values so the sixth measurement wouldn't be considered an outlier. If one of the residuals is much larger than the others, the associated measurement might have been made or recorded incorrectly. Rather than simply throwing out that measurement, it would be best to make the measurement again to verify its accuracy. (It is possible that it's the most accurate measurement in the file in which case throwing it out would be a disadvantage.)

Making measurements for the aircraft configuration file

The ability of this application to create accurate performance curves hinges on the accuracy of the measurements included in the aircraft configuration file. The airspeed indicator measures ram air pressure which is correlated with what is known as "indicated airspeed", which tracks true airspeed at sea level, but reads lower than true airspeed at higher altitudes. The indicated airspeed can be corrected for the thinner air at higher altitudes to give an estimate of true airspeed. This correction can be done using tables, or equations, and is sometimes built into a dial on the airspeed indicator itself. Still, the problem with using the airspeed indicator for the configuration file measurements is that they are notoriously inaccurate. Errors of 5 knots or more are quite common.

A better way to make these measurements is by using GPS data. It's not as simple as reading the airspeed reported the GPS since the GPS reports ground speed, not airspeed. We can compute the true airspeed from the ground speed by using wind forecasts, however, those forecasts are not accurate enough to yield estimates any better than what you would get by using the airspeed indicator. All is not lost however, because it is possible to estimate both the aircraft's true airspeed and the wind speed and direction from a collection of GPS data points taken while flying a triangular or square course. (Theoretically, two legs in different directions are sufficient, but 3 or 4 legs yield much more accurate results).

Suppose we have a collection of GPS data consisting of say 90 position locations (for example) collected over a triangular course.

Here is the wind triangle where the blue vector (T) is the True airspeed, the red vector (w) is the wind and the green vector (v) is the velocity measurement from the GPS (i.e. ground speed). The course is short enough that we can assume the wind speed and direction are constant. We hold the power setting and altitude constant so that we can also assume that the magnitude of the T vector is constant, although its direction is changing. This means that the magnitude and direction of the v vector will be varying throughout the course. So our job is to find the w vector and the magnitude of the T vector that is the most consistent with the 90 recorded GPS points and the 90 different wind triangles that they form. At first, this seems like it will be another job for fminsearch (i.e. non-linear regression) but it turns out that with a little vector algebra, we can turn it into a more easily solved linear regression problem.

If each vector has a length of 90, we can represent the 90 wind triangles with the vector equation:

The advantage of squaring both sides is now apparent.

we get the following matrix equation:

vx(1) | vy(1) | 1
vx(2) | vy(2) | 1
vx(3) | vy(3) | 1
... | ... | ...
... | ... | ...
vx(90) | vy(90) | 1
 *
2 wx
2 wy
β
=
|v(1)|2
|v(2)|2
|v(3)|2
...
...
|v(90)|2

Let's call the blue matrix, the green vector, and the red vector A, B, and C respectively, then we can write this over-determined equation as:

AB = C

And we can solve for B (in the least squares sense) with Matlab's backslash operator:

B = A\C

Then we have wx = B(1)/2, wy = B(2)/2, and β = B(3) and finally our best estimate for the magnitude of the True airspeed:

Reframing the problem as a linear regression has several advantages. The coding is simpler, and the backslash operator is very efficient making it practical to use large data sets. Also there is no chance of converging on a false local minimum as can happen with the fminsearch method.

The airspeed.m application doesn't actually perform the above calculation to estimate true airspeed, but there is another application in this toolbox that does, called gpsLog.m (which was used to create the figure you see here). This GPS data was collected by a panel-mounted GPS receiver, although you could also record the data using your GPS-enabled phone or tablet. Then GPSBabel was used to convert the data to the universal csv format that is accepted by gpsLog. After setting the time limits to include the data only from the triangular course, we click the TAS tag in the menu box and then we can read out the best estimate for the true airspeed (133.59 knots) in the text box below the plot.

To learn more details about the gpsLog application and how it was used to create this figure, see the gpsLog section.

Armed with this relatively easy and accurate method of estimating true airspeed, make as many measurements as you have patience for during different flights on different days to average out the effects of variables that are difficult to control for such as non-standard temperature or pressure, flying thru rising or sinking air, changes in aircraft loading, slight variations in mixture adjustment technique, and many more. This will result in a better estimate of the drag coefficients which in turn will allow airspeed.m to create performance curves that accurately reflect your airplane.

Copyright © 2024
Paul Mennen


carlo.m
(Monte Carlo simulation of 3 dice problem)

The recreational mathematician and chess master Sam Loyd was also one of the most famous authors of puzzles of his day (1841 - 1911). Among the thousands of his puzzle creations is a game he called the Carnival Dice Game which works as follows: You bet 1 dollar to play which allows you to roll 3 dice:
  • if no sixes appear, you get nothing.
  • If only one six appears, you get 2 dollars
  • If two sixes appear, you get 3 dollars
  • if three sixes appear, you get 4 dollars
The question is: Is this a good bet to make?

Actually, this problem is not that difficult to solve if you have any experience with counting problems. Consider that there are 216 different possible outcomes (63) and each of these outcomes is equally likely. So let's assume we play the game 216 times and during that time we see each of the possible outcomes once. Now we can calculate our winnings:
  • Only one of those outcomes will have all three dice rolling a six, which adds $4 to our winnings.
  • How many of those outcomes will have two sixes? Well, consider that if the first two dice are both sixes then the third die could be a 1,2,3,4, or 5. (It can't be a six because we are counting only outcomes with two sixes.) So that is 5 possibilities right there, but we also have to count the 5 cases where the last two dice are both sixes as well as the cases where the first and the last die are sixes. This leads to the conclusion that 15 of the outcomes will pay out $3 for total winnings of $45.
  • How many outcomes will have just one six? Consider that if the first die is a six, there are 25 possible outcomes for the remaining two dice since each of them can range from 1 to 5. And there are another 25 outcomes where the second die is a six and another 25 if the third is a six. So 75 outcomes in total will pay out $2 adding $150 to our winnings.
So our total winnings for the 216 bets is $199 (4 + 45 + 150). But we had to pay 1 dollar to make each of these bets so we end up with a net loss of $17. So on average, we will lose about .0787037 dollars (17/217) or nearly 8 cents on each bet. (This calculation of the average loss per bet is repeated inside carlo.m in lines 65 thru 69 where the last of those lines also puts the result into the third trace of the plot (using "expected value" for its trace ID and depicted as a horizontal dotted white line).

But suppose we didn't know how to make this calculation (or more likely we were dealing with a much more complicated problem where the calculations are intractable). Faced with this problem, can we still get an approximate answer to the question? Well yes, we can start rolling 3 dice and after each roll, we add the winnings to our total (as well as subtracting the dollar we pay for each bet). At first, this will yield a poor approximation of the true answer but the more times we repeat the process the better our answer becomes.

This idea is probably old, but the first time that we know about where a computer was used to execute this type of simulation was during the Manhattan project when they programmed the ENIAC computer to estimate the average distance a neutron would travel before it collided with an atomic nucleus and how much energy would be produced from this collision. Until this time, the method did not have a name, so one of the physicists on the project suggested using the name of a casino (the Monte Carlo in Monaco) near where he grew up. This seemed appropriate since games of chance are central to the business of a casino.

Although we don't need the Monte Carlo method to solve Sam Loyd's dice game puzzle, knowing the answer before running the Monte Carlo simulation allows us to observe how well the method estimates the true answer.

There are three different ways you can start this application:

carlo | When called with no arguments, the simulation is set up but no bets occur until you click a button.
carlo(25) carlo 25 | Sets up simulation & makes 25 bets and then stops. (This is an example of course ... you can use any positive number)
carlo(0) carlo 0 carlo('run') carlo run | Sets up the simulation & makes bets continuously until you click stop.

Suppose we start the application using the first way (i.e. no arguments). Then we can simulate making one bet at a time by clicking the "1 bet" button. Every time we click, it rolls the 3 dice and we see the faces of the dice in the green squares above the plot. It calculates how much you would win or lose based on the number of sixes and displays this value to the right of the 3 dice and then appends the total earnings so far to the "Accumulated earnings" (purple trace) on the left hand axis. It also computes "Earnings per bet" which it appends to the green trace on the right hand axis. Doing one bet at a time helps us see what is happening in the application as we continue to play the game, but this is too slow to make any significant progress in estimating the Earnings per bet expected value. For this, we need to use the "run" button to execute the bets repeatedly.

The speed slider

The speed slider is used to control how fast we make the bets once the run button is pressed. The negative numbers (when the slider is close to the left edge) are for very slow speeds. A setting of -20 (for example) means that a delay of 20/100 seconds (or two-tenths of a second) is added after each bet is made. After clicking run, you will see a report below the plot (to the left of the Xaxis label) that will show approximately 5 bets/sec. Moving the slider all the way to the left (-100) gives a delay of 1 second and the readout will show about 1 bet/sec. This is too slow to make much progress towards approximating the Earnings per bet, but it allows us to watch the dice being rolled and the earnings accumulate. If you set the slider to zero, the plot will be updated after each bet and then the next bet will be made with no delay. On my (2020 vintage) computer, it reports that about 50 bets are being made every second. (You may see a faster or slower rate depending on the speed of your computer.) This is still pretty slow, but we can go faster. For example, if we move the slider to 900 (all the way to the right, 900 bets will be made before updating the display. This is much faster since updating the display takes a lot longer than making a bet and accumulating the results. On my computer when the slider is set to 900 it reports that it is making about 45 thousand bets every second. Even though that is the top end of the slider, we can go even faster than that by typing in a number into the slider edit box. For example, typing in "5E5" results in a speed of about 10 million bets per second. That's about as fast as we can go, since when we are making 500,000 bets for every display update, the display updates are taking up a small fraction of the cpu resources. Typing in an even bigger number will slow the display update rate, but it won't significantly increase the number of bets made per second.

Here I have set the speed to a fairly slow setting (10 bets per display update) so I could watch the progression and run it for 10 seconds or so. Notice that the green line is starting to approach the dotted line (at the expected value of -.0787 dollars) but is still not that close. We can see the losses steadily building by the decline of the purple trace (accumulated earnings) into negative territory. But as expected for a random process this decline is somewhat jagged with many small peaks and valleys.

Now let's set a fairly high bet rate by typing the number 9000 into the speed slider, and run it for around another 20 seconds.

Note that the Xaxis label shows that the Xaxis units are now in millions of bets instead of just the number of bets in the previous plot. (The plt('metricp') function makes this easy.) Also, note that the Yaxis units are now in thousands of dollars instead of just dollars in the previous plot.

The green trace is now getting very close to the true expected value shown by the dotted white line. Note that the right hand axis has zoomed in by about a factor of 40 compared with the previous plot. This happens because some of the less accurate early data gets dropped off due to data decimation (which is discussed later). This allows us to see the difference between the estimated average earnings per bet and the exact computed value. Because we are now showing so much data, the purple trace (accumulated earnings) looks like a straight line. However, if we could zoom in to get a detailed view of any portion of this trace it would look just as jagged as it does in the previous figure.

This application is unique in that the plot is updated every time the number of points plotted for each trace increases by one. By doing this continuously and rapidly we would soon be plotting lines containing millions of points which would start to slow down the update rate as well as the speed of the simulation (bets per second). To combat that problem, whenever the program sees that the lines contain more than 6000 points, it decimates the data by a factor of 10 (i.e. nine points are removed between each successive point that we are keeping). Because we are starting with so many points you can't tell on the display that we are not plotting all the original points. (After all, there are only a fixed number of pixels in the plotting area, so no more than about 600 of the data points can actually be plotted.)

By running this program from demoplt we can see that the carlo application consists of 87 lines of code (not counting the comments). Perhaps you will agree that this is pretty short considering the complexity of the simulation and the plotting process as we have described above.

Copyright © 2024
Paul Mennen


circles12.m
(Solving the 12 circles puzzle)

Years ago I ran across this little-known but interesting geometric puzzle:

Draw 12 circles in a plane so that every circle is tangent to exactly 5 others.

This is the first figure created by circles12. I can't take credit for this solution since it was published alongside the puzzle. However, the equations for the circle centers and radii are my own, as well as all the alternative solutions discussed later.

The four radii (from smallest to largest) necessary to make this solution work are approximately:

[r1 r2 r3 r4] = [1 1.4259 4.5872 12.3914]

which can be computed from the following equations:

s = csc(π/5) r1 = 1 r2 = 1/(s-1)

r3 is the largest root of ax2 - bx + c where
 c = 1+2r2 b = 2(s √ c + r2 ) a = s2 - 1

r4 = r3 (s + 1)

The circles with radii r1 thru r4 are red, green, blue, and purple respectively.

Note that there is a button on the left labeled "start rotation". When you click it, the plot begins to slowly rotate, and the button label changes to "stop rotation". Of course, clicking on the stop rotation button does exactly that and the button label reverts to "start rotation". This rotation adds no value to the solution of the geometrical puzzle but was added to demonstrate how easy it is to create moving plots and some of the programming techniques required to do that.

Actually, when you start this program normally (with no arguments) this figure will start out already rotating. If you find that distracting you can include any numeric or string argument (e.g. circles12(0)) and the figure will be created in its stopped (i.e. static) configuration.

You can control the speed of the plot's rotation with the "Rotation speed" pseudo edit object below the plot. Click on the left or right side of the speed setting number to decrease or increase this number respectively. (Higher numbers rotate faster.) For more details about how to create and use a pseudo slider, see the pseudo slider object section

The number of display updates per second is displayed underneath the plot right next to the rotation speed control. This gives you a measure of the system's plotting speed. You can use this to compare the relative performance of different computers, operating systems, and Matlab versions.

Clicking on the "Help" tag in the menu box near the lower left corner of the figure opens the document you are now reading. If you right click on this Help tag instead of left clicking, the help system will open pointing at its top level instead of at the specific topic specified (i.e. the circles12 help page). Left or right clicking on the Help tag in the other three figures described below behaves similarly.

I discovered this solution when it occurred to me that since the quadratic equation used above to solve for r3 has two roots, there must be some meaning to both of them. Indeed when I used the smaller of the two roots, the solution shown here was created (which I'm calling solution 2). This is the second figure created by the circles12 program.

There is a simple modification of both solutions 1 and 2 which create new solutions. In both figures, note that the red circle is tangent to the inside of the five green circles. In each case, you can make the red circle bigger so that it is tangent to the outside of those same five green circles. Although these are distinct solutions (which I will call solutions 1a and 2a respectively), I decided that they were not unique enough to deserve to be plotted by the program.

It is amazing that there is even one solution to this puzzle, but to my eye, this solution is both the most surprising and the most beautiful. This is the third figure created by circles12, and I refer to this as the third solution.

Looking at the tangent point at the bottom where four circles meet, the radii of the red, purple, green, and blue circles are 17/11, 2.2, 3, 4 respectively.

Looking at the tangent point at the far right or far left where four circles meet, the radii of the red, purple, green, and blue are 1, 2, 3, 4 respectively. The only requirement for that sequence is that the middle two numbers have the same sum as the end two numbers, so for example, 1,3,4,6 would work equally well although it might not be as pleasing to the eye.

For the 1,2,3,4 sequence chosen here, notice that there are only 6 different circle sizes. All 3 blue circles are the same size and all 3 green circles are also the same size. Yet the red circles come in two sizes as do the purple circles. Surprising indeed! At first glance, you might think the 3 purple circles are also the same size, but the bottom one is slightly larger than the other two (r = 2.2 vs 2.0).

As a bonus, this solution looks somewhat like a face, perhaps of an alien or a bug?

For the final figure below (showing the last two solutions) instead of the usual screen capture to create a .png file, I created an svg file (scalable vector graphics) including just the plot area. To create the svg file I followed these steps:
• | Right click on the "Data" tag in the Menu box to make the menu bar visible
• | Click on "plt" in the menu bar and select "Hide/Show cursor controls"
• | Click on "plt" in the menu bar and select "Hide/Show TraceID box"
• | Click on "File" in the menu bar and select "Save As" and then select "Scalable Vector Graphics file" in the "Save as type" popup menu at the bottom

(This is a thumbnail image. Click on it to see the full resolution .svg image)

Solution 4 (the one on the left) looks similar to solution 3 but it has 120 degree rotational symmetry, whereas solution 3 has only left/right symmetry. Also solution 3 requires 6 different circle sizes whereas solution 4 has only 4 different sizes (1, 2, 3, and 4.11684397).

Solution 5 (the one on the right) also has 120-degree symmetry, and amazingly requires only 3 different circle sizes. This gives us a way to make an already difficult problem even harder: Draw 12 circles in a plane with at most 3 different sizes so that every circle is tangent to exactly 5 others.. Of the 7 solutions presented, only this last one is a solution to the new problem. The three sizes used for this last solution are: 1, 2.30930734142, and 5. If you can derive a formula to compute the exact size of the mid-sized circle you win the mathematician of the week award! You would deserve the same award for coming up with another solution different than the seven I have presented. I certainly would want to know if you have some success with either feat.

Here a few of the programming techniques used in the creation of this program:
  • The code is simplified by using complex numbers to hold the xy positions of the plotted points.
  • Demonstrates using prin for creating the Trace IDs.
  • Demonstrates how to make circles look true (i.e. a one-to-one aspect ratio) by using a zero in the 'Pos' argument (width or height).
  • Demonstrates how to use the 'Link' parameter to link all 4 figures together so that when any of them are closed, the remaining 3 figures are also closed.
  • Shows how to use the funcStart function to initiate an infinite plotting loop, so that control returns to the command window while the plotting loop continues to run.
  • A multi-line uicontrol button is created by using html tags in the uicontrol string property.
  • For each figure we draw 24 circles with only 12 traces (i.e. each trace renders two circles, one for solution 4 and the other for solution 5. If we just concatenated z{1} (the 12 circles for solution 4) with z{2} (the 12 circles for solution 5) then there would be an unwanted line connecting each pair of circles. Too avoid that we insert 12 NaNs (NaN+ones(1,12);) between z{1} and z{2}, one NaN for each pair of circles.

    Copyright © 2024
    Paul Mennen


curves.m

Author: Paul Mennen
Email:  paul@mennen.org
This application (located in the math folder) displays a large variety of planar curves which can be both educational and recreational. Type curves at the command prompt to open the figure window.

Left click on the curve selector popup (The large yellow text in the middle of the lower edge of the figure). That will bring up this menu allowing you to choose one of the 43 curve types. In this example, I have chosen curve #26 which is called the Hypotrochoid, resulting in the figure window shown below.

Rather than picking a particular curve, you may just want to view all the curves in sequence. To do this, instead of left clicking on the curve selector popup, right click it instead. This will advance to the next curve in the sequence. For example, if curve #1 was selected, right clicking on the curve name (popup) will advance to curve #2, without ever opening the popup menu. And the next right click will show you curve #3, etc. Finally, if you are looking at curve #43 (the last one) then right clicking once more will bring you back to curve #1.

Another way to cycle thru all the possible curves is to click the "Cycle" button near the left edge of the figure. It will automatically advance to the next curve every second (by default). This is too fast to read the historical notes for each curve and is more for verifying that the program can correctly display all the curves. You can change the time delay between each curve with the delay popup just below the cycle button. To maximize the "wow" factor, you can set the delay to zero and watch as it cycles thru all 43 curves in about a second.

Note in the figure below that the graph includes six traces, (green, purple, cyan, red, blue, white) corresponding to trace numbers 1 thru 6. The information that curves uses to draw these six traces includes the equation (shown in red immediately below the plot and the 9 parameters shown above the plot (a,b,c,Xoffset,Yoffset,tSteps,tMin,tMax,Style). Actually, it is really 39 parameters, because 6 of those 9 parameters (a,b,c,Xoffset,Yoffset,Style) are saved independently for each of the six traces. The edit box called "Trace" just to the left of these 9 parameters tells you which of the six sets of 9 parameters you are viewing. It currently shows "1", but as you click on that number it will cycle thru the six traces and you will see that many of these 9 parameters will be changing. As you are doing this you will also notice that the small pointer (<<) to the right of the trace name moves to the trace whose parameters are being displayed.

All 39 parameters for each of the 43 curve types as well as the equation itself and the historical notes that appear when the curve type is selected are all stored in the curves.m source code which helps explains why curves.m (at around 43 thousand characters) is the largest application .m file in the toolbox. However discounting this data in the .m file, the actual code (about 210 lines) is far less extensive than some of the other applications.

The ability to modify these 39 parameters for various curve types while observing the effects of these changes accounts for this program's educational value - and sometimes recreational value as well. For example, if you select curve #37, this program essentially becomes a Spirograph toy. Changing the a, b, and c parameters is like choosing the sizes of the two gears and the length of the pen arm. You can create all figures possible with a Spirograph as well as some that the Spirograph can't produce.

All 1677 parameters (39 x 43 curves) are individually stored in the program and will be preserved until you right click on the defaults button. That resets all 1677 parameters to the default values. (Actually, there really are fewer than 1677 parameters since many of the curve types don't use all six traces.) Left clicking on the default button resets only the currently viewed curve type to the default values. When you exit the program any changes you have made to all these parameters are lost and so are reset to the default values when the program is restarted.

Note that the equations defining the curves always contain the variable t, because that is the independent variable. It is computed based on the tMin, tMax, & tSteps parameters with the Matlab expression t = tMin : (tMax-tMin)/tSteps : tMax. When you click on any curve, the value of the t variable associated with that point will be displayed next to the x cursor value below the plot. (This is implemented by saving the t vector in the Zdata line property which is then displayed using the Xstring parameter. See the Cursor commands section for a description of that parameter.) Note that if the t vector is identical to the t vector (as is usually the case when the curve is defined in cartisian form, then the t vector is not saved in the Zdata line property nor is the t value displayed since that would be redundant.

In this example, the equation defines the variable z, where z is always taken to be a complex quantity. The real part of z will of course be plotted along the x axis and the imaginary part of z will be plotted along the y axis.

The equations for many of the other curves will define the variable r which means the curve has been defined using polar coordinates (i.e. r is the radius or distance from the origin, and t is theta, i.e. the angle from the x axis measured in radians). Instead of defining z or r, some of the equations will explicitly define both x and y in terms of t.

The equations shown in red below the plot appear as standard Matlab expressions of the vector t and the parameters a, b, and c except for three syntax changes to improve readability:
  • Multiplication is indicated with a dot instead of an asterisk
  • Exponentiation is indicated with superscript instead of with the ^ symbol
  • The expression 1i*t appears as simply it
  • The variable pi is replaced with the symbol π
Although not shown in the equations, the parameter Xoffset is added to the x coordinate and the parameter Yoffset is added to the y coordinate. These offsets allow us to position the six traces in different (usually non-overlapping) regions of the plot.

This application is a good example of how a graphical interface can benefit from the pseudo edit object. If we used traditional Matlab graphical objects for the ten controls and labels at the top edge of the figure these controls would take up much more space resulting in a smaller plotting area. Also, the increment/decrement feature of the pseudo edit object proves valuable here. I recommend checking out the The pseudo edit object section to improve your understanding of how to use this pseudo object.

If you have any questions about the use or coding of this application, don't hesitate to contact me at the email address shown at the top of the page.

Copyright © 2024
Paul Mennen


gauss.m
(Sum of uniform distributions)

1 | 2 | 3 | 4 | 5 | 6
1 | 1 | 1 | 1 | 1 | 1

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12
1 | 2 | 3 | 4 | 5 | 6 | 5 | 4 | 3 | 2 | 1

3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18
1 | 3 | 6 | 10 | 15 | 21 | 25 | 27 | 27 | 25 | 21 | 15 | 10 | 6 | 3 | 1

Suppose we roll two dice and consider the sum that appears on their faces. The smallest such sum is two and the largest is twelve. Now consider how many ways we can achieve each of these sums. For example, for a sum of six, there are five ways - (1 5) (5 1) (2 4) (4 2) (3 3). The second table above shows the results when we count these ways for each possible sum. Note that if we add up all the blue numbers in this table we get 36 which makes sense because there are 62 possible outcomes from rolling two dice. So if we divide the blue numbers by 36 we will have the probability that each sum occurs each time we roll the dice. We don't even have to plot this one to see that the shape of this function is two sides of a triangle (i.e. a line with positive slope followed by a line with a negative slope)

The first table above is for a single die and hardly needs mentioning since there is only one way of achieving each of the sums. The probability distribution we get when we divide by six is known as a uniform distribution (i.e. all the probabilities are the same)

The third table shows the results when we count the ways to achieve each possible sum when three dice are rolled. For example, for a sum of six, there are ten ways - (2 2 2) (1 1 4) (1 4 1) (1 1 4) (1 2 3) (1 3 2) (2 1 3) (2 3 1) (3 1 2) (3 2 1). Note that if we add the blue numbers in this table we get 216 (63) as we expect.

This is the result when we plot the counts in this last table on the y axis vs. the sums on the x axis, and can be thought of as the sum of three uniform probability distributions.

If you have seen plots of Gaussian distributions (also called normal distributions) this curve will look familiar to you - with its concave downwards shape near the peak and its concave upwards shape near the tails. It's not precisely Gaussian, but it is close enough to fool the eye. If we were to plot the results for four dice, the shape would be even closer to Gaussian.

So why is this happening? The result can be expected once we understand The Central Limit Theorem, a remarkable result that can't easily be attributed to a single mathematician. (Contributions are from de Moivre, Laplace, Lyapunov, Polya, Cauchy, Bessel, Poisson and others.) The theorem tells us that when you take repeated (infinite) samples from the same uniform distribution, the result is characterized by the equation e-x2 (known as the normal distribution). Actually, the theorem is more general than that, stating that as long as the repeated samples are taken from the same distribution, it doesn't matter what this distribution is, the final result will still be normal. (The generalization to any distribution is perhaps the most remarkable aspect of the theorem.)

When you start the gauss.m application this is what you will see (without the yellow zoom box which I created after the application started). gauss.m does essentially the same thing as the experiment above with the dice, except instead of starting with a length six uniform distribution, here we use a length 100 uniform distribution. And instead of combining just 3 of those distributions we combine 10 of them. However, when this application starts, only the first four traces are enabled. The first trace is the true normal distribution calculated using its exponential formula (e-x2) and is shown as a white dotted line. The next 3 traces are what you get from combining 2,3, or 4 of these uniform distributions using a convolution (an easier way to solve the counting problem mentioned above where we count the number of ways each outcome can be achieved). By the time we get up to combining 4 of the distributions (the red line of trace 4), the result is so close to the dotted white line that it is hard to see the difference. So at least for the default zoomed-out overall view, we don't need to enable the remaining traces.

Since the traces are so close together, you probably would think of zooming in on a portion of the display so we can see the differences clearly. Here I created the yellow zoom box around one portion of the plot. This is typically done with a double click and a drag of the mouse. There are other ways to create the zoom box and to move it around once it's created, and you can find out about this from the Zooming and panning section. As you will learn in that section, normally after creating the zoom box, you click inside or somewhere near the zoom box (but not on the zoom box itself) and the display limits will be updated so that the entire plot area is filled with the data that was inside the zoombox. From that section, you will also learn about the magnifying lens mode where an additional figure is created to show the data inside the expansion box.

If you haven't yet experimented with the lens mode, now would be a good time. BEFORE you create the expansion box, RIGHT click on the button labeled with an "o" in the 4 button group in the lower left corner. Then create an expansion box and observe that a new magnifying lens type of figure is created.

Now turn off the lens mode by right clicking on the "o" button again and close the lens figure. Then create another expansion box such as the one in this figure.

Usually creating an expansion box with the lens mode turned off will not open a magnifying lens figure, but in this application it does. That's because inside gauss.m we have included a function called lens and have enabled the function by including the 'MotionZoom',@lens parameter in the plt argument list. The lens function inside gauss.m is enabled whenever plt's lens mode is turned off. In this example the internal gauss lens function is quite similar to the plt lens function, but not exactly the same. For example, notice that below the TraceID box there is an additional box containing 10 numbers (one number for each trace). This number is the area under each curve between the expanded x limits (.433549 & .746673 in this example). For the first number (.10246 in white) this represents the probability that a number chosen from a Gaussian distribution of zero mean and unit variance will fall between .433549 and .746673. The other nine areas associated with the other nine traces (Sum2 thru Sum10) only approximate this probability and is noticeably inaccurate for the second trace (purple - Sum 2) since that is the convolution of just two of the uniform distributions. Note that as we drag the expansion box left or right in the main window, the area under the curve numbers are continually updated just like the traces are. (The area numbers don't change if we drag the expansion box vertically, the numbers include the areas down to the x axis.)

Now that we have zoomed in so much with the lens figure we can easily see the separation between the four traces and so you may want to enable all the traces in this view. Simply click on the trace name in the TraceID box and that particular trace will be enabled. Or double click on any trace name in the TraceID box and all 10 traces will be enabled.

This unique lens figure was included partly so that we could display this extra information (the areas), but mostly to show how easy it is to create an application where the lens mode does anything we want.

To remind you that you can see this zoomed in view by opening an expansion box, this help text appears in the upper left corner of the plot. To reduce clutter, this help text disappears as soon as you click anywhere inside the plot, although you can re-enable this help text by right clicking on the Help tag in the menu box.

Another way to see how the 10 traces differ is to plot the error functions (i.e. how each trace differs from the true Gaussian function). Since trace 1 is the Gaussian function itself, its error function is not interesting (i.e. zero) so it isn't plotted. To display the error functions simply check the "Plot errors only" checkbox to the left of the plot which will produce the plot shown here. (All 10 traces have been enabled to produce this figure, which again is most easily done by double clicking on any of the TraceID items.) Unchecking the checkbox will return the display to its original form.

Note that there is a button just below the checkbox called "Cum" which stands for "Cumulative". When you click it, a new figure window appears which looks like the one shown below.

Here we have again only enabled the first four traces, but of course, you can enable all the traces or any subset of them from the TraceID box. The first trace (dashed white line) is the integral of the normal distribution and so it represents the probability that a randomly chosen sample from the normally distribution is less than the value on the x axis. So for example, at x = 0 the curve has a value of 0.5 which means that there is a 50% chance that the random sample will be less than 0. This makes sense because we have been considering a distribution with a mean of zero. As x approaches infinity, the dashed curve approaches one. This also makes sense because there is a 100% chance that a random sample chosen from the distribution is less than infinity.

The remaining curves (Sum2 thru Sum10) are approximations of this cumulative distribution. This approximation becomes more accurate as we add in more uniform distributions, so if you enable just the first and last traces you will see that they are quite similar.

Copyright © 2024
Paul Mennen


gpsLog.m version 01Jul23
(Analysis of GPS data)

File format

The first step in using gpsLog.m to analyze your GPS data is to convert your data into the format that gpsLog will accept which is known as universal csv (comma separated variables). This is a simple text-based format where the first line of text contains a list of the data fields and each of the remaining lines contains the data for a single location. Take a look at the gpsLog.csv file created by this application (in the plt\ini folder) to gain an understanding of the basic idea behind this format. (You don't need to understand the details of the universal csv file format to use this application since GPSBabel handles the details for you.)

If your GPS receiver creates a log file using this format, you are good to go, but more often you will need to convert your log file to the universal csv format. Fortunately, there is a free application called GPSBabel which can do this job for you (and runs on Windows, Mac, or Linux computers).

You can downloads this application from their website: gpsbabel.org. It converts data from about 60 different formats which covers most GPS receivers and applications.

In the example shown here, the data from the GPS receiver was in NMEA format, so that format was selected in the popup menu at the top. Be sure to check the "Tracks" checkbox and then select "Universal csv" for the output format and type in the file name and path where you want to save the result ... then click OK and GPSBabel will do the rest. If you are converting dozens of files, you may find the command line interface more convenient.
When you look at the csv output from GPSBabel you may see a dozen or so fields listed in the header, but gpsLog only uses these six fields:

latitude | in degrees - for example 37.456182
longitude | in degrees - for example -122.112418
time | hh:mm:sec - for example 13:59:59 or 13:59:59.999 if milliseconds are included
elevation | in meters (the heading 'altitude' is accepted as an alternative to 'elevation')
course | in degrees (true)
speed | in meters per second

Only the first three characters of the field name are used, so for example, the elevation field could be specified as 'ele' or 'alt'. These fields may appear in any order. gpsLog determines the order of the data in each line from the order the field names appear in the header line. Latitude and longitude are required fields and the remaining 4 fields are optional. If the time field is not included, gpsLog will assume the first data point is at 00:00:00, the second data point is at 00:00:01, and so on (i.e. a one-second data rate). If the elevation field is missing, an elevation of zero is assumed.

Calling sequence: gpsLog filename
  • If filename doesn't include an extension, the extension .csv is assumed.
  • If filename doesn't include path information, the file is assumed to be located in the plt\ini folder.
  • If filename consists of just a single character (i.e. gpsLog 0) then a file dialog box will appear, allowing you to choose the desired path & file.
  • If no argument is supplied, gpsLog will read the data in the file gpsLog.csv located in the plt\ini folder. If that file is not found, a file with that name will be created containing simulated GPS data from a route predefined inside gpsLog.m.
Let's first assume we start the application by simply typing gpsLog (i.e. no arguments):

This figure (referred to as the speed & elevation chart) as well as the figure below (referred to as the position chart) is what you will see if you start the application with no arguments. (Assuming you have not modified the gpsLog.csv file).

The blue text on this plot, as well as the blue and orange text on the other plot contain some simple help messages to get a new user started, even if they haven't had the time to read this document. This help text only appears when you have loaded the default csv file (gpsLog.cvs). To avoid clutter, the help text disappears as soon as you click inside the plot area. Right clicking on the Help tag in the menu box toggles the help text on or off.

The first trace (TraceID = Alt) in green shows the altitude in meters. Here the cursor has been placed on the altitude trace near the 12-and-a-half minute mark of the log file. You can see in the X cursor edit box that the exact cursor location is 12.69692 minutes after the start of the log file (which was 19:50:30 Zulu time, as specified in the X axis label). You will often want to know the exact time associated with the cursored location. You could of course compute it by adding 12.69692 minutes to the log start time, but this has already been done for you, and you can see the result to the nearest millisecond (20:03:11.815 Z) on the text box below the plot in the other figure (the position plot shown below).

The next trace (purple) shows the speed in meters/sec and its TraceID is "sCalc" which reminds us that this speed has been calculated from the position and time information in the csv file. If successive points are close together, this speed calculation will be somewhat noisy, so before being plotted, it is passed thru a simple six-point moving average filter (by default). The length of the moving average filter is identified by the Avg edit box in the upper left corner of the figure. Click on the "6" slightly to the right of the number to increase this value, and click slightly to the left to decrease the number. If you decrease it to "0", then the raw data will be plotted (i.e. no filtering).

The next trace (red) also shows speed and its TraceID is "sFile" which reminds us that this speed value has been read from the CSV input file. You don't see the red trace in this example because the default gpsLog.csv file does not have a column for speed. Whether this column exists or not will depend on the GPS receiver you used and the application used to record the data. Sometimes speed is reported and sometimes not. (Note that the "sFile" TraceID is in italics. This indicates the trace has been turned off.) The GPS receiver computes speed in a somewhat similar way as we do for the sCalc trace however it has the potential to be more accurate. There are two reasons for that. The first is that a GPS receiver usually uses a Kalman filter which is more sophisticated than the moving average filter used here and that filter is likely tuned to be optimal for the application the receiver was designed for. The second reason is that in good signal conditions, the receiver can make use of doppler information derived from the GPS signal to improve the speed calculation.

The top plot (in cyan) shows the True Course. As with the sCalc trace, this is also calculated from successive position points and also filtered with the same moving average filter. As you change the length of the moving average filter, you can observe its effect on the true course plot. If a portion of the track has a heading that is close to zero or 360 degrees, the display may look noisy as the course bounces back and forth between a course of 1 degree and a course of 359 degrees (for example). Even though the trace may look noisy in that situation, it doesn't indicate that the data is poor in that region.

The two Yaxis edit boxes to the right of the "y" label show the Y cursor readout for the lower and upper plots. The right one (cyan) reads out the cursor value of the upper plot (true course). The left one (green) shows the altitude, but it will change show the speed (and change to purple) when you click on the purple speed trace. (This is the usual arrangement that plt uses for subplot cursors.) One drawback of this cursor style is that you can't see readouts for the altitude and speed at the same time. For that reason, the function of the lower plot Yaxis edit box is essentially duplicated by the green and purple text below the Xaxis label. They show the altitude and calculated speed for the cursored position respectively. Another use of these green and purple text items is that they allow you to change units by clicking on them. Clicking on the green text alternates between the five allowed altitude units (meters, kilometers, feet, thousands of feet, and miles). Clicking on the purple text alternates between the five allowed speed units (meters/sec, km/hour, knots, miles per hour, and feet/sec.) The left and right axis labels and limits will be adjusted to reflect your unit choices. Once you set your preferred units, those units will remain in effect as you move the cursor around.

The csv log file may also include a column for the course calculated by the GPS receiver. In that situation, the upper plot will include an additional trace (in red) to show that data, and an additional TraceID box will appear on the right side of the plot allowing to select which of the two traces you would like the cursor to follow. You don't see it here because the default csv file does not include course data. An example will be shown later so you can see what the additional traces for file data (speed & course) look like.

This is the second of the two figures (the position chart) that appears when you start the application. As mentioned above, the orange and blue help text on the plot disappears as soon as you click in the plot area and as before, right clicking on the Help tag in the menu box toggles the help text on or off.

The GPS track contained in the csv file fills the entire area of the position plot. This allows you to see the most detail but it doesn't show you the true shape of the plot because the aspect ratio is not 1. (i.e. a square course may look like a rectangle and a circular course may look like an ellipse. If you want to see the true shape, LEFT click on the "Aspect" button below the plot and the axis limits will be adjusted to produce an aspect ratio of 1 (i.e. a square or circular course will actually look like a square or a circle.) The axis limits can be restored to what they were before you clicked the Aspect button by RIGHT clicking on the same button. This is a multi-level undo, and if you RIGHT click one more time after all the LEFT clicks have been undone, the axis limits will be restored to the values they were when the application first started.

The latitude/longitude/time associated with the position marked by the large yellow cursor is shown in the box next to the Xaxis cursor edit box near the lower edge of the figure and we can also see that this is the 377th point recorded in the GPS file (near the Yaxis cursor edit box). And since the cursors between the two figures are always linked, we can see that the elevation, speed, and course associated with this point are 80.68 meters, 5.795 meters/sec, and 217.8 degrees. To move the cursor, we can simply click on the part of the plot we are interested in using either figure. Or sometimes it is more useful to move the cursor forward or backward one sample at a time. To do this left or right click on the X axis label. (This works in either figure). After the click, you can hold down the mouse button while you watch the cursor continue to move forward or backward in time.

If the position markers are far apart, it can be useful to see a solid line linking the markers. To do this, click on the "o" button in the lower-left corner of the figure. Click on it a second time to show the track as a solid line without the markers. (That's the most useful mode to see as much detail as possible when there are so many sample points in the file that the markers start to overlap.) Click on it a third time to return to the markers-only display.

If you zoom into a portion of the speed/elevation chart, then the position chart will no longer show the whole track. It will only show the portion of the track that is within the time limits shown on the x-axis of the speed/elevation chart. This is usually useful, but occasionally you may want to see the complete route even when the speed/elevation chart is showing just a portion of the data. To do this, simply check the "allPoints" checkbox.

Suppose we now click the "kmz" button in the speed/elevation chart (without changing any of the parameters in the kmz box). This will create a file with the same name and in the same folder as the .csv data file except that the .csv extension is changed to .kmz. The kmz button will flash off and on briefly to give you some visual feedback that the kmz file was written. kmz is the native format for Google Maps or Google Earth, so you can easily import this file into either of those applications. You won't be able to cursor the data points as precisely and as easily as with the position chart described above, however, the big advantage with Google Maps or Google Earth is that you can see the route in context (roads, terrain, etc.)

This screen capture of Google Maps shows a small portion of the route contained in the default gpsLog.csv file used above. The route is shown with a thin solid white line superimposed with yellow dashes. The yellow dashes help make the trace more visible and also allow you to see where the GPS data points are. (There are always GPS data points at both ends of each yellow dash.)

You can change the width of both the solid line and the dashes in the kmz box. The defaults are widths of 2 and 6 for the solid and dashed lines respectively. You can change these widths to any value between 1 and 9 to suit your taste. You can also change the colors used for the solid and dashed line which is especially useful when you are comparing two or more similar tracks recorded at different times, or recorded at the same time by two different GPS receivers. For example, to change the color of the yellow dashes, click on the yellow bar in the kmz box and you will see a new figure that you can use for making the new color selection. (If you are not clear on how to use this color selection box, see the ColorPick section of the help system.)

You may also disable either the solid line or the dashed line by assigning a width of zero to the respective track. If you prefer to have a compact kmz file, the kmz file size will be much smaller when you assign a width of zero to the dashed track.

If instead of left clicking on the kmz button, you right click on it instead, then a kml file will be created instead of a kmz file. A kml file is essentially the same as a kmz except that it is not compressed. Since the kml file is larger, you will rarely want to use that format, but kml is best if you later want to edit it with a text editor. (You can't open a kmz file with a text editor). Google Earth and Google Maps natively support both kmz and kml files. You can also convert between kmz and kml files using a zip compressor or extractor.

Instead of using the kmz button, you could create a kml file directly from your original GPS data file by converting it to kml format using GPSBabel. However, that approach has several disadvantages:
• | You won't have the option of producing both the solid and dashed tracks of the type shown in this picture. (I've found that style to be generally the most useful.)
• | Using the kmz button allows you to select the portion of the track that you want to be included in the kmz by zooming and panning the altitude/speed plot. GPSBabel always includes the entire data set.
• | GPSBabel doesn't support .kmz directly (although you can manually zip the kml to create a kmz file).
• | With GPSBabel you would have to enter the desired track color using 6 hex digits instead of selecting the color more naturally from a color palette.

Now let's load a more complicated .csv file to demonstrate some additional features of gpsLog.m. This data was collected during a 57-minute flight traveling 142 statue miles, taking off from Mariposa Yosemite airport in California and landing at Palo Alto airport. The data was saved in NMEA format which was the native format of the panel-mounted GPS receiver. GPSBabel was used to convert this data to the universal comma-separated variable format that can be read by gpsLog.m. This csv file contains the same columns as the previous file used above (gpsLog.csv) plus these three additional columns:
date: | The date associated the first data point in the file is included in the figure name, following the csv file name.
speed: | This column contains the ground speed computed by the GPS receiver (in meters/sec) and is plotted by the third trace in red (sFile) of the lower plot. The plots above did not include this third trace since no speed data was included in the data file. As before speed is still calculated from the lat/lon data and plotted by the 2nd trace in purple (sCalc). At this scale, the red and purple traces look pretty much the same, but zooming in will show that there are differences because of the different way that speed is calculated.
course: | This column contains the true course computed by the GPS receiver and is plotted in red on the upper plot. As before, course is still calculated from the lat/lon data and plotted in cyan. In this case, the two course traces are so similar that one or the other of the traces is not visible, being covered up by the other. Still, if you zoom in far enough, differences will be noticeable.
Often there will be several additional columns (such as fix number, fix type, HDOP, and more), but those columns are ignored by gpsLog.m.

From the true course shown in the upper plot, we can observe that after the first 4 minutes the flight heads westbound (approximately 270°) and remains that way until the last eight minutes of the flight - that is except for the noticeable 6-minute deviation starting about 16 minutes into the flight. In that section, the true course curve shows a series of steps separated by about 90 degrees. This is a square course segment which will become more obvious when we look at the position chart below.

The cursored position is 10.583 minutes into the flight (from the Xaxis cursor edit box). The time at that point is 12:51:06 Z which we can read from the position plot. The Z is an abbreviation for "Zulu" time which is also known as GMT (Greenwich Mean Time) or UTC (Coordinated Universal Time). The altitude at this point is 7968.8 feet as indicated by the green text below the Xaxis label. (We clicked on this green text to select "feet" as our preferred altitude unit.) Below that we see that the speed calculated from the lat/lon/time data is 116.82 knots and the 117.99 value (in red and surrounded by parentheses) is the speed data included inside the csv file. (We clicked on this purple/red text to select knots as our preferred speed unit.)

The true course (calculated) at the cursored point is 273.53° (from the second Yaxis edit box). We know that this is a calculated value because both the cursor and the edit box are cyan which matches the color of the "Calc traceID shown in the traceID box to the right of the upper plot. Although it wouldn't make much difference in this case since the two traces are so similar, but if you wanted to readout the values of the course data from the file, simply click anywhere in the traceID box to the right of the upper plot. Then the next time you move the cursor, the cursor marker and the Yaxis edit box will turn red indicating that the edit box is now displaying the course data from the file.

The Aspect button has been pressed so that the track shows the true shape of the track. Then the axis was panned so the track was in the lower portion of the plot (just so I could make a smaller screen capture here). The cursor is always linked to the other figure, so we know the large yellow cursor is at the 10.583-minute mark. We can see that this is the 128th data point in the file and that the path length since the beginning of the flight is 18.134 miles. (We clicked on the path length string to select miles as the preferred unit for this readout.)

The markers have been removed from the track by clicking on the "o" button. This allows us to see more detail because the data points in this view are so close together that those markers would overlap, obscuring some of the detail.

Now let's expand the x-axis of the speed/altitude plot to focus on the square loop portion of the flight. Perhaps the easiest way to do that is by using the xView slider above the true course plot. The black bar spanning the plot width represents the entire x-axis range of the data and the size and position of the smaller gray bar represent the portion of this range that is currently displayed. If you grab this gray bar near the middle and slide it back and forth, the speed/altitude plot will continually update to show the selected region and the position plot will be continually updated to show only the data points it that range. More details on how to use the xView slider can be found near the end of the here in the Zooming and panning Section.

You might guess that the reason for flying this square path was for sightseeing, but actually the purpose was for making a precise estimate of airspeed. For this measurement, it's important to keep the altitude constant. By zooming into a small altitude range, we can see that I didn't quite manage this, losing about 40 feet during the circuit. However, that deviation is small enough that it wouldn't invalidate the measurement.

The xView slider in the plot above was adjusted until the position plot included only GPS samples acquired while flying the square path. Markers were enabled again from the "o" button since we are now only displaying a much smaller set of positions. Then I clicked on the "TAS" tag at the bottom of the menu box at the left edge of the figure. In response to that click, the true airspeed and the wind velocity is calculated from this portion of the GPS data with the results displayed next to the Xaxis edit box at the bottom of the figure. (This area is normally used to display the latitude/longitude/time associated with the cursor position.) Since the results of this calculation are used by the airspeed.m application, the calculation is described in more detail in the here in the airspeed.m help section.

If you want to see the climb profile for this flight, click on the "Climb" tag at the bottom of the menu box on the speed/elevation plot. This will create a new figure such as this one.

The cursor is on the point 37 minutes into the flight and is showing that at that point the descent rate was 236 ft/min and the descent angle was 0.926 degrees.

The same moving average filter (length 6 in this example) that was chosen for the speed and course calculations is also used to filter the climb rate and climb angle to make these plots less noisy.

Now let's create a kmz file so we can view the flight in Google Earth Pro. First, we click on the "2" (the default track width for the solid track) and select "2e" from the popup menu. The "2" still refers to the track width and the "e" stands for "extrude" which indicates we want to see lines added that project the 3D locations down to the surface. As we will see below, this will make it easier to visualize the flight path.

Here is an overview of the entire flight. This is a low-resolution thumbnail image. Click on the thumbnail to see the full-resolution image. (All the remaining images below are also clickable thumbnails.)

Here we have zoomed in to the takeoff and early climbout portion of the flight. Note how the extrusion lines make it easier to visualize the flight path. (If we zoom in even more, we can view the taxiing movement before takeoff.)

This image shows the landing phase of the flight. I haven't shown it here, but Google Earth also gives you the option to show the elevation profile below the flight track. And as with the gpsLog application, Google Earth links the cursors so when you point to a spot on the elevation profile, the corresponding track location is identified.

The default white/yellow colors for the solid/dashed track are great for Google Earth or other satellite views, but a darker scheme such as this one will be easier to see on Google Maps in its default mapping mode. The solid color is green at 50% intensity and the dashed color is blue at 100% intensity. Of course, the colors and intensities you prefer will depend on your taste, which is why these selections and the colorpick menu are provided.

Here is the overall flight path shown in Google Maps using the color scheme described above. Remember to click on these thumbnails to view the full-resolution images.

And again we zoom in on the beginning and end of the path to see the takeoff and landing phases in more detail.

I have demonstrated using this application with flight data, but you will find it just as useful to analyze your motion over the earth during your favorite activities, no matter the medium (sky, sea, dirt, ice, roads, mountains, etc.)

Copyright © 2024
Paul Mennen


hermite.m
(Hermite polynomial interpolation)

Suppose we would like to fit a polynomial through six points on a plane. We can show using linear algebra (or Lagrangian interpolation) that this can be done exactly with a polynomial with six coefficients (i.e. 5th order). Now suppose we also want the polynomial to satisfy prescribed derivatives (slopes) at these six points. It turns out that this can be done with a polynomial with 12 coefficients. And if we also want the polynomial to satisfy prescribed second derivatives at these six points, then this will require a polynomial with 18 coefficients (and so on for more derivatives). When one or more derivatives are prescribed, this type of polynomial interpolation is often called Hermite interpolation.

Consider the slightly simpler problem of fitting a polynomial through only three points (x1,y1) (x2,y2) (x3,y3) while also having derivatives y1' y2' y3' at these same points. According to what I have said above, this should require a polynomial with six coefficients, which I will represent as:

y = a + b x + c x2 + d x3 + e x4 + f x5

Note that the derivative of this polynomial is:

y' = b + 2c x + 3d x2 + 4e x3 + 5f x4

Now the requirement that the point (x1,y1) satisfies the first equation above can be written:

y1 = a + b x1 + c x12 + d x13 + e x14 + f x15

And of course, we have two more similar equations for the second and third points.

The requirement that the first point has a derivative of y1' satisfying the second equation above can be written:

y1' = b + 2c x1 + 3d x12 + 4e x13 + 5f x14

And like before the second and third points give us two more similar equations.
So we have six equations in all which can be written in matrix form as follows:

1 | x1 | x12 | x13 | x14 | x15
1 | x2 | x22 | x23 | x24 | x25
1 | x3 | x32 | x33 | x34 | x35
0 | 1 | 2 x1 | 3 x12 | 4 x13 | 5 x14
0 | 1 | 2 x2 | 3 x22 | 4 x23 | 5 x24
0 | 1 | 2 x3 | 3 x32 | 4 x33 | 5 x34
 *
a
b
c
d
e
f
=
y1
y2
y3
y1'
y2'
y3'

Using the notation used in the herm( ) function at the end of hermite.m the blue matrix is called [A; B] where A and B are the top and bottom halves of the matrix. The green matrix is called p and the red matrix is called [y; y'] (again separating the top and bottom halves).

So the matrix equation can be written:

[A; B] * p = [y; y']

and using Matlab's backslash notation, the solution to this equation is:

p = [A; B] \ [y; y']

The hermite application fits a polynomial through six points on the plane, also satisfying six derivatives at those same points. The points we want the polynomial to go through are represented by the tails of the white arrows and the direction of the arrows represent the derivatives (i.e. slopes) at each point. (The length of the arrow has no significance, so if you grab the head of an arrow and make it longer, the interpolated polynomial will not change.)

This is what the application looks like when it is first started. Some on-screen text is included to give you basic instructions on how to use the application. These instructions will disappear when you start to move either end of one of the arrows.

Here I have clicked on the head of the last arrow. At first, the usual plus-sign shaped cursor will appear at the end of the arrow. But then after clicking on the "Modify" button, the cursor will change to the diamond shape shown here, which indicates you can drag that point to a new location. (If the persistent edit mode checkbox below the plot had been checked, then the diamond shape cursor would appear as soon as you clicked on the arrowhead... so no need for the Modify button.)

Now I have moved the arrowhead downward (i.e. a more negative slope). I also made the arrow longer, but the length of the arrow is not significant. Notice how the shape near the end of the curve has changed to ensure that the curve is still tangent to the arrow. In this case, the rest of the curve did not change noticeably, although often a small change to an arrowhead may drastically change the shape of the whole curve.

Next, I clicked on the tail of that same vector. After clicking "Modify" the diamond cursor appears. Then I dragged the diamond cursor slightly upwards. Again the shape of the curve changes so that it now passes through the new arrow tail location.

This application demonstrates several programming techniques, including the use of the Pquiv auxiliary function to draw arrows on a plot as well as the 'MotionEdit' parameter to allow the display to update as the arrow is being moved around. The whole application consists of just 28 lines of code (not counting comment lines or the on-screen comments). The fact that a sophisticated application can be created so concisely is a testament to both the expressive power of Matlab as well as the advanced features built into the plt plotting routine.

Copyright © 2024
Paul Mennen


julia.m version 01Jul23
(A Julia set explorer)

Author: Paul Mennen
Email:  paul@mennen.org

Introduction

Julia set images are traditionally generated by repeated application of the equation z = z2 + c (z and c are complex). The color of the image is determined by the number of iterations it takes for the magnitude of z to grow larger than some fixed value (2.0 for this program). The Mandelbrot set uses the same equation and the same color assignment method but differs in how the equation is initialized.

It's easy to find dozens of Julia set graphing programs in nearly every language (including Matlab), so I wouldn't fault you if you were skeptical of the need for yet another application with this purpose. However, my goal was to show how fun it is to explore Julia sets and to make this application more compelling than any similar application out there. I'll let you be the judge of how well I have met this challenge.

Program initialization

Start this application by typing "julia" at the command prompt. (julia.m calls the other .m files supplied). When julia.m starts, it creates two images which we refer to as the left and right images (or sometimes the "opposite" image to refer to the right image when we are editing the left and vice versa). Initially, both images show the traditional Mandelbrot set (i.e. with the exponent equal to two). The images will look identical (i.e. the same axis limits, the same color map, the same color depth, and the same resolution), however after taking any action, the two images will no longer be the same as in the screen capture shown here. There is nothing special about the left or right image. Any action we can take on the right image can also be taken on the left. However, you will soon see the benefits of having two images to play with. (Click on this screen capture to view it in its full resolution.)

The Julia set exponent

There are two controls related to selecting the Julia set. The first control, labeled "exp", is located near the lower-left corner of each image and determines the exponent (e) used in the recurrence relation z = ze + c which determines the color of each point on the image. Although 2 is the most common exponent used for Julia sets, other integer exponents are also used (the generalized Julia set). The easiest way to change the exponent is to use the edit object's "auto-increment" mode (i.e. click on the right side of the exponent to increase its value by one and click on the left side to decrease its value). The image will immediately update to reflect the new exponent. Non-integer values may also be used, which often produce even wilder images. The easiest way to enter a non-integer value is to right click on the exponent and then type in whatever value you want. Sometimes it is useful to be able to change the auto-increment value from one to some other value. Suppose you want to change this from 1 to 0.1. Simply right-click on the edit object and type in "0.1i". After you press return, the auto-increment value will change to the entered value. (You can think of the "i" at the end as referring to "increment".) This is especially useful for creating animated Julia sets because holding down the mouse button continues to modify the exponent, which often yields eye-popping animations. With the default settings the display update rate will probably be too slow for this purpose, but below you will find methods described to increase the update rate. This method of changing the auto-increment value and continuous incrementing can be used with any edit pseudo object and will also be useful for creating animations by changing the Julia constant (described next).

The Julia set constant

The second control related to selecting the Julia set is a popup menu located near the upper left corner of each image (which initially appears as the yellow label "Mandelbrot"). Assuming the exponent is at its default value (2), this is the popup that will appear when you click on that label. For the first selection of the popup (Mandelbrot) z is initialized to zero and c is chosen to correspond to the position we are determining the color for. For the remaining selections (Julia sets), z is initialized to the value corresponding to the pixel we are determining the color for, and then the recurrence relation is computed using one of the 14 constants shown in this popup menu. These 14 constants were chosen somewhat haphazardly although with the goal of creating interesting images. After one of the constants is chosen, the real and complex components of the constant are placed in edit pseudo objects so they may be modified easily. The default autoincrement amount is set to .01 but this may be modified by entering an increment value as described above. And as before, Julia set animations may be created by holding down the mouse to slide the real or imaginary components of the constant around the Julia set landscape while looking for interesting territory. If you change the Julia set exponent from 2 to any other value, the list of constants in the popup menu shrinks from 14 to 4. (This is to simplify the task of placing the constants in interesting territory.) Notice that the four choices given continue to change as you change the exponent. They change in an attempt to remain in interesting territory and this generally is successful for relatively small (i.e. less than 10) whole-number exponents. This is also at least somewhat successful for small fractional exponents. Instead of opening up the Julia set constant popup with a left click, you will probably find it more convenient to use the right mouse button instead. This will scroll thru the various Julia set constants (updating the image and the edit boxes for the real and imaginary parts of the constant) without having to open up the pseudo popup menu. After the last constant in the menu is selected, the next right click will cycle you back to the Mandelbrot set.

Zooming the image

To appreciate the beauty and complexity of Julia sets you need to zoom into ever smaller regions of the set. The flexible and intuitive zoom features of this application are what makes it fun to use and set it apart from the many dozens of Julia set graphing programs you can find. There are many ways to zoom an image (see the Zooming and panning section), but the easiest method for this application is to double click the mouse while holding the mouse button down after the 2nd click. Then drag the mouse while continuing to hold down the mouse button. As you do this, a zoom box will be drawn - expanding in size as you drag the mouse. Even during this zoom box drawing operation, the opposite image will display the portion of the data inside the zoom box, behaving much like a magnifying glass. The smaller the zoom box, the higher the magnification and the more pixilated the image will appear. However, once you let go of the mouse button, the magnified region is recomputed to the currently specified resolution (1 bit per pixel by default) at which point you will be able to see details in the image that were not visible before. If you find the double click and drag method awkward, you might try clicking both mouse buttons at the same time and holding them down while you drag. (My testing indicates that this works when you are using Matlab version 2014a or any earlier version, but not in more recent versions.) If you don't mind using the keyboard for this, another method requiring less dexterity is to hold the keyboard shift button down while dragging the mouse from the first corner to the opposite corner of the zoom box. (No double clicking required). This method works with all versions of Matlab.

Moving the zoom box

You can change the zoom box using one or more of these methods:
  • Reposition the zoom box (without changing its size) by grabbing the midpoint of any one of the four zoom box edges and dragging it with the mouse. Note that as you drag the image on the opposite side will again behave like a moving magnifying glass, with a pixelated image. The magnified image will be recomputed (thus removing undesired pixelated effects) as soon as you release the mouse button after the repositioning operation.
  • Resize the zoom box by grabbing any one of the four zoom box corners. Again, as mentioned above, the magnified area recomputes as soon as you release the mouse button after the resize operation.
  • If you want any of the zoom box coordinates to be a particular exact value simply type it into one of the edit boxes below the graph (xmin/xmax on the left and ymin/ymax on the right). As soon as you hit "enter" after typing a new value, the position of the corresponding edge of the zoom box will change to reflect the new entry, and the opposite image will update (with full resolution) to show the area associated with the new zoom box region. If you want to enter all four zoom box coordinates, it is easiest to start with the xmin value because after entering the xmin value you can use tab (instead of enter) to enter the new value and proceed to the xmax value (then ymin, then ymax) without having to interrupt your typing to select the next edit box.

Magnification

Usually, after the zoom window is drawn and you see the full detail in the magnified region you see something in the magnified region that you want to see in yet more detail. Use the same method to draw a zoom box again. In this manner, you can bounce back and forth between the left and right images, getting higher and higher magnifications on smaller regions of the Julia set. Notice that below the plot, the magnification is shown compared with the original scaling (i.e. when the Julia set was first plotted). In the example shown here, the magnification is 4.09e7 which means that the area being displayed is 40 million times smaller than the original image. The edit boxes on the left show that the x values shown in the displayed region are between .31787 and .31825 (approximately). More digits are displayed than typical for these edit boxes because of the extreme magnification often used for plotting Julia sets. Similarly, the y axis limits are shown in the two right-hand edit boxes. Right below the plot, the number of times the recurrence relation was computed to generate the image is shown (289 iterations in this example). You will notice that as you zoom in to higher magnifications, more iterations are computed since this is necessary to achieve the same detail level. The magnification readout can also be used as a zoom control. Simply left-click on it to zoom out by a factor of 100 (in area), or zoom out to the original axis limits by right-clicking instead. You can also experiment with panning the image (just click and drag the mouse inside the image) or do that with the right mouse button instead to Zoom in or out (without using a zoom box). Left or right-clicking the Zout tag will zoom out or in by a small amount (doubling or halving the area), although usually, you will find you want the large zoom factors available with the other methods.

The sync check box

The method described above for zooming to higher and higher magnifications (by alternating between the left and right images) is probably the easiest way; however, you may want to zoom into a region on one image without changing the image on the opposite side. For instance, you may be interested in comparing two different regions of a Julia set or comparing the same region of two different Julia sets. In such situations, you should uncheck the sync checkbox (just to the left of the "Magnification: 4.09e7" indicator in the above screen capture). This changes the plot from the "automatic sync" mode to the "manual sync" mode. To prevent automatic synchronization in both directions, you must uncheck the sync checkbox under both image plots.

Once in manual sync mode, after drawing a zoom box you will see no changes to the image right away except for the existence of the zoom box. You can follow the drawing of a zoom box with one or more of these actions:
  • Reposition or resize the zoom box by any of the three methods mentioned above.
  • Left-click anywhere in the plot area (not near the zoom box) to expand the plot so that it shows just the region inside the zoom box. The Julia set will be recomputed to match the requested resolution.
  • Right-click anywhere in the plot area to cancel the zoom operation (and remove the zoom box from the display).
Even in manual sync mode you sometimes may want to synchronize the two displays. This is done by RIGHT-clicking on the sync checkbox (which will not toggle the checkmark like the left click does). Right-clicking on the sync box under the left-hand image when a zoom box is visible in the left-hand image will copy the data inside the zoom box to the right-hand image and then recompute the right image so that it is displayed in the full requested resolution. If you do the same thing when no zoom box is visible, then the entire left-hand image is copied to the right image. You might expect that the right and left images would look exactly the same after such an operation, and in fact this is usually the case. However, there are two display parameters (the depth parameter and the pixels per bit parameter, are both discussed below) that are not transferred during the sync operations. This means that one side may show more or less detail than the other side after a sync operation.

The pixels/bit parameter and the depth parameter

When you are using the edit box auto-increment feature to cycle thru small changes in the Julia set exponent or the Julia set constant a fast update rate is essential to finding interesting regions of the Julia sets. The update rate is probably going to be too slow for this purpose when using the default settings, however, there are three things you can do to speed this up. The first is to make the figure window smaller. You may have already tried expanding the figure to full-screen mode, and indeed this does produce large images with great detail. However, the update rate will be slower since it increases proportionally with the number of pixels. Shrinking the figure will conversely speed up the update rate, although if you shrink the figure too much, the controls will become too small to use easily.

The second way you can speed up the update rate is to reduce the number of times the recursion formula is executed. This is done using the depth parameter which can be changed using the edit box just above the menubox near the lower left corner of each image. When you select a new Julia set (by changing the Julia set exponent or constant) a default depth (usually 50) is chosen which produces a pleasing image for that particular set. Try setting the depth to 5 and notice that the display updates 10 times faster. Then notice as you click the mouse on the different colors of the image the Z value (which appears just above the sync checkbox) will range from zero to five and the image will include no more than six colors. Z=0 indicates that the recurrence relation never exceeded a magnitude of 2, and Z=5 indicates that the recurrence relation exceeded a magnitude of 2 on the 5th iteration. The number of iterations only matches the depth setting when the magnification is equal to one. As you zoom in, the iterations automatically increase. (The number of iterations doubles every time the magnification is increased by a factor of 1000.)

The last (and strongest) influence you can make on the update rate is the pixels/bit setting. The default setting is "one" indicating that the color for each pixel of the image is determined independently. Thus for a 500 by 500 pixel image, the recurrence relation must be repeated 250 thousand times for each iteration. However, if you change the pixels/bit setting (just above the image) to 3 (for example) you reduce the computations by a factor of 9 since each 3x3 block of pixels will be assigned the same color. The resulting blocky image is not as pleasing, but it is still fine for finding the interesting regions where you want to focus your interest. You can use higher settings (lower resolutions) as well, but 3 is probably as high as you will need to go to get acceptable update rates. In fact, a setting of 2 will often be sufficiently fast as well.

The color map selectors

If you click on one of the vertical color bars to the left of each image, a new mapping between the Z values and the image colors will be selected. There are 10 different color maps and after selecting the 10th one, clicking again will select the first one again. Also right-clicking will cycle thru the color maps in the reverse order. Most of the color maps are designed to make it easy for you to see the general shape of the surface contour (i.e. high vs low areas) based on the image color. However two of the color maps (colorcube and lines) are designed to allow you to see as many level transitions as possible, and the general shape of the contour is difficult to discern (and impossible with the lines color map). Clicking on the color bar next to the left image only changes the color map for that image, so this makes it easy to compare two identical Julia sets colored with different maps. However, if you are using a version of Matlab older than version 2014b you will find that changing the left color map automatically changes the right color map (and vice versa). This is because those versions only allow one color map at a time to be associated with any figure window.

Initial help text

When this application first starts, some very basic help text appears in the left image (white characters). Although it can't substitute for the full help information in this document, it may be enough to get you started. As soon as you click in the plot area or make any changes to the fractal parameters, the help text disappears so it doesn't interfere with viewing the fractals.

I hope you enjoy exploring Julia and Mandelbrot sets using this program.
Please let me know if you have any questions or comments about this application.

Copyright © 2024
Paul Mennen


motion.m
(Brownian motion simulation)

The simulation starts with 128 particles at the center of the box with a normal distribution of velocities. (The standard deviation of this distribution corresponds to a particular temperature.)

Typing motion(12) tells the simulator to start the simulation, run it for 12 time-steps ... resulting in a figure such as this one.

12 steps are not enough for any of the particles to reach the sides of the box yet, so all the particles are moving radially outward. A random selection of marker shapes, edge colors, and interior colors are used to simulate different types of molecules. The gray arrows represent the velocities with the arrow lengths being proportional to the particle speed.

The default number of steps is 20, so typing motion (with no arguments) is equivalent to typing motion(20).

With enough steps, the particles will begin hitting the walls at which point they will undergo an elastic collision (i.e. it bounces off with the same energy ... and the angle of reflection equals the angle of incidence).

Collisions between the particles are not simulated, but this simulation would still be accurate for a low-density gas.

Now suppose we click the Walk button. This will start up the simulation from where it left off. Notice that the Walk button has now turned into a STOP button. The simulation will continue to run forever (or until you hit the STOP button).

Also, notice that an "updates per second" report has been added to the title bar. This is useful mostly as a performance measure, so you can compare the performance of different computers, operating systems, or Matlab versions.

While the simulation is still in progress, try changing the speed control and you will see the motion speed up or slow down. This is best thought of as a simulation speed control, but you could also think of it as a temperature adjustment for the simulation. Also, try increasing the # of points slider (or typing a new number into the edit box next to the slider. Suppose for example we increase it from 128 to 228. Then 100 new points will appear to explode from the center of the box and those new particles will quickly become indistinguishable from the points that were already there. These two sliders, as well as their respective labels and edit boxes, were created using the slider pseudo object, which greatly simplifies the coding needed to add these additional components associated with the slider. There are two formats for the pseudo slider (regular and compact) ... and both of the sliders here are of the compact form. For more details about how to use this pseudo object, See the pseudo slider object section

Every time you click the Walk button, the particle velocities are randomized again (always with a Gaussian distribution). To see this clearly, try this experiment. Move the # of points slider all the way to the left (which will select 2 points - the minimum value allowed). Then move the speed slider all the way to the top - the fastest setting. Now click the Walk button and observe the motion pattern. Then click STOP, and then the Walk button again a few times, each time noting the motion pattern changes (dramatically).

Previously I described how to start the simulation with a fixed number of time steps, but it is also possible to start the simulation with an infinite number of steps. To do this type motion(0) or alternatively motion('go') (or the command form motion go). Actually it doesn't matter what character string you use, so, for example, the strings 'run', 'walk', 'start', or 'forever' will work just the same. They all start the program in its dynamic state, and the simulation will continue to run until you click on the STOP button. Note that the last line of the motion function uses funcStart to invoke the walk routine. If we used the simple way of starting the simulation (line 61 -which is commented out, then in the case where the argument is zero (or 'go') the program does not terminate which means we can no longer use the command window until after the STOP button has been pressed. More details about the funcStart auxiliary function can be found here in the Auxiliary functions section.

Typically the lines created by pltinit contain hundreds of points to make up each trace, however, in this example, all 513 lines created by pltinit consist of just a single point (representing a single particle) except for the last trace. The last trace plots 512 arrows, one arrow for each of the 512 particles. Note that the first 512 traces are created by the first two arguments of the pltinit call while the last trace (for the arrows) is created by the last two arguments of the pltinit call. The data for all these traces is set to zero which is just a placeholder, since the real data for all these lines (markers) and arrows are set inside the walk function.

Normally pltinit is limited to creating at most 99 traces because otherwise, the TraceID box would become too large to fit in the figure and still leave a reasonable amount of area for the plot. However, if we disable the TraceID box (which we don't need for this simulation) then there is essentially no limit to the number of traces we can create with the pltinit command. (The TraceID box is disabled by including 'TraceID',[] in the argument list. Note that we could have called plt instead of pltinit for the command that creates the 513 traces. This is because plt recognizes that traces are being created and simply passes the entire parameter list to pltinit. It's a matter of taste (explicit vs. conciseness) which function you want to use to create the traces.

You may wonder why ishandle(S.B) is included as one of the conditions of the while loop. It's there to cause the while loop to terminate when we close the figure window. If we didn't do that, closing the figure window while it was walking would generate a Matlab error message ('Nonexistent handle').

Copyright © 2024
Paul Mennen


Programming examples

plt programming examples

The seven applications in the signal processing folder and the nine applications in the math folder discussed so far are all good demonstrations of the use of the plt plotting routine and many of its features. However, the focus on those applications is more about the signal processing and math topics than as a tool for learning how to use plt. Also, most of them are complex enough that it would be a challenging place to begin.

For that reason, this toolbox includes an additional set of 21 demo programs (located in the demo folder). These demo programs are intended to highlight just a few of the plt plotting features and are kept simple enough that they will be a good introduction to the plotting package.

At first, you probably won't know which of these 21 demo programs will be the most relevant to you. So if you have never used this toolbox before, the best starting point is to run demoplt.m which is also included in the demo folder. You can read more about it in the demoplt section, but really all you need to know is to start it by typing demoplt in the command window, and then clicking on the All Programs button.

The 21 demo programs (not counting demoplt.m) in the demo folder are described below in the order those programs appear in demoplt:

CLICK on the thumbnails below to see the full-size image

plt5.m

 This is a simple script that creates a plot containing 5 traces. Hopefully, you have already been running this script while following through the earlier sections.
  • Note how the five y-vectors are combined to form a single plt argument.
  • Note the use of the 'Xlim' and 'Ylim' arguments to control the initial axis limits.
  • Note the use of 'LabelX' argument to assign a label for the x axis.
  • Note the use of the 'LabelY' argument to add a label for both the left and right-hand axes.
Note that plt will use a right-hand axis since two labels were included in the LabelY parameter. Usually the 'Right' parameter is included to specify which traces are to use the right axis, but in this example, the parameter was omitted, so plt defaults to putting just the last trace on the right-hand axis.

plt50.m

 This script is an expansion of the simple plt5.m example to demonstrate additional features of plt.

Note that two plots appear in this figure. There are two methods that you can use with plt to create figures containing multiple plots. The first is to use the subplot parameter to create multiple plots with a single call to plt. (This is demonstrated by the subplt.m, subplt8.m, subplt16.m, subplt20.m, pub0.m, pub2.m, pltmap.m, and weight.m programming examples). The second method (which is used here as well as in the pub3.m example) is to use a separate call to plt for each plot. The first plot (upper) is created by a call to plt that is quite similar to the one used in the simple plt5.m example. plt creates the figure window as usual and then creates the upper plot inside the new figure. Both a left and right hand axis are used for this plot. We are free to put as many traces as we want on either the left or right hand side, although in this example we put all the traces of this plot on the left hand axis except for the last one (trace Tr40) which is put on the right hand axis (and is also drawn with a thicker trace). The two major differences between this (first) call to plt and the plt call used in plt5.m are:
  1. The number of traces has been expanded from 5 to 40. Without additional action, this would create a TraceID box (legend) containing 40 trace names in a single column. However, this would not work well or look good to cram such a long list into the small space available. To solve that problem the the TIDcolumn parameter has been used to create a TraceID box with two columns. The 'TIDcolumn',20 included in the plt argument list actually specifies the number of items to put in the second column, which in this example means that both columns will contain 20 items.
  2. The 'xy' parameter is included to specify the location of the plot within the figure window. This wasn't needed before (in plt5.m) since the plot was automatically sized to fill the entire figure window. But now we want to create the plot in only a portion of the window to leave room for a second plot to be created. The object ID (-3) in the first row indicates that this position is to be used for both the left and right axis and also that all the cursor object positions should be positioned relative to these axes. (ID 0 also refers to both left & right axes but does not cause the other cursor objects to be repositioned as well). Although plt makes its best guess for the positions of the TraceID and Menu boxes often you will want to reposition them with the xy parameter to make best use of the available space. The 2nd row of the xy matrix repositions the TraceID box. The last row repositions the Yaxis label which otherwise would have been covered up by the TraceID box. Note that you don't need to figure out the numbers in the xy matrix since they will be reported to you as you adjust the positions of the screen objects with the mouse. (See the description of the xy parameter in the Axis properties section of the help file.)
Following the first call to plt which displays the first 40 traces, a second call to plt is used to display the remaining 10 traces in a plot below the first one. As before we use the 'xy' parameter to get the plot to fit in the remaining open space of the figure. As with the first plot, we also include both the left and right hand axis. We were again free to put as many of these traces as desired on either side, but we choose to put only trace 5 on the right hand axis with all the remaining traces on the left axis. The most important difference between the first and second plt calls is that in the second call, we include the 'Fig',gcf in the parameter list. (gcf stands for "get current figure handle"). This tells plt not to open a new figure for the plot as usual, but rather to put the plot in the specified figure. The 'Fig' parameter must be either at the beginning or at the end of the plt parameter list. (All other plt parameters may be placed anywhere in the parameter list). You may notice that the xy parameter for this plot includes an imaginary component in the last element of the axis position. The reason for this is that the sizes of the cursor objects are relative to the plot size. This sometimes makes the cursor objects too small when the plot is a small fraction of the figure size. To fix this problem, one can enter a minimum width or height in the imaginary component of the width and/or height values.

A few other features of the first (upper plot) are worth pointing out:
  • With so many traces, the ability to use the legend (i.e. the TraceID box) to selectively enable or disable individual traces becomes even more compelling. Although the traces and the legend are color-coded, it's difficult to distinguish every trace based on color, so clicking on a legend item is often useful to uniquely identify a trace.
  • The 'Pos' parameter is used to increase the figure area by about 30% from the default of 700x525 pixels to 830x550. This gives room to fit both plots into the figure area without overcrowding.
  • The 'HelpFileR'' parameter is used to specify which help file will appear when you right-click on the Help tag in the menu box. Normally the file specified will contain help for the currently running script. In this case, prin.pdf is just used as an example and in fact, has nothing to do with plt50.
  • The use of the 'closeReq' parameter is shown, although in this case, the function specified merely displays a message. Look at the afilt.m and wfall.m demos to see examples of a somewhat more sophisticated close request function.
  • In situations like this with so many traces on the plot it can be difficult to find the cursor. The line following the first call to plt solves this problem by increasing the cursor size from 8 to 20 as well as by changing the cursor shape from a plus sign to an asterisk.

pltn.m

 Similar to plt5 and plt50, except that this is a function instead of a script. This function takes an argument that specifies how many traces to plot. For instance pltn(1) will plot a single trace, and pltn(99) will plot 99 traces. If you specify more traces than this, the trace IDs are not displayed (since there will not be room for them). pltn with no arguments does the same thing as pltn(99). You can change the number of traces plotted even after pltn is already running by entering a new number in the "# of lines" edit box (under the TraceID box). Try entering "1000" into this edit box just to see that plt can handle such a large number of traces! Going much beyond 1000 traces is a good performance test, since on slower computers you will start to notice a significant lag in pan operations.
  • The TIDcolumn parameter is used to divide the trace IDs into up to three columns if necessary. (Showing 99 trace IDs in one column wouldn't be practical.)
  • TraceIDs are disabled when more than 99 traces are specified. (Otherwise, plt would give an error message.)
  • Uses the 'Ystring' parameter to show a continuous readout of the cursor index
  • Uses the 'Xstring' parameter to show a continuous readout of the date and time corresponding to the cursor X position. Note the edit box form is selected by placing a question mark character at the beginning of the string.
  • A popup menu (pseudo object) is created below the x-axis label which allows you to adjust the line thickness. Notice that you can right-click on the popup to increment the line thickness (which sometimes is more convenient than opening the popup menu).
  • A callback is written for the Xstring edit box that moves the cursor to the index with a corresponding time as close as possible to the entered value. For example, try this:
    1. Click on the top trace (which makes it easy to see the cursor).
    2. Enter dates into the edit box - e.g. "30 dec 2006", or "3-jan-07 9:59", etc.
    3. Verify that the cursor moves to the corresponding point.

vbar.m

 This script demonstrates the use of Pvbar.m and Pebar.m to plot vertical bars and error bars respectively. Some things to note about vbar are:
  • The first Pvbar in the argument list plots two functions on a single trace (green) with the 1st function (phase1) defining the position of the bottom of the vertical bars and the 2nd function (phase2) defining the position of the tops of the bars.
  • The second Pvbar in the list plots 3 functions (called serp, bell1, and bell2). The 3 columns of the first Pvbar argument define the x coordinates for those three functions. The next argument (0) indicates that the bottom of all the vertical bars is at y=0. The last Pvbar argument gives the y coordinate for each of the 3 functions (one function per column).
  • The next trace definition (the data argument pair after the 'linewidth') plots two traces corresponding to the two columns of poly23. The 1st column is a 2nd order polynomial and the 2nd column is 3rd order
  • The next trace definition uses Pebar function to create two error bar traces, the first trace defined by the first column of each of the 3 arguments and the second trace defined by the second column.
  • The 'Linewidth' argument appears in the middle of the plt call to change the width of only the traces defined earlier in the argument list.
  • The 'TraceID' argument is used to assign names for each trace that are appropriate for the data being displayed.
  • The 'xy' argument is used to widen the TraceID box to make room for the longer-than-usual trace ID names
  • The '+FontSize', '+FontWeight', '+FontAngle', '+Xtick', '+Ytick', arguments are used to modify the main axis properties of the same name (without the plus).
  • The Grid pseudo object is used to create an 8x3 table of character data. This table doesn't have anything to do with the plot (and indeed is just filled with random gibberish), but it was included just to demonstrate an unusual way to use this pseudo object.

wrkspace.m

 To demonstrate the workspace plotter, this script creates several vectors in the workspace (including a structure containing two vector fields) and then starts the workspace plotter by calling plt with no arguments. Workspace plotting is described here

movbar.m

 This function plots a series of 40 random bars and displays a horizontal threshold line which you can move by sliding the mouse along the vertical green line. As you move the threshold line, a string below the plot reports the number of bars that exceed the threshold. (This demonstrates the use of the plt xstring parameter.) These two buttons are created:
  • Rand: Sets the bar heights to a new random data set.
  • Walk: Clicking this once starts a random walk process of the bar heights. Clicking a second time halts this process. The Walk button user data holds the walk/halt status (1/0 respectively) demonstrating a simple way to start and stop a moving plot.

Note that you can move the threshold or press the Rand button while it is walking. Also, if you click on one of the vertical purple bars, the horizontal threshold bar will then follow the upper end of that vertical bar.

If movbar is called with an input argument, the value of the argument is ignored, but movbar will start as if the walk button has been hit. The walking is initiated using the funcStart function so that control returns to the command prompt even though movbar continues to update the plot.

trigplt.m

 This example demonstrates:
  • showing the line characteristics in the TraceID using the TraceMK parameter
  • setting the cursor callback with the moveCB parameter
  • setting axis, TraceID box, and MenuBox positions using the xy parameter
  • setting trace characteristics with the Linewidth, Styles, and Markers parameters
  • setting an initial cursor position
  • enabling the multiCursor mode
  • modifying the colors and fonts of the Trace IDs.
  • The use of the slider pseudo object
  • The use of the plt 'HelpText' parameter to display temporary help information at the top of the plot window. This help text disappears when any parameter is changed but can be re-enabled by clicking on the help button or by right-clicking on the help tag in the MenuBox.
  • Shows how to use inf in the 'Pos' parameter to position the figure in the upper right corner of the screen. In this example an extra 48 pixels is allocated to the title bar so that the menu bar and one toolbar can be enabled without pushing the title bar off the top of the screen.
  • The clipboard button captures the figure as a bitmap into the clipboard
  • Using zeros(6) in the plt call to define 6 traces. The slider callback will overwrite these zeros with the actual data to be displayed. Note that nan(6) would also have worked equally as well for this purpose.
  • Using the ^ButtonD parameter to allow the user to change the axis title color using the ColorPick pseudo object. (Simply click on the axis title.)

subplt.m

 The 'SubPlot' argument is used to create 3 axes. plt puts a single trace on each axes except for the main (lower) axis which gets all the remaining traces. In this case, since there are 5 traces defined, the main axis has 3 traces. Note that the traces are assigned to the axes from the bottom up so that the last trace (serp) appears on the uppermost axis.
  • The 'LabelY' argument defines the y-axis labels for all three axes, again from the bottom up. You can also define the y-axis label for the right hand main axis, by tacking it onto the end of the LabelY array (as done here).
  • The 'Right',2 argument is used to specify that the 2nd trace of the main axis should be put on the right hand axis. If this argument was omitted, plt would still have known that a right hand axis was desired (because of the extra y-label in the LabelY array) however, it would have put trace 3 on the right hand axis. (By default, the last trace goes on the right axis).
  • The LineWidth and LineStyle arguments define line characteristics for all 5 traces.
  • The 'TraceMK' parameter enables the trace selection box to show the line characteristics and the 'xy' parameter widens the trace selection box to make room for this.
  • Note that all three plots have their own cursor supporting almost all the cursor features. The exceptions are delta cursors, the xView slider, and the multi-cursor mode. These modes will still be active but they apply only the the main (lower) axis.
  • Only a single x-axis edit box is needed since plt keeps the cursors of all three axes aligned. Also, note that if you zoom or pan any of the 3 plots, the other two plots will adjust their x-axis limits to agree.
  • A brief description of this example is added to the screen using the 'HelpText' parameter. As you will see in the other demo programs, the help text is usually removed when you start using the program, but in this case, the help text is left in place since it doesn't interfere with the plot area or controls. (However you can toggle the help text on or off by right-clicking on the Help tag in the menu box.)

subplt8.m

 This script shows a slight expansion of the ideas found in subplt.m by increasing the number of axes from 3 to 8. The axes are arranged in two columns which allow the use of two different x axes (one for each column).
  • Note that the four axes on the left are synchronized with each other as well as the four on the right, although the left and right halves are independent of each other and have different x axis limits and units.
  • There are 11 traces defined in the plt argument list but only 8 axes are specified. The extra 3 traces go to the main plot (lower left). This means that the first 4 traces are on the main plot and the remaining 7 traces are assigned to the other 7 subplots.
  • Although the black background used in most of the example programs makes it easier to distinguish the trace colors, some people prefer a white background and this script shows how to do that by using the 'ColorDef' parameter to select Matlab's default color scheme. Matlab's default trace color order only includes six colors and this may not be long enough or ordered ideally for a particular graph. The ColorDef parameter may be used to set the trace colors as desired. If (as in this example) the ColorDef parameter is a color specification (3 columns of numbers between zero and one) this color spec is used instead of Matlab's current trace color order default. The first line of this script defines this color order using Matlab's traditional style. The 2nd line defines the same color sequence using an alternate style allowed by plt which you may also use if you find that more convenient than the traditional style. There's a special case (not used here) for the first entry in this color array. If it's [.99 .99 .99] (or 999999 in the alternate style) then the remaining colors are appended to the Matlab default color trace order. This may be convenient if for example you just want to add a few colors to the end of the list instead of merely replacing the whole color trace sequence.
  • One advantage of the white background is that it is easier to publish a screen capture since the colors will not need to be inverted. When you click on this thumbnail, you will see a .svg file that was created from the menu bar's File SaveAs menu. (Enable the menu bar by clicking on "Menu" in the menu box). If this svg file was for publishing, we probably would have wanted to reduce the clutter of the figure by temporarily removing all the cursors and their associated controls and readouts. You do this by right-clicking on the y-axis label of the main plot - the one on the lower left. (I didn't do that for the svg file here so you could see all the screen elements.) After creating the svg file you can re-enable the cursors by right-clicking on the y-axis label again.

subplt16.m

 This short script again is a slight complication from the previous example (supblt8). Not only do we double the number of axes but we take advantage of all the features of the subplot argument by varying the number of plots in each column as well as adjusting the vertical and horizontal spacings.
  • Note that the whole number parts of the subplot argument specifies the plot widths and heights whereas the fractional parts specifies the horizontal and vertical spacing between the plots.
  • So for example the "99.04" near the end of the subplot argument (for the rightmost plot) means that this plot should occupy 99% of the available height. The fractional part means that the space below the graph should be increased by 4 percent of the height of the available plotting area.
  • Also remember that the negative numbers in the subplot argument are used to break up the plots into columns. So for example, the "-25.96" value tells plt that the first column should contain four plots (because it follows four positive numbers). The whole number part (25) means that the first column should use up 25% of the available plotting width. The fractional part (.96) means that we want to reduce the default spacing to the left of this column by 4% of the plotting width. (The default spacing results in a comfortable easy-on-the-eyes layout, but sometimes we want a tighter layout so we can have bigger plots.) For a more complete description of the subplot argument, refer to the Axis properties section as well as the GUI building with plt section of the help file.
  • As in the previous example, the cursors for the various plots in each column are linked to each other but are not linked in any way to the cursors of the other columns. So for example if you move the cursor in the "tribell" plot (top of column 2) all the cursors of the four plots below it will also move so that they all point to the same x position. If you pan or zoom the x-axis of the tribell plot, the x-axis of the four plots below it will also be zoomed or panned so that the x limits remain the same for the entire column. This is what we call the subplot "linked" mode. The unlinked (or "independent") mode is demonstrated in the next example program (subplt20).

subplt20.m

 The default subplot "linked" mode (demonstrated by the previous 3 subplot examples) makes sense when the columns share a common x-axis. However, in this example, the plots do not share a common axis, so the "independent" subplot mode is more appropriate. We tell plt to use the independent mode by putting an "i" after the first number of the subplot argument (Note the "32i" in the subplot argument of this example).
  • The only thing now shared between the columns is space for displaying the cursor values. For example, the x and y edit boxes below the first column display the cursor values for the plot that you last clicked on in that column. The color of these edit boxes changes to match the color of the trace that you clicked on so you can tell at a glance which plot the cursor values refer to.
  • One advantage of the independent mode is that we can fit more plots into a given space. We could probably display these 20 plots using the linked mode as well, but the figure window would have to be very large since in the linked mode a separate y-axis cursor edit box is included for every axis.
  • As with the previous subplot examples, there are more traces than axes (21 traces and 20 axes). That means the first plot (lower left) gets 2 traces and a traceID box is added to allow you to select which one to display (or both).
  • In this example all 21 traces contain the same number of points (301). However, this was just done for the convenience of the code generating the fake data to display. Each of the 21 traces could include a different number of points and the script would work equally as well.
  • As you experiment with these plots, be aware of the concept of the "current cursor" (or "current plot" if you prefer) which is important since there are 16 different cursors visible. The current cursor is the cursor belonging to the last plot that you clicked on. When you click on one of the five menu box tags (LinX, LinY, Mark, Zout, XY<->) the appropriate menu box operation will only be applied to the current cursor. Likewise for the up/down arrow buttons (peak/valley finder) as well as the "circle" button which toggles whether markers are positioned over the trace data values. The only exception is the Delta button (delta cursor). This always operates on the main plot (lower left) regardless of which cursor is current.

picplot.m

 This is the simplest example that demonstrates the use of the image pseudo object. A jpeg image (from the plt documentation section) is read in, converted to indexed color, and then plotted using plt and the image pseudo object. Running this program gives you a good opportunity to experiment with the cursoring, zooming, panning, and data editing techniques with image data.

The input argument specifies the picture to display. For example: picplot C:\users\paul\Pictures\myFavorite.jpg

If no input argument is given, the file glider.jpg is read from the plt\ini folder. The selected image file is displayed in color using the colorcube image map (see the thumbnail to the left).

If you want to see the image in black & white (i.e. greyscale), check the gray checkbox below the plot. This will give you a greyscale image shown in the thumbnail to the right. (Remember you have to click on this thumbnail to see the complete image.)

pltmap.m

 The purpose of this function is to demonstrate the use of the image pseudo object as well as to demonstrate 2D interpolation and 2D convolution.

The subplot parameter is used to partition the figure into two parts. The left part displays a conventional 2D plot which includes the following five traces.
  1. A circle (red trace) whose radius is controlled by its amplitude slider (the leftmost slider above the plot in the "Y amplitudes" section).
  2. A hyperbola (orange trace). Its amplitude slider controls the asymptote slope.
  3. A polygon (yellow trace). Its amplitude slider controls both the size of the figure and the number of sides (which range from 3 to 7).
  4. A line (green trace). Its amplitude slider controls both the line's position and its orientation. The line is horizontal for slider values less than 5 and vertical otherwise.
  5. A line (blue trace). Its position and orientation are controlled by its slider in the same manner as trace 4.
To better understand how the Y amplitude sliders affect the shapes, sizes, and positions of these 5 elements of the left hand plot, first stop the display from moving by clicking on the stop button. Then make adjustments to the five Y amplitude sliders while observing the changes in the left plot.

One method used to create the image on the right axis is to use 2D convolution.

This process is controlled by 3 controls to the left of the image (the kernel popup, the kernel size slider, and the kernel scale slider). This is what the kernel popup looks like once the popup is opened. First, we start with a completely black image by setting the z matrix to zero and choosing a modified version of the jet colormap which has had the first color element changed to [0 0 0]. Then we essentially make a copy of what's on the left axis onto the right axis image by changing one pixel of the image for each of the 500 markers on the left axis (100 markers for each of the 5 traces). The z values for these 500 points of the image are chosen using the appropriate index into the jet colormap so that their colors match the colors that appear on the left axis. (Direct scaling is used since otherwise the colors could not be made to match.) You can see what the image looks like after this first step by moving the kernel scaling slider down to its smallest setting.

Next, the energy of these 500 points is spread out to affect the neighboring pixels (although the original 500 points are not changed). This is done by using one of these convolution kernels:
uniform
1 | 1 | 1
1 | 1 | 1
1 | 1 | 1
The uniform kernel is a commonly used "blur" effect where every kernel element is a one. So for example, this would be the kernel when the kernel size slider is set to 3.
conv11
1 | 4 | 6 | 4 | 1
4 | 16 | 24 | 16 | 4
6 | 24 | 36 | 24 | 6
4 | 16 | 24 | 16 | 4
1 | 4 | 6 | 4 | 1
For a 1D smoother we can convolve the data with [1 1], sometimes called a [1 1] filter which can be represented as H(z) = 1 + z-1 (i.e. a single zero at z = -1). For more smoothing we could put two zeros at z = -1 which is the [1 2 1] filter (i.e. [1 1] convolved with [1 1]). Yet another zero at the same place gives us the [1 3 3 1] filter, and one more to yield [1 4 6 4 1] (the 4th row of Pascal's triangle if we start counting at 0).

The moral equivalent of this for 2D filtering would be to start with [1] and then convolve with ones(2,2) five times (assuming the size slider is set to 5). This would yield the convolution kernel shown here. Notice that the 4th row of Pascal's triangle appears along each of the four edges.
pascal
1 | 1 | 1 | 1 | 1
1 | 4 | 4 | 4 | 1
1 | 4 | 6 | 4 | 1
1 | 4 | 4 | 4 | 1
1 | 1 | 1 | 1 | 1
Another way to extend the 1D [1 4 6 4 1] filter to 2 dimensions would be to place that row in both the center column and center row as well as along both diagonals.
reverse
6 | 6 | 6 | 6 | 6
6 | 4 | 4 | 4 | 6
6 | 4 | 1 | 4 | 6
6 | 4 | 4 | 4 | 6
6 | 6 | 6 | 6 | 6
This kernel is essentially the reverse of the previous one, with the largest values on the outside instead of the inside.
edge
1 | 1 | 1 | 1 | 1
1 | 0 | 0 | 0 | 1
1 | 0 | 0 | 0 | 1
1 | 0 | 0 | 0 | 1
1 | 1 | 1 | 1 | 1
This kernel has ones along all four edges and zeros elsewhere. This kernel and the previous one (reverse) have little to no practical applications and are included here just for the cool visual effects.
All the kernels described above are scaled by an amount related to the scaling slider (the slider right below the slider that controls the kernel size). If you move the scaling slider to the bottom (minimum value) the effect of the convolution will be barely noticeable (or unnoticeable). As you move the scaling slider up the effects of the convolution become more noticeable and dramatic.
nearest | Even though this selection (as well as the following one) is in the kernel popup menu, it does not represent a convolution kernel. Instead, it refers to another method of creating the image known as nearest neighbor interpolation. The kernel size has no meaning here, but we use the kernel size slider to control one aspect of this interpolation. Let's start with the simpler case by setting the kernel size slider to its minimum value (1). The first step is similar to the first step for the convolution methods, i.e. 500 points are set that align with the same colors used for the 500 markers in the left hand plot. Again direct scaling is used for the image to allow these colors to match up. Next, each of the remaining points of the image is set to the color of the point that is nearest. So in the end, every point in the image is set to one of the 5 colors in use. This type of interpolation produces a continuous but steppy 3D surface (clearly not differentiable). Matlab's built-in griddata function can be used to perform this interpolation but to better expose the method, this interpolation is performed by the nearest function included inside pltmap.m. (This function consists of just a few lines of code.)
cubic | While the nearest neighbor interpolation results in an image with sharp color changes, the cubic interpolation produces a smooth (differentiable) 3D surface, so the colro changes are gradual. Start again with the kernel size slider set to 1 so the colors of the image match up with the colors of the markers of the left plot, again making it easier to see how the two plots are related. Next change the kernel size slider to any number bigger than 1. This causes several changes in the way the image is created. For one, instead of using fixed z values at the marker positions that relate to the color of the markers, the z value is taken from one of the five z value sliders (one slider for each of the 5 traces). Since these z values sliders move up and down randomly after the run button is clicked, a truly mesmerizing image is created with patterns that seem to never repeat. (Since all the Y and Z axis sliders are controlled by pseudo random sequences, the pattern will of course repeat eventually, but certainly not in your lifetime.) Secondly, the image scaling is changed from direct to scaled and also the z axis limits (clim) are adjusted to show only the 68% of the data in the middle of the range (i.e. μ ± &sigma). This results in a more vibrant display. With all these changes it is difficult to see how the image relates to the marker positions of the left plot, but you may be so hypnotized that you don't notice this. cubic is the default setting for this popup and so this will likely be the only mode seen by the casual user of the pltmap program.

Try exploring the features of pltmap by running through these tasks, both while the display is running and while it is stopped:
  • Disable and enable the various traces by clicking on the trace names in the TraceID box. Note that the intensity map shape is determined only from the enabled traces. There is one exception to that rule that occurs when just a single line (trace 4 or 5) is enabled. In that situation, griddata won't have enough data to interpolate the image, so the data for trace 1 will also be used even if it has been disabled on the left hand plot.
  • Select the nearest neighbor interpolation and enable the use of the Z amplitude sfliders by selecting a kernel size bigger than one. Then adjust the "Z amplitudes" for each of the 5 traces using the sliders and notice how these sliders change the image. (Only the right plot is affected.) You can also do this with the cubic interpolation.
  • Click on the "color bar", the vertical color key strip near the upper left corner of the intensity map. Note that this cycles the color map through 11 choices (The 10 default color maps and one additional one called jetB added by pltmap. Often which color map to use is simply a visual preference, but for this program, the jetB color map is best so that the colors can match up between the left and right plots (at least when the Z amplitude sliders are not being used.)
  • Note that the intensity map appears somewhat pixelated. This is because it is composed of a relatively small number of pixels (200x200). Try zooming in on an interesting-looking region of the intensity map using a zoom box. Hold the shift key down and drag the mouse to create the zoom box. Then click inside the zoom box to expand the display. Even though you still have only 200x200 pixels, the display will look smoother because all these pixels are focused on a smaller region of the more quickly changing z data. You can also zoom in by right-clicking on the "view all" button in the lower right corner. Then left-click on the view all button to expand the limits back to their original values to show the entire z data set.
  • Also try opening a zoom box in the 2D plot (left). You can do this as before (shift key and mouse drag) or try the "double click and drag" mouse technique which avoids having to use the keyboard. You may be surprised to see that the intensity map zooms to show the region inside the zoom box of the 2D plot even as you are dragging the edge of the zoom box. If you then click inside the zoom box the 2D plot will also expand to show just the region inside the zoom box ... but let's not do this just yet. First, try moving the zoom box around. Do this by clicking the mouse near the mid-point of any edge, and dragging the zoom box around while holding the mouse button down. Also, note that if you drag one of the corners instead of a midpoint then the zoom box changes its size instead of its position. In both cases, the intensity map continues to update so that it shows only the zoom box region. These mouse motions are further described in the heading Adjusting the expansion box in the Zooming and panning section.
  • Try sliding the resolution slider (just to the left of the color bar) all the way to the top of the slider. This will select a resolution of 800x800 (16 times as many pixels as before) so the display will look much smoother, but the drawback is that the update rate will be much slower. Try moving the slider all the way to the bottom (50x50 pixels). Now the intensity map will look very blocky, and the update rate will be very fast. Note that when you click inside the intensity map, the cursor will center itself on one of the blocks even if you click near the edge of one of the blocks. This makes it easier to interpret the Z value cursor readout (shown below the intensity map. Also, note the x and y cursor readouts are updated as you would expect every time you click on the image. Reset the resolution slider to 200 before continuing. The resolution slider works differently when 2D convolution is being used (i.e. when one of the first 5 choices in the kernel popup menu is selected.) In that case, the value from the slider is divided by 3 to determine the resolution. So for example, if the slider is set to 800, the resolution of the image is actually 267 by 267. This is done because high resolutions make it difficult to see what is happening with the 2D convolutions.
  • Both plots are set in motion by clicking on the "Run" button. What happens is that a random selection of the 10 sliders above the 2D plot is selected to start moving. (The remaining sliders that are held fixed are made invisible so you can easily see what is changing). As the sliders are moving up and down, both the 2D and 3D plots are continuously updated to reflect the new information in the sliders. As this is happening you will see the small (blue) frame counter below the Run/Stop button counting down from 100 to zero. When zero is reached, a new random selection is made from the set of 10 sliders and the frame counter begins down counting anew from 100. While all this is happening, you may change the speed slider to adjust the motion rates and you also may adjust any of the other controls as well and observe the effects of these changes immediately. If you find that 100 frame count is too long or short for your taste, simply click on the yellow "100" and you will be presented with a popup menu allowing you to vary this frame count from as small as five to as large as 1000.
  • Note that if you call pltmap with any argument, pltmap will start as if the run button has already been pressed. The funcStart function is used to initiate the run which allows pltmap to return control to the command prompt even while continuing to update the moving intensity maps.

gui1.m

 Usually plt is used to build gui applications which include plotting, however this example doesn't include plots so that it remains trivial, making it a good example to start with if you have no previous exposure to Matlab GUI programming. The only pseudo object used in gui1 is the pseudo slider which is a collection of 5 uicontrols designed to work together to control a single parameter. The remaining controls used in gui1 are standard Matlab uicontrols.

This GUI doesn't perform any useful function other than to demonstrate how to create various controls and move them around until the GUI appears as desired. The slider callback generates new random numbers for the listbox and textbox. The remaining callbacks are just stubs that notify you that you clicked on the object.

You can most easily absorb the point of this example (as well as afilt.m) by reading this section of the help file: GUI building with plt.

wfalltst.m

 This program demonstrates the use of pltwater, a general purpose 3D plotting utility which is described here in the Auxiliary plt functions section.

A surface consisting of a sequence of sync functions is created in a 800 x 200 array (z) which is then passed to pltwater.

We could have called pltwater with just a single argument (z) containing the data, but in this example we have included many additional parameters to tailor the display, including:
  • nT
  • skip
  • x
  • y
all of which are described in the pltwater section of the help file as well as in the comments in pltwater.m. The remaining parameters included in the pltwater command in this example are not unique to pltwater, so they are passed directly to plt and are described in the main plt programming section of the help file. Those parameters include:
  • HelpText
  • TraceC
  • CursorC
  • Title
  • ^Fontsize
  • LabelY
  • xy

wfall.m

 This example has been largely superseded by the following example (wfalltst.m) which uses the general purpose pltwater 3D plotting routine. That's a far easier way to create a waterfall plot, although this example doesn't do that since it was written before pltwater was created. However, this example is still included since it may still be a good starting point if you want to develop a special purpose waterfall display that can't be created using pltwater.
  • Demonstrates how to do hidden line removal which makes a waterfall plot much easier to interpret.
  • Type wfall or wfall(0) to start wfall in its stopped state. (i.e. the display is not updating)
  • Type wfall(1) or wfall('run') to start wfall with the display dynamically updating. The funcStart function is used to initiate the dynamic display updates which allows wfall to return to the command prompt while continuing to update the waterfall plot.
  • One trace color (green) is used for all 30 traces ('TraceC' parameter)
  • The 'TraceID' parameter is set to empty to disable the TraceID box.
  • The figure user data is used to pass the handle structure (S) to the callback.
  • Extensive use of the slider pseudo object to control the plotted data.
  • The 'Linesmoothing' option is selected (which surprisingly speeds up the display dramatically on many systems)
  • A pseudo popup in "super-button" mode is used to start and stop the display.
  • The number of display updates per second is calculated every second with the results are shown in a large font below the plot.

pub0.m

 All the plt examples discussed so far (above) use plotting formats appropriate for data exploration which is the main design goal for plt. However, plt can also use formats appropriate for creating plots for publication. This script demonstrates this by creating three different figures windows. Note that all three windows are created by calling pltpub() which calls plt with several parameters optimized for creating publishable plots. (pltpub.m is located in the main plt folder.) pub0 calls pltpub with the optional 'Cpick' parameter which disables all mouse-driven panning and zooming and also allows you to change the colors of most of the plot objects simply by clicking on them. Experiment by clicking on the various axes, labels, and lines in the 3 windows created by this program. If it is not obvious how to pick new colors from the ColorPick figure window, read about it in the Pseudo objects section.
  • The first window (plot 1 - appearing near the top of the screen) is a bar chart that demonstrates how to embed the plot data inside the script as comments. It also demonstrates the use of the prin function to display a table of random numbers in a text box. The vertical position of the plot depends on the screen size.
  • The second window (plot 2 - lower left portion of the screen) demonstrates how to distribute 15 functions among 5 subplots by using the 'SubTrace' parameter and how to set the trace colors and line styles.
  • The third window (plot 3 - lower right) contains two traces with error bars, shows how to use the TraceID box as a legend. The special character ']' is used in the first TraceID to disable the shading of the trace name that is normally used to indicate the trace is on the right hand axis. Also the '+XtickLabel' parameter is used in the plt call to remove the tick labels on the x axis. Then an array of text objects are used to create specially formatted tick labels. The third window also demonstrates various ways of modifying the grid lines, and also shows the use of these prefixes:
    Prefix Object to modify
    + the left axis
    - the right axis
    < the left y-label
    > the right y-label
    . the x-label
    " TraceID text

The three thumbnails to the left were created by shrinking screen captures of the three figure windows created by pub0.m. When you click on any of those thumbnails you will see a .svg image file that was created from the figure's file menu (Save As).

pub1.m

 Shows the use of the pltpub command to produce publication-quality output and demonstrates some unusual graphics techniques such as multicolored lines and rotating text.
The plot on the left side of the figure draws the solution to the following problem ...

Divide a circle into n congruent parts so that at least one part does not touch the center.
(Hint: as far as I know, the only solution uses n = 12)
The plot on the right side of the figure draws several parametric curves including two Archimedean spirals (in red and blue) and the text is placed along a third Archimedian curve with the font size growing smaller as the text curve spirals inward. The small green curve in the middle of the spirals is known as a hypotrochoid.

This thumbnail was created by shrinking a screen captures of the pub1.m figure window. When you click on this thumbnail you will see a .svg image file that was created from the figure's file menu (Save As).

pub2.m

 In this example, a plt figure is created in its usual data exploration mode showing 6 traces of randomly generated data. Each trace contains over 50 thousand data points, although the display is zoomed to show only a small portion of the data. The 'xView' option is used to enable the xView slider which is particularly useful in situations like this where you are viewing only a small portion of a long data record. (The xView slider appears above the primary plot.) The idea is to use the xView slider or other cursor controls to pan and/or zoom the display to some area of interest and then press the "pub" button to generate a figure containing the selected data and optimized for publication.

What makes this more interesting is that when you pan to a new section of the data and again press the "pub" button, the publication figure is redrawn using subplots to show both selected portions. In a like manner, successive presses of the pub button further subdivide the plotting area with each new data range appearing above the previous ones. To reset the pub figure so that only a single axis is plotted simply right-click on the pub button.

The x axis of the data exploration window is plotted in units of days past a time reference (1-Jan-2013 in this example), but custom date ticks are used on the x axis of the publication plot. To reduce clutter, only the day and month are shown for all vertical grid lines except the last one (which includes day, month, & year).

The TraceID box is typically placed to the left of the plot, although for the publication figure in this demo the TraceID box is placed right on top of the plot (more like a legend). This means that sometimes the TraceID box will obscure some of the data, but note that you can easily use the mouse to drag the legend around to a spot that does not interfere with the plot.

This example may seem somewhat contrived - and indeed it was conceived mostly to demonstrate as many unusual plt parameters and programming techniques as possible.

pub3.m

 In the previous three demos (pub0, pub1, pub2) we have seen how multiple plots can be created with a single call to plt (either in a single axis as in pub1, or in multiple axes by use of the subplot parameter). This demo also creates multiple plots in a single figure but it uses a separate call to plt for each plot. (This is allowed by using the 'Fig' parameter.) Some of the advantages of this method over using subplots are:
  • Each plot may include a traceID box as well as a right hand axis.
  • The position of the plots is completely general and doesn't demand fixed column widths as with subplots. (Note that the positions of the four plots in this example would have been difficult to create using subplots.)
  • The cursor for each plot has the full generality and all the options of single plot graphs, unlike the restricted set of cursoring features provided by subplots. This advantage is not relevant for this particular example since cursors have been disabled by using the 'Nocursor' option. (Cursors were not needed since the goal of pub3 was to create an uncluttered publication quality result.)
On the other hand, as the number of plots required on the figure increases, the restrictions of the subplots become an advantage because they allow a more compact plot spacing. You may also find the subplot method somewhat simpler to use, but these examples will allow you to compare these trade-offs.

The traceID box is enabled for each plot in this example, primarily as a legend, but it can also be used to enable or disable any trace on the figure.

Note that the 2nd trace of each plot (with traceID "samp") actually consists of 12 superimposed traces. (This is done by separating each of the 12 traces with a NaN element so that a line is not drawn from the end of each trace to the beginning of the next.) This could have been done by using a separate trace for each of the twelve "samp" traces, each with their own traceID, but that would have made the legend unnecessarily large and cumbersome. The blue trace is the average of the 12 superimposed traces and the red trace (markers only) is the standard deviation of those same 12 traces.

The xy parameter contains the positions and sizes of each of the four plots. Note that a -3 is inserted in front of each of these positions. The -3 indicates that this position refers to both the left and right axes and also indicates that the traceID box (and the cursor controls if they were enabled) are to be positioned relative to the positions given for the left and right axes. This is described in the description of the xy parameter in the Axis properties section of the help file.

xChart.m

 Data was collected in the t array measuring the performance of 13 different computational and graphics tasks, on an AMD Ryzen9 Windows system with 4 different Matlab versions. All these tasks are from the plt\demo folder except cbench (code specified in help text). Because plt runs in all versions of Matlab since 2001, these demo programs offer a good way to compare graphics performance between Matlab versions. To achieve more consistent results, Windows was rebooted before each version of Matlab was tested.

The rows of the t array that represent time are inverted so that longer bars always represent better performance. Then each row of the "t" array is normalized so that the poorest performing version is given a value of one.

I wouldn't normally overlay so much text over the plot, but in this instance I wanted the data to be self-explanatory. If you don't like the colors chosen for any of the text items, click on them and adjust the color with the ColorPick window. (This is possible because pltpub was called with the 'Cpick' argument.)


demoplt

There are 37 demo and application programs included in this toolbox distributed between the demo, sig, and math folders. All of these programs can help you learn how to take advantage of most of plt's features. Also included in the demo folder is demoplt.m, a program that makes it easy to start any of these 37 programs by clicking on the button with the name of the program. But more often, demoplt is used as your first introduction to this toolbox in which case you will want to run all of these programs in sequence by clicking the "All Programs" button.

This is the figure you will see after typing demoplt in the command window.
• | The top three rows of buttons (green) are the programs in the demo folder and are designed primarily to demonstrate particular features of the plt plotting routine. The first program in this list plt5 is the simplest example and demonstrates plt's most basic features.
• | The next row of buttons (yellow) are the signal processing applications that are located in the sig folder.
• | The remaining buttons (purple) start the programs in the math folder which are designed to illustrate a mathematical or engineering oriented topic.

I recommend running through the All Demos sequence at least once after installing this toolbox. This will verify that the toolbox was installed properly. I know most users don't do this because I get many questions of the form "can plt do xxxxx?" but they would have easily seen that the answer was "yes" if they had seen the one of figure windows that appears during the All Demos sequence.

As each demo is run, you may peruse the code for the demo program currently being run by using the scrollbar on the right side of the list box. Note that the number of lines of code (not counting the comment lines) appears in the lower right corner of the figure to give you an idea of the complexity of each example. If the text is too big or small for your taste, adjust the font size using the fontsize popup menu in the lower right corner of the demoplt figure. This font size is saved (in init\demoplt.mat) along with the current figure colors and screen location so that the figure will look the same the next time demoplt is started.

Clicking on the small help button to the right of the All Programs button brings up the plt help file. On Windows-based systems this is done using the windows 'hh.exe' utility to read the plt.chm help file. For other operating systems, the default browser is started to view the plt documentation.

When you click on the "All Programs" button, the first demo program appears (plt5.m) and the All Programs button changes to a continue button. In addition, this figure appears above the plt5 window to remind you that you need to keep pressing the continue button to see the remaining demo programs.
If you are using demoplt as a test suite to verify that plt is installed properly, you can do this without having to manually click the mouse to start each demo as described above. Simply RIGHT click on the All Programs button. This will cycle quickly thru all the demo programs with no human interaction required and you won't see the small window above reminding you to press the continue button. There are no pauses between each demo program and so this is also useful for performance testing since the elapsed time required to run all the demos is displayed in the command window. A similar way of doing this without even having to press the All Programs button is to type this at the command prompt:

demoplt go

Or of course, you could use the functional form demoplt('go'). This will cycle automatically thru all 37 programs just as if you had right clicked on the All Programs button, except that the demoplt figure window is closed after the last demo program has been executed.

One additional way of starting demoplt is by typing:

demoplt Go

The result is similar to what happens with the lowercase "go" except that a one-second pause is added after each demo is executed. This gives you a little more time so you can verify that each figure was rendered properly before moving on to the next demo in the sequence. The elapsed time shown after completion will be about 37 seconds longer than with the lowercase "go" because of the one-second pause after each program is started.

In addition to its main role as a demo program launcher, demoplt demonstrates the use the ColorPick pseudo object. (A pseudo object is a collection of more primitive Matlab objects, assembled to perform a common objective.) The ColorPick pseudo object is useful whenever you want to allow the user to have control over the color of one of the graphic elements. In demoplt there are 4 such elements: The text color, the text background color, the button color, and the figure background color. The ColorPick window is activated when you click on any of the three small color squares (frames) or if you right-click on the figure background edit box. When the ColorPick window appears you can use the sliders or the color patches to change the color of the respective graphic element. For more details, see the Pseudo objects section of the help file.

An optional feature of the ColorPick object is the color change callback function - a function that's called whenever a new color is selected. This feature is demonstrated here by reporting all color changes at the top of the listbox (i.e. before the example code listing).

If you have changed the figure size or position, the fontsize popup, or any color selection, then when you close the demoplt figure window you will see a dialog box consisting of these three buttons:
  • Save setup changes (will create a "demoplt.mat" file in the plt\ini folder)
  • Exit without saving
  • Reset to default settings (will delete the demoplt.mat if it exists)
Note that when demoplt creates the figure window, it assigns the 'CloseRequestFcn' property to the command 'demoplt close' which creates the dialog box discussed above.

See the pltSig.exe section for information about running the compiled version of demoplt.


pltSig.exe

The pltSig toolbox includes many useful applications in signal processing and other engineering and mathematical topics and some people would like to run this applications even though they don't have Matlab available to them. For these users, I have created a Windows application called pltSig.exe which can be run without having a Matlab license. pltSig.exe is not included with the Matlab toolbox, but you can download it from my website here: pltSigEXE.zip (24 Mbytes).

Unzip the archive to a folder of your choosing. Then you will see that the folder contains these two .exe files:

pltSig.exe | Double clicking on this file opens the pltSig window shown below, which allows you to run all the toolbox applications. (This window behaves quite similarly to the demoplt.m program included in the pltSig Matlab toolbox.) You will probably find it convenient to create an icon on your desktop for this application which you can do by right clicking and dragging the exe file from the file manager to the desktop and selecting "Create shortcuts here". I usually remove the " - Shortcut" and the end of the name by right clicking and selecting rename. This gives you the icon shown on the right.
erip.exe | This is the filter design application. Create the icon shown on the right using the same method described above for the pltSig.exe icon. Many users download pltSig.exe primarily to run this filter design application, which explains why I have included this exe file. However you don't really need this exe file nor its icon becuase you can also start the filter design application by clicking on the yellow erip button in the pltSig window (only slightly slower than using the erip icon.)

There will also be several dozen DLL files in this folder. Some cautious virus checkers may mark some of these DLL or EXE files as suspicious simply because they don't know what they are. However most of the DLL files have been created by the MathWorks (a well known and respected software supplier) and the Mathworks grants the right to distribute these DLLs as part of a compiled application. The presense of these DLL files will not effect any of the applications you may have on your system including Matlab if you also happened to have it installed. Four of the files (pltSig2.exe, plt.dll, pltinit.dll, and cur.dll) where created by me using the Matlab compiler (from Matlab ver 6.1). All the source code used to create these files is available in the pltSig toolbox which you can download for free from the Mathworks file exchange.

This is the figure you will see after clicking on the pltSig.exe icon. You may also see a large black figure window, but it will disappear after a few milliseconds (since that window is usually a distraction). However, if there are any error messages they will appear in that that black figure window. If you want to see these messages, you can prevent that window from disappearing in one of two ways. The first way is to rename the shortcut again to remove the .exe at the end of its name. The second way, is to rename the file dosHide.txt (in the ini folder) to dosShow.txt. (This works the same way for the erip.exe icon.)

You probably downloaded this Windows application because you wanted to run one of the signal processing applications (yellow buttons) or one of the math/engineering oriented programs (purple buttons). But before you click on one of those buttons to start the application of interest, I suggest that you start with the first green button on the upper left (plt5). Exploring this demo will help you understand how the zooming, panning, and cursoring operations work which will help you get the most from any of the applications you can start with pltSig.exe. While you are doing that, it will help to take a quick look at these sections of the help file:
  • Preliminaries
  • Cursoring
  • Selecting traces
  • Zooming & panning
  • Right hand axis
  • Menu box & menu bar

Clicking on the small help button to the right of the All Programs button brings up the plt.chm help file pointing at the section that you are now reading. While running any of the applications started from one of the yellow or purple buttons, clicking on the Help tag in the applications menu box will bring up this same help file, but pointing at the section relevant to that application.

Most of the remaining programs in the green button section are more oriented toward Matlab programmers, however, you also may find some of them interesting.

When you click on the "All Programs" button, the first demo program appears (plt5.m) and the All Programs button changes to a continue button. In addition, this figure appears above the plt5 window to remind you that you need to keep pressing the continue button to see the remaining demo programs. You won't usually want to run thru all the programs this way, although it is a good way to make sure that pltSig.exe was installed properly and all the applications run. You can also do this more quickly by right clicking on the All Programs button instead of left clicking. When you do that, all the programs will be executed in turn without waiting for you to press the continue button. However, all 37 programs will have been started and stopped in 10 or 20 seconds or so ... too fast to actually see what any of the applications are doing.

Although you will likely be satisfied with the figure colors, you can change the figure background color by right clicking on the edit box (that currently shows the color [0 .2 .3], and then selecting the color you want from the color pick pallet. You can also change the color of the top three rows of buttons by clicking on the button color.

If you change anything about the figure's appearance (colors, figure size, or figure position) then when you exit pltSig.exe it will give you the option of saving the current appearance so that it will look the same the next time pltSig is started.


Calling sequence and line styles

This section (not including the two large tables at the end) is a good introduction to how to use plt and the differences between plt and the native Matlab plot.

Usually, you will call plt with at least two arguments:

plt(x,y);

This plots the data in vector x along the horizontal axis and the data in vector y along the vertical axis. x and y may be row or column vectors. plt will transpose one of the arguments if needed to line things up, so x could be a row vector while y was a column vector. x and y must be the same length, however. If not you will get an error message saying that the vectors must be the same length.

If y is a real vector, plt(y) is equivalent to plt(1:length(y),y).

To plot more than one trace, include the x and y vectors for each trace in the argument list. For example, this command plots three traces:

plt(x1,y1,x2,y2,x3,y3)

Quite often several traces share the same x vector. In this case, we can simply repeat the x vector in the argument list, as in:

plt(x,y1,x,y2,x,y3)
or
plt(x,[y1;y2;y3]). (a shorthand way of writing the above).

That would work only if the y1,y2,y3 were row vectors. If they were column vectors you would need to write:

plt(x,[y1 y2 y3])

You can call plt using an output argument, which will return a column vector of trace handles. For example:

h = plt(x,[y1 y2 y3])

will return a 3 by 1 column vector h of handles. h(1) of course would be the line handle associated with the y1 trace. Most often when you type the plt command at the command prompt you don't need to save plt's return value. However, when plt is called from a program sometimes the line handles are needed to allow further manipulations of the plot.

If x and y are both matrices of the same size, plt(x,y) will create one trace per column.

None of this so far should come as a surprise since it is identical to Matlab's plot command.
Some of the ways that plt and plot differ will become clear from what follows.

With plot the data to be plotted must be passed in via the argument list.
However, you may call plt without any arguments, allowing you to choose the data to plot interactively.
Find out about this method here: The Workspace Plotter.

Unlike plot, plt will accept data passed in cell arrays. For example, the following two commands do the same thing:

plt(x,[y1; y2; y3])
plt(x,{y1; y2; y3})

Although in the example above, y1, y2, and y3 must be the same length so there isn't a big advantage for the cell array input. However, now consider these two commands (again, these two lines are equivalent to each other):

plt(x1,y1,x2,y2,x3,y3)
plt({x1 x2 x3},{y1 y2 y3})

With plot, you must use the first form because cell arrays are not allowed. You can't combine the arguments into vectors because they may be different lengths. When typing in the command window the first form is probably easier anyway, but inside a program, the second form is far more convenient, especially when the data is read from files.

If y is a complex vector, plt(y) is equivalent to plt(real(y),imag(y)). Matlab's native plot works that way too. Unlike plot, plt treats complex arguments this way no matter where they appear in the argument list. For instance if a and b are both complex, plt(a,b) is equivalent to plt(real(a),imag(a),real(b),imag(b)). (Why this doesn't work with plot has sometimes been a mystery and an annoyance to me.)

Also like the plot command you can include any line property in the argument list. For example:

plt(x,y,'LineWidth',2) is equivalent to set(plt(x,y),'LineWidth',2)

However, the behaviors of plt and plot differ in that with plot these line properties must appear after all the data vectors in the argument list. (plot gives an error otherwise). With plt the line properties may occur in the middle of the argument list. In that case, the line property is applied only to the lines defined earlier in the argument list. For example:

plt(x,[y1;y2],'Marker','Diamond',x,[y3;y4]);

only sets the Marker property for the first two traces. An equivalent to the above is:

a=plt(x,[y1;y2;y3;y4]); set(a(1:2),'Marker','Diamond');

By using cell arrays, you can set properties differently on each trace. For example:

plt(x,[y1;y2;y3],'LineWidth',{5 4 3},x,[y4;y5]);

This would set the LineWidth of the trace associated with y1,y2,y3 to 5,4,3 respectively while the remaining two traces (y4 & y5) would keep the default linewidth of 1. A column ({5;4;3}) would have worked equally as well. To accomplish the same width assignments using plot you would need the somewhat more cryptic:

set(plot(x,[y1;y2;y3;y4;y5]),{'LineWidth'},{5;4;3;1;1});

Two more examples:

plt(x,[y1;y2;y3;y4],'LineStyle',{'-' ':' '-.' 'none'});

plt(x,[y1;y2;y3],'Marker',{'square','none','+'});

This method of assigning properties works with any line property. In the two particular line properties used above, you could have replaced 'LineStyle' with 'Styles' and 'Marker' with 'Markers' and the results would be the same. Styles and Markers are not really line properties, however, plt allows you to use those alternate forms to allow some additional flexibility in how you write the parameter that follows it. (For example, a character array may be used in place of the cell array.) The details of the additional flexibility provided by using these two alternate parameters are described in the Trace properties section.

The special plot types vertical bars, error bars, and vector fields (arrows) are plotted with the help of auxiliary functions Pvbar, Pebar and Pquiv. The use of these functions is described in the Auxiliary functions section.

plt vs. pltinit

The code for the plotting package portion of this toolbox is broken up into these three routines:

pltinit.m | Contains the code used to create a new plt pseudo object which means creating a new plot axis or set of axes. This also normally means creating a new figure window as well. (The only exception to that is when the 'Fig' parameter is included in the parameter list.)
plt.m | Contains the code used to create or modify any of the remaining pseudo objects (including grid, edit, pop, slider, image, ColorPick, and HelpText) but not including the cursor pseudo object.
cur.m | Contains the code used to create or modify the cursor pseudo object.

From this description, you might expect that since all the command examples shown above create a new plot, that they should really be calling pltinit( ) instead of plt( ). While this is technically true, plt recognizes from the syntax when a new plot is being created and simply passes all of its arguments on to pltinit. The advantage of this is that "plt" is shorter and easier to type which is especially important when used from the command window. To create a new plot from a script or function, it is more a matter of taste which function you use. For a complicated gui, pltinit might be a better choice since it will make it easier to see where the plots are created.

This completes the introduction. What remains in this section and all the remaining help file sections might be too long and detailed to serve as an ideal way to learn about these parameters and commands. Perhaps an easier way to learn how to program with plt is to run thru all the demo programs (conveniently done with demoplt.m) while reading the comments at the top of each example program. The program comments may also be found next to a screen capture of each demo program in the Programming examples section . You will learn about nearly every plt parameter and option this way. Then you can use what follows merely as reference material.

Figure application data:

After a call to plt, the following information is available from the application data of the figure window as well as the application data of other objects:
(The quoted strings are case-sensitive.)

getappdata(gcf,'axis') | Returns a row vector of handles of the axes containing the plotted data. The first handle in the vector is the left-hand main plot. This is followed by the subplot axes (if any) from the bottom up. Finally, the last element of the vector will be the handle of the main plot right hand axis (if any).
findobj(gcf,'tag','click') | Similar to above except only a single axis handle is returned (the main plot left-hand axis)
get(ax,'user') | Returns the cursor ID for the axis with handle ax which may be any axis that appears in the vector returned from the command above.
getappdata(gcf,'cid') | Returns the cursor IDs for each axis, starting with the main (lower) axis and working upwards to include all the subplot axes. (There is not a cursor object associated with the right-hand axis since the main axis cursor also displays data from the right hand axis.)
getappdata(gcf,'Lhandles') | Returns a list of all handles of all data traces created by plt. Note that this is identical to the plt return value.
getappdata(ax,'Lhandles') | Each axis (including the right-hand axis) also has a 'Lhandles' application data value. This contains a list of all lines contained in that axis. The main (lower) plot is an exception since its Lhandles list includes the traces on both the left and right-hand axis.
findobj('name','Abc') | If the plt call includes a parameter such as 'FigName','Abc' then this command will return the handle of the figure window that plt created. This can be useful in programs that create multiple plt figures.
findobj(gcf,'user','TraceID') | Various plt arguments may be used to modify the location, appearance, or contents of the TraceID box. However, occasionally you may want to make further modifications after the plt call and this command will allow you to do that by returning the handle of the axis that is used as the TraceID box. An alternate way of finding this handle is to use the cur(CID,'obj') command. See the Cursor commands section for a description of this command.
findobj(gcf,'tag','MenuBox') | This command will return the handle of the MenuBox. Assuming the menu box is in its default configuration, a command such as: get(findobj(gcf,'tag','MenuBox'),'child') will return a list of text objects with string properties of: 'XY<->', 'Zout', 'Mark', 'Data', 'Grid', 'LinY', 'LinX', and 'Help'.
findobj(gcf,'user','grid') | This command returns a column vector containing the line handles of the plot grids for all the axes in the figure. If there is just a single axis (i.e. one grid object) then this is equivalent to plt('grid',0,'get').
getappdata(gcf,'params') | Returns a cell array list of the parameters specified on the plt command line. (All plt arguments are included except the arguments specifying the data arrays.)
getappdata(ax,'xstr') | Returns the value that was specified in the 'Xstring' parameter when plt was called. ax refers to the primary left-hand axis.
getappdata(ax,'ystr') | Returns the value that was specified in the 'Ystring' parameter when plt was called. ax refers to the primary left-hand axis.
getappdata(gcf,'multi') | Returns a column vector of handles to the objects used to render the Multi-cursor (the text objects, followed by the markers, followed by the dotted vertical line). If the multi-cursor is not currently enabled, then this vector will be empty.
getappdata(0,'Mver') | Returns a numerical value derived from the string returned by the Matlab version command. Before this string is converted to a number it is modified in a way that preserves chronological order (i.e. later versions will return a larger number). For example, in Matlab version '9.11' this root application data will be assigned a numerical value of 9.11 (i.e. the same as the string), however in Matlab version '9.8' this will be assigned a value of 9.08 (different from the numerical value of the Matlab version string). Some of the demo programs use constructs that are slightly different in different Matlab versions, so these programs use the 'Mver' application data so that these demos will run properly in any Matlab version.

Single argument actions:

The command strings here (as with most plt commands) are not case sensitive.
So for example "plt help" and "plt HELP" are equivalent.
plt help | Displays the plt help file. You could also use the functional form of this command: plt('help'). Alternatively, if you just want a one-page list of the plt parameters type type help plt.
plt version | Returns the plt version. Same as: plt('version')
plt save | Opens a dialog box allowing you to select a .plt figure file that can be opened later using the plt open item in the file menu. If you want to avoid the file dialog box add the file name as a 3rd argument (i.e. plt save filename). The use of these plt figure files is described in more detail in the Menu box section. Note that this command is also available from the file menu.
plt open | Opens a dialog box allowing you to select a .plt figure file that was saved using the plt save item in the file menu. If you want to avoid the file dialog box add the file name as a 3rd argument (i.e. plt open filename). Note that this command is also available from the file menu.
plt close | If a programming error causes plt to crash, you may find it difficult to close the plt figure windows (because they use the close request function). This command solves the problem by closing all currently open plt figure windows. Figure windows not created by plt are not closed. (And of course, you may also use the functional form.)
plt show | If the current figure was created by plt, then this command, or the equivalent functional form plt('show'), will return a list of trace numbers that are currently being displayed. For example, if you run the demo program "plt5.m" (which has five traces) and then turn off traces 3 and 4 (by clicking on their trace IDs) then this command will return [1 2 5] showing that those three traces are currently active. You can also use this command with an argument (the functional form only) from a program or the command window to set the traces you want active for the current figure. For example after running plt5.m, the command plt('show',2:5) will turn off the first trace while leaving the remaining four traces on. Note that the TraceIDs will change their appearance to indicate which traces are enabled just as if you had done the same operation by clicking on the trace names in the TraceID box. To disable all traces, use plt('show','') and to enable all traces, use plt('show','all') or plt('show',1:n) where n is the number of traces defined.
plt HelpText
plt HelpText off
plt HelpText on 		The first two forms to the left (off or with no argument) delete the help text and the last form (on) recreates that help text again (which it can do by retrieving the help text information using getappdata(gcf,'helptext')). Help text is usually created by using the 'HelpText' parameter when the plt window is created (this is described in the Labels and figure properties section.) (And of course, you may also use the functional form, i.e. plt('HelpText','on');).
plt move | This command (which has the same effect as right-clicking on the delta cursor button) sets the current plt figure into its repositioning mode. This allows all gui objects to be resized and/or repositioned using the mouse. The new positions are displayed in the command window. Typing plt move a second time cancels the repositioning mode and returns the controls to their prior functions. Details may be found here: GUI building with plt


Trace properties

Right

 You specify which traces should appear on the right-hand axis with the 'Right' parameter. For example, if you included 'Right',[1 4:2:10 17] in the parameter list, then plt would put trace numbers 1,4,6,8,10, and 17 on the right axis and all other traces on the left axis. A slight shading is used behind the Trace IDs associated with the right-hand axis so you can tell at a glance which traces belong to which axis. (You can disable this shading if you prefer. To see how read the description of the TraceID parameter below). You can also tell which axis a trace is on by the shape of its cursor ('+' for left axis and 'o' for the right axis). You can optionally specify a label for the right hand axis (see LabelY) as well as the axis limits (see YlimR). Specifying an empty list, as in 'Right',[] tells plt to use the left axis for all the traces (the same as if you omitted the Right parameter altogether.)

Markers

 The Markers parameter is a shorthand way of setting a different marker property for each line. For example:

plt(x,y,'Markers',s)

is equivalent to:

a = plt(x,y);
for k=1:length(a) set(a(k),'Marker',s(k,:)); end;

The argument may be an array of characters or a cell array of strings. The latter method is easier when the elements are different sizes because you don't have to pad with blanks as with the character array. (Wherever a character array is allowed in a plt argument list, a cell array of strings is also allowed and visa versa.) For example, these two lines have give the same result:

plt(...,'Markers',['square';'+ ';'none ']);
plt(...,'Markers',{'square','+','none'});

This sets the marker for the first two lines to a square and a plus sign respectively while the third line will be rendered without any markers.

The following example shows two ways to set the markers of the six traces to x,+,square,o,asterisk,x (respectively). The shorter method used in the 2nd line is possible because every marker may be represented with a single character:

plt(...,'Markers',['x';'+';'s';'o','*','x']);
plt(...,'Markers','x+so*x');

Styles

 The Styles parameter is a shorthand way of setting the LineStyle property in a similar way that the Markers parameter is used to set the Marker property. For example, to set the first trace to normal, the 2nd and 3rd traces to dotted and dashed respectively, and the 4th trace to none (useful when you want the markers with no lines connecting them) you would use the following command:

plt(...,'Styles',{'-',':','--','none'});

The shorthand for single-character styles mentioned above also works. For instance, to alternate between normal and dotted among eight traces one could use:

plt(...,'Styles','-:-:-:-:');

One additional trick applies to the Styles parameter. If a single character is given which is not a valid line style, then the linestyle property is set to none and the given character is applied to the marker property. As an example, the following command defines eight traces of which the first four are rendered as continuous lines (i.e. without markers) and the last four are rendered with plus sign markers placed at each x,y location specified by the data arrays but with no lines connecting the markers:

plt(...,'Styles','----++++');

Since there are no marker property values that can also be linestyle property values, there is never any ambiguity as to which property should be set.

GridStyle

 This parameter allows you to select the grid line style. For example:

plt(...,'GridStyle',':');

will select a dotted or dashed line (depending on the graphics renderer). If this parameter is not included the default is usually a solid line ('-') although there is one somewhat complicated exception to this which is described in the default section of the GRIDc parameter which you can find here.

TraceID

 This parameter allows you to assign a name to each trace. This name will appear in the trace selection box (also sometimes called the TraceID box). The number of characters that will fit in the trace selection box depends on the size you choose for the plt window. For the default figure size there is room for about 5 uppercase or 6 lowercase characters. In the example below, both forms are equivalent:

plt(...,'TraceID',['Rtemp';'Ltemp';'RV1 ']);
plt(...,'TraceID',{'Rtemp';'Ltemp';'RV1'});

Default: ['Line 1';'Line 2'; ... 'Line n'];

If you want the plot to be created without a TraceID box, call plt with a TraceID parameter of zero or the empty set ([] or ''). Since plt can't create a TraceID box containing more than 99 IDs, if you want to plot more than 99 traces, you must include 'TraceID',0 (or with the equivalent empty set value) in the parameter list.

When specifying trace IDs, you must have one trace ID for every trace on the main and right-hand axes. However, if you don't want a trace ID for a specific trace to appear, just use the null string ('') for the trace name. If you do that, the trace ID box will be made smaller to account for the fewer trace IDs displayed.

Normally trace IDs associated with the right-hand axis will appear in the TraceID box with a slight shading so you can identify those traces at a glance. If you want to disable this shading, insert the special character ']' at the beginning of the first TraceID name. The right bracket will be removed from the trace name before it is used. The third plot of the pub0.m demo program demonstrates the use of this special character as does the main figure of the erip.m program (located in the sig folder).

You may specify a callback function (fcn) to execute when the user clicks on any of the TraceID tags by including the parameter 'TIDcback',fcn in the argument list. If the string '@TID' occurs anywhere inside the function string then it's replaced with the handle of the trace ID string. Likewise if the string '@LINE' occurs anywhere inside fcn, it is replaced with the handle of the trace itself, and occurrences of '@IDX' are replaced with the index of the selected trace. (i.e. 2 for the second trace listed in the TraceID box). See the demo program vBAR.m for an example using the TIDcback parameter. In that example, the name and color of a trace is displayed in the command window when you click on a Trace ID tag. (Not particularly useful, but this example was contrived to demonstrate all the possible substitutions.) To define a quote within a quote in Matlab, one uses two single quote characters in a row. Since this can get confusing at times, recent versions of Matlab allow the use of a double quote character instead of two successive single quotes. Callbacks defined within plt may use this double quote shorthand even with older versions of Matlab that don't usually support it. In addition to a string, fcn may also be a function handle of the form @func or {@func,arg1,arg2,...,argn}. Note that the string substitutions can't be used with the function handle form of this parameter.

You also may change the trace IDs after the plot has been created. For example, if the current figure contains a plot with four traces, these traces can be renamed with a command such as:

plt('rename',{'First' 'Second' '3rd' '4th'});

The rename operation accepts an additional argument (which defaults to 1 if not included) specifying the first trace to rename. For example to rename just traces 2 and 3 you could use the command:

plt('rename',{'Second' '3rd'},2);

You don't need a cell array when renaming just a single trace. For example, this command would rename just the 3rd trace:

plt('rename','NewName',3);

It's possible for a figure to contain more than one TraceID box. In that case, the rename function assumes that the trace names are concatenated in the order the TraceID boxes were defined. (The demo program editz.m is an example of this.) So for example if the first plot had 5 traces and the 2nd plot had 4 traces, then this command would rename the last trace in the first plot and the first two traces of the second plot:

plt('rename',{'NameA5' 'NameB1' 'NameB2'},5);

If there are other changes you want to make to the TraceID box from your program (as in the curves.m example), you can get the handle of the axis that contains all the TraceID objects with the following command:

tbox = findobj(gcf,'user','TraceID');

An alternate method of getting the handle of the TraceID axis is with the following commands:

h = cur(cid,'obj'); tbox = h(14)
(where cid is the cursor ID associated with the plot axis)

The first method is simpler if there is only one TraceID box in the figure window. However, if there is more than one, tbox would contain a vector of handles to all the TraceID boxes, so in that case the second method is easier.

Then, for example, the following command would make the TraceID box invisible:

set([tbox; get(tbox,'child')],'vis','off')

An easier way to make the TraceID box invisible would be to simply move it outside the figure area:

set(tbox,'pos',[-2 0 1 1]).

Or in the unlikely event you wanted to reverse the order of the TraceIDs (i.e. bottom to top ordering in the TraceID box), use the command:

set(tbox,'view',[0 270]).

TraceMK

 This parameter allows you to show the line types in the trace selection box to help identify the traces. This can be visually pleasing and is especially helpful if you are color blind. If the argument is a vector, it specifies the marker positions within the trace selection box. For example 'TraceMK',[.6 .7 .8 .9] would tell plt to place a horizontal line next to each TraceID label beginning and ending at x = .6 and .9 with markers at the four locations specified (assuming the line type in the plot included markers). The area between x = 0 and .6 (i.e. the first 60%) would be used for the text label. If the first element of the vector is less than .25 then plt will not display the text labels since there probably would not be room for them anyway. (Clicking on the lines in the TraceID box have the same effect as clicking on the labels, so the labels can be removed without loss of functionality). If the argument is a scalar, plt will use that value as the first element of a length 3 vector whose last element is .9. Thus 'TraceMK',.6 is shorthand for 'TraceMK',[.6 .75 .9]. A special case is when the scalar argument is zero, in which case no lines are inserted into the trace selection box (as if the TraceMK parameter was not used at all). For examples of using the TraceMK parameter, see the demo programs trigplt.m, subplt.m and pub0.m.

TIDcolumn

All TraceIDs will appear in the trace selection box (aka TraceID box) in a single column except when the TIDcolumn parameter is included. This is useful when you are using so many traces that the TraceID box becomes too crowded to fit all the trace names in a single column. The simplest way to use the TIDcolumn parameter is to supply an empty argument to the parameter (i.e. '' or []). When this is done plt will use just a single column for the TraceID box when the number of traces is 24 or less. Two columns will be used when the number of traces is between 25 and 48, and three columns will be used when there are more than 48 traces. (The TraceID box will not appear when more than 99 traces have been defined). This default will probably work in nearly all situations but if you want exact control over how many columns are used and how many trace IDs appear in each column, you can do that by specifying a non-empty argument to the TIDcolumn parameter as follows: If TIDcolumn is a scalar, it specifies the number of TraceIDs to put in the second column. If it is a vector, it specifies the number of TraceIDs to put in columns 2,3, etc, with the remaining going into column 1. For example, if 30 traces are displayed, and you use 'TIDcolumn',8 then the first 22 TraceIDs appear in the first column and the last 8 appear in the second column. 'TIDcolumn',[5 5 5] would tell plt to arrange the 30 IDs in four columns as follows: (1-15, 16-20, 21-25, 26-30).

DIStrace

 By default, all the traces defined by plt are visible until you change that from the trace selection box. You can change the default by disabling some traces from the plt call. For example:

plt(...,'DIStrace',[1 1 0 0 0]);

This tells plt to start the display with the first two traces disabled and the remaining 3 traces enabled. Of course, you can later enable the first two traces via the trace selection box. If the parameter has fewer elements than the number of traces, it is extended by adding zeros. This means that we could have used [1 1] above to the same effect. After the call to plt has been made, if you want to change which traces are enabled/disabled you can click on the TraceIDs as described in Selecting traces. However, if you want to do that from a program you can use the plt('show',...) command which is described at the very bottom of the Calling sequence and line styles section.

ENAcur

 By default you will be allowed to cursor every visible trace in the plot area. You can change this default using this parameter. For example, if we had five traces, but wanted to use cursors only on traces 1,4, and 5 you would use:

plt(...,'ENAcur',[1 0 0 1 1]);

If the parameter has fewer elements than the number of traces, it is extended by adding ones. This means that we could have used 'ENAcur',[1 0 0] above to the same effect.

+

 This parameter allows you to reserve space for additional traces to be added to the figure after the plt window has been started. For example plt(x1,y1,x2,y2,'+',5); opens the plt window with two traces, the first one defined by x1,y1 and the second one by x2,y2. Then, room is reserved in the TraceID box for up to 5 more traces that can be added using the pltt.m function. This parameter is normally only used inside script or function files because when you type the plt command in the Matlab command window an automatic '+',8 is assumed. You could still include the + parameter from the command window in the unlikely event you were planning on adding more than 8 traces. When plt is called from a script or function, you can't add traces after the plt window has opened unless you had included the + parameter in the argument list.

It is unusual to want to add dozens of traces with the pltt function, but it is possible. For example with the command plt(x,y,'+',39,'TIDcolumn',''); plt will reserve space in the TraceID box for 40 traces. The first is specified in the plt command and the remaining 39 can be added using the pltt function. The TIDcolumn parameter was needed in this case because, without it, plt would attempt to cram all 40 TraceIDs into one column which would probably be unreadable.

You may include the TraceID parameter in the argument list as well if you like, and you should be aware that there are two ways of doing this. The first (and by far the most common) way of doing this is to put the 'TraceID' parameter before the '+' in the argument list. When done in that order, that TraceID argument specifies the trace names only for the traces defined in the argument list. Then when the '+' parameter is encountered, plt expands the TraceID list using default names that will usually be overwritten by the trace names included in the calls to pltt. When done in the opposite order, the TraceID argument should include the trace names you want for the traces that will be added later (even though the trace names will be invisible until those traces are added). And if the TraceID argument does not include enough trace names for this, when a trace is added after the list has been exhausted, the new trace will be added without any corresponding entry in the TraceID box (which occasionally might even be what you wanted).

Typically the + parameter is placed after all the traces defined inside the plt argument list, however, this is not strictly necessary. In fact, multiple + parameters may be included and they may be interspersed with the trace definitions in the parameter list. When you do that, the space reserved in the TraceID box for the traces to be added later will be interspersed with the defined traces in the order in which they appeared. This flexibility is rarely needed, but nevertheless, it is available if you want it. Note that when traces are added with the pltt function, the reserved slots are used in order (top to bottom, as well as left to right if multiple columns were enabled).

When calling plt from the command line, you can use '+',0 to indicate that you don't want to reserve any slots for adding traces later with pltt although there is no particular advantage in doing that. You can also include '+',0 in the argument list when calling plt from a script or function to allow the use of pltt to delete one or more of the defined traces. (You can't use pltt unless the '+' parameter was included explicitly or implicitly (by invoking plt from the command window).

properties

 If a property name appears in the argument list, then the property value will be assigned to all the lines that have been defined so far in the argument list. For example:

plt(x,[y1;y2;y3],'LineWidth',5);

will define 3 traces, each with a linewidth property set to 5. The property value may also be a cell array of the same length as the number of traces that have been defined so far. In that case, each cell array element is applied to its respective line. For example:

plt(x,[y1;y2;y3],'userdata',{27 16 3});

will create three traces. The trace associated with y1/y2/y3 will contain userdata values of 27/16/3 respectively.


Axis properties

Xlim

 plt(...,'Xlim',[xmin xmax]);
Specifies the x-axis limits. If you are using a 2 column subplot, you can specify the x-limits for both columns by using a cell array. i.e. 'Xlim',{[xminL xmaxL]; [xminR xmaxR]}; If you want to specify just the right column limits, replace the left column limits with the string 'default'.

Ylim

 plt(...,'Ylim',[ymin ymax]);
Specifies the y-axis limits for the left-hand y axis of the main plot. Alternatively you may specify the limits for both the left and right-hand y-axes of the main plot using a cell array as in: 'Ylim',{[ymin ymax] [yminR ymaxR]}. The 'Right' parameter should also be included in this case, however if you don't, plt will default to placing the last trace on the right-hand axis. Note that this parameter only specifies limits for the main plot and never for any of the other subplots. If you need to set the y-axis limits for the other subplots, use the set command with the axis handles obtained from getappdata(gcf,'axis').

YlimR

 plt(...,'YlimR',[ymin ymax]);
Usually the y-axis limits are specified using the Ylim parameter (above) however if you only need to specify the limits for right-hand y axis use the YlimR parameter. The 'Right' parameter should also be included, however if you don't, plt will default to placing the last trace on the right-hand axis.

xy

 plt(...,'xy',p]);
 where p specifies new xy position/size coordinates for various graphical objects created by plt.
p is a 5 column matrix in the following format:
OID1 | x | y | w | h
OID2 | x | y | w | h
... |  |  |  |
OIDn | x | y | w | h

OID1 thru OIDn (Object IDs) are integers that specify the objects (often an axis) to be resized and repositioned. x and y represent the coordinates of the lower left edge of the object and w and h specify the width and height. (x,y,w,h may be in pixels or normalized units i.e. as a fraction of the window size).
The OIDs are described in the following table:

OID
1 | The main (left) plot axis
2 | This usually represents the right-hand axis. However, if subplots have been specified This represents the first subplot. The remaining subplots are numbered sequentially and then the next number after the last subplot is assigned to the right-hand axis. (Note that this means that the OID for an axis will be it's index into the getappdata(gcf,'axis') array since it is ordered the same way.)
0 | This is the only OID that refers to more than one object, i.e. both the main left and right-hand axes. If there is no right-hand axis, OIDs 0 and 1 are equivalent. Also this is the default OID if none is given (which is only allowed if the OID parameter contains only a single row). This means that plt(...,'xy',[x y w h]) is equivalent to plt(...,'xy',[0 x y w h]).
-1 | Represents the axis containing the traceIDs.
-2 | Represents the axis containing the menu box items.
-3 This is similar to OID 0 except that in addition to adjusting the positions of the left & right axes, it also adjusts the associated cursor object positions and sizes (TraceID box, menubox, cursor readouts, etc.). For small axes, this can sometimes scale the cursor object too small or too close to the axis so there is a way to define this scaling independently as follows:
AxisSize = [.3 .3]; AxisPosition = [.2 .4];
CursorSize = [.5 .5];
plt(...,'xy',[-3 AxisPosition AxisSize + CursorSize*1i]);
The correction for the cursor size (using the imaginary component) may be applied in both x & y directions as in the above example, or it may be applied to either direction alone. Both the plt50.m and editz.m example programs demonstrate the use of the imaginary component in the y direction only.
other | All graphical objects created by plt as well as those later created in the same figure window have a unique OID and therefore may be repositioned using the xy parameter. To determine an object's OID, enter the repositioning mode by right-clicking on the delta cursor button. Then clicking on any other object will display its OID followed by its current position coordinates.

For example:
plt(...,'xy',[-1 .01 .8 .12 .18; 1 .2 .16 .7 .8]);
 will set the traceID box to normalized position [.01 .8 .12 .18] and set the main axis to normalized position [.2 .16 .7 .8].

If you only want to specify the position of the main plot axis, the OID is optional. For example, the following two lines are equivalent:
plt(...,'xy',[1 .2 .16 .7 .8]);
plt(...,'xy',[ .2 .16 .7 .8]);

Although you can determine and enter these position coordinates manually, it is usually far easier to use the plt repositioning mode to determine the coordinates. See GUI building with plt to learn how this is done. That section also demonstrates in detail how to use the xy parameter to reposition any of the graphical objects in the plt figure window.

AxisPos

 plt(...,'AxisPos',p);
Usually, the size and position of the plot and TraceID box are modified using the xy parameter described above, however AxisPos provides an alternate method that is included primarily for backward compatibility with older programs written before the xy parameter was added. Although on rare occasions the AxisPos parameter may be easier to use than the xy parameter. p is a 4-element vector that modifies the size and position of the plot axis in the figure window. The first two elements modify the x and y coordinates of the lower left corner of the axis. The last two elements modify the axis width and height respectively. For example if p = [1 1 .9 1], the width of the plot will shrink by 10%. If p = [1 2 1 .8] then the space between the bottom of the figure window and the bottom of the x-axis will double and the plot height will shrink by 20%. Changing the size and position of the axis is often useful when building applications to make room for additional GUI objects. If p is a 5-element vector, the width of the trace ID box is increased by a factor of p(5) to allow longer trace names. If p is an 8 element vector, the position of the trace ID box (xLeft,yBottom,width,height) is multiplied by the last four elements of p (i.e. p(5:8))

axisCB

 plt(...,'axisCB',s);
Evaluate string s when either the x or y-axis limits are changed. This callback function can also be specified by the cursor command cur(cid,'axisCB',fcn) which is described in more detail in the cursor commands section. At the top of that section, there is also a table that describes the string substitutions that plt performs on the string before evaluating it. These substitutions can make the callback more powerful while using less code. In addition to a string, s may also be a function handle of the form @func or {@func,arg1,arg2,...,argn}. Note that the string substitutions can't be used with the function handle form of this parameter.

Note that if the function is defined as a string argument often consecutive single quote characters are required (quotes within quotes). In that case readability can be improved by replacing all sequences of two consecutive single quotes with a double quote character. For example 'disp(''ABC'');' could be written as 'disp("ABC");'. Note that this trick also works for Matlab callbacks in general if you are using a relatively recent version of Matlab, but not for older versions. But it does work for any callback defined within a plt(...) function call regardless of the Matlab version.

moveCB

 plt(...,'moveCB',s);
Evaluate string s whenever the cursor is moved. This callback function can also be specified by the cursor command cur(cid,'moveCB',fcn) which is described in more detail in the cursor commands section. The moveCB is not really an axis property, but is included in this section because of the parallels with the above axisCB parameter. As with the axisCB parameter, the string substitutions are performed before evaluation. You may use function handle forms as well if you don't need the string substitutions.

ENApre

 plt(...,'ENApre',[ENAx ENAy]);
ENAx or ENAy = 0 to disable metric prefixes on the x/y axis.
ENAx or ENAy = 1 to enable metric prefixes on the x/y axis (default).
If the argument has only one value, then it is used for both ENAx & ENAy.
When metric prefixes are enabled plt will choose the best unit for the respective axis. As an example, suppose the x-axis label is 'seconds' and the x-axis data is [0 1 2 3 4 5]*1e-8. With metric prefixes disabled, the x-axis tick-labels and cursor readout will be in scientific notation. With metric prefixes enabled, the x-axis label will change to "nano-seconds" and scientific notation will no longer be required, making the graph and cursors far more readable. (Note: metric prefixes are not used on the right-hand axis).

AxisLink

 plt(...,'AxisLink',m);
Tells plt to start with the left/right axes linked if m=1 or unlinked if m=0. For more details about linking the axes, see the right hand axis section.

+AxisProp
-AxisProp
<LabelProp
>LabelProp
.LabelProp
^TitleProp
"TtxtProp
?TboxProp

Property prefixes

  • If a property name is prefixed with a + or a - character then the property value will be applied to the left or right-hand axis respectively.
  • If a property name is prefixed with a <, >, . or a ^ character then the property value will be applied to the left hand axis label, right-hand axis label, x axis label, or the axis title respectively.
  • If a property name is prefixed with a " then the property value will be applied to the TraceID text.
  • If a property name is prefixed with a ? then the property value will be applied to the TraceID box (i.e. the axis containing the TraceID text).
Some examples:

plt(...,'+Ycolor',[0 0 1],'-Yscale','Log');
In this example, plt will assign the value [0 0 1] (blue) to the Ycolor property of the main (left-hand) axis, and it will apply the value 'Log' to the Yscale property of the right hand axis. The plus and minus signs are referred to as "property prefix characters" and in this case specifies which axis you want to modify.

plt(...,'>FontName','Lucida Handwriting');
In this example, the font used for the right-hand axis label is changed to Lucida Handwriting.

plt(...,'+<.^FontSize',13);
This example shows that more than one property prefix character may be included in front of a property name. In this case, the font size for the left-hand axis tick labels, the left y-label, the x-label, and the axis title are all increased to 13.

plt(x,[y1;y2;y3],'?pos',[.01 .4 .1 .4]);
plt(x,[y1;y2;y3],'xy',[-1 .01 .4 .1 .4]);
The above two lines are equivalent. They both move the location of the TraceID box from its default position to the normalized position [.01 .4 .1 .4]. The first line uses the property prefix for the TraceID box and the second line makes use of the 'xy' parameter.

The example programs hermite.m, pub0.m, and xChart.m also demonstrate the use of these property prefix characters.

Note that if a property name appears without one of these eight leading prefix characters (+-<>.^"?), then the property value will be assigned to all the lines that have been defined so far in the argument list. This is described more completely in the last entry of the Trace properties section.

SubPlot

 plt(...,'SubPlot',v);
Normally plt puts all the defined traces on a single plot (which may have left and right-hand y-axes) that fills most of the figure area. However, there are two methods (each with their unique advantages) to create more than one plot in a single figure. The first method is by using the 'Fig' parameter which is described at the end of the Labels and figure properties section. The second method is to use the SubPlot parameter which is described here.

When the SubPlot parameter is used, all the plots in the figure will be arranged in either one or multiple columns. All plots in a column usually use the same x-axis which allows all the cursors in the column to move left or right together. (This is called the "Linked" mode). With the alternate mode (called "Independent") however, each plot even within the same column may have different x-axis values. The subplot in the lower left corner has a special designation (the main plot) since that is the only plot that includes a traceID box. Also, some of the cursoring features are only available on the main plot (peak/valley finder, delta cursors, expansion history, the Mark/Zout/LinX/LinY tags, the x-axis slider, multi-cursors and the xView slider. (The 'Fig' parameter method doesn't suffer from any of these restrictions since each plot is a "main" plot, although linked cursors are not available with that method.) Each subplot however has its own y-axis cursor readout. These cursor readouts are easy to identify since their background color matches the trace and axis colors. The full panning and zooming features of plt are supported for each subplot. When the x-axis of any subplot is modified (by panning or zooming for instance), the x-axis limits of all the other subplots in the same column are set to match the newly selected range.

Single column
 To create a single column of plots (all using the same x axis), the subplot parameter should consist of n positive numbers, where n is the number of plots desired. Each number specifies the percent of the area to be occupied by each plot (starting from the bottom). Normally the sum of the array should be 100, although if the sum is less than 100, there will be some unused space at the top of the figure. For example, 'SubPlot',[40 30 15 15] tells plt to create four plots. The bottom one (the "main" plot) will use 40% of the available height. The plot above that will use 30% of the height, and the remaining two will take 15% each. Each subplot except the main (lower) plot is normally assigned a single trace, with the last trace defined appearing in the uppermost axis, the second to last trace appearing in the axis below that, etc. For example, the command plt(1:50,rand(7,50),'SubPlot',[40 30 15 15]) will create seven traces containing random data, with the first four traces displayed on the main (lower) plot (with a traceID box containing four labels), and the last three traces are displayed in the other three subplots. The example script demo\subplt.m demonstrates the use of single column subplots. Usually, only the main plot may contain multiple traces, although the SubTrace parameter (see below) allows you to change this behavior.

Dual column
 The example script demo\subplt8.m demonstrates the use of dual-column subplots. To create two columns of plots, insert a negative number into the subplot argument. The number of entries to the left of the negative number indicates how many plots will appear in the left column, and similarly, the number of entries to the right of the negative number indicates the number of plots in the right column. The negative number itself specifies the width (in percent) of the left column. Some examples will help clarify this. In all the examples below, assume that y = [a b c d e f] where a through f are column vectors of the same length as x.

plt(x,y,'SubPlot',[100 -60 100],'Right',[2 3]);
The subplot parameter tells plt to create two plots both of which fill the entire height available in the plotting area of the figure. The left (main) plot fills 60% of the width with the second plot filling the remaining 40%. Since six traces are defined, the first five traces (a through e) appear on the main plot and the last trace (f) appears on the right plot. Since the TraceID parameter was not included, the TraceID box next to the main plot will contain the default trace labels (Line1 thru Line5). To label the traces more informatively, a parameter such as 'TRACEid',{'a' 'b' 'c' 'd' 'e'} could be added to the plt argument list. Since the 'Right' parameter was included, the main plot will include both right and left axes, with the 2nd and 3rd traces (b and c) on the right and the remaining three traces (a, d,e) on the left. The left and right axes will be separated by enough space to leave room for the axis labels, and this space will be increased when the 'Right' parameter is used so that there is room for an axis label on the right side of the main (i.e. left) axis.

plt(x,y,'SubPlot',[50 30 20 -55 70 20]);
In this example, three plots will be created in the left column which fills 55% of the width of the plotting area. The main plot on the bottom (containing traces a & b) fills 50% of the height, the middle plot (trace c) fills 30%, and the top plot (trace d) fills the remaining 20% of the height. Two plots are created in the right column which fills the remaining 45% of the width. The lower of these (trace e) fills 70% of the height and the upper (trace f) fills 20%, with the upper 10% remaining blank. Note that both traces in the main plot use the left-hand axis since no 'Right' parameter was given and no limits or labels were specified for the right-hand axis. (With this many subplots it's best not to use a right-hand axis since it makes all the subplots significantly narrower to make room for the right-hand axis ticks and labels.)

plt(x,y,'LabelX',{'meters' 'pascals'},'Ylim'{[0 5] [0 .1]});
Even though the subplot argument is not included here, plt will split the plot horizontally as if you had included 'SubPlot',[100 50 100] in the argument list. This is because two different x-axis labels are specified with the 'LabelX' parameter and so plt recognizes that a second column is needed. The right column plot will contain trace f and the left column (main) plot will contain traces a thru e. Since two y-axis limits are specified, plt will put both left and right axes on the main plot. In this example the 'Right' parameter is not included, plt will default to putting the last trace of the main plot (trace e) on the right axis with the other four traces on the left axis. (Be careful not to confuse the concepts of the right and left axes of the main plot, with the right and left columns of subplots.) Also, remember that the 'Ylim' parameter can't specify axis limits for a subplot. To set the y-axis limits for the subplots, use the set command with the axis handles obtained from getappdata(gcf,'axis') or use the cur(cid,'xylim',p) command described here.

More than two columns
 As you can see from the example script demo\subplt16.m you may use as many columns as you want. The negative numbers in the subplot parameter are used to separate the plots into columns. For example 'SubPlot',[50 50 -30 50 50 -30 50 50 -30] specifies an array of six plots (2 rows and 3 columns). Each column is split 50/50 between the two plots. Since each of the 3 columns occupies 30% of the available plot width about 10% of the available width to the right of the last column will be blank (possibly to be filled in later with other graphic elements or controls). The width of the last column does not need to be specified. In this example, if the last number (-30) was omitted, the last column would take 40% of the available width since plt wants to fill the whole plot area unless instructed otherwise.

Plot spacing
 By default, plt allows plenty of space between the subplots to allow for axis ticks and labels. Sometimes you may want to decrease the horizontal or vertical spacing so that you can fit more plots into a given space or to allow each plot to have as much area as possible. Or you may want to increase the spacing to allow room to add additional controls or graphic elements. It would be awkward to require an additional array the size of SubPlot to specify the desired row and column spacing, so this information is embedded into the SubPlot argument. This is done by using the integer part to specify the plot heights and widths (as described above) and by using the fractional part to specify the deviations from the default inter-plot spacing. Fractional parts from 0 to .5 indicate the default spacing should be increased. Fractional parts from .5 to .9999 indicate the default spacing should be decreased. This is best shown by example. Consider a slight change from the previous example: 'SubPlot',[50.02 50.97 -30.96 50 50 -30.01 50 50] The first two fractional parts (.02 and .97) tell plt to increase spacing below the first plot by 2% and to decrease the spacing below the second plot by 3% (of the available plot height). The fractional parts of the two negative numbers (.96 and .01) tells plt to decrease the spacing to the left of the first column by 4% and to increase the spacing to the left of the second column by 1% (of the available plot width). At first, this may seem confusing, but with a little practice you will find that the SubPlot parameter gives you complete subplot positioning flexibility. In the rare situations where you can't get the subplots positioned as desired, you can always use the 'xy' parameter to move or resize any or all of the subplots.

Linked vs. Independent mode
 The three sample scripts mentioned so far use the default "linked" mode which is intended to be used when all the plots in each column have the same number of elements and the same x-axis limits. When you move a cursor, all the cursors for the remaining subplots in the same column will be moved left or right so all the cursors in the column remain vertically aligned. Likewise, if you change the x-axis limits of any plot (by panning or zooming) then the x-axis limits of the remaining plots in that column will also change so that all the plots in the column share the same x-axis limits. Note that changes in one column will never affect any of the other columns. When you don't want the cursors and x-axis limits to be linked in this manner, you should specify the "Independent mode" which is done by putting an "i" after the first SubPlot element. The sample script demo\subplt20.m and the application sig\pltZoom.m both demonstrate the use of the independent mode.

SubTrace

 When using subplots, it is important to understand that the default behavior is to allow only a single trace on each subplot except for the main axis (lower left). The main reason for this is to allow plt to provide a simple cursoring mechanism that allows every trace to be cursored. However, there are two situations where you may want to change this default behavior. The first is where cursoring is disabled (usually because the plot is to be used for publication instead of for data exploration). Since cursoring is not an issue, there is no reason to stick with the default behavior for assigning the traces to the axes. The second plot in the script demo\pub0.m is an example of how the SubTrace parameter might by used in this situation. The second situation where you might want to use this parameter is when you plan on modifying the cursor behavior to make sense for the particular trace arrangement you have in mind. This requires a detailed understanding of plt's cursoring commands but is doable when the trace configuration and desired cursoring scheme are reasonably simple. An example of this second situation can be found in the script demo\weight.m.

There are two ways to use this parameter to assign the traces to the various subplots. For either method you must know how plt numbers the axes. Axis number one is always the main axis (lower left). Then axis two is the one directly above the main axis and axis three is the one above that, continuing to the top of the left column. Then the lowest axis of the second column (if it exists) is assigned to the next number, and continuing upwards as before. Finally after all the subplots have been assigned a number in this manner, the right-hand axis of the main plot (if it exists) is assigned to the next higher integer.

Suppose for example, you have 4 axes and 9 traces and that you want to put two traces on each of the first 3 axes and then put the remaining 3 traces on the last axis. The first way to do this is to specify how many traces to put on each axis, i.e. 'SubTrace',[2 2 2 3]. Instead of specifying how many traces are on each axis, an alternate way to do this is to specify which axis each trace goes on. So an equivalent to the previous parameter you could use 'SubTrace',[1 1 2 2 3 3 4 4 4]. Of course, this second method is always going to be longer than the first method, so you would likely only use it if you needed to assign the traces to the axes in a different order, for example 'SubTrace',[1 2 3 1 2 3 4 4 4] (which is not possible to specify using the first method). plt will always be able to figure out with method you are using.


Labels and figure properties properties

Labels and figure properties

Title

 plt(...,'Title',s);
Inserts the title string t above the plot area.
s is usually a string, although it may also be a cell array of strings to specify a multi-line title.

The Tex interpreter is used to render the string allowing entry of Greek and other special characters. If you don't want the Tex interpreter to be used, you can turn off the interpreter with a command such as:

set(get(gca,'title'),'interpreter','none');

A shorter (although more cryptic) way of turning off the interpreter is to include the string [TexOff] anywhere in the first line of the title. (The [TexOff] string will be deleted from the title before display). That method works only for graph titles.

A set command similar to the one above may be used to change the font size or other title properties. The plot height is automatically shrunk by the amount needed to make room for the title assuming the default font size. If you increase the title font size you may need to adjust the plot size using the xy parameter described in the Axis properties section.

If s is a number is will be converted to a string. For example:

plt(...,'Title',{123 7.88})

will create a two-line title with '123' as the first line and '7.88' as the second line.

Default: no title

LabelX

 plt(...,'LabelX',s);
Uses string s as the x-axis label. If you are using subplots with two columns, you may also specify the x-axis label for both the left and right columns of plots by using a cell array:

plt(...,'LabelX',{'left Xlabel' 'right Xlabel'});

If there is only one column of subplots (or no subplots at all), the cell array argument represents a multi-line axis label, as in:

plt(...,'LabelX',{'First line' 'Second line'});

Even when you have multiple X labels, you can still use multi-line labels, as in:

plt(...,'LabelX',{'X left' {'Xright line1' 'Xright line2'});

The Tex interpreter will be used by default for X and Y axis labels. If for some reason you want to disable the Tex interpreter, you can do so with a command such as:

set(get(gca,'Xlabel'),'interpreter','none');

Default: 'x axis'

LabelY

 plt(...,'LabelY',s);
Uses string s as the left hand y-axis label of the main plot. You can specify both the left and right labels by using a cell array. For example, if there are no subplots, both these lines are equivalent:

'LabelY',{'ab' 'cd'}
'LabelY','ab','LabelYR','cd'

If there are subplots, the right-hand axis label must come last. For example with 3 subplots:

plt(...,'SubPlot',[50 20 30],'LabelY',...
{'lower-axis' 'middle-axis' 'upper-axis' 'right-hand-axis'});

In a similar way as described for titles, if you specify a number for the X or Y axis label it will be automatically converted to a string. You may also use multi-line text for the Yaxis labels in a similar way as described above for the Xaxis labels. The demo program gpsLog.m is a good example of how to use multi-line text for both X and Y axis labels.

Default: 'Y axis (Left)'

LabelYR

 plt(...,'LabelYR',s);
Uses string s as the right hand y-axis label. The 'right' parameter should also be included in this case, however, if you don't, plt will default to placing the last trace on the right hand axis. Note that using a cell array argument to the 'LabelY' parameter (described above) is usually a more convenient way to specify the y-axis label, and the 'LabelYR' parameter is primarily used in legacy code.

Default: 'Y axis (Right)'

FigName

plt(...,'FigName',f);
Uses string f as the name for the plt figure window.

Default: 'plt'

Position
Pos

 plt(...,'Position',[xLeft yBottom height width]);
plt(...,'Position',[height width]);
 Specifies the figure size and position on the screen in pixels.

Since 9 and 55 are the default values for xLeft & yBottom respectively, the second form above (with xLeft and yBottom omitted) is equivalent to: plt(...,'Position',[9 55 height width])

If you prefer conciseness, you may use 'Pos' as an abbreviation for 'Position'.

This argument has a similar effect to the set(gcf,'Pos',[xLeft yBottom height width]) command that you would use to position plots created using the native Matlab plot command, but the coordinate system is slightly different. For example, if you use yBottom = 0 in the set command, then the figure will be positioned at the bottom of the screen, even if you have the taskbar there. (Likely not the desired result since the figure will cover up part of the taskbar.) However, if you use yBottom = 0 in the plt 'Pos' parameter, the plotting figure will be positioned at the bottom of the screen only if the taskbar is located somewhere else. If the taskbar is at the bottom, the figure will be placed just above the taskbar. The 'Pos' parameter also has two features (called 'size priority' and 'position priority') that makes it easier to place one or more optimally sized figures on the screen when you don't know the screen size and taskbar position ahead of time. To learn the details of these features, read about the figpos function which is described here: Auxiliary functions section. (plt calls the figpos function to convert the values given for the 'Pos' parameter to absolute screen coordinates.)

If the height is specified as zero, plt will choose a height so that a unit along the x-axis is the same as a unit along the y-axis (i.e.if you plot a circle, it would look like a circle and not an ellipse). If the width is specified as zero, plt chooses the width to meet the same condition. (You can't specify zero for both the height and the width). If you resize the figure window with the mouse, then the units along the x and y axes will no longer be equal (and a plotted circle may appear to be an ellipse). If you wish that the equal units property to be maintained even after the figure window is resized, you should follow the plt command with the command axis('equal').

If you specify the same position vector for more than one plt command, plt will add a small offset to all the figure window positions (except the first one) so that no two figures are exactly on top of each other. This feature makes it less likely that you will completely lose sight of one of the figures and also makes it much easier to select or move any figure with the mouse. If a second plt command specifies a position that differs from the first plt command by even one pixel, then this feature will not be engaged.

Default: [9 55 700 525] (if sublots are not used). With subplots, as you add more columns of axes the default width increases from 700 to a maximum of 980. As you add more axes to a column the default height increases slightly from 525 to a maximum of 600.

HelpText

 plt(...,'HelpText',v);
 This parameter creates a HelpText pseudo object at the same time as the plt pseudo is being created. v is a string or cell array specifying the displayed text. When this argument is included in the plt argument list, right clicking on the menu box Help tag will toggle the help text on or off (unless the 'HelpFileR' argument is also included in the argument list). See the Pseudo objects section for a description of the format of the v argument.

See the following demo programs for examples of the use of the HelpText pseudo object:
demo folder: | movbar, subplt, trigplt, vbar, wfalltst, xChart
math folder: | airspeed, curves, gauss, gpsLog, hermite, julia
sig folder: | afilt, editz, weight

Link

 This parameter is used to force a group of plt figures to close when any member of the group is closed. Consider the following sequence:

plt(x1,y1);
g = gcf;
plt(x2,y2,'Link',g);
plt(x3,y3,'Link',g);

This of course will create 3 plotting figures. Closing any one of the three figures will also cause the other two to close.

The link parameter is ignored if it is empty. This makes it easier to link figures created in a loop. For example, this loop creates five linked figures:

g = '';
for k=1:5
plt(x{k},y{k},'Link',g);
if isempty(g) g=gcf; end;
end;

In almost all cases, the plt arguments are case insensitive, but this one is a rare exception since 'Link' and 'link' have slightly different meanings. As we described it so far, this parameter creates a two-way link. So for the first example above, when we create the second figure, closing it closes the first figure, AND closing the first figure closes the second. But now consider this example using the lowercase parameter:

plt(x1,y1);
plt(x2,y2,'link',gcf);

Now if we close the first figure, the second figure automatically closes, however closing the second figure does not close the first figure.

These programs are examples of the use of the link parameter:
demo folder: | pub0, pub2
math folder: | airspeed, circles12, gauss, gpsLog
sig folder: | erip
All of these use the capitalized parameter ('Link') except for gauss and gpsLog which use the lowercase ('link') parameter.

CloseReq

 This parameter specifies a function that will be run when the plt window is closed. The argument may be:
  • a string (as shown in the plt50.m example).
  • a function handle (as shown in the afilt.m example).
  • a cell array containing a function handle and its arguments (as shown in the wfall.m example).
Note that if the function is defined as a string argument often consecutive single-quote characters are required (quotes within quotes). In that case readability can be improved by replacing all sequences of two consecutive single quotes with a double quote character. For example 'disp(''ABC'');' could be written as 'disp("ABC");'. Note that this trick does not work for Matlab callbacks in general, but it does work for any callback defined within a plt(...) function call.

Fig

 Normally plt opens a new figure window when it is called. In some situations, you may want to tell plt to use a pre-existing figure instead. (The most common reason this is done is to put more than one plot into a single figure.) This parameter tells plt to do this and specifies which figure window should be used. For example, to open plt using figure number 4, you would use plt('Fig',4,...);. More often you will probably use plt('Fig',gcf,...); which will open plt in the current figure. Generally, the plt parameters may be placed anywhere in the parameter list, and in fact, the Fig parameter is the only exception to this. The Fig parameter must be placed either as the first or the last parameter in the argument list. The Fig parameter is ignored otherwise. This restriction is due to lazy programming more than any other reason, and the restriction might be removed in a later release.

There are three example programs (afilt.m, plt50.m and pub3.m) which are described in the programming examples section that demonstrate the use of the Fig parameter to put multiple plots in a single figure. The first two (afilt & plt50) are oriented towards data exploration and takes advantage of the full generality of plt's cursoring system for both plots. The third one (pub3) is oriented towards creating a figure for publication and so all the cursors have been disabled to create a clutter-free result.

There is also another method to create multiple plots in a single figure, and this makes use of the subplot parameter. (This is demonstrated by the subplt.m, subplt8.m, subplt16.m, subplt20.m, pub0.m, pub2.m, pltmap.m, and weight.m programming examples). You might not expect that there would be a need for two different methods for achieving the same end, but it turns out that each of these methods has its unique advantages. The subplot method (described in the Axis properties section) is sometimes simpler because all the plots are created with a single call to plt. The subplot method imposes significant restrictions on the plots, but in turn, this allows the cursor controls to be more compact which makes cursoring possible on a figure with many more plots than would be possible with the Fig method. The subplot method also provides an option for linking the cursors of the plots in a single column. Most of the programming examples with multiple plots per figure would have been difficult to impossible without the correct choice between the subplot and Fig parameter methods.


Options

addTag

 plt(...,'addTag',{'tag1' @action1 'tag2' @action2 ... });

Adds one or more tags to the bottom of the menu box. 'tag1' can be any string that you want to add to the menubox and @action1 can be any function name that will be executed when this new tag is clicked. If you need to include parameters to the function you can replace @action1 with something like {@action1 arg1 arg2 arg3}. If you want to define the function as a string you could replace @action1 with something like 'disp("The tag1 tag has been clicked");'.

The default color for the new tags is yellow so that it will stand out from the usual menu box tags, but you can change the color of the tag if you like by appending the color to the name of the tag deliminated by two vertical bar characters. For example if you want to make the first tag blend in by using the [.7 .8 .95] default color of the usual menu box tags and make the second tag red you would use:

plt(...,'addTag',{'tag1||.7 .8 .95' @action1 'tag2||0 0 1' @action2 ... });

or equivalently you could use the one of the alternate plt color specifiers as in:

plt(...,'addTag',{'tag1||708095' @action1 'tag2||1' @action2 ... });

The programs demo\vbar.m, math\gpsLog.m, and sig\winplt.m all serve as examples of using the addTag parameter.

HelpFile

 plt(...,'HelpFile','filename'); Specifies the left click action of the menu box Help tag:

If the filename is specified with complete path information the helpfile will be read from the specified location. If no path information is included, plt looks for this file on the Matlab path (except for compiled applications in which case plt looks for the file in the same folder that contains the executable). The file extension must be included in the filename string since the extension determines which application is used to open the help file. (If you don't include an extension plt will assume that it is an executable command, and plt will simply call that executable when you click on the Help tag.) The extension may be .html, .pdf, or .chm, or any file type that your operating system knows how to open. (Usually, chm files are only supported on PC systems.) Assuming the help file is found, it will be opened when you left-click on the help tag. Also if the file specified is a chm file, then it also may be followed by a topic specifier which causes the chm file to open pointed at the chosen topic. (The demo programs plt5.m, editz.m, winplt.m and julia.m demonstrate the use of the HelpFile parameter to open a chm file at a specified topic.)

If the HelpFile parameter is not included, left-clicking on the help tag will open the default plt help file (plt.chm on Windows systems and the file plt.htm otherwise). If both plt.chm and plt.htm are not found, then one of the files xxx.chm, xxx.htm, or xxx.pdf will be opened where "xxx" is the current figure name. If none of those files are found, a warning message will appear indicating that no help files were found.

HelpFileR

 plt(...,'HelpFileR','filename'); Specifies the right click action of the menu box Help tag:

The rules for finding the help file are the same as described above for the HelpFile parameter. Assuming the help file is found, it will be opened when you right-click on the help tag. If this parameter is not included, right-clicking on the help tag will toggle the helptext if it was defined using the plt 'HelpText' argument. If the 'HelpText' argument was not supplied in the plt argument list, then right-clicking on the help tag will open the default plt help file. Often the help tag left click will be used for help on the plt plotting package and the right click will be used for help on the currently running program. Or the roles of left and right clicks may be reversed. The demo program demo\plt50.m gives an example of using the HelpFileR option.

Options

 plt(...,'Options',s);

s is a string specifying one or more options. The options allowed are:

'Ticks' | Use tick marks (i.e. no grid lines)
'Menu' | Enable the figure menu bar
'xView' | Enable the xView slider
'Slider' | Enable the x-axis control slider
'Xlog' | use logarithmic scaling on the x axis
'Ylog' | use logarithmic scaling on the y axis
'multiCur' | Enables the multiCursor. The Y cursor edit box only displays the value of the currently selected trace, but with the multiCursor enabled, the values of all the traces are shown at the current cursor index. This mode can also be turned on or off by the user after the plot has been created. See the "multiCursor" heading in the Cursoring section
'tidcuR' | The multiCursor option described in the line above is the clearest way to show the values of all the traces at the current cursor index ... however, it does add a lot of clutter in the plot region. An alternate method of displaying all these cursor values without cluttering up the plot region is to use the TraceID box to display these values. The 'tidcuR' option enables this alternative. If you find 'tidcuR' cryptic you could use 'trace id cursoR' or 'multicursoR' or simply 'R' or any other string containing the capital letter R. The fseries.m application (in the sig folder) demonstrates the use of the 'tidcuR' option. By default, these multicursor values are displayed using 6 digits (with the '%6v' conversion format). If you wanted to change this to 5 digits, you could add the command: setappdata(gca,'tidcuR',5) or you could specify a conversion string such as: setappdata(gca,'tidcuR','%07.4f'). (The example program pub2.m demonstrates the use of this application data value for controlling the tid cursor formatting.)
'Nocursor' | Tells plt to hide all cursor objects. They may be re-enabled with the command: cur(0,'visON');
'noButton' | Tells plt to hide the cursor button group (4 buttons). They may be re-enabled with the commands: a = cur(0,'obj'); set(a(7:10),'vis','on');
'Hidden' | Tells plt to exit with the plot figure as usual, but leave the figure window hidden.
'Linesmoothing' | Tells plt to use Matlab's line smoothing algorithm (anti-aliasing) for all traces. The line smoothing property may also be controlled from the cursor button group which is described in the Cursoring section. Be aware that line smoothing probably will not work on versions of Matlab older than about 2008. Also the line smoothing property is ignored in version Matlab R2014b or later because lines are always smoothed (at least with relatively recent graphics hardware).
'Ignorecfile' | If you have defined your own color scheme using a color file, this option allows you to revert to the default plt color scheme without deleting the color file used for your other applications.
'-Help'
'+Help' 		removes/adds the Help tag from the menu box
'-Xlog'
'+Xlog' 		removes/adds the LinX/LogX tag from the menu box
'-Ylog'
'+Ylog' 		removes/adds the LinY/LogY tag from the menu box
'-Grid'
'+Grid' 		removes/adds the Grid tag from the menu box
'-Data'
'+Data' 		removes/adds the Data tag from the menu box
'-Mark
'+Mark' 		removes/adds the Mark tag from the menu box
'-Zout
'+Zout' 		removes/adds the Zout tag from the menu box
'-Rotate'
'+Rotate' 		removes/adds the XYrotate (XY↔) tag from the menu box
'-All'
'+All' 		removes /adds all menu box items

These options strings are case sensitive and in fact only the capital letters are significant. You can add whatever lowercase letters, spaces, and other delimiters that you want to make the string more readable. For example, suppose you wanted the display to initialize with the menu bar and multiCursor enabled and the grid lines off. Any of these commands would achieve that goal:
plt(...,'Options','Menu','Options','multiCur, Ticks');
plt(...,'Options','Menu multiCursor Ticks');
plt(...,'Options','MCT');
plt(...,'Options','M,C,T');
In addition to those options, suppose you wanted to remove the "Grid" tag from the menuBox. Then we would use something like one of the following:
plt(...,'Options','Menu Cur Tick -Grid');
plt(...,'Options','MCT-G');
You can also use a plus sign on the menu box tags if you would rather specify which tags to include instead of which tags to remove. For example, both of the following commands would remove all the menu box items except for the x and y axis lin/log controls:
plt(...,'Options','-H-G-D-M-Z-R');
plt(...,'Options','+X+Y');
If you remove all menu box items (i.e. 'Options','-A'), the box outline is not displayed as well.


Colors

All the arguments below identified by rgb refer to a color specified in the usual Matlab way, i.e. as a 3-element row vector where each element is between zero and one. However, for convenience, you may also use two alternative formats when specifying colors with plt.
  • The first alternative is to specify the color values as percents instead of fractions. For example the Matlab color triple [.23 .45 .67] may also be written as [23 45 67]. Whenever you include a number bigger than one in a color triple, plt assumes that you are using this entry style.
  • The second alternative is to specify the red, green, and blue components as a single number with two digits assigned to each value. For example, the color triple shown above could also be written as 234567. For clarity, you may choose to always use six digits by using leading zeros when necessary, although the leading zeros are not required.
Based on the above description of the second alternative, you would expect that 013301 would represent the color triple [.01 .33 .01] however actually it represents the color triple [1.00 .33 1.00]. This is because the digits 01 are treated as a special case representing the color value 1.00. So for example, the following TRACEc argument specifies a 10-color trace sequence identical to the first 10 colors shown in the default color list below:

plt(...,'TRACEc',[000100; 010001; 000101; 010000; 206001;
010101; 016020; 203001; 012060; 200160])

The leading zeros add clarity, but they are not required. So for example the first entry in the array above could be written as 100 if you prefer brevity. Note that the second alternative style does not allow you to specify .01 in a color triple. In that highly unlikely case, you would have to use either the first alternative style or the traditional Matlab color triple format. Also, you must stick with the traditional Matlab format if you need to specify more than two decimal places of precision (which is also quite unusual).

TRACEc

 plt(...,'TRACEc',ct);
 Specify trace colors. Usually, you will specify at least as many colors in ct as there are traces, however, if ct doesn't include enough colors, plt will start over from the beginning. For example, if you specify the trace colors with any of these commands:

plt(...,TRACEc,[1 0 0; 1 1 0; 1 1 1])
plt(...,TRACEc,{[1 0 0]; [1 1 0]; [1 1 1]})
plt(...,TRACEc,[010000; 010100; 010101])
plt(...,TRACEc,{010000; 010100; 010101})

then plt will use red, yellow, and white respectively for traces 1,2, and 3. If you had a fourth trace, plt would use red for that trace and continue cyclically. If ct includes just a single color, then plt will use that color for all traces. The second form above (cell array of vectors) is particularly useful when transferring colors from one plot to another. (See the cum and mZoom functions inside gauss.m for an example of this.)

If the TRACEc parameter is not included and color files are not being used (see 'ColorFile' below), then the following default colors are used:

Default (for the first 40 lines):
          [0  1  0;   1  0  1;   0  1  1;   1  0  0;  .2 .6  1;  % plt default trace colors
           1  1  1;   1 .6 .2;  .2 .3  1;   1  1  0;   1 .2 .6;
          .5 .5 .5;  .2  1 .6;  .8  1 .5;  .6 .2  1;   0 .5  0;
          .5  0 .5;   0 .6 .6;   0 .3 .3;  .5  0  0;   0  0  1;
          .3 .3  0;  .7 .2 .2;  .2 .7 .2;  .3 .3 .3;  .4 .4 .7;
           0 .6 .3;  .8 .5 .5;  .4 .6 .3;  .5 .5  0;  .7 .7 .2;
          .5 .5  1;  .7 .2 .7;   1 .4 .4;  .4  1 .4;  .4 .9  0;
          .5 .1 .3;  .3  0 .8;  .5 .5 .8;   0 .3 .8;  .7 .2  0];
 You will find a picture showing what these 40 colors look like in the Default colors section. These trace colors have been carefully chosen so that with a dark background the colors appear to be as different as possible. When using the first 10 or 20 colors distinguishing the traces based on the colors alone is quite easy although this starts getting a bit more challenging as more traces are added.

The defaults for lines 41 to 80 are the same as the colors listed above for lines 1 to 40 except that they are 26% dimmer. The defaults for the lines 81 to 99 are again 26% dimmer than the trace colors for lines 41 to 59. Default colors are defined only for up to 99 traces. If you have more than 99 traces, plt will start assigning colors cyclically as described above.

CURSORc

 plt(...,'CURSORc',rgb);
Specify the cursor color. Here, and on the rest of this page,rgb must be a 3-element row or column vector.

Defaults:
Only one trace (dark plot backgrounds): [1 1 .5]
Only one trace (light plot backgrounds): [0 0 .5]
More than one trace: Cursor color is set to match trace color

DELTAc

 plt(...,'DELTAc',rgb);
Specify color indicating the delta cursor.
Default: [1 0 0]

PltBKc

 plt(...,'PltBKc',rgb);
Specify the plot area background color.
Default: [0 0 0]

FigBKc

 plt(...,'FigBKc',rgb);
Specify the figure window background color.
Default: [.25 .15 .15]

xyAXc

 plt(...,'xyAXc',rgb);
Specify the color of the x and y axes.
Default: [1 1 1]

xyLBLc

 plt(...,'xyLBLc',rgb);
Specify the color of the x and y axis labels.
Default: [.64 .78 .94]

GRIDc

 plt(...,'GRIDc',rgb);
Specify the color of the grid lines. (See also GridStyle.) Normally the grid lines are drawn in exclusive-or erase mode, however, if any of the rgb values are negative (e.g. 'GRIDc',[0 -.2 .4] ), then the grids are drawn in normal mode, which is often preferable especially if the right-hand axis is not being used. The actual grid color used in the above example is [0 .2 .4]).

Default:
If no right-hand axis is enabled then the default is [-.13 .13 .13] i.e. a very dim gray line using normal erase mode. (The grid lines will be solid unless the GridStyle parameter is included). If a right-hand axis is enabled the defaults (again assuming that the GridStyle parameter is not specified), depending on the Matlab version. For Matlab versions earlier than R2014b the default GRIDc is [.13 .13 .13], and the default GridStyle is - i.e. a very dim solid gray line drawn using xor erase mode. For Matlab version R2014b or later the default GRIDc is [-.26 .26 .26], and the default GridStyle is : i.e. a dim gray dotted line in normal erase mode (which is the only erase mode supported in the newer Matlab versions).

The defaults described above apply only if a very dark plot background is being used, including of course the default plot background (black). If a bright background has been selected (by using either the 'PltBKc' or 'COLORdef',0 parameters) then the defaults above are inverted (i.e. subtracted from one). These defaults may sound complicated but they have been chosen to be pleasing to most people under the various circumstances.

TIDc

 plt(...,'TIDc',rgb);
Specify the background color of TraceID box.
Default: If this parameter is not included, the color specified by the PltBKc parameter (or its default) is used.

COLORdef

plt(...,'COLORdef',c);
Sets the PltBKc, FigBKc, xyAXc, and xyLBLc colors mentioned above to be consistent with Matlab's current default colors.
Trace colors are set as follows:

c = 0 or 'default' | Matlab's current default trace colors are used. i.e. TRACEc = get(0,'DefaultAxesColorOrder')
c is a 3 column array | c is used instead of Matlab's default trace color order. Note that you may use the traditional Matlab color triple entry or one of the alternate styles described above. The example program subplt8.m demonstrates how to use both the traditional and alternate color entry styles for this argument.
c is a 3-column array with a special first entry [.99 .99 .99] | The special case for the first entry (which is 999999 if using the alternate style) means that the remaining colors in the array will be appended to the current Matlab default trace colors. This is useful if you like Matlab's default colors, but the color sequence is not quite long enough and you just want to add a few colors to that sequence.

If TRACEc so defined has fewer rows than the number of traces to be plotted the colors will be used cyclically as described above.

ColorFile

 plt(...,'ColorFile','filename');
The normal behavior of plt (i.e. when the ColorFile argument is not included) is as follows.
  • If plt is called from the Matlab command line, when plt initializes it looks for a file called pltcolor.mat in the folder where you installed plt. If this file isn't found, no action is taken. If it is found, the file is loaded causing all the default colors and the colors specified by all the other parameters shown on this page to be overwritten with the data saved in the .mat file. When you select the "Save figure colors" selection under the Color menu, plt saves the current colors to this same file (pltcolor.mat).
  • If plt is called from a Matlab function or script file, the behavior is similar except that the file name used is fnameColor.mat where fname is the name of the top-level Matlab command or script. Again, plt looks for this file in the folder containing the .m file defining fname (or in the folder containing the fname.exe file for compiled applications).
When the 'ColorFile' parameter is included in the plt argument list, the above behavior is modified as follows:

'ColorFile','filename' | If the filename is specified without any path, plt uses the specified file name both on startup (to load the color scheme) and when "Save figure colors" is selected. plt looks for this file in the folder containing the .m file defining fname (or in the folder containing the fname.exe file for compiled applications). The extension used is always .mat and you may not include an extension or a period in the filename string.
'ColorFile', 'drive:\path\filename' | As above, plt uses the specified file both on startup (to load the color scheme) and when "Save figure colors" is selected. However, when path information is supplied, plt will look for the file only in the location given. As before, you may not include an extension in the filename string
'ColorFile','' | plt will use its default colors, and any colors specified by the other parameters in this section. A color file will never supersede them on startup, even if fnameColor.mat exists. If you select "Save figure colors", plt will allow you to save the current colors to any file of your choosing (by opening a file selection box), but these colors will not be loaded unless you change to one of the above 'ColorFile' options, or remove the 'ColorFile' parameter altogether.


GUI building with plt

Over the years I have created dozens of GUIs using Matlab, nearly all of which involved collecting and/or viewing data and interacting with the data or data collection process in some way, and I suspect the same is true for the GUIs that you need to create. The first GUIs I created were quite difficult, but as I built up my bag of tools each new program became easier and quicker to write. The key I found was to avoid re-inventing the wheel each time and the best way to do that was to create a series of "pseudo objects". A pseudo object is a collection of Matlab graphics objects embedded with features commonly needed in Matlab GUI applications. (I chose to call them pseudo objects to distinguish them from the graphics objects supplied in the standard Matlab environment.) These pseudo objects are combined into one file exchange submission called pltSig (or sometimes shortened to plt for historical reasons). My primary goal is to make building GUI applications in Matlab easier, faster, and more fun while enabling you to create clearer, more concise code that is compatible across all Matlab platforms and versions.

The two main tasks in creating a GUI application are:
  1. Choosing the graphical elements and configuring the sizes and positions of these elements.
  2. Writing the code that enables these graphical elements to serve their intended purpose.
Matlab's GUI building tools (App Designer since 2016 and Guide for older Matlab versions) helps a lot with the first task but contributes little to the second. However, I've found that for all but the most trivial applications, the second task takes the most effort by far. My strategy is to aid the second task by providing a rich set of pseudo objects mentioned above. It may seem like this is a tall order for these new objects, but I hope the examples that follow will convince you that they can impressively reduce the amount of code you need to write. The pseudo-object idea is persuasive and once exposed to it you may find yourself inspired to create your own.

A parallel goal is to make it easier to learn Matlab GUI programming by providing many well-commented examples that demonstrate the use of all the pseudo objects. It's easier to begin a new GUI application by starting with an example that has at least some of the graphical features that you need. To this end, plt includes 37 application programs and demos covering a wide variety of GUI features and programming techniques. Although the standard Matlab plotting and graphical elements are thoroughly documented, a common complaint is that this information is spread out over thousands of pages of Matlab documentation making it difficult to find what you are looking for. Also, it is difficult to find examples for most features. This inspired me to design the plt help file to avoid these pitfalls by organizing plt's many features into one coherent help file including many examples. Every question from a plt user leads me to reexamine the documentation to see if I have described each feature and example as completely and clearly as possible.

Although the first task mentioned above (configuring sizes and positions of graphical elements) is not where most of the time is spent, without an appropriate tool this could be a painstaking task. Matlab's App Designer provides a reasonable solution to this problem however I found several annoyances with this tool:
  1. App Designer forces me to adopt a particular programming methodology and style. Although plt offers unique pseudo objects you are free to use all or none of them to suit your purpose and no demands are made on your programming style.
  2. App Designer saves all the work into a proprietary file format (.mlapp) that can only be viewed or modified by App Designer which makes me feel that I have lost control. GUI creation using the plt tools however saves everything as standard .m text files that can be edited with Matlab's editor or with your favorite programmer's editor.
  3. An App Designer GUI can only be run on the version of Matlab that you have (or newer). While using the plt GUI creation tools, one can create applications that can run on any Matlab version going all the way back to Matlab 6.1 (released in 2001)
  4. And most importantly App Designer is not compatible with the powerful pseudo objects that I have created.
As you will see from the examples below, the procedure for object positioning using the plt GUI tools are not quite as automated as App Designer, but I think it strikes a good balance between automation, power, and flexibility.

Since the pseudo objects are the key innovation that simplifies Matlab GUI programming, let's briefly summarize the ten pseudo objects that have been implemented so far:
  • Cursor - My goal for the cursor pseudo object was to tailor plt for its role in data exploration. The ease and responsiveness of the cursoring, panning, and zooming operations are unmatched by any other Matlab plotting package (at least according to reports from some plt users). Although you can create cursor pseudo objects independently, by default a cursor object is created for you when you define a plt pseudo object. This relieves you of the need to know about the many details of the cursor object.
  • Grid - This pseudo object creates grid lines with options for the grid line color, thickness, and line style. The standard Matlab grid lines also offer this flexibility, but does so in a way that is not compatible with older Matlab versions, whereas the grid pseudo object is compatible with all Matlab versions. As with the cursor pseudo object, you will rarely need to define a grid pseudo object explicitly since it is also created by the plt pseudo object. The defaults are to most people's liking so you may never need to adjust them.
  • plt - This can be thought of a super axis (or collection of axes). In that respect it is similar to Matlab's plot and data is passed to plt in the same way making simple calls to plt look the same as the call to plot (except for the missing "o"). However unlike plot, plt also (by default anyway) creates:
    • a cursor pseudo object.
    • a grid pseudo object.
    • a menu box containing various plotting controls.
    • a TraceID box that serves both as a legend and as a way to enable and disable individual traces.
    • a menu bar containing the traditional Matlab menus (File,Edit,View,Tools,etc.) as well as a plt menu containing items unique to plt.
    The plt pseudo object integrates all these elements in a consistent logical manner with the defaults oriented towards the typical data exploration needs of the most common graphical interfaces. This allows you to take advantage of the plt features in your design even before learning about the many ways to tailor plt to your needs. It might seem strange that I use "plt" both as the name of this pseudo object as well as the name of the entire toolbox, however, I think you will find that you can tell which one I'm referring to by context.
  • Slider - Matlab's slider is the most versatile uicontrol because of the many ways you can change the value (dragging the slider bar, clicking the left or right arrow, clicking the trough). Plus it is the only uicontrol whose action can repeat continuously as you hold down the mouse button. This allows you to smoothly vary a parameter over a range while observing its effect. However, the slider control rarely can stand alone. At a minimum, you need a label to identify what the control is for and usually, you also need a more precise representation of the slider value than the slider bar itself provides. A text box is often used for this purpose, although an edit box is better since it allows a way to set the slider value precisely. It is also common to want labels to identify the minimum and maximum values associated with the left and right slider bar positions. Furthermore, sometimes the slider value must be restricted to be an integer or a multiple of some other factor or other condition (such as a power of 2 for example). Also, you may want to adjust the step size for the arrows or the trough or to make the steps logarithmically spaced. These requirements mean that you usually need to write a lot of code to make a slider useful for your application. The pseudo slider solves this problem by integrating all the elements and options just mentioned into one object that you can simply drop into your application and move around as a unit. All the code you need to make it useful is already done for you!
  • Edit - As mentioned above, the ability of the slider and the pseudo slider to respond continuously as the mouse button is held down is a powerful feature. The one drawback of these objects is that they take up quite a bit of space. For a GUI with many controls, you may not have room to use sliders for many of them. The edit pseudo object is the answer to such a problem. It takes up even less space than a uicontrol edit box. It doesn't have all the features of the pseudo slider, but you can continuously increment its value by holding down the mouse on the right side of the object, and likewise decrement its value on the left side. Like the pseudo slider, you can select the increment amount as well as the min/max limits. The edit object may also be used to contain vectors or strings. (The auto-increment features do not apply in that case.) Usually, edit objects require a label to identify the purpose of the control. The edit pseudo object includes a label (optionally) as an integrated feature. When you reposition the edit control, the label moves right along with it. One less graphical object to define, size, and position. Other advantages over the uicontrol edit box include:
    • Auto evaluations
    • More flexible formatting
    • More powerful callbacks
    • Tex interpreter support
    • Choice of figure or axis coordinates
    As an example of an Auto evaluation, consider that typing "cos(pi/6)" into the pseudo object would set its value to 0.866025 (with fewer or more digits depending on the format code). The last point also deserves elaboration. Normally you will want to define the edit pseudo object in figure coordinates (what I call a type I edit pseudo object) just as you do with a uicontrol. However, if you want to associate the object with a plot (so it moves with the plot if it is repositioned, or if you want to create an array of edit objects it is more convenient to use axis coordinates (i.e. a type II edit pseudo object).
  • Popup - The popup pseudo object closely mimics the function of Matlab's popup uicontrol but has these advantages:
    • You can fit twice as many pseudo popups into a given space as uicontrol popups.
    • You can cycle through the popup's options without opening the popup by right clicking on the object. When you want to see the effect of all the possible selections, it is far faster with the pseudo popup than with the uicontrol.
    • Optionally swap the role of left & right clicks (super-button mode).
    • Integrated label (optional).
    • More powerful callbacks.
    • Independent control of the location/appearance of the opened and closed view of the popup.
    • More flexible formatting.
    • Tex interpreter support.
    • Ability to open or close the popup from a program.
    Of course, uicontrol popups and uicontrol edit boxes may still be used and may be preferred when you don't need any of the advantages listed above.
  • Box - The box pseudo object is somewhat similar to the uicontrol frame object in that is often used to group several logically related controls. The most important advantage of the pseudo box over the frame is that when you drag the pseudo box to a new location (using the plt move mode) all the objects inside the box move along with it so that they remain in the same position inside the box. The other advantage of the pseudo box is the more flexible styling (face and border colors, border thickness, and transparency control)
  • Image - Most of plt's features are tailored toward 2D plotting (functions of a single variable). However, plt provides two methods to plot functions of two variables. One is to use a waterfall plot which makes use of the auxiliary function pltwater.m. The other method is to use the Image pseudo object. The Image pseudo object provides cursoring methods appropriate for this object type and also includes several optional components including:
    • A color bar that serves as a legend for the z-axis values as well as provides a method of changing the colormap used to represent the z data.
    • A slider (labeled 'edge') that allows you to control how wide a range around a midpoint is used when determining the color used to represent each array element.
    • A slider (labeled ''mid'') that allows you to control the center value of the range of values used to determine the color for each array element.
    • A checkbox that allows you to control the visibility of the axis gridlines.
    • A 'view all' button, that when clicked on resets the axis limits so that all the data is visible. A secondary feature of this button is activated by right-clicking on it instead which zooms in on the center of the region currently in view.
  • ColorPick - Nothing is quite as individual as the colors we prefer in our applications, and allowing the user to choose the application colors is a true sign that the programmer cares about the user. However, choosing several colors that have to blend in a pleasing way is not a simple matter, and providing a substandard interface for color choice can be more of a curse than a blessing. The ColorPick pseudo object was carefully designed to make it as easy as possible to give your application this flexibility. I've found it is important to present the user with palettes of colors to choose from and have the selected objects change instantly when a new choice is made. A very simple example of the use of the ColorPick object is given in afilt.m. A more elaborate example is the demoplt.m program which also includes the code to save the selected colors in a setup file so that the chosen colors remain permanent until changed again.
  • HelpText - To make our GUIs as easy to use as possible, it is nice to show help messages right on the main GUI figure. Even if the GUI is complicated enough to need a manual, most people won't read it and even if they do, some reminders of the basics when the program starts up can be useful. Of course, these reminders will quickly become annoying if they get in the way or take up valuable screen area that could be better used by the application. The HelpText pseudo object was designed to solve this need by making it easy to format and position the help messages as well as to make them disappear as soon as you start using the GUI. When this pseudo object is created along with the plt pseudo object (by using the 'HelpText' parameter), right-clicking on the menu box help tag will make the help text reappear, although it is easy to create a dedicated button for that purpose if you prefer.
A more complete description of these pseudo objects from a programming perspective can be found here: Pseudo objects.

Now you are prepared to dive into the examples that follow:

A first example (gui1.m)

Our first GUI example doesn't do any plotting or anything useful for that matter, so it's not a very interesting program ... however, the code is very short and simple, giving you a chance to see how to use plt GUI tools with little effort.

I find that it is best to start working on a new GUI with pencil and paper. Imagine the control types and arrangement for the application and then sketch a mock-up such as this. Your finished application may not look like your first sketch, but with the rapid prototyping possible with Matlab and plt you can quickly iterate improvements in form, concept, and implementation.

Here I have decided on 3 pseudo sliders across the top followed below by a pseudo pop and a radio button on the left as well as a frame on the right containing 4 uicontrols (a popup, slider, button, and checkbox).

The bottom part of the GUI consists of two large objects for displaying lists of numbers. On the right is a simple text box with room to show about 10 lines of text. On the left is a listbox, which by virtue of its scroll bar can display a far larger data set (80 lines of text in this example).

The figure window is created in the first line of code. I decided to use a figure window 430 pixels wide and 350 pixels high, although this is just a guess and can be easily modified later. The menu bar is not needed for such a simple GUI so the menu property is set to 'none'. I chose a dark blue-green for the figure background:
function gui1()
  figure('name','gui1','menu','none','pos',[90 90 430 350],'color',[0 .3 .2]);
 The next line defines the choices for the popup menu, and the line after that creates a cell array containing the positions for each of our twelve objects (Three pseudo sliders, a pseudo popup, a pseudo box (frame), and seven uicontrols):
  cho = {'choice A' 'choice B' 'choice C'}; % choices for popup control
  p = arrange(12);
 The positions created by arrange are essentially haphazard and will have no relation to the final positions of the objects. This may seem a strange way to design a GUI, but it turns out to be a waste of time typing in position coordinates for the 12 objects when we will end up changing them anyway. This is a fast way to get started. Next, we create our 12 objects by calling plt for the pseudo objects and by calling uicontrol for the standard objects. We collect the handles of the object into vector h:

  h = [plt('slider',p{1}, 10,'Slider 1',@CBsli);
       plt('slider',p{2}, 60,'Slider 2',@CBsli);
       plt('slider',p{3},800,'Slider 3',@CBsli);
       plt('pop',p{4},cho,'disp("pseudo popup")','label','PSEUDO POPUP');
       plt('box',p{5});
       uicontrol('Style','radio',   'backgr',[.5 1 1],     'units','norm','pos',p{6},...
                                    'String','RadioButton','Callback',@CB);
       uicontrol('Style','popup',   'backgr',[.5 1 1],     'units','norm','pos',p{7},...
                                    'String', cho,         'Callback',@CB);
       uicontrol('Style','slider',  'backgr',[.5 1 1],     'units','norm','pos',p{8},...
                                    'String','slider',     'Callback',@CBsli);
       uicontrol('Style','pushb',   'backgr',[.5 1 1],     'units','norm','pos',p{9},...
                                    'String','button1',    'Callback',@CB);
       uicontrol('Style','checkbox','backgr',[.5 1 1],     'units','norm','pos',p{10},...
                                    'String','check001',   'Callback',@CB);
       uicontrol('Style','listbox', 'backgr',[.5 1 1],     'units','norm','pos',p{11});
       uicontrol('Style','text',    'backgr',[.5 1 1],     'units','norm','pos',p{12})];
 The code above (in blue) is straightforward and will be the preferred style for many of you. Some of you may prefer more compact code (which I find easier to read), so what follows is the code that I actually included in gui1.m:

  h = [plt('slider',p(1:3),{10 60 800},{'Slider 1' 'Slider 2' 'Slider 3'},@CBsli);
       plt('pop',p{4},cho,'disp("pseudo popup")','label','PSEUDO POPUP');
       plt('box',p{5});
       uic('Style', {'radio'      'popup' 'slider' 'pushb'   'checkbox' 'listbox' 'text'},...
           'String',{'RadioButton' cho    'slider' 'button1' 'check001' ''        ''    },...
           'Callb', { @CB          @CB     @CBsli   @CB       @CB       ''        ''    },...
           'backgr',[.5 1 1],'units','norm','Pos',p(6:end))];
 Note that the first three lines have been collapsed into one, since plt can create multiple pseudo objects with a single command. Also, the last 12 lines that create the 7 uicontrols has been collapsed onto 4 lines by using uic (which is similar to uicontrol except that it allows multiple controls to be created with a single command). There is nothing wrong with either style and which one you choose is largely a matter of taste. The workings of uic should be reasonably clear from this example, but you can see the details of this command (as well as the arrange command used above) in the Auxiliary functions section. And finally, the last line of the gui1 function:

set(gcf,'user',h); CBsli; % save the handles for the slider callback and execute it

We finish up by writing the control callbacks. The first four callbacks just send a message to the command window so you know that the callback has been executed. The last one (the slider callback) is the only one that does something, which is to update the data tables with new random data. Note that the random numbers are converted to strings using prin a substitute for sprintf that includes features commonly needed in GUI programming. To see the full documentation for prin simply type prin with no arguments into the command window.

function CB(a,b)        % default uicontrol callback ---------------
  disp(get(a,'style')); % display the uicontrol style

function CBsli(a,b)           % The slider callback ------------
  h = get(gcf,'user');        % Get the handle list
  r = 1e20.^(rand(3,80))/1e6; % generate some random numbers
  % Use the same random table of numbers for both the listbox and the textbox
  set(h(11:12),'string',prin('3{%6V  }~, ',r),'fontname','courier');

Now that we are done with the coding (just 18 lines of code!), start the GUI by typing gui1 in the command window. The figure window on the left will appear. You will see that the program is functional, and the callbacks get executed as expected. However, the control placement looks nothing like our sketch. We are about to fix that.

Type plt move in the command window to enable the mouse-driven repositioning mode. To indicate that the repositioning mode is active, plt will gray out the graphical objects, indicating that the object callbacks have been disabled. Now we can:

• left-click, hold, and drag to move an object.
• right-click, hold and drag to resizes an object.
• Double click to open an object's property inspector window.

Now use the mouse to resize and position the objects to match your sketch, or until the arrangement pleases you. (BTW, when moving the pseudo popup, drag the "choice A", not the popup's label in capital letters.) Then click on each object once in the order they are defined in the h array. As you do this you will see something like the following text appears in the command window:

sli: 401  .020 .920 .300     ;  % Slider 1
sli: 406  .350 .920 .300     ;  % Slider 2
sli: 411  .680 .920 .300     ;  % Slider 3
pop: 101  .270 .500 .250 .200;  % choice A
axi:  51  .540 .500 .440 .280;  % axes
uic: 207  .030 .560 .200 .060;  % RadioButton
uic: 206  .680 .710 .170 .050;  % choice A
uic: 205  .570 .610 .380 .060;  % slider
uic: 204  .570 .520 .170 .060;  % button1
uic: 203  .780 .520 .170 .060;  % check001
uic: 202  .015 .040 .500 .410;  % 2.40e7  10098.  .02290
uic: 201  .540 .040 .440 .410;  % 2.40e7  10098.  .02290
Copy this text from the command window and paste it into your editor, right after the arrange command. (Now we can close the gui1 figure, as it has served its purpose). The first column is a three-letter identifier for the object type. The native Matlab types - uicontrol, uitable, uipanel, axis, and text are identified as uic, uit, uip, axi, txt respectively and plt's pseudo objects are identified as sli, edi, pop, and xy. (xy refers to elements created by the cursor and plot pseudo objects.) The next column is a unique integer associated with the object. We won't need either of these columns this time, so we can simply delete those two columns, which can be done most quickly by using the column mode of your editor. (In example 2 below, we will make use of the 2nd column we just deleted). So now we are left just with the three or four-element position vectors followed by a comment which identifies the string property of the object in question.

Now let's edit this section further to put square brackets around each position vector, and make the whole list into one cell array by surrounding it with curly brackets. Finally after editing the comments to better identify the object on each line, we have transformed the above text to look as follows:

  p = {[.020 .920 .300     ];  % PseudoSlider 1
       [.350 .920 .300     ];  % PseudoSlider 2
       [.680 .920 .300     ];  % PseudoSlider 3
       [.270 .500 .250 .200];  % Pseudo popup
       [.540 .500 .440 .280];  % Pseudo box
       [.030 .560 .200 .060];  % radio button
       [.680 .710 .170 .050];  % popup
       [.570 .610 .380 .060];  % slider
       [.570 .520 .170 .060];  % button
       [.780 .520 .170 .060];  % checkbox
       [.015 .040 .500 .410];  % listbox (80 lines)
       [.540 .040 .440 .410]}; % text (10 lines)
Now you can comment out, or remove the line right above this (p = arrange(12)) although that is not really necessary, since those position values will be overwritten by the code we just pasted in.

Now click the save button in your editor, and type "gui1" in the command window. If you have done everything correctly you should see the desired figure looking somewhat like this.

Try clicking on the slider as well as moving the pseudo sliders to verify that these actions update the text in the listbox and the textbox at the bottom of the figure. Also, click on the other controls to verify that the callback displays the expected response at the command window.
Suppose you are not quite satisfied with the positioning. Again type "plt move", make the adjustments you want, and again click on each object to get the updated positions. Then go back to your editor and highlight the region shown below in blue (using the editor's column mode).

  p = {[.020 .920 .300     ];  % PseudoSlider 1
       [.350 .920 .300     ];  % PseudoSlider 2
       [.680 .920 .300     ];  % PseudoSlider 3
       [.270 .500 .250 .200];  % Pseudo popup
       [.540 .500 .440 .280];  % Pseudo box
       [.030 .560 .200 .060];  % radio button
       [.680 .710 .170 .050];  % popup
       [.570 .610 .380 .060];  % slider
       [.570 .520 .170 .060];  % button
       [.780 .520 .170 .060];  % checkbox
       [.015 .040 .500 .410];  % listbox (80 lines)
       [.540 .040 .440 .410]}; % text (10 lines)
Then delete the blue section and paste in the new coordinates from the command window. If you just want to move one or two objects, you don't necessarily need to replace all the position coordinates.

You might want to try expanding the size of the figure window and note how all the objects grow in proportion to the figure size. This is because we used normalized coordinates throughout. If you want to convert this GUI to pixel coordinates, enter repositioning mode, type set(findobj(gcf),'units','pix'), and then click on every graphics object, again in the same order as they are defined. Again, cut and paste the coordinates (which now are integer pixels) into the program as we did before. Also replace the 'units','norm' in the uic command with 'units','pix'. When you run it, the GUI will at first look the same as before, but when you stretch the figure size, all the objects will stay exactly the same size in the same position, thus creating empty space inside the figure. Usually, you will stick with either pixels or normalized coordinates, although you can mix them if it suits your purposes.

SnapTo resolution

You may have noticed that in repositioning mode the objects moved smoothly as you dragged them around the screen allowing you to position the object anywhere. However sometimes you may prefer less flexibility in where you can put the objects so objects move in steps of a fixed size. This can make it easier to align related objects. You can select a fixed step size by using the SnapTo figure.

There are three ways to bring up the SnapTo figure shown below:
  1. Type plt move res in the command window.
  2. Click on the plt menu in the menu bar. Then click on the last option in that menu which says "SnapTo Grid size". This will open the SnapTo figure. Note that if the menu bar is not visible you first have to right click on the Data tag in the menu box to enable the menu bar.
  3. While using method 2 above, you may notice that to the right of the "SnapTo Grid size" menu choice, there is a shortcut shown in blue ("Delta + Rclick Delta"). This is reminding you that the equivalent of selecting this menu option is to first left-click on the delta button, followed by a right click on the same button. Perhaps you won't necessarily consider this a shortcut, although it does allow you to open the SnapTo figure without first enabling the menu bar.

Since by default, the SnapTo feature is disabled, you will first see this figure with the slider bars at zero (i.e. all the way to the left) since 0 is the indication that you don't want restricted positioning. A good starting point when trying this feature is to set both sliders to the middle (100) as shown here. This means that objects may be placed at only 100 different positions horizontally and 100 different positions vertically. This means that if you are using normalized coordinates as you drag or resize objects, the positions shown on the command window will only have two digits after the decimal point (i.e. you might see 0.44 or 0.45 for example, but no values in between). If you set both sliders at "10", then you could see 0.4 or 0.5 for example, but no values in between. (That large step size would usually be to too big to be practical). If you set the sliders at 200, you will have such fine control of position that you may not even notice that the SnapTo mode has been enabled.

A second example (afiltS.m)

Now that we have covered most of the basic concepts and techniques, its time to explore the true power of plt by reviewing the design of a real GUI in one of the application areas that Matlab was designed for. The application I have chosen is the display and analysis of the classical analog filters. Granted this is not a particularly novel idea as it probably has been done before in Matlab and other languages, but nonetheless it serves various educational and practical needs and there is always room to apply our own slant to the project. I'll start with a relatively modest set of goals:
  • Display the magnitude frequency response for the five "classical" analog filters (Butterworth, Bessel, Chebyshev type 1 & 2, and Elliptic). The user should be able to easily select which of these filters to display, as well as allow all of them (or any subset) to be plotted at the same time.
  • Interactive selection of the filter order and type (lowpass, highpass, bandpass, stopband).
  • Interactive selection of the number of decades to plot as well as the frequency resolution.
  • Both numerical entry and slider control of cutoff frequencies and pass/stopband ripple.
  • Cursors should be provided which allow for the easy readout of the frequency response at any point as well as delta readouts to verify stop band and pass band ripples. Peak finding should be provided as well as the ability to annotate the plot with text and markers to document features of interest.

As is my habit, I start with a sketch to clarify my thoughts. I decide to use an array of four pseudo sliders along the top to control the continuously adjustable parameters (edge frequencies & ripple) The four remaining filter and display parameters are grouped to the left of the sliders inside a box for visual appeal and to allow those four parameters to be moved around as a group when repositioning. The Trace IDs to the left of that will be named after the classical filter types and be used to select which filters to display. The plot and the cursor controls and readouts along the bottom edge are the standard ones created by the plt pseudo object.

By the way, I've called this program afiltS.m (supplied in the demo folder) where the "S" stands for "Simplified" because it is a simplified version of afilt.m (also in the demo folder) which has several more advanced features. But of course, here we want to start simple.

Ok ... it's time to start writing code. First, we use the arrange function to initialize the positions of the 10 pseudo objects we will be creating. The next two lines define band and filter types to be plotted and the fourth line creates the first pseudo object (a plot) and assigns it a screen position of p{1}, the first position in the cell array created by the arrange function. Actually this object is a super pseudo object, including an axis, traces, axis labels, grid lines, and cursor controls (the last two being pseudo objects in their own right):
function afilt()
  p = arrange(10);
  band =   {'low pass' 'high pass' 'band pass' 'stop band'};
  ftypes = {'Butter'   'Bessel',   'Cheby1'    'Cheby2'      'Elliptic'};
  S.tr = plt(0,zeros(1,5),'TraceID',ftypes,'Options','logX','xy',p{1},...
             'Ylim',[-80 5],'LabelX','\omega (radians/sec)','LabelY','dB');
Notice that we used plt's 'xy' parameter to position the plot within the figure window (although you will soon learn that this parameter can do far more than that). The data to be plotted for all 5 traces is defined in the plt call (as it must), but notice that each trace just contains the single point (0,0). When calling plt from the command line, you almost always include the actual plot data in the argument list, however in a GUI more often than not the data supplied is just a placeholder. The real data is loaded later (by the callback in this example). The callback will need to know where to put the plotting data, so the trace handles are saved in a 1x5 array called S.tr (the plt return value). In the code below, we will add the handles of the remaining pseudo objects to the S structure.

Next, we will define a frame (the box pseudo object), followed by four more pseudo objects (an edit object and three popups) that will be placed inside that frame:
  plt('box',            p{2}); % create a frame for the 4 filter parameters
  S.n    = plt('edit',  p{3} ,[5 1 25],   @cb,'BackGr',[1 1 1]/12,'label','Ord~er:');
  S.band = plt('pop',   p{4} ,band,       @cb,'index',3,'swap');
  S.dec  = plt('pop',   p{5} ,1:5,        @cb,'index',3,'label','Decades:','hide');
  S.pts  = plt('pop',   p{6} ,50*2.^(1:5),@cb,'index',2,'label','Points:', 'hide');
Note that when you change any of these controls, the same callback function (cb) is executed. For more complicated interfaces you may want to have different callbacks for each control, but this one function suffices here. For the last popup, the choice vector could have been specified as [100 200 400 800 1600] but I chose to write it in terms of powers of 2. The [5 1 25] parameter of the pseudo edit object means that its initial value will be 5 with min/max limits of 1 and 25. The string 'Order' is used as a label for the edit object, but notice that there is a tilde character (~) inserted into that string. This is to change the amount of space (width) allocated to the label. You might want to experiment by moving and/or removing the tilde character to see how that affects the label width. Notice that the first popup defined (S.band) includes the 'swap' parameter at the end of the call. The effect of that extra parameter is described in the afilt section, where you can also learn other details about this application not discussed here.

And finally, let's finish off the main function by creating the last four pseudo objects (sliders):
  S.Rp   = plt('slider',p{7} ,[ 2  .01    9],'Passband ripple', @cb);
  S.Rs   = plt('slider',p{8} ,[ 40  10  110],'Stopband ripple', @cb);
  S.Wn   = plt('slider',p{9} ,[.1  .01   10],'Cutoff frequency',@cb,5,'%3.2f 6 2');
  S.Wm   = plt('slider',p{10},[.3  .01   10],'frequency 2',     @cb,5,'%3.2f 6 2');
  set(gcf,'user',S);  cb; % save parameters for cb, and initialize the plot
% end function afiltS
The [2 .01 9] on the first slider has the same meaning as the similar pseudo edit parameter mentioned above, i.e. 2 is the initial value with min/max limits of .01 and 9. The @cb specifies the callback function. For the last two sliders, there are two additional parameters after the callback. The first of these ("5") indicates that the slider will move logarithmically (so for example the slider will move the same number of pixels going from .01 to .1 as it does when changing from .1 to 1. The final parameter '%4.3f 6 2' is shorthand for '%4.3f %6v %2v' and specifies the display format for the min value, current value, and max value respectively. For more details about the parameters for these pseudo sliders consult the Pseudo objects section.

All we have left to do to finish the coding is to write the callback. We could choose to write a stub that merely returns, or perhaps displays a string on the command window, as we did some of the callbacks in the previous example. That way we could get on with the task of positioning the objects in the figure window. The advantage is that sometimes just moving these objects around makes us realize we have conceived them poorly and inspires us to go back to the drawing board. In this case, since the interface is pretty simple, let's go ahead and complete the callback. The comments in the code should be sufficient to understand how the callback works:
function cb() % callback function for all objects
  S = get(gcf,'user');  N = plt('edit',S.n);             % get handle list and filter order
  dec = plt('pop',S.dec);  pts = 50*2.^plt('pop',S.pts); % get # of decades and # of points
  bi = plt('pop',S.band);                                % get filter band index (low,high,band,stop)
  Wn  = plt('slider',S.Wn);  Wm = plt('slider',S.Wm);    % get filter frequency & frequency 2
  Rp  = plt('slider',S.Rp);  Rs = plt('slider',S.Rs);    % get passband and stopband ripple
  if bi>2 Wn = [Wn Wm]; v = 'visON'; else v = 'visOFF'; end;
  X = round(log10(mean(Wn)) - dec/2 + .5);  X = logspace(X,X+dec,pts); % X-axis (radians/sec)
  plt('slider',S.Wm,v);       % hide or show frequency 2
  af(bi,N,Wn,Rp,Rs,X,S.tr);   % compute frequency response & set trace data
  cur(-1,'xlim',X([1 end]));  % set Xaxis limits
%end function cb
 The first six lines of the callback retrieve the parameters from every control. (We have to do this because cb doesn't know which control has been modified.) If we have selected low pass or high pass with the popup control (bi = 1 or 2) then we don't need the second frequency slider, so we turn it off ('visOFF'). If it is a bandpass or stopband filter (bi = 3 or 4) then we turn the second frequency slider on ('visON'). A lot happens in the next line, a for loop that sequences thru the five filter types (Butterworth, Bessel, Chebyshev types 1 and 2, and Elliptic), computing the frequency response by calling the af function and saving the results into the y coordinate of the respective trace. The last line sets the x-axis limits in case it was the number of points or the number of decades that had been modified.

Ok, after 27 lines of code we are done with the GUI design. The only remaining part is to write the af function which computes the frequency response of the five filter types. I won't discuss this function here since this is a purely computational function and does not add to our understanding of GUI programming (although this might be the most interesting part of the code for some).
Now when you type afiltS at the command prompt, you should see a figure similar to this. Although the program is essentially functional at this point, the haphazard arrangement of the controls makes that difficult to determine. So let's fix that now.

As with the previous example, we could allow the controls to be moved around by typing plt move into the command window, this time there is an easier way because we have included a plot in our design. Simply click on the red string plt move; that is in the upper right corner of the plot. You can tell that you are now in "move" mode because the pseudo sliders have been grayed out (as well as any uicontrols, although in this example there are none).

The "Order" control is far too tall, so start by shrinking that down to size using the right click and drag method. The gray box near the top is the frame, so move that to a spot resembling the sketch and then move the 4 controls inside it. When you get that frame as well as the 4 pseudo sliders positioned properly, you will have room to make the plot bigger, filling most of the figure area. After doing that you should click on the red "plt move;" again and you will notice that the TraceID box and the cursor controls now appear. (Initially, those elements were hidden because the plot was too small to allow room for them.). Then click on the red "plt move;" again to re-enable the move mode so you can make your final adjustments.

Once you get the 10 objects positioned as you want, as with the previous example, left-click once on every object in the order that they were created. As we are doing this, the text below will appear in the command window:
 xy:   1  .125 .105 .850 .760;  % axes
axi:  53  .108 .888 .228 .088;  % axes
edi: 211  .174 .936 .040 .030;  %   5
pop: 102  .110 .718 .150 .200;  % band pass
pop: 103  .314 .756 .050 .200;  % 3
pop: 104  .290 .716 .080 .200;  % 200
sli: 401  .350 .946 .150     ;  % Passband ripple
sli: 406  .510 .946 .150     ;  % Stopband ripple
sli: 411  .670 .946 .150     ;  % Cutoff frequency
sli: 416  .830 .946 .150     ;  % frequency 2
 Then as before, we copy and paste those lines into the beginning of the source code, remove the first two columns and add brackets so that the position arrays for a ten-element cell array. After editing the comments somewhat we have:

  p = {[.125 .105 .850 .760];  % plot    position
       [.108 .888 .228 .088];  % box     position: Parameter frame
       [.174 .936 .040 .030];  % edit    position: filter order
       [.110 .718 .150 .200];  % popup   position: filter band
       [.314 .756 .050 .200];  % popup   position: # of decades
       [.290 .716 .080 .200];  % popup   position: # of points
       [.350 .946 .150     ];  % slider  position: Passband ripple
       [.510 .946 .150     ];  % slider  position: Stopband ripple
       [.670 .946 .150     ];  % slider  position: Cutoff frequency
       [.830 .946 .150     ]}; % slider  position: frequency 2

After saving afiltS.m and then restarting the program, we should see something like this.

Notice the red text "plt move;" is no longer there in the corner of the plot window. (It appears only when the arrange function has been used to generate the positions.) But if you want to go back into positioning mode to make further adjustments (which will almost always happen) you can RIGHT click on the delta button near the lower left corner of the figure. (Or you can always type "plt move" in the command window.). RIGHT click on the delta button again to turn off the positioning mode and return to normal operation.

In this example, the 'xy' parameter in the plt command (line 4 of afiltS) included just a single array (p{1}) which specifies the position of the plotting axis. However the 'xy' parameter may include positions for other objects that are part of the plt super object, such as the TraceID box, the menu box, axis labels, and various cursor controls. (See the 'xy' command explained in the Axis properties section.) The OID (object ID) described in that section is the number in the 2nd of the two columns that we removed from the text that we pasted in from the command window. For example, suppose we wanted to make the Y edit box wider. Change to position mode, and use the mouse to resize that object. When you release the mouse button you will see a line in the command window such as:
 xy: 206  .786 .006 .140 .047;
 The "206" is the OID for the 'xy' parameter. If we add this row of 5 numbers (starting with the OID) to the xy parameter, then those coordinates will be used to position the Y edit box.

You can find more information about this simplified version (afiltS) as well as the more advanced version (afilt) discussed below in the afilt section.

Enhancing afiltS (creating afilt)

Play with afiltS for a while first to see that it behaves as you expect and then to see if you have any ideas for how you might enhance the program to make it more useful or pleasing to use. I came up with the seven enhancements below and after adding them to the code I renamed it to afilt.m. The original version afiltS.m is better for a gentle introduction to GUI design, but if you are interested in learning more following the coding of these seven enhancements below could be a could place to start.

1.) Adding a multi-line text string (elliptic transition ratio)

I was curious (mostly for the elliptic filter) how the width of the transition band (the space between the passband and the stopband) varied as the filter order changed as well as the four slider parameters. Could I define such a measure, figure out how to compute it and find a place on the GUI to display it?

For a low pass or passband filter, I characterized the transition width in terms of the ratio of these two frequencies:
• The frequency where the stop band spec is first achieved
• divided by the last frequency where the passband spec is still achieved
For a high pass or stopband filter, the ratio is:
• The frequency where the pass band spec is first achieved
• divided by the last frequency where the stopband spec is still achieved

I added an additional line to the position array at the beginning of the program, to define the location for the new text object. As with the other object positions, initially it was just a wild guess which was refined using the plt repositioning mode.

Once the transition ratio is calculated (saved in the variable h) it only takes this one line to convert it into a multi-line string:
set(S.etr,'string',prin('Elliptic ~, transition ~, ratio: ~, %5v',h));
 Because the sprintf function does not handle cell arrays, this operation normally would require several awkward lines of code, but this is simplified by using the prin.m supplied in the plt folder. To see the full documentation for prin simply type prin with no arguments into the command window.

2.) Selecting colors

My second idea was to allow the user to control at least some aspect of the color choices used in the application. Actually, I don't think such a simple application like this really needs this flexibility, but my ulterior motive was to showcase the ColorPick pseudo object and how easy it is to add to your GUI and how easy it is to select the color you find most pleasing for any display element.

This was one of the simplest of the five enhancements requiring just the three extra lines shown below. I decided to enable color adjustment of five screen elements. The first four are the pseudo sliders at the top edge of the figure that control the pass and stop band ripple and the cutoff frequencies. The fifth element to gain this color adjustment feature is the multi-line text string (elliptic transition ratio) mentioned above. (It would be easy to extend this to other graphic elements.) In this figure, I have changed the background color of the sliders from its default gray to orange. I encourage you to play around with this ColorPick figure. (Just right-click on any of afilt's pseudo sliders or the multi-line text string to bring up ColorPick.) If you have ever dealt with the frustrations of assigning screen colors, I think you will be pleasantly surprised about how easy it can be. (Read about the details of the ColorPick object here: Pseudo objects)

The first line (below) puts the handles of all the objects associated with pseudo sliders into "h". Then the 5th element of each slider is removed, since that is the edit box portion which generally is set to a contrasting color. The second line assigns the ColorPick object as the buttondown function (the right click action) to the 4 pseudo sliders as well as to the elliptic ratio text object. If we stopped there (i.e. omitted the third line) then we would be able to pick 16 different colors for the sliders (i.e 4 components for each of the 4 pseudo sliders). However, that's more flexibility than we probably would want and actually might be a nuisance. It's probably better to tie all 16 of those objects together so that when you change any one of them, the other 15 automatically change to agree with the new color. The third line accomplishes that by setting an application data variable as explained in the ColorPick Pseudo object documentation.
h = plt('slider',S.sli,'get','obj'); h(:,5)=[]; h = h(:);
set([h; S.etr],'buttondown','plt ColorPick;');
for k = 1:length(h) setappdata(h(k),'m',{'backgr' h}); end;

3.) Adding the phase display

Since the phase of the frequency response is an important characteristic in some applications, I thought it would be useful to have the plot show phase as well as amplitude. So far we have only used the left-hand axis, so the easiest way to show the phase information is to plot it on the right-hand axis. Usually, the right-hand axis is used to superimpose traces on the left hand axis, although in this instance I decided it would be clearer to plot the phase above the magnitude so the traces would not overlap. That choice allows us to display both magnitude and phase for all five filter types at once without the display becoming hopelessly cluttered and confusing. This is accomplished by adjusting the left and right Yaxis limits so that the magnitude and phase traces don't overlap. The tick mark labels are set explicitly so that they only appear in the valid range of the display. Here is an example of what the display looks like when a single filter type (Cheby1) is enabled.

It would have been natural to expand the TraceID box to include 10 elements instead of 5 however that has the following drawback. To enable or disable the traces associated with a particular filter type (Cheyb1, for example) one would have to click on two traceID tags, one for the magnitude and one for the phase. That is somewhat unnatural and it would be less cumbersome to do this with just a single click. The trick to make this happen is to link traces 1 and 5 (Butterworth mag/phase) and to link traces 2 and 6 (Bessel mag/phase), etc. so that 5 is enabled only when trace 1 is enabled, etc. This linking can be done in a single line inside the callback function, and I will show that line in section 4 below since that enhancement also involves linking traces.

4.) Adding the pole/zero plot

The analog filters are designed by first designing a prototype filter (always low pass) and then transforming it as selected (high pass, stopband, passband) with the requested filter cutoff frequencies. Displaying the roots of the prototype filter in the s-plane can help our understanding of the design and use of the five filter types displayed by afilt. Alternatively, you may display the poles and zeros of the filter after it has been transformed by unchecking the prototype checkbox above the pole/zero plot.

I could have used plt's subplot parameter to create both plots (Mag/Phase plot on the left and Pole/Zero plot on the right all with a single call to plt. However because the characteristics of these two plots are so dramatically different, this would have been difficult and I decided to use a separate call to plt for each of the two plots. This necessitated using the Fig parameter on the second call so that both plots are created in the same figure window. Like the Mag/Phase plot on the left, the Pole/Zero plot uses 10 traces (the first 5 to display the zeros for the 5 filter types, and the next 5 to display the poles. Actually, an 11th trace was added to show a unit circle on the plot. A unit circle doesn't have the significance it does on z-plane plots, but it still provides a useful size reference, and some of the filter types have roots placed along or near the unit circle. Usually, the x/y cursor readout appears below the plot, but for this plot, they were moved to the top because that's where there was the most room (allowing for a bigger plot in a given figure size). If you enable only the Bessel filter traces, the figure will look like the one shown here. Note that the Bessel filter has only poles (X's on the plot) whereas some of the filter types will have both poles and zeros (X's and O's).

This single line was added to the callback function so that if you enabled or disabled the magnitude display of a certain filter type (Butterworth for example) then the associated phase trace, zeros trace and poles trace would also be appropriately enabled or disabled:
t = plt('show');  t = t(find(t<6));  plt('show',[t t+5 t+10 t+15 21]);
 % for traceID callback
 t (using the find subscript) will contain a list of magnitude traces that are enabled, while t+5 will be a list of the associated phase traces, t+10 will be a list of the associated zeros traces, and t+15 will be a list of the associated poles traces. Trace 21 (showing the unit circle) is always enabled.

Often when the TraceID callback is a simple one or two-line function, it is more convenient to include the entire TraceID callback in the plt parameter list. Look at the function afiltALT.m (an alternate version of afilt also supplied in the demo folder) for an example of how this is done.

5.) Display of phase delay and group delay

Since the figure window is already becoming a bit crowded, I decided to display the phase delay and group delay in separate figure windows. LEFT clicking the button shown here above the prototype checkbox opens a new figure window showing the phase delay for all 5 filter types using all the currently selected parameters. This is essentially the phase response divided by the frequency at each point. RIGHT clicking the button opens yet another figure window showing the group delay for all 5 filter types. This is essentially the derivative of the phase response.

6.) Saving/restoring the GUI state using a configuration file

It would be nice if whenever we made a change to the figure size/position or the color selection, that these changes would be recorded so that the application looks the same the next time it is restarted. While we are at it, we might as well remember the state of the eight filter parameters (shown above the plot) so that on start up, the figure looks identical to the way it was when it was shut down. Also, we save the numerators and denominators of the s-plane polynomials for all 5 filter types that were used to generate the frequency response on the display when the figure was shut down.

To implement this, before the call to plt, let's choose a file name and path for saving the configuration data:

S.cfg = [which(mfilename) 'at'];

Next let's add a new function, called cfg which saves the current configuration to this file as a 11 element cell array:
function cfg() % write configuration file
  S = get(gcf,'user');  s = plt('slider',S.sli(1),'get','obj');
  cf = {plt('edit',S.n);    plt('pop',[S.band S.dec S.pts]);  plt('slider',S.sli);
        get(s(2),'backgr');  get(S.etr,'color');              get(gcf,'position')};
  num = get(S.tr(1),'user');  den = get(S.tr(2),'user');
  save(S.cfg,'cf','num','den');
Then right before we initialize the plot, we load the configuration file if it exists and set the GUI elements to agree with the data in the file:
  if exist(S.cfg) load(S.cfg);
                  % load configuration file if it exists -------------------
                  plt('edit',S.n,'value', cf{1});
                  plt('pop',[S.band S.dec S.pts],'index',num2cell(cf{2}));
                  plt('slider',S.sli,'set',num2cell(cf{3}));  set(h,'background',cf{4});
                  set(S.etr,'color',cf{5});                   set(gcf,'position',cf{6});
  end;
And finally we add this parameter to the plt call:

'closeReq',@cfg

This instructs plt to call the function that saves the configuration data when the user closes the figure window to exit the application.

7.) Adding temporary user help message

To define the help text shown in the figure below, these four lines were added to the beginning of the afilt routine:

htxt = {'Select the filter order & type' 'in the parameter box above.' '' ...
        'Vary the ripple & frequency' ' parameters using the sliders.' -1.28+.72i 2i ...
        's plane'  .4+.2i 'color' 'yellow' 'fontsize' 20 2i ...
        'Prototype filters' '(low pass with' '1 rad/sec cutoff)' .05+.98i};

This help text allows a new user of the program to get started without having to consult any help files or manuals. The most important consideration should be that the help messages are not distracting in any way to the user who is already familiar with the help information presented. The HelpText pseudo object is ideal for this task since it provides a mechanism for removing the messages once you start using the program.

Note that we have defined three help strings within the htxt cell array, each delimited by 2i (which actually may be any complex number).

The first of these strings consists of the 5 lines that appear inside the left-hand plot (the 3rd line is a blank line). The -1.28+.72i after those five lines indicates that the upper left corner of the 5-line help text should be positioned at x = -1.28 and y = .72 (in normalized coordinates relative to the pole/zero plot axis). Since no color was specified, the default helptext color was used (orange).

The second help string consists of just the one line "s plane" and is positioned at x = .4 and y = .2 (again in normalized coordinates in the pole/zero plot axis). A font size of 20 and a color (yellow) was specified for this string. (Note that instead of the string 'yellow' we could have used the traditional Matlab color triple vector ([1 1 0]) or the alternate vector form allowed in plt calls ([100 100 0]) or by the single number 010100 (the most compact way of specifying colors in plt parameters). These alternate ways of specifying colors are explained more completely at the top of the Colors help section.

Finally, the last help string consists of three lines that appear inside the pole-zero plot (again using the same reference frame for the xy position as the previous two help strings and again using the default orange help string color).

Then this line was added at the end of the afilt routine to make the help text visible on the screen:

plt('HelpText','on',htxt); % show help text

And lastly, this line was added to the end of the callback function (cb):

plt('HelpText','off');

That line ensures that as soon as the user starts doing anything with the program, the HelpText will disappear so that it does not become a distraction.

All the extra features added to afilt expanded the code considerably from the simpler afiltS version and it now consists of 176 lines of code. If one were to code an application with all the same features using Matlab without the pseudo objects provided by plt, the source code would like span two or three times as much code, and probably even more than that if you chose some other programming language. (GUI programming is notorious for its complexity.)

This concludes our discussion of the afilt example. To further your education in GUI programming with plt, I recommend reviewing the applications in the sig and math folders. Most of them have a rich GUI design and use the various plt features in interesting ways. Many of them also involve moving plots, which will be especially helpful if that is something you need for an application you are creating.


Cursor commands

The cursor pseudo object, the most complex pseudo object supported by plt, consists of one or more markers and several uicontrols with complex interactions. Because of this you normally will not create cursor objects on your own, but will rely on plt to create them automatically when you specify the data to be plotted. However, it is possible to create a cursor object manually with the cursor init command described at the end of this section. For many casual plotting applications, the plt cursors will behave as desired out of the box. For more sophisticated applications you may want to modify the cursoring behavior using the plt parameters (DualCur, Xstring, Ystring, Options) or the independent cursor commands shown below.

DualCur parameter:

Normally the cursor value for only a single trace (referred to as the active trace) is shown in an edit box below the plot. However, sometimes it is convenient to show the y-value for two traces simultaneously. This is done by using the 'DualCur' parameter which specifies the trace to display its y-value on the screen (in addition to the display of the active trace). Try out the editz.m demo program which uses Dual Cursors to simultaneously display the magnitude and phase of a transfer function. (In fact, transfer function displays were the problem that inspired the Dual Cursor feature.) The alternate method of specifying the dual cursor trace (as an offset from the active trace) is indicated by using a negative number for the DualCur parameter. The demo program afilt.m demonstrates the use of that mode. The use of the DualCur mode is covered in more detail near the end of the cursoring guide found here: Cursoring.

Xstring/Ystring/Zstring parameters:

The 'Xstring' and 'Ystring' plt arguments allow you to add text strings just to the right of the cursor X or Y readout values. Since these strings occupy the same screen area as the delta cursor readouts, they get covered up when you are in delta cursor mode (or if a zoom window is visible). However those are usually temporary modes, so as you will see, these strings still prove useful. Also, those are only the default positions for these strings. As you will see later, you can easily move these strings anywhere in the figure. The 'Zstring' parameter is similar except the default position for the string is near the center of the lower edge. This might overlap with the Xaxis label, so some repositioning might be required either of the Zstring parameter or the plot position (using the 'xy' parameter).
String | Replacement value
@CID | cursor ID
@XVAL | active cursor X position
@YVAL | active cursor Y position
@ZVAL | Z value at cursor index position
@XY | same as complex(@XVAL,@YVAL)
@IDX | active cursor index
@HAND | handle of active trace
@LNUM | line number of active trace
@Q1 | getappdata(gcf,'Q1')
@Q2 | getappdata(gcf,'Q2')
@Q3 | getappdata(gcf,'Q3')
@Q4 | getappdata(gcf,'Q4')
@V1 | getappdata(gcf,'V1')
@V2 | getappdata(gcf,'V2')
@V3 | getappdata(gcf,'V3')
@V4 | getappdata(gcf,'V4')

Most of the power of the Xstring and Ystring parameters stem from their string replacement feature described in this table. Strings in the first column of the table are replaced with the value shown in the second column.

@Q1 thru @Q4 can be used to provide additional arguments to the Xstring/Ystring/Zstring parameters. You can set a value to one of these arguments with a command such as setappdata(gcf,'V1',ArgValue), or as you will see in the examples below you can specify the value of the argument in the XYZ string by using its cell array form. (The latter is the most concise method in the situation where the argument value is known at the time the XYZ string is created and the argument value will not be modified.)

@V1 thru @V4 are similar except that they are assumed to be vector quantities and they must be followed by a parenthetical expression containing an index into the vector. For example, you can use something like @V1(4), or @V1(@IDX). If you have used up all the scalar quantities (Q1 thru Q4), you could put a scalar quantity into V1 (for example). Then you could access the scalar within an XYZstring by using @V1(1).

The replacement table to the right shows that @Q1 is replaced by getappdata(gcf,'Q1'). That is a simplification, in that it is actually replaced by plt('misc',21) which has the same effect as the getappdata command, but has the advantage that it will work in a multi-figure application even when the focus might be on a figure that does not contain the application data. (If the 'misc' command does not find the application data in the current figure it looks at all linked figures to find the data). Similarly, if you change the 21 in the 'misc' command to 22,23,24,11,12,13,14 it will retrieve the value for Q2,Q3,Q4,V1,V2,V3,V4 respectively.

Notes:
  • The Xstrings, Ystrings, and Zstrings are all updated every time the cursor is moved.
  • You might think that the Xstring/Ystring/Zstring parameters would be related to the x,y,z trace data respectively. Often they do, however, you can use these three strings to display whatever information you want to.
  • If you are using the default colors, the handles to the Xstring, Ystring, and Zstring can be with findobj(gcf,'color',[.7 .8 .94])

Xstring/Ystring Examples:

Suppose it was important to see the cursor index as well as the usual cursor x and y values (i.e. you want to know that you are looking at the sixty-fifth data element for instance). You could do this as follows:

plt(x,y,'xstring','sprintf("index = %d",@IDX)');

A string within a string (such as the 'index = %d' above) is normally written in Matlab using two consecutive single quote characters on both sides of the string. Since this can get verbose and confusing at times, callbacks defined within plt may use a double quote character instead of two successive single quotes. That's why the double quotes appear in the line above.

If the default color and position of the Xstring is not suitable, you could change them by using the cell array form, for example:

plt(x,y,'xstring',{'sprintf("index = %d",@IDX)' 'color' [1 0 0] 'pos' [x y]});

You could also include additional text string properties in this list such as 'FontSize' and 'FontName' for example. The easiest way to determine the [x y] position is to enter the plt reposition mode by right clicking the Δ button (or by typing plt move into the command window) and then drag the Xstring to the desired location with the mouse. Then the required [x y] position will be reported in the Matlab command window.

Although the mean of the active trace y values can be shown using one of the usual cursor features, suppose you wanted to display the mean of the entire data set (independent of the viewing window). Suppose also that you want to continuously display the y/x ratio. (This ratio is also a standard cursor feature, but it's not continuously visible.) You could accomplish both of those feats as follows:

plt(x,y,'xstring','sprintf("mean: %f",mean(get(@HAND,"y")))',...
'ystring','prin("Y/X: %5w",@YVAL/@XVAL)');

Suppose your x axis is measured in seconds with a zero reference of 5pm, 21-Jan-2007 UTC. The cursor x-axis readout will be in seconds past the reference, but you may want an additional cursor readout that shows the actual time of day. This can be accomplished as follows:

plt(t,y,'xstring',...
'sprintf("utc:%s",datestr(datenum("21-Jan-07 17:00")+@XVAL/86400,13))');

Note the 86400 (the number of seconds in a day) is needed because date numbers are measured in days. If your x axis unit was "weeks", you would replace /86400 with *7. If you removed the ,13 near the end of the line (date string format), then the readout would show the complete date and time instead of just the time. Another way to code the statement above is:

plt(t,y,'xstring','sprintf("utc: %s",datestr(@Q1+@XVAL/86400,13))');
setappdata(gcf,'Q1',datenum(2007,1,21,17,0,0));

The second statement puts the reference time in a figure application data value which is used by plt when updating the Xstring. This method is much more convenient when the reference time can change. Note that the reference time is identical to that used above, although it's written in the vector format instead of the character format.

A more convenient way of writing the above command when the value of Q1 is not going to change is to use the cell array form as follows:

plt(t,y,'xstring',{'sprintf("utc: %s",datestr(@Q1+@XVAL/86400,13))' ...;
'Q1' datenum(2007,1,21,17,0,0) 'color' [1 1 0] 'fontsize' 12});

Sometimes the 1 second resolution provided by datestr is not sufficient. You can increase this resolution to 1 millisecond by using the date string function provided by plt as follows:

plt(t,y,'xstring','sprintf("utc: %s",plt("datestr",@Q1+@XVAL/86400,13))');

Occasionally its useful to use an edit box instead of a string for one or both of these customized cursor controls. (The pltn.m example does this for the Xstring, although the Ystring is still rendered as a text string.) To do that, simply insert a question mark before the string. The first example above is rewritten below to use an edit box.

plt(x,y,'xstring','?sprintf("index = %d",@IDX)');

See the following applications and demo programs, for more examples of the use of the Xstring/Ystring/Zstring parameters:
math folder: | airspeed, carlo, curves, gpsLog
sig folder: | afiltALT, editz, erip

Cursor commands

Notes: The cid (cursor ID) that appears in all the commands shown below is an integer that identifies the cursor the command is to act on. This integer is returned from the cursor initialization command used to create the cursor. If an axis contains a cursor, its cid is saved in the axis user data. (The cid stored in the axis user data is always a scalar since an axis may only contain a single cursor object.) You can specify that the cid should be retrieved from the axis user data by specifying a zero for the cid. So for example the following two commands have the same effect:

cur(0,'visON')
cur(get(gca,'user'),'visON')
Although cid is usually specified as a number, you may also specify it as a decimal character string. This allows many of the cursor function calls to use the Matlab command syntax. For example, these three lines have the same effect:

cur(1,'visON')
cur('1','visON')
cur 1 visON
The figure 'cid' application data variable contains a vector with the cursor IDs for all the cursor objects in the figure. You can specify that the cid should be retrieved from this vector by supplying a negative number as the cid (for example -2 specifies the 2nd element of this vector). This means that the following two lines have the same effect:

xy=cur(-2,'get');
c=getappdata(gcf,'cid'); xy=cur(c(2),'get');
All the following commands are case-sensitive (unlike all the other plt parameters previously described) and must use the exact case shown below.
All the cursor commands below may return up to two arguments. If the return arguments are listed for a cursor command, the return values will be as specified. However if the return arguments are not listed for a particular command, the first return value (if requested) will be the active cursor handle and the second return value (if requested) will be the active line handle.
See the following programs for examples of the use of the cursor commands described below:
demo folder: | movbar, pltmap, pltn, pub2, trigplt
math folder: | airspeed, circles12, gauss, gpsLog, julia
sig folder: | afilt, editz, erip, fseries, weight
util folder: | pltwater

[xy k] = cur(cid,'get',n);
Get x and y coordinates of the cursor location the last time it was on trace #n. The trace number is optional - if it is not specified then the position of the active trace is returned. xy is a complex value. Its real part is the cursor x-coordinate and its imaginary part is the y coordinate. The second return value (if requested) is the index into the x data vector of the cursor position.

[z k] = cur(cid,'get','z');
A special form of the get command is when the last argument is a string. In this case the z value of the active trace as the current cursor index is returned.

[n h] = cur(cid,'getActive');
Returns the line number of the active cursor. The second return value (if requested) is the handle of the active trace.

h = cur(cid,'obj');
Returns a 16-element vector of handles to the following cursor objects:
1: x label | 5: y cursor readout | 09: marker/linestyle button | 13: main (left) axis
2: y label | 6: y cursor expansion | 10: delta button | 14: right hand axis (0 if none)
3: x cursor readout | 7: peak button | 11: expansion box | 15: current cursor marker
4: x cursor expansion | 8: valley button | 12: delta cursor | 16: TraceID axis (0 if none)

u = cur(cid,'expHis');
Returns an array containing the display expansion history.
Each row contains one display expansion as [xmin, xmax, ymin, ymax, code] where:

  • code = -1 indicates the row was not used
  • code = 0 indicates a valid display expansion
  • code = 1 indicates the current display limits

cur(cid,'visON');
cur(cid,'visOFF');
Shows or hides the following objects:

  • peak/valley/delta cursor buttons
  • active trace cursor
  • auxiliary trace cursor (dual cursor)
  • x and y axis edit boxes and respective labels
  • xstring and ystring objects
  • x-cursor slider
  • cursor id string

Note that this function is invoked alternately (visOFF/visON) when you right-click on the plot y-axis label (which also hides/shows the menu box).

cur(cid,'aux','on');
cur(cid,'aux','off');
Shows or hides the auxiliary (dual) cursor and its edit box

cur(cid,'setObjPos',p);
Sets the cursor object positions to p, where p is a 9 by 4 element array. Each row contains (x,y,width,height) which represents the position and size of the following objects:

  1. x-axis edit box label
  2. y-axis edit box label
  3. x-axis edit box (cursor readout)
  4. x-axis cursor expansion edit box
  5. y-axis edit box (cursor readout)
  6. y-axis cursor expansion edit box
  7. peak button
  8. valley button
  9. delta cursor button

Note that this command does not set the position of the optional x-axis control slider. However you can set this position using the plt 'xy' parameter, or with a command such as:
set(findobj(gcf,'tag','xslider'),'position',p);

cur(cid,'xlim',p);
Set new x axis limits and update expansion history, where p=[xmin,xmax] Normally the action string ('xlim' in this case) is case insensitive, but for this command the case of the 'L' character is significant. If the L is lower case then the axis callback function is executed after the axis limits have been updated and if the L is upper case then the axis callback function is not executed. The case of the other characters in the action string are ignored (so 'xLim' and 'XLIM' are equivalent). This sensitivity to the case of the L character applies to the next two cursor commands described below as well ('yLim' and 'xyLim')

cur(cid,'ylim',p,pAux);
Set new y axis limits and update expansion history, where p=[ymin,ymax].
and optionally pAux=[ymin,ymax] (for the right hand axis).

cur(cid,'xylim',p,pAux);
Set new x and y axis limits and update expansion history, where p=[xmin,xmax,ymin,ymax].
and optionally pAux=[ymin,ymax] (for the right hand axis).

cur(cid,'exRestore',u);
Restores an expansion history previously saved in u.

cur(cid,'axisCB',fcn);
String fcn will be evaluated whenever an axis limit is changed. This cursor command overwrites any axis callback function entered using the 'axisCB' parameter on the plt command line. The rules for string substitutions and function handles are the same as mentioned below in the moveCB command.

cur(cid,'moveCB',fcn);
String fcn will be evaluated whenever the cursor is moved. Before the fcn string is evaluated all occurrences of the strings in the 1st column of the table above ( @CID, @XVAL, @YVAL, @XY, @IDX, @HAND, @LNUM, etc,) are replaced with the values in the 2nd column of that table. fcn is not called by events initiated from outside the figure window containing the cursor. (For example, a button push that moves the cursor in another figure window would not activate the callback. This prevents infinite loops when figure A modifies figure B's cursor and visa versa.) If you do want to enable the callback for external events, insert an extra semicolon as the first character of the moveCB callback string. This cursor command overwrites any axis callback function entered using the 'moveCB' parameters on the plt command line. In addition to a string, fcn may also be a function handle of the form @func or {@func,arg1,arg2,...,argn}. Note that the string substitutions can't be used with the function handle form of this parameter. Also note that a similar callback is provided for the TraceID fields, although the string substitutions allowed are different than the ones mentioned above. See the 'TIDcback' parameter under Trace properties.

cur(cid,'moveCB2',fcn);
This call operates similarly to the set moveCB command shown above and the functions specified in both these calls are executed whenever the cursor is moved. However, normally you will not want to use this call because the moveCB2 function is used internally by plt to keep the CursorID tag (just to the left of the y-axis cursor readout) so that it always identifies the cursored trace name. It's also used by plt in the linked subplot mode to keep the plots in a column synchronized. In rare situations, you may wish to modify those behaviors, which you can do with the moveCB2 function.

cur(cid,'setActive',a,k);
Switches the active cursor to the line specified by a (a must be an integer between 1 and the number of lines in the plot). The cursor will be placed at index k. If k (optional) is out of bounds or not supplied, then the cursor will be placed in the center of the array. When a is zero (a special case), the active line remains the same and only the cursor index is changed - which would have the same as calling the update command (below). Normally the cursor callback function will be called, but you can suppress the call to the callback by supplying the index (k) and setting it to the negative of the desired index location.

cur(cid,'update',k);
cur(cid,'updateH',k);
cur(cid,'updateN',k);
Moves the active cursor to index k in the data set and calls any user-defined cursor callbacks (moveCB, xstring, ystring). If k is out of bounds, the cursor is set to the middle of the array associated with the active trace. If you do not supply the argument k, then the command does not move the cursor, however, it does execute the cursor callbacks. If update moves the cursor to an area that is not inside the current axis limits, it will shift the axis limits to make the cursored data element visible. However when the cursor is moved by updateH the axis limits will never be adjusted. (Think of this as "Update, Hold".) Also updating the cursor with the updateN command has the same effect as using the updateH command except that the cursor callback function (defined by 'MoveCB') is not called like it is with the update and updateH commands.

When the index is not needed we can abbreviate the update command by omitting the 'update' string. This means that the following two lines are equivalent:
cur(cid);
cur(cid,'update');
We can abbreviate the update command even more by omitting the cursor ID which defaults to -1. This means that the following two lines are equivalent:
cur;
cur(-1);
And finally, there is one more variant of the update and updateH commands:
cur(cid,'update',k,x,y);
cur(cid,'updateH',k,x,y);
The moves the active cursor to the index k as above. Normally x and y would be the position of the kth element of the array associated with the active trace, in which case this command behaves the same as if you didn't include the last two parameters. However x and y can be any position on the axis, and the visible cursor marker will be moved to those coordinates. (It's rare to want to move the cursor off the line, but it may sometimes be useful.)

cur(cid,'updateS');
Updates the Xstring, Ystring, and Zstring text without moving the cursor or calling any other callback functions. This is most useful after modifying one of the @Q or @V variables that affect the contents of one or more of the XYZstrings.

cur(cid,'peakval',0); Moves the specified cursor to the next peak
cur(cid,'peakval',1); Moves the specified cursor to the next valley
cur(cid,'peakval',2); Moves the specified cursor to the highest peak
cur(cid,'peakval',3); Moves the specified cursor to the lowest valley
You can also use the command form of these commands. For example, if you are referring to cursor associated with the current axis, the 'lowest valley' command above could be written as cur 0 peakval 3;

cur(cid,'clear');
All the cursor objects are deleted.

cur(cid,'TraceIDs');
Returns a cell array containing the original TraceIDs associated with cursor cid. If the TraceIDs were modified (by using the plt('rename') command for example), those changes will not be seen with this cursor command. For example, when running the demo program afilt.m, if you click on the left hand plot and then type cur(0,'TraceIDs') or the command form cur 0 TraceIDs you will see:
ans = 5x1 cell array
{'Butter'}
{'Bessel'}
{'Cheby1'}
{'Cheby2'}
{'Elliptic'}

cur(cid,'Emode',mode);
Puts the cursor specified into the permanent erase mode specified by the case-insensitive string argument mode as follows:
mode | action
'UpDown' | Allows the edit cursor point to move only in the y-axis direction. Only the first character of the mode parameter is used, so 'UpDown' is equivalent to 'UD', or just 'u'. This command has a similar effect to right clicking on the last selection in the popup menu shown below (except that action toggles this persistent data editing mode on or off). See the Data editing section for details.)
'LeftRight' | Allows the edit cursor point to move only in the x-axis direction. Equivalent to 'lr' or 'L' for example.
'Both' | Allows the edit cursor point to move in both the x and y directions. Equivalent to 'B' or 'b' for example.
'Off' | Turns off any persistent editing mode that was initiated by one of the above 'Emode' commands or that was initiated by right clicking any of the bottom three selections of the popup menu shown at the right. (Note: This is the popup that appears when you right click on the Ycursor edit box. Again, see the Data editing section for details.) Equivalent to 'O' or 'o' for example. See the editz.m, erip.m, and hermite.m applications for examples of the use of the cursor Emode commands.

plt hideCur;
Hides the cursor objects. (This has the same effect as right-clicking on the y-axis label). See cursoring.

plt hideTID;
Hides all the TraceID boxes in the current figure

Note: The following cursor commands were designed primarily for plt internal use, although sometimes they may also be useful in your programs. (These commands are case-sensitive.) The "0" in the first seven commands below refers to the current cursor. You may replace the "0" with the actual cursor ID number, or "-n" to refer to the nth cursor.

plt cleft 0 ZoomOut; | Zoom out both x & y axis by 40%.
plt cright 0 ZoomOut; | Zoom in both x & y axis by 40%. With the functional form (which applies to the command above as well), you may also include an additional argument that specifies the zoom ratio. For example, this command specifies a 20% ratio (half of the default amount): plt('cright',0,'ZoomOut',.2);
plt cleft 0 peakval 0; | Move the cursor to the next peak. (The last argument may be omitted in this case.)
plt cleft 0 peakval 1; | Move the cursor to the next valley
plt cleft 0 peakval 2; | Reset the peak finder (i.e. move the cursor to the highest peak)
plt cleft 0 peakval 3; | Reset the valley finder (i.e. move the cursor to the lowest valley)
plt cleft 0 TGLlogy; | Toggle the y-axis between linear/log
plt cleft 0 TGLlogx; | Toggle the x-axis between linear/log
plt cright 0 TGLlogy; | Open Hardcopy menu
plt cright 0 TGLlogx; | Swap x & y axes
plt cleft 0 markCB; | Toggle the delta cursor mode on or off
plt cleft 0 mlsCB; | 3-way toggle of all traces between markers only, lines only, and both lines & markers
plt cleft 0 mark; | Adds a text label identifying the current cursor location
plt xleft TGLgrid; | Toggle between grid lines and ticks
plt xright TGLgrid; | Toggle between default and alternate grid style
plt xleft data; | Open a cursor data window
plt xright data; | Toggle the menu bar on/off
plt xleft mark 2; | Open a window allowing editing plt figure colors
plt xleft mark 3; | Write a file saving the current plt figure colors
plt xleft link; | Toggle right hand axis link status
plt xleft RMS; | Equivalent to clicking on the cursorID tag which rotates between the five cursor modes [normal, Avg, RMS, y/x, sqrt(x^2+y^2)]. After five of these commands the cursor mode will be the same as it was before the first of those commands (having rotated thru all the modes).
plt xleft EDIT 1; | Enter data editing (using last used editing mode)
plt xleft EDIT 2; | Open up Yedit popup menu
plt xleft EDIT 5; | Exit data editing mode
plt xleft Yedit 1; | Open a window allowing editing the line properties of cursored trace. (The command plt xright mark;) also does the same thing.
plt xright Yedit 1; | Open a window allowing editing the plt figure properties
plt xleft Yedit 2; | Toggle multiCursor mode
plt xleft Yedit 3; | Toggle xView slider
plt xleft Yedit 4; | Cancel data editing mode
plt xleft Yedit 5; | Enter data edit mode (Range)
plt xleft Yedit 6; | Enter data edit mode (Range left/right)
plt xleft Yedit 7; | Enter data edit mode (Range up/down)
plt xleft Yedit 8; | Enter data edit mode (Modify)
plt xleft Yedit 9; | Enter data edit mode (Modify left/right)
plt xleft Yedit 10; | Enter data edit mode (Modify up/down)
plt xright Yedit 8; | Toggle persistent data edit mode (Modify)
plt xright Yedit 9; | Toggle persistent data edit mode (Modify left/right)
plt xright Yedit 10; | Toggle persistent data edit mode (Modify up/down)

Mouse motion functions:

If you create a figure with a plt command that includes the parameter 'MotionZoom','funcname', then if you create a zoom box (see The expansion box) while you are adjusting the size of the zoom box the function funcname([x1 x2 y1 y2]) will be continually called as the mouse is moved (i.e. for as long as the mouse button is held down). The coordinate [x1 y1] is the position of the lower left corner of the zoom box and [x2 y2] is the coordinate of the upper right corner. It may require some imagination to see how using such a parameter would enhance your user interface. The example gauss.m (in the demo folder) demonstrates the use of the MotionZoom function, athough its use of the MotionZoom feature is somewhat contrived in that it was thrown in there to demonstrate the feature more than for any practical reason. The pltmap.m example (in the demo folder) also makes use of the MotionZoom feature, and in this case it does provide a useful benefit.

In place of the character string 'funcname' you may also use @funcname or to insert extra parameters to the function, use {@funcname param1 param2}. (The 4-element vector specifying the zoombox corners will be the 3rd parameter of the function in this example.) These alternate forms also apply to the other mouse motion functions

Including the parameter 'MotionZup','funcname' has a similar effect except that the function 'funcname' only is called when the mouse button is released. The MotionZoom and MotionZup functions are called when the zoom box is moved or resized as well as when it is first created.

You may also create, modify, or remove these mouse motion functions after the call to plt by modifying the corresponding application data variable associated an axis. For example, these commands will set the mouse motion functions as expected:
setappdata(gca,'MotionZoom','funcA');
setappdata(gca,'MotionZup',@funcB);

Three additional mouse motion functions (MotionEdit, EditStart, EditStop) may also be defined and are described in the Data Editing section.

Creating a cursor pseudo object:

Usually the cursor objects are initialized from the main plt() call that specifies the data arrays to be plotted. However, you may also create the cursor objects using this cursor 'init' call after creating a figure on your own (i.e. without using the plt pseudo object).

Ret1 = cur(axis,'init',In1,In2,In3,In4,In5,In6,In7,In8,In9);

where:

  • Ret1: the cursor ID (cid) used to control the cursor with additional calls to plt.
  • axis: a scalar if the cursor is assigned to a single axis. A two-element row vector is used to assign the cursor to a pair of axes. The second axis (right-hand axis) is normally overlaid on top of the primary axis and is used to provide a separate y-coordinate axis on the right side of the graph.
  • In1:
    Row | [x y w h]
    1 | label for x cursor readout
    2 | label for y cursor readout
    3 | x cursor readout
    4 | x cursor expansion
    5 | y cursor readout
    6 | y cursor expansion
    7 | peak find button
    8 | valley find button
    9 | line markers button
    10 | delta cursor button
    11 | x-axis cursor slider (optional)
    an 11 by 4 array containing the positions of the cursor controls. Each row contains [x y w h] where x,y is the position of the control and w,h is its size. The units for all the values in the In1 array must be either pixels or normalized (no mixing units). The last row specifies the position of the slider and is the only optional row. If In1 contains only ten rows then the x-axis cursor slider will not be created. The rows of In1 are assigned as described in this table.
  • In2:
    Row | [r g b]
    1 | x/y cursor label color
    2 | cursor readout color [*1]
    3 | expansion box color
    4 | delta cursor color
    5 | line #1 cursor color [*1]
    6 | line #2 cursor color [*1]
    ... ............................... [*1]
    4+n
    (Must have a row for
    each line object
    that has a cursor)     		line #n cursor color [*1]
    a 3-column array [red green blue] specifying the colors for the cursors and cursor readout text. The rows of In2 are defined as shown in this table.

    [*1] Set to [0 0 0] to make the color of this element track its associated line color.
  • In3: Text for the x/y cursor labels. ['xlabel';'ylabel'] or {'xlabel', 'ylabel'}
  • In4: Cursor markers. Must have one marker for each line that has a cursor.
        For example for 3 lines one could use '+ox'
        or to use the same symbol for all 3 cursors use '+++'.
  • In5: Cursor size (in points)
  • In6: Format strings for x and y cursor readouts.
        e.g. ['%2.1f'; '%5w '] or {'%2.1f','%5w'}.
        (Type "help prin" for a description of these format strings.)
  • In7: Visible flag (first optional argument). 'on' or 'off'.
  • In8: (optional) Monotonic flag.
        Set to 1 if the x data is monotonically increasing, and 0 otherwise (such as with Nyquist plots).
  • In9: (optional) Axis limit change callback function.
        Executed when the axis limits are changed.

Notes:

When the cur('init' function is called, plt will attempt to add cursors to all lines of the axis created by plt. If you want plt to skip adding cursors to some of the lines, you should tag the line with the string 'SkipCur'. For example, a cursor would not be created for a line created with the following command: line(x,y,'tag','SkipCur');
Another way to restrict which lines are to be cursored is to add the application data key 'Lhandles' to the axis. (For example: setappdata(ax,'Lhandles',[h1 h3]); would tell the cursor initialization routine to add cursors only to those two handles.)


Pseudo objects

The sections below describe the GUI building pseudo objects provided with the plt toolbox. Since this section jumps immediately into the details, it would be best if you first read the overview of these pseudo objects provided in the GUI building with plt section.

  • plot, cursor, & grid
  • edit
  • popup
  • slider
  • image
  • color pick
  • box
  • help text


Plot, Cursor, and Grid pseudo objects

The plot, cursor, and grid pseudo objects

plt

(pseudo
plot
object)
The plt pseudo object can be thought of as a super axis and in that respect it's similar to Matlab's plot and plotyy function. You create a plt pseudo object by calling either the plt function or the pltinit function. The plt object is actually created by the pltinit function, but the reason you may also use plt for this is that plt can recognize when you are trying to create a plt pseudo object and when it does, it simply passes all of its arguments to pltinit. Calling pltinit directly of course is more efficent, but the difference is neglible, so it doesn't really matter. Usually we create the plt pseudo object using the plt function when we are doing so from the Matlab command line, simply because it is shorter to type. When doing so from within a .m file, usually pltinit is called because it makes it more clear that we are creating a plt pseudo object, but you could also justify using plt for concisenss.

When a plt pseudo object is created, a new figure window is immediately created to contain the new pseudo object and also two other pseudo objects are automatically created as well (namely cursor and grid described below).

The calling sequence used to create a plt pseudo object is described here.

There are many parameters that control various aspects of the plt pseudo objects, and they are described in these five sections:
  • Trace properties
  • Axis properties
  • Labels and figure properties
  • Colors
  • Options

cursor

(pseudo
cursor
object)
The cursor pseudo object, more than any of the others is what gives the plt toolbox such an advantage for GUIs involving plotting and data exploration. Once you get used to the smooth and natural methods provided for cursoring, zooming, and panning you will wonder how you lived without them and you will want all your GUI tools to be similarly equipped.

You can learn how to create a cursor pseudo object and how to control its behavior in the Cursor commands section.

grid

(pseudo
grid line
object)
The grid line pseudo object provides the ability to create grid lines for your plots using whatever color and style you choose. Recent Matlab versions provide this capability natively, but the advantage of the grid line pseudo obect is that it works with all Matlab versions which is not true of the native Matlab grid commands.

Grid lines are positioned at each tick label. Additional (sub-decade) grid lines will also be used for logarithmically scaled axes that span six or fewer decades. (The six decade limit may be changed by adjusting the logTR figure application data property which defaults to 1.0e6)

A grid pseudo object is created and controlled using these calling sequences:

plt('grid',ax,'init',color,erMode,LineStyle,In7,In8,In9,In10)
  - Initializes grid lines on axis ax of color color with erase mode erMode and LineStyle LineStyle.
  - color is optional with default [.13 .13 .13]
  - erMode is optional with default 'xor'.
  - LineStyle is optional with default '-'
  - In7,In8 is an optional parameter/value pair to apply to the grid lines
  - In9,In10 is an optional parameter/value pair to apply to the grid lines
            The vbar.m program (in the demo folder) uses this call to create a tabular list next
            to the main plot area. Although that demo as well as the three line example below call
            this 'init' action, you will rarely if ever do this because the grid pseudo object
            is created automatically when you create a plt pseudo object.

plt('grid',ax,'toggle') - toggle grids (on/off)

plt('grid',ax,'get') - get grid line handle

plt('grid',ax,'off') - turn grids off

plt('grid',ax,'on') - turn grids on

plt('grid',ax,'update') - update grids

plt('grid',ax) - same as above

plt('grid') - equivalent to plt('grid',gca)

All the above calls return the grid line handle. Setting ax to zero in any of these commands is equivalent to specifying gca.
To experiment with these functions, try typing this at the command prompt:

>> plt('grid',axes,'init',[.7 1 1]); % create axis & grid pseudo object
>> set(gca,'ylim',[0 6]); % change axis limits
>> plt('grid'); % update grid lines


Edit pseudo object

The edit pseudo object

The slider control is Matlab's most versatile way to control a numeric parameter because it allows us to change a value continuously and repeatedly using several different methods (click & hold on an arrow, click & hold in the trough, and drag the slider bar). The pseudo slider object (described later in this table) enhances this capability further by providing more movement options and coupling it with the labels and numeric readout normally needed with the slider. The only downside to the pseudo slider is that it takes up too much space to use it everywhere in a GUI containing many numeric controls. For this reason an edit box (i.e. uicontrol('Style','Edit')) is often used to control a numeric parameter. However, because the only way to change the value of an edit box is to type in a new number, it is difficult to use when many adjustments are needed to arrive at an optimal setting or when you want to develop a feel for the effect of small changes in the parameter. The edit pseudo object was designed primarily to overcome that difficulty, although it also has the following additional advantages over the uicontrol edit box:
  • A label is almost always required for a numeric parameter and is sometimes needed for string parameters as well. The uicontrol edit box doesn't have this label so you must add another uicontrol for that purpose, complicating your program and making it more difficult to reposition. The edit pseudo object includes an optional label so when you move the edit object its label automatically moves along with it.
  • The edit pseudo object may be positioned using figure coordinates or using the normalized or data coordinates of an axis. Which coordinates to use is usually obvious and depends on the use of the particular control. (The uicontrol edit box may only use figure coordinates.)
  • The edit pseudo object takes up even less space in your GUI than an uicontrol edit box so you can fit more controls into a given area. This often simplifies your GUI design by avoiding the need for additional modes to control which parameters are in view.
  • The edit pseudo object has the (optional) ability to honor set minimum and maximum values.
  • The string substitutions of the edit pseudo object's callback function often greatly simplify the callback routines.
There are two types of edit pseudo objects:
  • Type 1: Two uicontrol objects are created (text style) inside the current figure.
        (If a label is not specified, only a single uicontrol is created.)
        This type is created if the 'pos' property has length 4 ([x y width height]).
  • Type 2: Two text objects are created inside the current axis.
        (If a label is not specified, only a single text object is created.)
        This type is created if the 'pos' property value has length 2 ([x y]).
For reference, this first table describes the edit pseudo object properties that you may set and query. How to use these properties will become clear later when the commands are discussed:

Property
Name 		Property
Value
value
v (scalar) | Sets the edit object's numerical value to v. (default = 1)
[min max] (length 2 vector) | Sets the edit object's allowed min/max values (default = [-1e99, 1e99])
[v min max] (length 3 vector) | Sets both of the above.
Except for the length 2 argument case, the callback function is called if it has been provided earlier in the argument list or in a previous call to plt('edit').
val | Equivalent to the value property above, except that the callback is never called.
minmax | Equivalent to the val property above. (For clarity, use this only when the argument has length 2 (i.e. [min max]).
callbk A callback to be executed when the edit text object is changed.
If the callback is defined with a string, then:
Occurrences of '@VAL' will be replaced with the current value.
Occurrences of '@OBJ' will be replaced with the edit text handle.
Also, note that if the function is defined as a string argument often consecutive single-quote characters are required (quotes within quotes). In that case, readability can be improved by replacing all sequences of two consecutive single quotes with a double quote character. For example 'disp(''ABC'');' could be written as 'disp("ABC");'. Note that this trick does not work for Matlab callbacks in general, but it does work for any callback defined within a plt(...) function call.
enable | -1=invisible, 0=disabled, 1=enabled (default=1). If disabled, the text will still be visible, but may not be modified.
incr | The increment value for a numeric edit pseudo object. (default = 1) When you click on the right/left side of the center of the object, the value of the pseudo edit object is increased/decreased by "incr". A negative value of "incr" is used to indicate that the increment factor is in percentage terms instead of absolute. For example, if incr = -0.1 then clicking on the right/left side of the edit object will increase/decrease the edit object's value by 0.1 percent (i.e. from 1000 to 1001 or 999). You can disable the increment/decrement feature in two different ways. The first way is to set the incr parameter to zero. When you do that, left or right clicking on the object will have the same effect (i.e. the edit mode is entering allowing you type in a new value). The second way is to set the incr parameter to inf. In that case you only enter edit mode when you right click on the object (just like when incr=1 or any nonzero number). Left clicking the object will call the callback function but has no other effect. This makes the object behave much like a button except that you can edit the button's value or string. (See below for an example of creating an editable button.)
length | The length of the vector allowed as the edit value. Usually, length=1 indicating the edit value must be a scalar. If length=4 (for example), an allowed edit value must be a row or column vector of length 4. Two special cases are length=-1 and length=0. length=-1 is used to indicate that a numeric array of any length is a legal value. length=0 is used for string edit objects i.e. the edit string is not interpreted as a number or vector. (default = 1)
format The number of characters used to display numeric edit objects (default=6).
Or you may specify the conversion string to use, such as '%0.4g' or '%3d' or '%4V' (Type "help prin" for a description of the allowed formatting codes.)
Note that 'format',5 is equivalent to 'format','%5w'
label If the argument is a string, that string will become the edit pseudo object's label. Usually, this is sufficient, but if you want more control of the label's position or appearance the argument may be a cell array of the form:
{'LabelString', offset, 'Property1',Value1,'Property2',Value2, ...}

Note that the label will be created with the same type as the main edit object (i.e. a uicontrol for type 1 and a text object for type 2). The property names in the cell array must be valid properties for that object type.

Note that the label will be created as a text object in the same axis that is used to display the popup choices. Property1, Property2, etc. must all be valid text object property names. The meaning of the offset parameter depends on its length as follows:
Offset | Meaning for Type 1 | Meaning for Type 2
'' or [] | plt estimates the best label size & position based on the # of characters in the label. | Same as for type 1.
q | The label width is set to q. plt estimates the best label height & position. | The label position is set to: edit object position + [real(q) imag(q)]
[q1 q2] | The label width is set to q1. The label position is set to: edit object position + [real(q2) imag(q2)] | The label position is set to [q1 q2 0]
[q1 q2 q3] | NA | The label position is set to [q1 q2 q3]
[q1 q2 q3 q4] | The label position/size is set to [q1 q2 q3 q4] | NA
For a type 1 edit object, if the estimated width of the label based on its character length is too big or too small you may adjust it using the "q" parameter as described above. However an alternate method is to use the label itself. You can make the width bigger by padding the label with blanks. You can also make the width smaller by using the tilde character. For example if the desired label is "Abcdef", you can make it slightly smaller by specifying the label as "Abcde~f" or smaller still by using "Abcd~ef", or "Abc~def" etc. (Note the tilde character itself does not appear in the label.)
***** If a property name is given which isn't in the list above then the property is applied to the main uicontrol or text object itself. (It must be a valid property name for type of object being used.) The position ('pos') of the edit pseudo object must be set this way. For a type 1 edit pseudo object the foreground/background colors ('foregr' / 'backgr' properties) are usually set this way, however, if these properties are not specified the background color defaults to 0.8 times the figure background color and foreground color defaults to [1 1 .4] or [0 0 .6] (whichever provides the most contrast). The text color of a type 2 edit pseudo object can be set this way as well ('color' property) with its default handled in the same manner as the foreground color for the type 1 object.

The following commands are used to create an edit pseudo object:

H = plt('edit', 'Property1',Value1, 'Property2',Value2,...) | The property names allowed and the interpretation of the property values are shown in the table above. You may use as many or as few properties as you need in whatever order you choose. If you favor clarity over conciseness, there is no need to consider the alternate forms shown below.
H = plt('edit',[x y],v, 'Property3',Value3,...) | Usually both the 'position' and 'value' properties are needed, so for conciseness, you may omit those property names if the property values appear first and in this order. Note that v in this form may also be a length 3 vector if you want to specify the min/max values. The position in this example is for a type 2 edit pseudo object but it could also be a 4 element vector [x y width height] specifying a type 1 edit object. Assuming at least one of the position coordinates is greater than 3 (indicating pixel units are being used), this command is translated into this line before execution: H = plt('edit','units','pixel','pos',[x y],'val',v,'Property3',Value3,...) If all position coordinates are less than 3 indicating that normal coordinates are being used, the translation is the same except 'pixel' is replaced with 'normal'.
H = plt('edit',[x y],v,@cb, 'Property4',Value4,...) | This command is equivalent to: H = plt('edit',[x y],v,'callbk',@cb,'Property4',Value4,...) Since most pseudo edit objects will be created with a callback, the 'callbk' property name can also be omitted by specifying the callback immediately after the position and value arguments. In this example the callback is specified by a function handle (@cb) but it may also be specified as a string. Property names other than these three ('pos', 'val', 'callbk') may not be omitted.

The above calls create an edit pseudo object and return the handle of the main uicontrol (for type 1) or main text object (for type 2) created. (By "main" I mean the object containing the value, not the label). This handle (H) may be used to modify or query the edit pseudo object parameters using the forms below:

plt('edit',H,
'Property1',Value1,
'Property2',Value2,...) 		The specified property values are applied to the edit pseudo object identified by handle H. The property names can be the ones described in the table above as well as standard Matlab property names to be applied to the main uicontrol (for type 1) or text object (for type 2).
plt('edit',H,'get','value') | returns the numeric value of the specified edit pseudo object. For conciseness, this command may also be written without the last argument: plt('edit',H,'get') or even without the last two arguments: plt('edit',H)
plt('edit',H,'get','minmax') | returns [min max] - the allowed limits for the value
plt('edit',H,'get','callbk') | returns the string or function handle that was set via the 'callbk' parameter
plt('edit',H,'get','enable') | returns -1/0/1 if the pseudo object is hidden/disabled/enabled
plt('edit',H,'get','incr') | returns the value that was set via the 'incr' parameter
plt('edit',H,'get','length') | returns the value that was set via the 'length' parameter
plt('edit',H,'get','format') | returns the string that was set via the 'format' parameter
plt('edit',H,'get','label') | returns the label handle
plt('edit',H,'get','cell') | returns an 9 element cell array that is a concatenation of the previous 8 get commands: {val min max callback enable incr length format label}. The word 'cell' may be replaced by any string other than one of the other 8 valid 'get' arguments.
The 9 'get' commands above also work when H is a vector of pseudo edit handles. For example, suppose H is a row or column vector containing 4 pseudo edit handles. Then the command plt('edit',H,'get','value') (the last two arguments are optional) will return a length 4 column vector of values from the 4 edit objects. Another example is the command plt('edit',H,'get','cell') which would return a 4 by 9 cell array where the nth row contains the 9 values from the nth pseudo object. There are two limitations to the get command with a vector of objects. The first is that 'get','callbk' only works if the callbacks are defined as strings of the same length. If the callbacks are function handles or strings of different lengths, the workaround is to use the 'get','cell' and save the fourth column of the result. The second limitation is that 'get','format' only works if the formats are strings of the same length. If they are strings of different lengths you can again use the 'get','cell' command and retrieve the formats from 8th column of the result.

Editable button:
As mentioned above in the description of the 'incr' parameter, it is possible to use the edit pseudo object to create an editable button. This could be used with a numeric value (as demonstrated by the "num quant" and "den quant" objects in the editz demo program) although perhaps more often it will be used with a string. (The 'length' parameter is set to zero to indicate the edit object is a string.) Here are two examples of string editable buttons, the first being a type 1 pseudo edit object and the second being a type 2:

plt('edit',[x y width height],0,@cb,'incr',inf,'length',0,'str','ABCD','Backgr',[0 0 1]);
plt('edit',[x y], 0,@cb,'incr',inf,'length',0,'str','ABCD','border',[0 0 1]);

Both of these will create a button containing the text "ABCD", the first one at the specified figure window coordinates with a solid blue background and the 2nd one at the specified axis coordinates with a blue border. (The border is not required but helps give the impression that the object acts like a button.) Left clicking on the button will call the @cb callback function. Right clicking on the button allows you to edit the button string. (The @cb function is also called after the string has been edited.)

Keyboard and mouse behavior:
Right-clicking on the edit text object always "opens" the object for editing. What this means is that the old edit string appears with the cursor (underscore) at the end of the string indicating that it is ready to accept keys typed at the keyboard. If you start typing right away, the new characters typed will be appended to the end of the old string. To insert the new characters at a point other than the end, simply move the cursor to the desired point using the left/right arrow keys. To remove characters, press <Backspace> or <Delete> to remove a character before or after the cursor. Pressing <Delete> when the cursor is at the end of the string deletes all the characters. This special case makes it easier to enter a new string that bears little resemblance to the previous entry. Note that while typing, the text object is shown in a different color to remind you that a new value is being entered. When you press <Enter>, the new string is accepted and the color returns to the original. If you type an invalid entry, the word "error" will appear. Click again on the "error" string to try the entry again or to recover the previous entry (via <Esc>). A summary of the special keys follows:

<Esc> | The edit text object is closed for editing and the original text value is restored as if the edit object was never opened for editing.
<Backspace> | Deletes the character on the left side of the cursor.
<Delete> | Deletes the character on the right side of the cursor. If the cursor is at the end of the string, all the characters are deleted leaving only the underscore cursor.
<Right arrow> | Moves the underscore cursor one position to the right.
<Left arrow> | Moves the underscore cursor one position to the left.
<Down arrow> | Removes all the characters. This is the same as pressing Backspace until only the underscore remains.
<Enter> | Closes the edit text object, accepting the current entry (without the underscore cursor) as the new value.
<Click> | Clicking the mouse on the edit text object while it is open has the same effect as pressing <Esc> on the keyboard.
i | When entering a scalar value, if lower case "i" (increment) is entered as the last character, this indicates that the entered value should be used as a new "incr" parameter for the object. In this case, the text object value from before the object was opened is retained.

Left-clicking on the edit text object also opens the object for editing except for one important exception. This exception happens when the edit text object is a scalar (i.e. the length parameter is equal to one). In fact this is more the rule than the exception since that is the most commonly used (and default) value for the length parameter.

In this (scalar parameter) case, when you left-click on the edit text object, its value is incremented or decremented by the object's "incr" parameter. Whether the value is incremented or decremented depends on the position of the mouse click. If you click to the right of the edit object's center, the value will be incremented. Likewise, the value will be decremented for clicks to the left of center. As an example, suppose the current edit text object has a value of 259, and the increment parameter is 1. Left-clicking on the 9 will change the edit object's value to 260 (because the 9 is right of the center of the text string). On the other hand, left-clicking on the two will decrement the value by one. Remember that if the increment amount is not convenient, you can change the increment amount on the fly by using the "i" character as described above.

An important property of the scalar increment/decrement feature is that the edit object will continue to increment as long as you hold down the mouse button. This is useful for many interactive controls and allows the edit objects to take the place of sliders (the only other handle graphic object to have this repeat property). When you hold down the mouse button, there will be a delay of 0.4 seconds before the auto-incrementing begins. After that delay, the value will be incremented once every 0.03 seconds. You can alter the repeat rate by setting the pseudo object's application data repeat property. For example, if you want a slower repeat rate (half the speed of the default) use the command:

setappdata(H,'repeat',0.06);

where H is the handle of the pseudo edit object. You can change the default repeat delay as well. For example, the command setappdata(H,'repeat',[0.06 0.25]); changes the repeat rate to 0.06 seconds and the repeat delay to 0.25 seconds. You can also inhibit the repeat feature by setting the repeat delay to a negative number, for example: setappdata(H,'repeat',-1);.

The easiest way to reset back to the default repeat and repeat delay values is to set the 'repeat' property to null ([] or '') or simply remove this property altogether, i.e. rmappdata(H,'repeat').

Whenever a number is being typed in, you may also type an expression instead. For example, the following entries are all equivalent:
  • 5
  • abs(3+4i)
  • [2 1] * [2 1]'
  • sqrt(3*2^3-cos(pi))
Typing sum(get(gca,'xlim').*[0 1])'would be equivalent to typing in the current upper x axis limit. And to be perverse, typing log(-1)/(2*pi) would be equivalent to typing ".5i" which as mentioned above would change the auto-increment value to one-half.

Creating multiple pseudo edit objects with one command

When using one of the forms of the plt edit command where the first argument specifies the position of the object, a single object will be created if that argument is a position vector. However, instead, that argument may be a cell array of position vectors. In that case, n pseudo edit objects will be created where n is the length of the cell array. If any of the remaining arguments are also a cell array of length n, then the kth cell array element will be applied to the kth pseudo edit object. For example the command:

H = plt('edit',{p1 p2 p3},In2,{In3A In3B In3C},In4,In5);

would have the same effect as the following 3 commands:
H = [plt('edit',p1,In2,In3A,In4,In5);
     plt('edit',p2,In2,In3B,In4,In5);
     plt('edit',p3,In2,In3C,In4,In5)];
In this example, In3 was a cell array while In2, In4, and In5 were not, but it could just as easily be the other way around, or any other combination. Although this example included 5 arguments, it can have more, and only the first two arguments (the position and value) are required.

Querying or modifying multiple pseudo edit objects with one command

All the commands listed above where the first argument (after the 'edit') is a handle will also accept a vector or cell array of handles. The rules for the remaining arguments (if any) after the vector of handles are the same as above, i.e. if the argument is a cell array whose length is the same as the handle vector, then the nth element of that cell array is applied to nth handle. Otherwise the entire argument is applied unchanged to all the handles. To experiment with dealing with multiple objects we can use the arrange utility to create multiple objects without the drudgery of typing in several position coordinates. For example, try typing these commands into the command window:

    % create 4 pseudo edit objects with values 77,66,55,44
    h = plt('edit',arrange(4),{77 66 55 44},'label',{'first' 'second' 'third' 'fourth'});
    plt('edit',h)                            % verify that it worked by displaying the 4 values
    plt('edit',h,'val',num2cell(rand(1,4))); % change the 4 values to random numbers
    plt('edit',h)                            % verify that it worked by displaying the 4 values
    g = plt('edit',h,'get','label')          % get the handles of the labels for each pseudo edit object
    set(g,'foregr','black');                 % change the labels so that the text color is black
    set([h; g],'vis','off');                 % make the edit objects including the labels invisible
    set([h; g],'vis','on');                  % make them visible again
See the following programs for examples of the use of the pseudo edit object:
math folder: | airspeed, circles12, curves, gpsLog, julia
sig folder: | afilt, editz, erip, psdZoom, winplt


Popup pseudo object

The popup pseudo object

Just as the edit pseudo object described above may replace an edit uicontrol, the popup pseudo object described here may be used to replace a popup uicontrol (uicontrol('Style','Popup')). The pop pseudo object has the same advantages as those listed above for the edit pseudo object.

The popup pseudo object is highly customizable, but here is a typical example. This is the popup in its closed state.

Click on the current selection ("square" in this example) and the popup will open to show the options as shown here. Note that in its opened state the currently selected item is shown in bold. As you would expect, simply clicking on the new selection you want, closes the popup with the new selection.

Notice there is a black "X" in the lower right corner of the opened popup. You can click on that to close the popup without making any selection if you opened the popup unintentionally. Alternatively you could just click on the currently selected choice (in bold), however this is not quite the same since the popup callback will then be called (if defined). The cancel ("X") will never invoke the popup callback.

The label may also be placed above the selection which sometimes works better to save space. (See the popup in the fseries.m example in the sig folder. It has the same choices as the popup shown here except the label is placed above the popup). Or of course you can omit the label if one is not needed.

The following table describes the popup pseudo object properties that you may set and query, although how to use these properties may not become clear until later when the commands are discussed:

Property
Name 		Property
Value
choices | A cell array of strings specifying the choices given when the popup is selected. If all the choices are numeric you can use a numeric array instead of the cell array. For example [3 5 7.5] is equivalent to {'3' '5' '7.5'}.
index | An integer specifying the current choice (default = 1, i.e. the 1st element of the choices cell array). After the popup is set to the specified index, the popup is closed if it had been open. The callback is not normally called when the popup choice is set using the index parameter, however if you specify the negative of the index desired, then the callback will be called after the index is set.
callbk A callback to be executed when the popup value is changed.
If the callback is defined with a string, then:
Occurrences of '@IDX' will be replaced with the popup index.
Occurrences of '@STR' will be replaced with the popup string.
position
or
pos 		[x y width height] for the opened popup in pixels or normalized units (relative to the figure window).
Note: You might initially get confused because you expect that the closed popup should be somewhere near the (x,y) position in the figure. However this is only true with the default offset (i.e. the closed popup is near the top of the open popup. But when the popup is near the bottom of the screen, one has to choose a zero (or small) offset which means the closed popup is near the bottom of the open popup. In that case the closed popup is not close to (x,y).

Two other options may be enabled by making the x or y value negative. Therefore these two lines are equivalent:

plt('pop','pos',[-.3 .5 .1 .2],...)
plt('pop','pos',[ .3 .5 .1 .2],'offset',0,...)

Likewise the following two lines are also equivalent:

plt('pop','pos',[.3 -.5 .1 .2],...)
plt('pop','pos',[.3 .5 .1 .2],'swap',0,...)

In both these examples the second line is preferred for its clarity. Refer to the descriptions of the offset and swap parameters below. Note that you also can make both x and y negative to enable both the offset and swap options.

If the position was specified previously you can use a scalar argument to modify just the height component or a two element vector to modify just the width and height components.
offset y or [x y] which specifies the location of the closed popup relative to the opened popup.
x is set to 0.08 if it is not specified (where 0/1 represent horizontal position of the left/right edges of the opened popup). The meaning of the y variable becomes clear by considering the example where the choices cell array contained 4 elements. Then:
  • y = 0: represents the vertical position of the bottom of the opened popup
  • y = 0.5: represents the vertical position of choice{1} of the opened popup
  • y = 1.5: represents the vertical position of choice{2} of the opened popup
  • y = 2.5: represents the vertical position of choice{3} of the opened popup
  • y = 3.5: represents the vertical position of choice{4} of the opened popup
  • y = 4.0: represents the vertical position of the top of the opened popup
Note that negative values for x or y may be used to indicate that the closed popup position should be to the left of or above the position of the opened popup.

If the offset parameter is not included, then the closed popup will be at the top of the opened popup, i.e. at position [0.08 n] (where n is the number of popup choices). To put the closed popup at the bottom of the opened popup simply include 'offset',0 in the parameter list.
colorbk | The background color used when the popup is open. (default = [0 .3 .4]);
colorfr | The foreground color used when the popup is open. (default = [.1 1 .9])
enable | -1=invisible, 0=disabled, 1=enabled (default=1)
swap Normally a left-click on the pseudo popup control opens the popup to reveal the list of choices while a right click merely advances to the next choice without opening the popup. Sometimes it is useful to reverse the roles of the left and right-click - a mode I refer to as the "super-button" mode. In fact if there is only a single choice, it behaves exactly like a uicontrol button in that the only effect from clicking on it is that its callback is called. If there are two choices in the choices cell array, it behaves similarly except that the button text toggles between the two choices with every click. Usually if there are fewer than four choices, the super-button mode is more convenient than the regular mode. The super-button mode is selected by including the swap parameter in the argument list. Often you will want to make the super-button look at least somewhat like a button by including a box around the super-button text. The color of this box is specified by the swap parameter. For example swap,'blue' will draw a blue box around the button text. Or instead of a Matlab color string, you can use an [r g b] color triple. Set the parameter to zero to use the current foreground color (which defaults to [.1 1 .9]). If the parameter is missing (which is only possible if the 'swap' argument is at the end) or if the parameter is set to '' or [] or 'none' then no box is drawn around the button text. Sometimes it is useful to be able to toggle the super-button mode on or off interactively. This is possible with the following quick procedure:
  1. First open the popup. (A left or a right click will do this, depending on which mode you are in.)
  2. Then use the mouse to move the figure window a very small amount (less than 15 pixels in any direction).
  3. And finally close the popup by clicking on any of the choices in the drop down list.
The 15 pixel requirement makes it quite unlikely that this action is performed unintentionally, especially since one rarely thinks of moving the figure while adjusting a control. The text swap toggled will appear in the Matlab command window to assure you that the operation worked as intended.
hide | A list of objects to hide before opening the popup. When the popup is later closed, these objects are shown again (unless they were hidden before the popup opened). The hide parameter is used to remove the distraction of objects that overlap or are too close to the popup list. If there is a zero in the list, before the list is used the zero will be replaced with the handle (or handles) of the plot grid lines. (This is convenient because grid lines are the most common object to overlap with opened popups.) Also setting the hide parameter to '' or [] or not supplying a parameter at all (only possible if the 'hide' argument is the last one) is equivalent to setting the parameter to zero.
interp | 'none' or 'tex' (default='none')
label {'LabelString', offset, 'Property1',Value1,'Property2',Value2, ...}

A label for the popup will be created as a text object in the same axis that is used to display the popup choices and will contain the string 'LabelString' (the first element of the cell array argument). The 2nd element of the cell array is a complex number containing the offset for the label position in pixels from the closed popup. (The real part is the x offset and the imaginary part is the y offset). For example if the 2nd element of the cell array is -10+20i, then the label will be positioned 10 pixels to the left and 20 pixels above the closed popup.

The remaining cell array elements (if any) must contain property/value pairs, and the properties must be valid text object property names. The specified properties will be applied to the closed popup text string.

Specifying an offset of '' or [] is equivalent to specifying the number 5 for this cell array element. (i.e. the default offset is 5 pixels to the left.)

Instead of specifying a cell array for the label argument, you may also specify a string. For example, the argument 'LabelString' is equivalent to {'LabelString',''}. Since the default offset is usually sufficient, it turns out that this shorter form is used far more often than the cell array parameter.
labely | This behaves just like the label parameter above except for the fact that the default offset when it isn't specified is 16i (instead of 5 as is used by the label parameter). This is the usual offset needed for placing the popup label directly above the closed popup text.
***** If a property name is given which isn't in the list above then the property is applied to the popup text object itself. (It must be a valid text property.) The text color ('color') is usually set this way, however if that property is not specified, the default color is [1 1 .4].

The following commands are used to create a popup pseudo object:

H = plt('pop', 'Property1',Value1, 'Property2',Value2,...) | The property names allowed and the interpretation of the property values are shown in the table above. You many use as many or as few properties as you need in whatever order you choose. If you favor clarity over conciseness, there is no need to consider the alternate forms shown below.
H = plt('pop', [x y width height], {'choice1', ... 'choiceN'}, 'Property3',Value3,...) | Both the 'position' and the 'choices' properties are required, so for conciseness you may omit those property names if the property values appear first and in this order. A command such as this equivalent to this longer form: H = plt('pop','pos',[x y width height],'choices',{'choice1', ... 'choiceN'},'Property3',Value3,...) Note that in any of these commands you may use either pixel or normalized units. (You aren't required to included the 'units' property since plt can figure out this property from the size of the numbers in the position vector.)
H = plt('pop', [x y width height], {'choice1', ... 'choiceN'}, @cb,'Property4',Value4,...) | This command is equivalent to: H = plt('pop',[x y width height],v,'callbk',@cb,'Property4',Value4,...) Since most pseudo popup objects will be created with a callback, the 'callbk' property name can also be omitted by specifying the callback immediately after the position and choices arguments. In this example the callback is specified by a function handle (@cb) but it may also be specified as a string. Property names other than these three ('pos', 'choices', 'callbk') may not be omitted.

The above calls create a popup pseudo object and returns the handle of the text object that is visible when the popup is closed. This handle (H) may be used to modify or query the popup pseudo object parameters using the forms below:

plt('pop',H, 'Property1',Value1, 'Property2',Value2,...) | The specified property values are applied to the popup pseudo object identified by handle H. The property names may include the ones listed in the table shown at the top of this page as well as standard Matlab property names of the popup text object. To see a list of the allowed property names and values, type set(text(0,0,'')) in the command window.
plt('pop',H,'get','string') | returns the currently selected element of the choices cell array. Equivalent to the shorter command: get(H,'string')
plt('pop',H,'get','axis') | returns the handle of the axis used to display the opened popup.
plt('pop',H,'get','choices') | returns the cell array of choices that was set via the 'choices' parameter
plt('pop',H,'get','index') | returns the index number of the specified edit pseudo object (between 1 and the length of the choices cell array). For conciseness, this command may also be written without the last argument: plt('pop',H,'get') or even without the last two arguments: plt('pop',H)
plt('pop',H,'get','callbk') | returns the function handle or string that was set via the 'callbk' parameter
plt('pop',H,'get','offset') | returns the number or vector that was specified via the 'offset' parameter
plt('pop',H,'get','colorfr') | returns the 3 element vector or color string that was specified via the 'colorfr' parameter
plt('pop',H,'get','enable') | returns -1/0/1 if the pseudo object is hidden/disabled/enabled
plt('pop',H,'get','hide') | returns the vector of handles that was set via the 'hide' parameter
plt('pop',H,'get','interp') | returns the string that was set via the 'interp' parameter
plt('pop',H,'get','label') | returns the label handle
plt('pop',H,'get','swap') | returns true if the swap (super-button) mode is selected.
plt('pop',H,'get','cell') | returns an 11 element cell array that is a concatenation of the previous 11 get commands: {axis choices index callbk offset colorfr enable hide interp label}. Note that this does not include the first get command ('string') listed above. Also 'cell' in this call may be replaced by any string other than one of the other 12 valid 'get' arguments.
plt('pop',H,'open') | opens the popup revealing the popup choices.

Mouse behavior:
Left-clicking on the popup text object "opens" the popup. What this means is that the popup string is replaced by a list of the popup choices (rendered using colorfr/colorbk). The user then clicks on the desired choice which then becomes the new text string. right-clicking on the popup text object changes the text string to the next available choice. If the last choice is already selected, then right-clicking will change the text string to the first available choice (in the "choices" array). If you hold down the right mouse button, the selection will continuously advance cyclically. You can alter the rate at which the cycling proceeds by setting the figure application data repeat property. The use of this property is more completely explained above in the description of the edit pseudo object.

Creating, querying, or modifying multiple pseudo sliders:

The techniques for this are pretty much the same as for the pseudo edit and pseudo slider objects, so you can look near the end of those sections for explicit instructions. But it will probably suffice just to follow a simple example. Try typing the following lines into the command window:

    figure('color','black'); % create a figure to hold the 3 pseudo popups created by the next line
    h = plt('pop',arrange(3),prin('{choice%d!row}',1:6),'label',{'first' 'second' 'third''});
    plt('pop',h,'index',{6 5 4});   % Change the popup settings to choice 6,5,4 respectively.
    plt('pop',h)                    % verify this worked by displaying the 3 index values
    plt('pop',h,'get','string')     % verify this worked by displaying the 3 popup strings
    plt('pop',h,'get','cell')       % display a 3x11 cell array. Row k shows all information for popup k
    g = plt('pop',h,'get','label'); % get the handles of the popup labels
    set([h;g],'vis','off');         % make all four pseudo popups invisible
    set([h;g],'vis','on');          % make them visible again

The first two lines above create the figure here (on the left) with all three popups in their closed state. If you click on "choice 1" of the third popup, the popup will open showing you the six possible choices as shown in the figure to the right.

In this example, all 3 popups have the same six choices. That may be want you want, but it's somewhat unusual. Suppose instead of the prin argument above we had used:

{prin('{aa%d!row}',1:3) prin('{bb%d!row}',1:2) prin('{cc%d!row}',1:7)}

(i.e. a cell array of cell arrays), then the first popup would have the 3 choices aa1,aa2,aa3 and the second popup would have the 2 choices bb1,bb2 and the last popup would have the 7 choices cc1,cc2,cc3,cc4,cc5,cc6,cc7.

See the following demo programs for examples of the use of the pseudo popup object:

demo folder: pltn, gui1, wfall*,
sig folder: afilt, erip, fseries, psdZoom*, winplt,
math folder: curves, gpsLog, julia, motion*,
util folder: pltwater*,

The four examples listed above followed by an asterisk use the swap parameter of the popup pseudo object to create a "super button".


Slider pseudo object

The slider pseudo object

When using a uicontrol slider to control a parameter in a GUI, besides the slider object itself, one generally also wants an edit box to show the current slider value as well as to allow data entry by typing a number. Also a label is usually required to identify the slider's purpose and two more labels indicating the minimum and maximum allowed values are also desirable. The pseudo slider object combines those five objects into one which makes it much easier to use sliders in your GUI designs. The additional movement and quantization modes often reduce the amount of code you need to make the control work as desired. To create a slider pseudo object, use the command:

H = plt('slider',In1,In2,In3,In4,In5,In6,In7)

This creates a pseudo object which usually looks something like this:

 The five component uicontrols that are created for the pseudo object are identified as:
------------label----------
min ValueEditbox max
-----------slider----------
The variables used in the above slider initialization command are described below:

H | The return value is the pseudo slider's handle which is used to read and modify the pseudo slider's properties. (See the get and set commands below.)
In1 [x,y,width] in pixels or normalized units. Values less than one are assumed to be normalized. Mixing units is ok although x & y must use the same units. [x,y] are the coordinates of the lower left corner of the pseudo slider label (which is also the upper left coordinate of the min value text). If width is missing 120 is assumed.

When the position is specified with a 2 or 3 element as just described, the pseudo slider will look similar to the object shown above which includes all 5 subcomponents. The second way to specify the pseudo slider position is with a 4-element vector in the traditional Matlab format (i.e. [xLeft,yBottom,Width,Height]). When using this form, the position vector only specifies the position and size of the actual slider uicontrol. plt will calculate what is hoped to be the optimal position and size of the label and edit box components. If the Width value specified is larger than the Height value, then the pseudo slider will end up looking similar to this:

 Note that the min/max labels are not rendered when the position is specified this way which makes it more compact. If the Width value is smaller than the Height value then the slider will be oriented vertically with the label placed at the top and the editbox placed at the bottom. (See the demo programs motion.m and pltmap.m for examples of the use of both these forms. If the label and editbox are not wide enough for your taste you can fix it by adding spaces to the left and right sides of the label since plt uses the length of the label string to determine the width of those two elements. Alternatively, you may include a 5th element to the position vector which specifies the width of both the label and the editbox. If you are very picky, you can set In1 to be an eight-element vector. In that case, the first four elements specify the position of the slider component and the last four elements specify the position of the label component (using the traditional Matlab positioning style). The editbox component will then be set to be the same size as the label component and placed on the opposite side of the slider from the label.

Finally, there is one last method (very rarely used) when complete flexibility is required where the In1 argument is specified by a cell array. This cell array must have either 2 or 5 elements. For the 5 element form, the entries specify the positions of the Label, Slider, MinText, MaxText, and EditBox components respectively. These positions must all be in either normalized or pixel units and each component must be either a 4-element Matlab-style position vector or an empty vector which indicates that the associated element is to be invisible. The 2 element form can be used when the MinText and MaxText elements are not needed. In that case first element specifies the position of the slider and the second element specifies the position of the label. The position of the editbox will be computed automatically so that it is the same size as the label but on the other side of the slider.

In1 is the only required parameter for this function.
In2 [value, smin, smax, emin, emax]
value is the initial value assigned to the slider.
smin/smax are the slider values at the leftmost/rightmost positions.
emin/emax are the smallest/largest values allowed when entering numbers into the edit box. If emin and emax are missing, 1e-99 and 1e99 are assumed.

If In2 is not supplied, [50 0 100] is assumed.
In3 | Slider label. If you don't want a label, don't supply this parameter, or set In3 to ''.
In4 | Slider callback. This expression will be evaluated whenever the user moves the slider control or enters a number in the edit box. Occurrences of '@VAL' will be replaced with the current value. This parameter is optional, however, you must include it if you want to specify any of the three parameters shown below. If you need to supply this parameter for that reason, but you don't need the callback, simply set In4 to ''.
Although the previous three parameters are optional, they must be supplied in the indicated order. For example, if you want to specify a slider callback (In4) you must also supply In2 and In3. The next three parameters are also optional but are different in that they may be supplied in any order since the data type can be used to identify which parameter is being supplied. They have been identified below as In5, In6, and In7 Although these three parameters (or any subset of them) may be included in the argument list in any order you choose (after In4 of course).
In5 This parameter controls how the slider moves when the slider left/right arrows are clicked or when clicking in the space to the left or right of the slider button.
In5 | Movement | Quantization
1 | Linear | none
2 | Linear | rounded to nearest integer
3 | Linear | rounded to nearest power of two
[4 q] | Linear | rounded to nearest multiple of q
5 | Logarithmic | none
[6 q] | Linear | rounded to nearest integer for slider arrows and to the nearest multiple of q for slider trough
If In5 is not provided then 1 is assumed.
For modes 4 & 6, q defaults to 10 if not specified.
In6 'fmin fval fmax'
fmin/fmax are formatting strings for the min/max labels and fval is the formatting string for the edit box. These 3 strings are concatenated together as one string separated by spaces. Each formatting string may contain c style printf formatting codes or the W,V,w,v formats. (Type "help prin" for a description of these formats).

For example: '%4w %5.2f %2w' specifies the w format with 4 and 2 digits respectively for the fmin and fmax values and the %5.2f floating point format for the edit box. Since the w format is the assumed default you may be slightly more concise by leaving out the w, ('%4 %5.2f %2' in this example). Instead of a concatenated string, you can use also use a cell array, as in {'%4' '%5.2f' '2'} for this example. However, the cell array form is discouraged because it can't be used in plt calls that create multiple pseudo sliders in a single call. (See below.)

Often it is sufficient to only specify the format for fval and accept the defaults for fmin and fmax (which are '%2w' and '%3w' respectively). We can do this by simply specifying a single format code. For example '%5.2f' is equivalent to '2 %5.2f 3'.

If In6 is not provided, then '6' (or equivalently '2 6 3') is assumed - i.e. the w format is used for all three elements.
In7 [LabelBG; EditBG; LabelFR; EditFR]
 This is an array containing 3 columns and up to 4 rows. Columns 1/2/3 respectively represent the proportion (0 to 1.0) of red/green/blue used in the control. The first two rows are the background colors for the label and edit fields respectively. The last two rows are optional and contain the label and edit field foreground colors. If the foreground colors are not specified then black is assumed (i.e. [0 0 0]).

If In7 is not provided then [.75 .75 .75; 0 1 1] is assumed.

For the commands below, H is the handle returned from the above slider initialization command.

The get commands:

plt('slider',H,'get','value') | returns the pseudo slider's current value
plt('slider',H,'get') | equivalent to the above
plt('slider',H) | also equivalent to the above (the shortest and most cryptic method of getting the slider's value)
plt('slider',H,'get','visible') | returns 1 if the slider is visible, 0 otherwise
plt('slider',H,'get','ena') | returns 1 if the slider is enabled, 0 otherwise
plt('slider',H,'get','position') | returns the slider position coordinates [x y width] in the same units as originally specified.
plt('slider',H,'get','pos') | same as above
plt('slider',H,'get','obj') | returns the slider object handles: [ Label; Slider; MinText; MaxText; EditBox ]

The set commands:

plt('slider',H,'set',a) | Sets the slider to value a. Returns a possibly limited value.
plt('slider',H,a) | The form above (or the form below using 'value') is recommended for clarity but if you want to be as concise as possible, this form is equivalent to the command above.
plt('slider',H,'value',a) | Equivalent to the two forms above. You may also use the extra parameter 'set' to add clarity as in plt('slider',H,'set','value',a) if you prefer. In fact, this extra parameter is allowed (immediately after the slider handle) in all the remaining commands shown in this table.
plt('slider',H,'val',a) | Same as above except the slider callback is not executed
plt('slider',H,'position',a) | Sets the slider position. (See In1 in the slider initialization description above).
plt('slider',H,'pos',a) | Same as above.
plt('slider',H,'minmax',a,b) | a contains [smin,smax,emin,emax] (see In2 above) b is a new slider value (optional) Returns a possibly limited value.
plt('slider',H,'visON') | Makes slider visible.
plt('slider',H,'visOFF) | Makes the slider invisible.
plt('slider',H,'enaON') | Enables the slider.
plt('slider',H,'enaOFF') | Disables the slider
plt('slider',H,'label',a) | Sets the label string to a. If a is empty, the label is made invisible.
plt('slider',H,'mode',a) | Sets the slider movement mode. (See In5 above). Returns a possibly limited value.

Creating multiple pseudo sliders with one command:

If the position argument (In1) is a cell array, then multiple pseudo sliders will be created. For example the command:

H = plt('slider',{p1 p2 p3},In2,{In3A In3B In3C},In4,In5);

would have the same effect as the following 3 commands:
H = [plt('slider',p1,In2,In3A,In4,In5);
     plt('slider',p2,In2,In3B,In4,In5);
     plt('slider',p3,In2,In3C,In4,In5)];
Note that when a cell array is used as an input, the nth element of the cell array is used for the nth slider, whereas when the input is not a cell array, that same input is used for all the sliders. In this example, In3 was a cell array while In2, In4, and In5 were not, but it could just as easily be the other way around, or any other combination. Also, this example happened to use five slider parameters (In1 thru In5) but it could have used just one parameter (The position In1) or as many as seven parameters. When multiple pseudo sliders are created, the return argument (H) is a column vector of pseudo slider handles.

The number of pseudo sliders created is always equal to the size of the first cell array (In1). Consider the following command:

H = plt('slider',{p1 p2},In2,In3,In4,In5,{In6A In6B In6C});

This creates two pseudo sliders (at positions p1 and p2). The values in the last cell array (In6) will not be distributed to the created objects because that cell array does not have the same number of elements as In1. So instead the entire cell array is used for the In6 parameter, making this command equivalent to the following two commands:
H = [plt('slider',p1,In2,In3,In4,In5,{In6A In6B In6C});
    plt('slider',p2,In2,In3,In4,In5,{In6A In6B In6C})];

Querying or modifying multiple pseudo sliders:

All the commands shown above where the first argument (after 'slider') is a handle may also accept vectors of handles or cell arrays of handles causing the command to operate on more than one pseudo slider. The rules are the same as mentioned above ... i.e. cell array arguments of the same length as the vector of handles are distributed so that the nth cell array element applies to the nth pseudo slider handle. To experiment with this notion, consider typing these commands into the command window:

    % create 4 pseudo sliders with labels 'first' 'second' 'third' 'fourth'
    h = plt('slider',arrange(4),[],{'first' 'second' 'third' 'fourth'}); % a figure will appear similar to below
    plt('slider',h,'value',{90 60 10 50}); % move the slider bars to 90,60,10,50 respectively
    plt('slider',h);                       % verify this worked by displaying the 4 slider values
    plt('slider',h,'get','pos')            % display 4x3 array (row k is the position of slider k components)
    plt('slider',h,'get','obj')            % display 4x5 handle array (row k are the handles for slider k)
    plt('slider',h,'visOFF');              % make all four pseudo sliders invisible
    plt('slider',h,'visON');               % make them visible again

See the following programs for examples of the use of the pseudo slider object:
demo folder: | gui1, pltmap, subplt16, trigplt, wfall
math folder: | carlo, motion
sig folder: | afilt, editz, erip, psdZoom, winplt
util folder: | pltwater


Image pseudo object

The image pseudo object (Intensity map)

Before getting much farther in this section, you should try running the picplot.m and pltmap.m programming examples so that you have a better context for the information that follows. The julia application (in the math folder) is another good example of the use of the image pseudo object.

The intensity map pseudo object (sometimes called the image pseudo object) provides cursoring methods appropriate for this object type and also includes several optional components including:
  • A color bar that serves as a legend for the z-axis values as well as provides a method of changing the colormap used to represent the z data.
  • A slider (labeled 'edge') that allows you to control how wide a range around a midpoint is used when determining the color used to represent each array element.
  • A slider (labeled ''mid'') that allows you to control the center value of the range of values used to determine the color for each array element.
  • A checkbox that allows you to control the visibility of the axis gridlines.
  • A 'view all' button, that when clicked on resets the axis limits associated with the image object so that the entire image data set is visible. A secondary feature of this button is activated by right-clicking on it instead, however, the right-click action is different depending on whether the Zout (zoom out) tag exists in the associated menu box. If this tag does not exist, then each time you right-click on the view all button the axis limits are zoomed in to expose only the middle 36% of the visible area. (This represents a 60% expansion of both the x and y axes.). However, if the Zout tag exists then the right-click action described above is not needed since it would duplicate the right-click action of the Zout tag. So in that case, right-clicking on the view all button saves the current image axis limits into an application variable of the axis called ilim. From that point on (or until you right click the view all button again), the left-click action of the view all button will set the axis limits according to the data saved in the ilim application data. This means that the right-click action essentially redefines what we mean by "all the data", when we select "view all".

Initialization

h = plt('image',axI,x,y,z,opt);
The image object is created in an axis and figure that must be created beforehand using the plt pseudo object. For example, suppose we create a figure with the command plt(x1,y1,x2,y2,'Subplot',[40 60]); This creates two axes, the smaller one (40% of the height) below and the larger one on top. And suppose we want to put the intensity map in the larger axis, then we would use plt('image',2,x,y,z);. I have set axI equal to 2 to indicate that the image should be inserted into the upper plot (using the usual rules for the ordering of the axes). You wouldn't expect it matters what is in x2,y2 since this trace is covered over by the image object, but in fact, x2 should be set to the same array as the x vector specified in the image initialization. This ensures that the intensity map cursors operate correctly. (The data in y2 on the other hand does not matter as long as it is the same length as x2).

opt is an optional cell array that specifies the image object options. This cell array may contain any or all of the following strings in the following table. These strings are case insensitive, and actually, all characters except the first one are ignored. So for example 'view', 'ViewAll', and 'V' would all serve the same purpose.

'cbar' | If this string is included the color bar image is created which serves as a color key (i.e. for displaying the current color map). You can click on this color bar to cycle through ten different color maps as follows: 0: jet 5: colorcube 1: rainbow 6: lines 2: sometric 7: hot 3: seismic 8: cool 4: gray 9: winter This entry is an exception to the rule that only the first character is significant, because you may also include a digit between 0 and 6 as the last character of the string. This specifies which color map to appear when the image object is initialized. For example 'cbar3', 'c3' or 'CbarSeismic3' all would initialize the color map to "seismic". If the last character of the string is not a digit then the the rainbow color map is selected as the default.
'edge' | If this string is included the "edge" slider will appear which controls the range of the zData that is mapped to the selected color map.
'mid' | If this string is included, the "mid" slider will appear which controls the midpoint of the zData range that is mapped to the selected color map. The effect of adjusting this slider, as well as the "edge" slider mentioned above, is described in more detail near the end of this page under the heading Edge and Mid sliders.
'grid' | If this string is included, a "grid" checkbox will appear which allows you to turn the grid lines for the image object on or off. This is an exception to the rule that the case is insensitive since 'Grid' and 'grid' have slightly different meanings. They both create the checkbox, but the capitalized version initializes the check box to 'on' (i.e. the grid lines are enabled) and the lowercase version initializes the box to 'off'.
'view' | If this string is included a "view all" button will appear which when left-clicked on will zoom the axis so that the entire image data set is visible. As mentioned above, you may also zoom into the middle of the visible region by right-clicking on this button. (The middle 60% of both the x and y axes will become visible.)
'direct' | Normally the z data is scaled so that the full range of the color map is used. That is, the smallest value in the z data is mapped to the first color of the colormap, and the largest value in the z data is mapped to the last color of the colormap. Sometimes you may only want to use a portion of the colormap. To do this, include 'direct' (or 'd') in the options list. This causes a 1 in the z data to map to the first colormap color and an n in the z data to map to the nth colormap color. Usually, the last colormap color is achieved when z = 64 or z = 256, although actually a colormap can have any length.

You may optionally include a 4-element position vector (in normalized coordinates) after any of these option strings except the last one ('direct'). If the position vector is not included, then a default position is chosen for the item. For example:

opt = {'Cbar' [.5 .4 .02 .24] 'Grid' 'ViewAll' [.91 .75 .04 .02]};

When this option cell array is used for the image object initialization, a color bar, a grid checkbox, and a view all button will be created, but the edge and mid sliders will not be since they are not included in the options list. The color bar and view all button will be positioned at the coordinates given, whereas the grid checkbox will be positioned at its default location since there is no position specified for it in the options list. You may use a space or comma as a delimiter between option elements (i.e. row vector form as in the above), or you may use semicolons if you prefer (i.e. column vector form).

Image update commands

The call that initializes the pseudo image object returns the image handle h which you may then use in any of the image modification commands shown below. First, we have the data update commands which come in seven forms depending on which coordinates you want to update:

plt('image',h,'x',x);
plt('image',h,'y',y);
plt('image',h,'z',z);
plt('image',h,'xy',x,y);
plt('image',h,'xz',x,z);
plt('image',h,'yz',y,z);
plt('image',h,'xyz',x,y,z);
Then we also have the following commands to change the values of the mid and edge sliders (if they were created during the image initialization), the x or y limits, and the color map:

plt('image',h,'mid',Value);
plt('image',h,'edge',Value);
plt('image',h,'xlim',[xLower xUpper]);
plt('image',h,'ylim',[yLower yUpper]);
plt('image',h,'cbar',mapNumber);

In the last command above, mapNumber is usually number between 0 and 9 specifying one of the 10 colormaps listed in the table above in the initialization section. However, you may also use this command to specify new colormaps. For example, the command:

plt('image',h,'cbar',{'Negative',flipud(colormap('gray'))});

will create a new colormap called 'Negative' and makes that colormap active. Now suppose we create yet another colormap using the command:

plt('image',h,'cbar',{'Noise',rand(256,3)});

Now as you left or right click on the colorbar next to the image, the colormap will rotate among 12 different colormaps instead of just the 10 colormaps defined by default. Also, we could use the commands:

plt('image',h,'cbar',10);
plt('image',h,'cbar',11);

to switch to the 'Negative' and 'Noise' colormaps respectively. The colormap selections will revert back to the 10 default colormaps when Matlab is closed and restarted. If you want to revert to the default colormap set in the current Matlab session, you could do that with the command:

setappdata(0,'cmaps',[]);

Finally, the last two image update commands are:

plt('image',h,'direct',1);
plt('image',h,'direct',0);

The first one selects the direct scaling for the z data (mentioned above in the initialization section) and the second one reverts to the usual z data scaling that is selected by default.

For convenience, you can combine any of the above image modification commands into a single command. For example to change the y and z data values, adjust the edge slider and the y-axis limits, and switch to the seismic colormap with direct scaling, you would use a command such as:

plt('image',h,'yz',y,z,'edge',1.5,'ylim',[0 5],'direct',1,'cbar',3);

Image data editing

Editing of image data is described near the end of the Data editing section under the heading 3d data editing

Mid and Edge sliders

These sliders were designed for situations where the z data has an approximately Gaussian distribution. They can also work with other distributions but not if the z data is bimodal or highly skewed. In those cases using these sliders will produce confusing results. Suppose these sliders are enabled and set to their default startup values (mid = 0, edge = 1). Then the axis clim values will be set to [μ - σ, μ + σ] where μ and σ are the mean and standard deviation of the current z data values. (Past z data values do not influence μ or σ). This means that approximately 68% of the data will influence the color choice for each pixel. All the z values below clim(1), about 16% of the data, will map to the first color in the colormap and all the z values above clim(2), also about 16% of the data, will map to the last color in the colormap. If you change the edge slider to 2, then the clim vlaues will be set to [μ - 2σ, μ + 2σ] and so 2 standard deviations of data are represented, and the tails (now each representing 2.5% of the data) will map to the first and last colors of the colormap. The advantage of using smaller values of edge is that you can see the effects of smaller variations in the z data in the image, but the disadvantage is that variations in the tails of the distribution will not be noticeable at all.

If we leave the edge slider set to 2 and then bump the mid slider from 0 to 0.1, the clim values will be set to [μ - 1.9σ, μ + 2.1σ]. This makes the lower tail somewhat bigger and the upper tail somewhat smaller. We increase the mid slider when the data larger than the mean is more interesting and we decrease the mid slider when the data smaller than the mean is more interesting. The general formula for the clim values is: [μ - (edge+mid)σ, μ + (edge-mid)σ].

Whenever new z data is loaded into the image, the following 4 element row vector is computed and saved in the image object's user data: [μ σ min(z(:)) max(z(:))].

The first two values are used by the mid and edge sliders to set the clim values, except for the special case where the edge slider is set to zero. In that case clim is set to the last two values in the user data vector.

The 4 element user data vector may also be used by a program to set the clim property or for other purposes (see the pltmap.m demo program as an example of this).


Color Pick pseudo object

The color pick pseudo object

Although you will probably choose an attractive color scheme for your GUI applications, the user's satisfaction with the application may be greatly improved by allowing them to choose the colors used for the major screen objects. By using the ColorPick pseudo object you allow the user to efficiently choose the screen colors in a consistent way within and among your applications. Also, you will find that selecting colors using the ColorPick figure is much easier than using the typical Windows style palette which attempts to show all possible colors in a single palette. I've found you need to see a fairly large area of your potential color choice before you can decide if the color is suitable. It is not possible to accomplish that using a single palette which is what makes it frustrating to use. By allowing you to fix one of the colors, ColorPick presents an 11x11 palette of choices for the remaining two colors which gives you plenty of area for each color block. (If that is not enough, you can resize the ColorPick figure to be as large as you want.) Even more important is that the instant you click on one of the 121 colors in the current palette the larger patch above the palette AND the objects in the gui that you are adjusting instantly change to the selected color. This instant feedback is really necessary to remove the frustration that usually arises from the task. Scrolling through the many possible palettes is also very quick. Just pick any slider and click on the left/right arrows for a finely changing palette, or click in the trough area of the slider for a more coarsely changing palette (which should be fine enough all but the most picky color chooser).

Any Matlab object that has either a callback property or a buttondownfunction property may be used to bring up the ColorPick window shown here. Before I describe how this is done from a programming perspective I will give a few more details about how the user selects colors from this window.

The text above the sliders is generally used to identify the object or objects that are to receive the color being selected. The three sliders always indicate the RGB values of the currently selected color. In the example shown at the left, the RGB values are 35%, 80%, and 60% respectively. In Matlab, this color would be represented by the vector [0.35 0.8 0.6]. The large rectangular patch object in the upper right corner always indicates the color that results when the proportions are set to agree with the values of the three RGB sliders. One of the sliders (the red one in this example) will always be shown with its text value in yellow and is referred to as the "active slider". A slider will become the active slider whenever you either type in a value into its edit box or when you click on the left or right slider arrows. There are 3 ways to alter the currently selected color:
  • You can simply select the desired color using the RGB sliders. Note that with this method it is irrelevant which slider is identified as active.
  • You can click on any one of the 121 small square patches that are arranged in an 11x11 grid. The colors in this grid are entirely determined by the active slider. In this example, since the active slider shows RED=35%, every color in the 11x11 color grid will have red = .35 with varying amounts of the other two colors. So in this example, the colors of the 4 corners of this grid starting at the lower left and moving clockwise are: [.35 0 0], [.35 1 0], [.35 1 1], and [.35 0 1]. and the square exactly in the middle of this grid has the color [.35 .5 .5]. Clicking on any one of these 121 patches will change the two inactive sliders to the values associated with the patch that you clicked on. (The active slider will be unchanged and will remain active). Also, the large patch and all the objects in the gui that are associated with this ColorPick object will change to show the new selected color.
  • Clicking on the large patch will cause the sliders to move to the "reset" values - i.e. the color that was in effect when the ColorPick window was first opened. Note that this color never changes for as long as the ColorPick window remains open. The reset values may be changed to the values shown currently by the sliders by closing the ColorPick window and re-opening it again.
Next, I will describe how the ColorPick object is created from the programmer's viewpoint. I will call the object that initiates the creation of the ColorPick figure window the "main object". You must do the following two things to make a ColorPick figure appear:
  • You must assign one of the following strings To the callback or buttondownfunction property (or both) of the main object :
    1. 'plt ColorPick' This will cause the ColorPick figure to be created when the callback or buttondownfunction is called except for one special case - which is when the callback of an 'edit' style uicontrol is called. The reason for this special case is that if you type the desired colors directly into an edit box usually this indicates that you didn't need the help of the ColorPick figure. Note that in this special case, the properties listed in the 'm' application data cell array are still modified as described above even though the ColorPick figure is not created. Note that the ColorPick window will appear when you right-click on such an edit box if the buttondownfunction of the edit box has been similarly assigned.
    2. 'plt ColorPick ccf' This has the same effect as above except that when the user changes a color with the ColorPick figure, in addition to changing the properties in the 'm' cell array, the function ccf is called. ccf stands for color change function and may be any string corresponding to a function name and may include numerical arguments, such as in 'plt ColorPick changeFunc(3,-1)'. In fact, the function can have string arguments as well although this is less convenient since you would need two sets of quotes around each string argument. For example 'plt ColorPick changeFunc(''StringArg'')'. You should especially avoid color change functions that required a string argument containing spaces although if you really wanted to do that it would be possible with an obscure-looking callback such as 'plt(''ColorPick'',''changeFunc(''''A string argument'''')'')'
    3. 'plt ColorPick 0 0' is similar to case 1 above and plt ColorPick ccf 0' is similar to case 2 except that the special case relating to the edit box callback does not apply (i.e. the ColorPick window will be created in every instance).
  • Usually when you select a color from the ColorPick figure, this color is applied to either the 'Color' property of the main object or (if the main object does not have that property) to the 'BackGroundColor' property. However often you will want to apply the color to different properties as well as to different objects. To do this you must assign an application data variable named 'm' to the main object with a cell array of this form:
    {'PROP1', H1, 'PROP2', H2, 'PROP3', H3, ... 'PROPn', Hn, 'label'}
    When the user selects a new color using the ColorPick figure, the 'PROP1' property of the object with handle H1 will be set to the 3 element vector [R G B] where RGB are the values from the ColorPick figure. If H1 is a row vector of handles, each of the represented objects will be treated similarly. Then in sequence the objects in H2, H3 ... Hn are treated in a like manner. If any of the 'PROPi' entries are 'str' or 'string', then the numerical 3-element color vector is converted to an Ascii string before being assigned to that property. In place of any property string, you may use a cell array of strings in which case ColorPick will assign the user-selected color to all the properties listed in the cell array of all the handles listed in the following argument. Finally, the last entry 'label' is optional, and if included will appear above the rgb sliders and is used to identify what screen elements are being controlled.
Only one detail remains to describe the operation of the ColorPick object, and that has to do with how ColorPick determines the starting positions of the RGB sliders when the window is first opened. It's very simple when the 'm' appdata is not defined, since in that case the initial color will be the same as the color of the main object (either its 'Color' or 'Backgr' properties depending on which property it has.) When the 'm' appdata is defined, this is a two-step process. First ColorPick must decide which object will be used to determine the initial color. Once the object is chosen, ColorPick must decide which property of this object to use. Here are the details of these two steps:
  1. Picking the object which will determine the initial color. Usually the handle of the main object will be among one of the handles included in the 'm' application data cell array and in this case the main object itself is used to determine the initial RGB slider positions. However, sometimes this is not the case. For example, in the demoplt.m program, we would like to open the ColorPick window when we click on the text object "text color" which serves as a label for the text color patch. However since this is just a label, it is not entirely appropriate for the color of this text object to change which means that it will not appear in the list of handles in the 'm' cell array. So in this instance, ColorPick determines the initial color from the object whose handle is the first element of H1 in the 'm' cell array. (This is the only instance where the order of the handles in this array is significant.)
  2. Picking the property of the selected object that determines the initial color.
    • If the selected object is a uicontrol. If the uicontrol is an edit box then its 'string' property is always used to determine the initial color. If the string includes fewer than 3 numbers, then extra zeros are added to the end of the vector. If the string includes any numbers that are greater than one, then these numbers are clipped at one and if any numbers are less than zero, then these numbers are clipped at zero. If the uicontrol is not an edit box, then first the 'string' property is examined to see if it represents a valid color vector (i.e. it must have 3 elements, all of which are between 0 and 1). If it is valid, then this vector is used as the initial color. If it is not valid, the uicontrol 'backgroundcolor' property is used as the initial color.
    • If the selected object is a text object, its 'string' property is used if this string translates to a valid color vector. Otherwise the 'color' property of the text object is used.
    • If the selected object is a patch object, its 'facecolor' property is used as the initial color.
    • For all other object types, the 'color' property is used as the initial color.
While all this sounds quite complicated, using ColorPick is usually not that difficult because in most cases it does what you would expect. It may be easier just to look at some examples of ColorPick in action which you can do in the following programs:

demo folder: | demoplt pub0 trigplt vbar
math folder: | gpsLog
sig folder: | afilt
util folder: | pltpub

In the simplest cases, the 'm' appdata may not needed (for example in gpsLog.m, vbar.m, and and trigplt.m). The remaining examples use the 'm' appdata to tailor how the ColorPick pseudo object is used.


Box pseudo object

The box pseudo object

The box pseudo object creates a frame, usually containing several other pseudo objects or ordinary Matlab graphical objects. The primary benefit is to create a visual grouping of objects used for a similar purpose. It also makes it easier to design the user interface layout because dragging the box pseudo object to a new location also moves all the controls that are inside it.

A box pseudo object may be created with any one of these commands:

H = plt('box',pos, 'Property1',Value1, 'Property2',Value2,...) | A new axis is created to group other user interface control objects. pos specifies the position of the axis object as [xleft ybottom width height]. If all four of these numbers are less than 5, normalized units are assumed, otherwise, pixel units are used. If any property/value pairs (optional) appear in the argument list, these properties are applied to the axis object created. The handle of the new axis is returned in H.
H = plt('box',pos,[r g b], 'Property1',Value1, 'Property2',Value2,...) | This is equivalent to: H = plt('box',pos,'color',[r g b],'PropName1',PropValue1,...) You can use either of the alternate color specification methods described in the the color section. So for example, the following two commands are equivalent: plt('box',pos,[.66 .11 .77]); plt('box',pos,661177); You can also use the string 'none' to indicate that the axis background color should be the same as the figure background color. (Other character string color representations such as 'blue' are not allowed here.)
H = plt('box',pos,[r g b], [R G B], 'Property1',Value1, 'Property2',Value2,...) | This is equivalent to: H = plt('box',pos,'color',[r g b],'xcolor',[R G B],'ycolor',[R G B],'PropName1',PropValue1,...) This surrounds the box with a border of the specified color. The default linewidth of this border is 3 but can be changed by using 'linewidth' as one of the property names in the argument list. As before, you may replace [R G B] with a single number using the alternate color format but you can't use a string such as 'red'.

As an example, the demo program afilt.m (or the simplified version afiltS.m) includes the following line:

plt('box',p{2}); % where p{2} had been defined earlier as [.108 .888 .228 .088] (normalized position)

This is the box that is created from that command.
(The objects inside the box were created by the other code in the program.)
The color of the box is the default value of [1 1 1]/12 (a very dark gray).

If you don't like the default box color, just supply a third parameter.
The dark blue box shown here was created with the command:
plt('box',p{2},30);.
In place of the "30" you could have used "000030" for more clarity or used [0 0 .3] (Maltab's traditional color triple format).

Using a transparent box, i.e. plt('box',p{2},'none'); would look like this.
When you specify 'none' for the box color a gray ([.5 .5 .5]) border is used by default.

Or if you wanted a brighter (white) but thinner box you could use a command such as:
plt('box',p{2},'none',010101,'Linewidth',1);

One final (slightly garish) example using a dark green box with a blue border:
plt('box',p{2},1500,1);

Creating multiple pseudo boxes with one command

If the first argument (pos) is a cell array of length n, then the command creates n pseudo box objects., If a parameter is a cell array of length n, then the nth element of that cell array is used for the corresponding parameter for each pseudo box object. Otherwise, the entire parameter is used for the corresponding parameter of every pseudo box object. For example, try typing the following command into the command window:

plt('box',arrange(20));

This will create 20 pseudo boxes 'arranged' somewhat haphazardly around the figure. Now let's glorify this example by adding some more parameters:

plt('box',arrange(20),num2cell(rand(20,3),2),80,'linewidth',5);

The first additional parameter is a cell array containing 20 random color triples so that each box is a different color. The next parameter (80) specifies that each box will have a dark blue border. The 80 is one of the alternate methods of specifying colors with plt and is shorthand for the Matlab color triple [0 0 .8]. The final parameter changes the border width from its default of 2 to a thicker 5. After adding these three parameters, the command will produce a figure such as shown here to the right. (Of course every time you type this command, the figure will look somewhat different because box colors are random.

See the following programs for more examples of the use of the pseudo box object:
demo folder: | gui1, pltmap, xChart
math folder: | carlo, gpsLog
sig folder: | afilt, editz, erip, fseries
util folder: | pltwater


Help Text pseudo object

The help text pseudo object

This pseudo object can be thought of as a super text object ... i.e. a collection of text objects that can be created and deleted with a single command. The pseudo object is created with either one of the following two commands:

plt('HelpText','set',v);
plt('HelpText','on',v);

where v is a string or more commonly a cell array of strings and string properties. The HelpText object is associated with the current figure window. Unlike the other pseudo objects, a figure may contain only one HelpText object, which is why an object handle is not returned. If you create a new HelpText object for a figure that already has one, the old one is simply deleted before the new one is created.

The second form above ('on') is equivalent to the following two commands:
plt('HelpText','set',v);
plt('HelpText','on');
where the 'on' command is described below. Thus the two initialization forms are similar except that when the first form is used the text will not become visible until the first 'on' command is issued, whereas with the second form, the text becomes visible immediately.

As mentioned in the Labels and figure properties section you may also create a HelpText pseudo object at the same time the plt pseudo object is created by including 'HelpText',v in the parameter list of the call to plt. (In fact, that is by far the most common way this pseudo object is created.) Creating the HelpText that way is similar to the 'on' command above in that the help text becomes visible right away (i.e. a separate HelpText 'on' command is not needed).

The structure of the v parameter is described in the table at the bottom of this page, but first I'll list the commands used to control the HelpText object. The functional form may be used but the command form is shown below since that is more convenient when only string arguments are needed:

plt HelpText on | Although the pseudo object is created with the above 'set' command, the individual text objects defined are not actually created until this on command is given. Most often this command is used to reenable the Help Text after it was turned off with the command shown below.
plt HelpText plt HelpText off | Both of these forms are equivalent. They delete the text objects created by the above on command. Often you will want the help text to disappear once the user starts using the plot so it doesn't get in the way. One way to do this is to include 'MoveCB','plt HelpText;' in the parameter list when you are creating the plt pseudo object. This sets the move cursor callback so that when you move the cursor or click on the plot, the help text will be removed.
plt('HelpText','get') | This returns the v parameter that was used to create the HelpText object. There aren't many uses for this command, although one use would be to copy a HelpText object from one figure to another.
plt('HelpText','text') | This returns a list of handles to text objects that were created after a helptext 'on' command has been executed. An empty list is returned if the helptext object is currently off. Since Helptext is marked with a special user value, an equivalent result is returned from the command findobj(gcf,'user',355/113);

The easiest way to describe the structure of the v argument used above is by a series of examples:

'abcdef' | Using a single string for the v parameter doesn't give the resulting HelpText object much utility beyond the underlying text object it creates but this is still sometimes useful because it is so easy to create along with the plt pseudo object.
{'abcdef' -.1+.5i} | Usually you will use a cell array to define the help text which allows you to specify more parameters other than just the string. The only required parameter when using the cell array definition is the text object location which is specified by a complex number (the real part giving the horizontal position and the imaginary part giving the vertical position). In this example, the horizontal position is to the left of the main axis (by 10% of the plot) and the vertical position is at the middle of the main plot. If the location is not specified (by using a string argument as in the example above) the position used is .03+.96i
{'abc' 'def' 'line3' .5+.96i
'color' 703050 'fontsize' 20} 		Each text string may be as many lines as you want (3 lines in this example). After the position element, you may specify as many property value pairs as you need. Only text properties are allowed. If the text color is not specified, the default color [1 .6 0] is used. The font size will default to 12 if not specified. Note that for this form (as well as the following one below), the position value (complex) is required. This helptext argument must be a row cell array. (A column cell array will not produce the expected result.) Note that in this example, the color is defined using one of plt's alternate forms (described in the Colors section) and the number 703050 used here could have been replaced with [70 30 50] or the traditional Matlab form [.7 .3 .5].
{'ab' 'cd' .5+.9i
'color' 'red' 2i
'line3' .1+.1i 2i
'line4' .9+.9i
'fontweight' 'bold'} 		With the full generality, you may define as many strings as you want, with each of these strings being in different locations with different string properties. This example creates three strings, the first of which is two lines. The "2i" (which occurs twice) in this example is a separator that indicates a new string is about to be defined. Actually, any complex number will serve the same purpose, but my habit is to always use "2i" for this separator to make it easy to recognize.

In this example, the first two help lines ('ab' & 'cd') will be red because the text is followed by the color specifier [1 0 0]. However the next two lines ('line3' & 'line4') have no color specifier, so they will be in the default color ([1 .6 0]). However, if you change the 2i that appears just before 'line3' to a -2i then line3 will also be red. This is because a delimiter with a negative imaginary component indicates that you want all the properties specified for the previous helptext line to apply to the current one as well. If you changed the next 2i to a -2i as well, then all the help lines defined will be red. If the same property is included both after the text string and in the carry-over property list (enabled by the negative imaginary component) then the property value after the text string takes priority.

See the following applications and demo programs for examples of the use of the HelpText pseudo object:
demo folder: | movbar, subplt, trigplt, vbar, wfalltst, xChart
math folder: | airspeed, curves, gauss, gpsLog, hermite, julia
sig folder: | afilt, editz, weight


Auxiliary functions

Auxiliary plt functions and .m files

All these functions are individual .m files in the main plt folder except for datestr, and metricp which are included inside plt.m. These functions solve problems often associated with creating GUI applications in Matlab, such as adding traces, special plot types, figure positioning, and text formatting issues.

arrange.m

(arrange objects
inside a figure)
p = arrange(n) returns a length n cell array of 4 element vectors specifying the positions of n objects covering most of the current figure. This is useful for positioning the screen objects during the initial design phase of a graphical interface. Although this produces a haphazard layout, you can then use the mouse to size and position the objects, which is easier than numerically specifying explicit locations for each object. Two examples that show how to do this appear in the GUI building with plt section.

Suppose we test the arrange function by typing the following line into the command prompt:
plt('box',arrange(8));
 This will produce the figure shown to the right. Notice that the first box is placed in the lower left corner and is four times the area of the other boxes. It's intended that you use p{1} (the first location generated) for the application's main plot (if there is one). Of course, you will likely move and resize this anyway, but it is easier if the main plot starts bigger than its supporting elements.

b64decode.m

(base64 decoder)
b64decode(infile,outfile)

Decodes the file named infile that was encoded using base64 encoding and saves it in a file named outfile. Most programs with a similar function contain at least one loop, but b64decode, true to good Matlab style, does not use any loops. (Of course we should use loops when they simplify the code, but this is a good example where loops offer no advantage.)

outfile may also contain just the extension of the output file.

For example:

b64decode('c:\path1\abc.txt','.exe') creates the output file c:\path1\abc.exe

If the second argument is not provided, '.bin' is assumed.

See erip.m as an example of the use of this function to automatically convert the supplied base64 encoded text file iir.b64 to create the iir.zip archive (which contains the iir.exe executable).

datestr

(serial date
number to
ascii)
s = plt('datestr',datenum,fmt) is similar to s = datestr(datenum,fmt) except that it displays the time with 1 millisecond resolution instead of 1 second resolution used by the Matlab datestr function. Let's compare the results from plt's and Matlab's date string functions:
a = now;
datestr(a,13) | 03:51:46
plt('datestr',a,13) | 03:51:46.153
datestr(a,14) | 03:51:46 AM
plt('datestr',a,14) | 03:51:46.153 AM
datestr(a,0) | 31-Mar-2015 03:51:46
plt('datestr',a,0) | 31-Mar-2015 03:51:46.153
datestr(a) | 31-Mar-2015 03:51:46
plt('datestr',a) | 31-Mar-15 03:51:46.153
For a description of the allowable formats type help datestr. Note that the formats can include arbitrary formatting strings or integers representing 32 standardized formats. Notice carefully the last example in the table above since the returned date format when no formatting code is specified is different between plt's and Matlab's datestr function. (2 vs 4 character year).

dealv.m

(deal vector)
Assigns each element of a vector to a separate variable. For example:

[e1 e2 e3] = dealv([77 66 55]); is equivalent to the three lines:

e1 = 77;
e2 = 66;
e3 = 55;

This is similar to Matlab's deal function, except for vectors instead of cell arrays. For examples of the use of this function, see the programs demo\wfall and sig\editz.

figpos.m

(figure positioning)
Normally the position of a figure window is specified in pixels as: [xleft ybottom width height] relative to the monitor, meaning that xleft = 1 refers to the leftmost position of the monitor. However it usually is more convenient to specify the figure relative to the usable screen space, which takes into account the space needed for the taskbar as well as the space needed for the window borders and title bar.

Consider the following two methods of creating a new figure window:

figure('BackgroundColor',[0 0 .1],'Position',p);
figure('BackgroundColor',[0 0 .1],'Position',figpos(p);

In the first method, the pixel coordinates in p are relative to the full screen which especially with a multi-window GUI makes it impossible to make good use of the screen area without knowing where the taskbar is and other desktop variables. In the second line however, the coordinates in p are relative to a pre-defined clear area of the screen which are converted into absolute screen coordinates by figpos (this routine).

To accomplish this, figpos must know the screen area that can accommodate the Matlab figure window. It gets this information from the screencfg.m routine which normally can determine this information automatically. (See the description of the screencfg function below.) screencfg saves this information in a text file as well as in an application data variable so that it doesn't have to recalculate the screen configuration the next time it is called. If you move or resize the taskbar figpos will continue to return positions consistent with the old taskbar position. To fix that problem, after moving the taskbar, type figpos('reset') or the command form figpos reset in the command window. This causes figpos to recalculate the screen configuration to take account of the new taskbar size and location.

First, I will explain how figpos computes the figure position from the input argument, although you may find it easier to understand by skipping ahead to the examples below.

In rare situations, you may want to specify the screen position using the standard Matlab convention [left bottom width height] referenced to the screen without reference to the border areas. Of course, then you don't need to call figpos in the first place ... except for the fact that figpos is called automatically by plt, so we need a way to bypass the usual figpos processing. The way to do that is simply to place an "i" after any element in the 1x4 vector. For example:

figpos([40 50i 600 500]) returns the vector [40 50 600 500].

It doesn't matter which element contains the "i", and in fact you can put the "i" in all 4 elements if you like, i.e. figpos([400 50 600 500]*1i);

Suppose you call figpos([p1 p2 p3 p4]) where all the terms are real and p3 and p4 are both positive. This is called "size priority mode" because getting the figure size correct takes priority over getting the left/bottom position in the specified place. In this mode, figpos will return [left bottom width height] where:
   width  = the smaller of p3 and the maximum clear width available
   height = the smaller of p4 and the maximum clear height available
   left   = p1 + left border width. However, if this position would make the
            right edge of the figure overflow the clear space available, then
            the left edge is moved rightward just far enough so the figure fits.
   bottom = p2 + bottom border width. However, if this position would make the
            top edge of the figure overflow the clear space available, then
            the bottom edge is moved down just far enough so the figure fits.
Suppose you call figpos([p1 p2 -p3 p4]), i.e. the same as the calling sequence above except that the 3rd element is negative. The height and bottom values are computed exactly as shown above (size priority), but the width and left values are now computed as follows (position priority):
   left   = p1 + left border width.
   width  = p3. However, if this width would make the right edge of the figure
            overflow the clear space available, then the width is reduced by
            just enough so that the figure fits.
Suppose you call figpos([p1 p2 p3 -p4]), i.e. the 4th element is negative. The left and width values are computed exactly as shown in the first all positive (size priority mode) but the bottom and height values are now computed as follows (position priority):
   bottom = p2 + bottom border width.
   height = p4. However if this height would make the top edge of the figure
            overflow the clear space available, then the height is reduced
            by just enough so that the figure fits.
If you call figpos([p1 p2 -p3 -p4]), then both horizontal and vertical coordinates use the position priority method described above.

An optional 5th value in the input vector is allowed to allocate extra space for the title bar. You may want to do this if you know that a menu bar or toolbar will be enabled since that will make the title bar larger. Since this is not accounted for in the screen configuration set up by screencfg, enabling these features could cause the top edge of the figure to fall off the top edge of the screen. For example:

figpos([p1 p2 p3 p4 48]) would allocate 48 extra pixels in the vertical space which would be enough for the menu bar (about 21 pixels high) and one toolbar (about 27 pixels high).

The default left/bottom coordinates are [5 5] which are used if they are not supplied. For example:

figpos([700 500]) gives the same results as figpos([5 5 700 500]).
figpos([700 500 48]) gives the same results as figpos([5 5 700 500 48]).

You also may specify only the figure length or the figure width and let figpos calculate the missing size parameter based on the most appropriate aspect ratio. For example figpos([1000 0]) and figpos([0 944]) both give the same results as figpos([1000 944]). This particular aspect ratio (1.006) was chosen so that if you plot a circle, the resulting figure is actually looks circular rather than elliptical. For example, this line plots a perfect circle using 600 points:

plt(exp((1:600)*pi*2i/599),'pos',[800 0])

The following examples may clarify the specification described above:

The first example creates 5 plots of the same size placed on the screen so that all the plots are as far away from each other as possible. The first four plots are placed right at the edge of the screen at the four corners, except not so close that any of the figure borders disappear and of course not obscuring the taskbar no matter where the taskbar is placed. On a small screen, even the first four figures would overlap. On a large screen, the first four figures would not overlap, but the fifth figure would overlap the corners of the other four (unless the screen had an exceptionally high resolution).
   y = rand(1,100);  sz = [700 480];     % data to plot and figure size
   plt(y,'pos',[ 0   0  sz]);            % bottom left corner
   plt(y,'pos',[Inf  0  sz]);            % top left corner
   plt(y,'pos',[ 0  Inf sz]);            % bottom right corner
   plt(y,'pos',[Inf Inf sz]);            % top right corner
   p = get(findobj('name','plt'),'pos'); % get positions of all 4 plt figures
   plt(y,'pos',mean(cell2mat(p))*1i);    % put 5th plot at the average position
 The "*1i" in the above line is necessary to prevent figpos from adjusting the position using the current border information. The raw pixel location is used because the get('pos') command returns raw pixel coordinates. With the "*1i" removed, the last figure would not be at the exact arithmetic mean position (although possibly the error might be too small to notice.

The next example also creates four figures at the corners of the screen, although this time, the first figure is a fixed size, and the remaining figures are tiled to fill all the remaining space on the screen.
   plt(y,'pos',[ 0   0   600  400]); % figure 1 is placed at the lower left corner
   plt(y,'pos',[ 0  440  600 -Inf]); % use all the remaining space above fig1
   plt(y,'pos',[615  0  -Inf  400]); % use all the remaining space to the right of fig1
   plt(y,'pos',[615 440 -Inf -Inf]); % use all the remaining space not used by figs 1-3
 Note that in the example above an extra 15 pixels in width and 40 pixels in height is used to create a small gap between the four figures.

figpos may also be called with one of the following string arguments:

figpos('reset') | Because the screen configuration is saved, you must execute this command after moving the taskbar. Otherwise, figures will not be positioned based on the old taskbar position.
figpos('full') | Returns the position coordinates of the largest figure possible that doesn't overlap the taskbar. To create such a figure use the command: figure('pos',figpos('full'),'menu','none')
figpos('width') | Returns the width of the figure mentioned above
figpos('height') | Returns the height of the figure mentioned above

In addition to the examples above, a good way to appreciate the value of the figpos function is to run demoplt.m and cycle thru all the plt demo programs using the "All Programs" button. Notice how well the various windows are placed on the screen. You will appreciate the intelligence of the placement even more, if you can rerun the demos using a different screen resolution and a different taskbar location. Without the figpos function, many of the demos would have to be more complicated to place their figures at appropriate screen positions.

funcStart.m

(spawn a
function)
Some GUI applications open a figure window containing moving images (such as these examples from the plt demo and math folders:

carlo run
circles12
curves go
fseries go
movbar run
pltmap run
wfall run

(Note, the optional argument 'run' or 'go' tells the application to start updating the moving image as soon as it starts.) Using a straightforward implementation of such a GUI program, the program would run continuously to update the moving image and therefore the command prompt would not reappear in the command window after starting the program. This means that you couldn't enter any more Matlab commands in the command window until the GUI program terminated, or the program was instructed to stop updating the figure. The funcStart function provides a way to initiate the moving image and return immediately so that the command window is again able to accept new Matlab commands. The eight programs listed above serve as examples of how to use funcStart to achieve this objective.

funcstart(@func,d);
funcstart({@func arg1 arg2 ...},d);

If funcStart is nested less than d levels below the command prompt then funcStart calls function @func (with arguments if supplied) and then returns to the calling function immediately without waiting for @func to complete. If funcStart is called from a more deeply nested function, then @func is called but it doesn't return to the calling function until @func is complete.

funcstart(@func);
funcstart({@func arg1 arg2 ...});

When called without the 2nd argument, d defaults to 2. This means that after @func is initiated, execution of the calling function will continue immediately only if funcStart was called from the command prompt or from the top level of a script or function. Otherwise @func is called but it doesn't return until @func is complete.

Because @func is called as a timer function callback, two extra arguments are added at the beginning of the argument list. This means that arg1 will actually be the 3rd argument of @func and arg2 will be the 4th, etc.

Note: If you don't care about the nesting depth you can use funcstart(v,Inf);

matplt.m

(.mat file
plotting)
Creates a figure for plotting variables contained in a .mat file. The figure created looks the same as the figure created for the workspace plotter.
Refer to that section as a guide to how to use the resulting figure.

Examples:

matplt('c:\mcode\plt\ini\myfile.mat') | Opens the specified file with full path information
matplt('c:\mcode\plt\ini\myfile') | Same as above
matplt | Presents a dialog box so you can choose a file
matplt('myfile.mat') | Searches the path for the specified file and opens it if it's found. If it's not found, matplt behaves as if it was called with no arguments.
matplt('myfile') | Same as above
matplt myfile | Same as above (command form)

metricp

(metric
prefixes)
[pfix mult] = plt('metricp',x);

When called with two arguments, this is used to convert a number to a form using standard metric prefixes. pfix is the metric prefix string that is most appropriate for displaying the value x, and mult is the x multiplier. This is often useful for scaling plots to avoid awkward scientific notation in the axis labels.

x = .3456E-6;
sprintf('%.3e Volts',x) | 3.456e-007 Volts
[pfix mult] = plt('metricp',x);
sprintf('%.1f %sVolts',x/mult,pfix) | 345.6 Nano-Volts

The metric prefixes used and their respective multipliers are as follows:
Kilo | 103 | Milli | 10-3 | Exa | 1018 | Atto | 10-18
Mega | 106 | Micro | 10-6 | Zetta | 1021 | Zepto | 10-21
Giga | 109 | Nano | 10-9 | Yotta | 1024 | Yocto | 10-24
Tera | 1012 | Pico | 10-12 | Ronna | 1027 | Ronto | 10-27
Peta | 1015 | Femto | 10-15 | Quetta | 1030 | Quecto | 10-30

When called with more than two arguments, this is used to change or query the metric prefixes used for the x or y axis of a plot:

xm = plt('metricp',ax,'x'); | Update Xaxis metric prefix. Returns new x multiplier
ym = plt('metricp',ax,'y'); | Update Yaxis metric prefix. Returns new y multiplier
[xm ym] = plt('metricp',ax,'both'); | Update both X and Y axis metric prefixes
[xm ym] = plt('metricp',ax,'get'); | Get current axis multipliers
plt('metricp',ax,'-x'); | Reset Xaxis multiplier to 1
plt('metricp',ax,'-y'); | Reset Yaxis multiplier to 1
plt('metricp',ax,'-both'); | Reset both X and Y axis multipliers to 1

The first three commands re-adjust the metric prefixes based on the current axis limits. So for example, if the current axis limits are [0 4300], the limits will be changed to [0 4.3] and the metric prefix will be bumped to the next level (from Kilo to Mega for example) and the line data will be divided by 1000.

For an example of how to use the metricp function to update the metric prefixes to accommodate changes in the plotted data, look at the carlo.m application in the math folder.

Pftoa.m

(float to ascii)
s = Pftoa(fmtstr,x) returns in string s an ascii representation of the scalar number x according to the formatting string fmtstr.

If fmtstr is of the form '%nW' then swill be the string representation of x with the maximum resolution possible while using at most n characters.

If fmtstr is of the form '%nV' then swill be the string representation of x with the maximum resolution possible while using exactly n characters.

If fmtstr is of the form '%nw' then s will be the string representation of x with the maximum resolution possible while using at most n characters - not counting the decimal point if one is needed.

If fmtstr is of the form '%nv' then s will be the string representation of x with the maximum resolution possible while using exactly n characters - not counting the decimal point if one is needed.

The lowercase formats (v,w) are typically used to generate strings to fit into gui objects of a fixed width. The reason the decimal point is not counted is that with the proportional fonts generally used in these gui objects, the extra space taken up by the decimal point is insignificant.

With all four format types, if the field width is too small to allow even one significant digit, then '*' is returned.

fmtstr may also use any of the numeric formats allowed with sprintf. For example:

Pftoa('%7.2f',x) is equivalent to sprintf('%7.2f',x)

Normally Pftoa must include exactly two arguments, but there is one exception. The command Pftoa('test') or equivalently Pftoa test will create a test file called PftoaTest.txt. Looking at the examples in this text file may help you better understand the differences between the four new format specifiers.

Normally you won't call Pftoa directly since calling it thru prin is more general and just as concise. (See prin below.)

prin.m

(sprintf
& fprintf
alternative)
s = prin(fmtstr,OptionalArguments);
s = prin(FID,fmtstr,OptionalArguments);

Converts the OptionalArguments to a string s using the format specified by fmtstr. Note that this does the same thing as sprintf or fprintf (with the same calling sequences) except that prin offers some additional features including four extra formatting codes. prin calls the Pftoa function described below to implement the new formatting codes. FID is usually a value returned from fopen but can also be a 1 or a 2 to direct the result to the Matlab command window. For a complete description of this function, see prin.pdf (in the plt\util folder). You can view that help file most quickly by simply typing prin (i.e. with no arguments) at the Matlab command prompt.

pp.m

(pretty printer
for vectors
& matrices)
pp(vector);
pp(matrix);

The array pretty print capability is actually built into prin but pp gives you quick access to this useful capability. Type pp or prin without any arguments to see the full documentation (pdf file). The pretty printing feature is described in the last two pages of the document.

pltpub.m

(publication plots)
Although plt is usually used for data exploration it can also be used to create publication-quality plots by using a suitable set of parameters. This can be simplified by calling pltpub which calls plt with these defaults:

The 'COLORdef',0 parameter | to select a white plot background
The 'TraceID','' parameter | to remove the TraceID box
The 'NoCursor' option | to remove the cursor objects
The '-All' option | to remove the menu box

The demo programs pub0.m, pub1.m, and pub2.m, and xChart.m demonstrate the use of pltpub and also shows that any of the pltpub defaults may be overridden.

If the first pltpub argument is 'Cpick' (as done in the pub0 and xChart demos) then the ColorPick action is assigned to all plt lines, axes, and text elements. This makes it easy to make final color adjustments before the screen capture. It also has the (usually desirable) side effect that all mouse-driven zoom and pan operations are disabled. (Don't include this argument if you need those mouse-driven operations.)

Consider creating your own functions similar to pltpub with sets of plt defaults that you use frequently.

pltt.m

(add plt trace)
pltt is so central to the way plt is used that this auxiliary function was given its own section. Please refer to the Adding traces section.

Pvbar.m

(vertical
bar plots)
This function is used to plot a series of vertical bars. It doesn't do any plotting itself, but returns an array which is then plotted using plt (or even plot). For example, suppose you want to plot 4 vertical bars at x-axis locations 2,3,7,8. Each bar is to start at y=0 and extend up to y=6,6,5,1 respectively. The following line would meet this objective:

plt(Pvbar([2 3 7 8],0,[6 6 5 1]);

Normally all three Pvbar arguments are vectors of the same length, however in this case since the lower y position of each bar is the same a constant may be used for the 2nd argument.

Although you don't have to know this to use it, Pvbar returns a complex array that is interpreted correctly by plt or plot to display the desired sequence of vertical bars. plt and plot displays complex arrays by plotting the real part of the array along the x-axis and the imaginary part of the array along the y-axis. The trick that Pvbar uses to display a series of lines with a single array stems from the fact that NaN values (not a number) are not plotted and can be used like a "pen up" command. (The Pebar and Pquiv functions described below use this same trick.)

The general form of the Pvbar function call is:

v = Pvbar(x,y1,y2)

If the inputs are row or column vectors, this would return a complex column vector which when plotted with plt or plot would produce a series of vertical bars (of the same color) at x-axis locations given by x. y1 and y2 specify the lower and upper limits of the vertical bar. It doesn't matter whether you list the upper or lower limit first. If y1 is a scalar, Pvbar expands it to a constant vector of the same size as y2.

Suppose you wanted to plot 30 red bars (specified by length 30 column vectors xr,yr1,yr2) and 30 green bars (specified by length 30 column vectors xg,yg1,yg2). You could do this with two calls to Pvbar as in:

plt(Pvbar(xr,yr1,yr2),Pvbar(xg,yg1,yg2));

That's probably the first way you would think of, but if xr and xg happen to be the same length (as in this case) you can accomplish the same thing with a single call to Pvbar:

plt(Pvbar([xr xg],[yr1 yg1],[yr2 yg2]));

The second form is especially convenient when plotting many bar series (generally each series in a different color). Interestingly, if you use plot instead of plt first form will not work so you must use the second form.

Note that Pvbar will expand the second argument in either dimension if needed. So for instance in the example above, if ya1 and yb1 were the same you could just use ya1 as the second argument. Or suppose the base (lower limit) of the first series was always 0 and the base of the second series was always -1. Then you could use [0 -1] as the second argument. If the base of all the bars in all the series was the same value, then the second argument may be a scalar.

To see Pvbar in action, look at the example program vbar.m in the demo folder. That example also demonstrates the use of the Pebar and Pquiv functions described below.

Pebar.m

(error bar
plots)
This function is used to plot a series of vertical bars similar to the above Pvbar function with the addition of a small horizontal "T" section on the top and bottom of each bar. This is commonly used to depict an error bound of a function, or a range of values that may be achieved by a certain function. Another difference with Pvbar is the way the lower and upper y positions of the bars are specified. With Pebar, the first two arguments (x,y) specify a reference position for each vertical bar, which is normally (but not strictly necessary) somewhere in the middle of the bar. The third/fourth arguments (l,u) specify the distance between the reference position and the lower/upper end (respectively) of the vertical bar.

The general form of the Pebar function call is:

e = Pebar(x,y,l,u,dx)

The position of the bottom of the error bars is y-l and the top is y+u. The last argument (dx) is a scalar that specifies the width of the horizontal Ts as a percentage of the average x spacing. The last two arguments are optional. If dx is not specified it defaults to 30 (%). If u is not specified it defaults to l (the 3rd argument) in which case the reference coordinates become the midpoints of the error bars. e,x,y,l,u are generally vectors or matrices of the same size, the only exception being that l and u are are allowed to be scalar. Read the description of Pvbar above for an explanation of how vector and matrix inputs are interpreted.

To see Pebar in action, look at the example program vbar.m in the demo folder.

Pquiv.m

(vector plots)
This function is used to plot vector fields represented by a set of arrows with arbitrary head and tail locations. It doesn't do any plotting itself, but returns an array which is then plotted using plt (or even plot). For example, suppose you wanted to plot 3 arrows (all in the same color) with tail locations (4,9) (2,3) and (1,7). Also, suppose you wanted each vector to be of length one, pointing up, down, and to the right respectively. This could be done as follows:

plt(Pquiv([4;2;1],[9;3;7],[0;0;1],[1;-1;0]));

This can also be done using Pquiv's complex input form as follows:

tail = [4+9i;2+3i;1+7i]; head = [1i;-1i;1];
plt(Pquiv(tail,head));

Note that row vectors could have been used instead of column vectors if desired. Now suppose in addition to those 3 vectors, you wanted to plot 3 more vectors (in a second color) with the same tail locations but pointing in the opposite direction. Using the previous assignments of tail and head, This could be done as follows:

plt(Pquiv(tail,head),Pquiv(tail,-head));

Or you could do the same thing with a single call to Pquiv:

plt(Pquiv(tail*[1 1],head*[1 -1]));

Of course, the equivalent 4 argument (real input) form of Pquiv could have been used as well.

There are 8 possible calling sequences for Pquiv depending on whether the input arguments are real or complex and on whether the optional arrow head size argument is included. Pquiv is smart enough to figure out which calling sequence you are using.

Calling sequence | Tail coordinates | Arrow width/length | Arrow head size
q = Pquiv(A,B) | [real(A), imag(A)] | [real(B), imag(B)] | 0.3
q = Pquiv(A,B,h) | [real(A), imag(A)] | [real(B), imag(B)] | h
q = Pquiv(x,y,B) | [x,y] | [real(B), imag(B)] | 0.3
q = Pquiv(x,y,B,h) | [x, y] | [real(B), imag(B)] | h
q = Pquiv(A,u,v) | [real(A), imag(A)] | [u, v] | 0.3
q = Pquiv(A,u,v,h) | [real(A), imag(A)] | [u, v] | h
q = Pquiv(x,y,u,v) | [x, y] | [u, v] | 0.3
q = Pquiv(x,y,u,v,h) | [x, y] | [u, v] | h

where:
q,A,B are complex vectors or matrices
x,y,u,v are real vectors or matrices
h is a scalar (Arrow head size - relative to vector length)

Read in the Pvbar description above how complex values and NaNs are used to generate the desired display. To see Pquiv in action, look at the example program vbar.m in the demo folder. That example also demonstrates the use of Pebar and Pvbar described above.

pltwater.m

(waterfall
plots)

A general purpose 3D surface (waterfall) display routine

Calling sequence:

pltwater(z,'Param1',Value1,'Param2',Value2);
  • All arguments are optional except for z which is a matrix containing the surface data.
  • Note that the arguments are arranged in Param/Value pairs.
  • However you may omit the Value part of the pair, in which case the default value of 1 is used.

pltwater recognizes the following 12 Param/Value pairs (case insensitive):

'go',1 | The animation begins immediately as if you had pressed the start button.
'go' | The same as above (since an omitted value is assumed to be one).
'run' | The same as above. Note that all the sliders and checkboxes mentioned below may be adjusted even when the display is running (animation).
'invert' | The surface is displayed upside down.
'transpose' | The surface is rotated by 90 degrees (x/y swapped).
'delay',v | A pause of v milliseconds occurs between display updates. Whatever value (v) is supplied, it may be changed later using the slider.
'nT',v | Determines how many traces will be visible initially. Later you may change the number of visible traces using the slider. If the nT parameter is not included, 40 traces will be used (initially).
'skip',v | Initially v records (rows of z) are skipped between each record access. (e.g. if v=1 only every other record is used.) This value may be modified using the slider.
'dx',v | Successive traces of the waterfall display are displaced by v pixels to the right which adds a visual perspective. (No perspective is perceived if v is zero.) This value may be modified with the slider.
'dy',v | Successive traces of the waterfall display are displaced in the vertical direction by v percent of the Zaxis limits. This value may be modified using the slider.
'x',v | Specifies the x values corresponding to each column of z. If this parameter is not supplied, the value 1:size(z,2) is used.
'y',v | Specifies the y values corresponding to each row of z. If this parameter is not supplied, the value 1:size(z,1) is used.
'smooth',v Line smoothing is a line property in most versions of Matlab (although it is not supported in R2014b or later). If this parameter is not included then line smoothing is enabled when the display is running (animating) and is disabled otherwise. That behavior may be modified as follows:
  • 'smooth', 1 Line smoothing is always enabled
  • 'smooth',-1 Line smoothing is always disabled
  • 'smooth', 0 The default line smoothing mode as described above.
If you are using a version of Matlab that doesn't support line smoothing, pltwater will not enable line smoothing mode regardless of the setting of this parameter.

If a parameter is included in the pltwater argument list that is not one of the above 13 choices, then this parameter along with its corresponding value are passed onto plt. The most common plt parameters used in the pltwater argument list are:
       'TraceC'  'CursorC'  'FigBKc'     'LabelX'  'Pos'   'HelpText'
       'Title'   'FigName'  'Linewidth'  'LabelY'  'xy'
Refer to the wfalltst.m demo program to see an example of how to create an application using the pltwater function.

screencfg.m

(screen configuration)
Saves screen information allowing Matlab to position figures without overlapping the taskbar.

If called without an argument, screencfg attempts to determine the current screen borders automatically. For Windows based systems it does this by calling the auxiliary function workingArea.m (described below). For non-Windows based systems it does this using the WindowState property except for with older versions of Matlab which don't have this property. In that case it will use a java frame. In unusual situations (for example with a non-Windows sytems and a very old Matlab version such as Matlab 6), the automatic procedure may fail. In that case, a manual method is used where a figure appears and the user must manually maximize the figure using the maximize button in the figure title bar.

screenfig returns a 4 element vector specifying the region where plots can be positioned without covering up any portion of the taskbar. It also saves this 4 element vector in the screenfig appdata of the root object and also saves the vector in a file called screencfg.txt in the plt\ini folder. This ensures that the calculations required to determine the screen configuration only have to be done once.

If you want screencfg to recalculate the screen configuration (for example after the taskbar has been moved, type the command figpos reset (or the equivalent functional form figpos('reset')). This will delete the screen configuration information saved by this function as well as the information saved by the workingArea.m helper function, so that the screen configuration is recomputed the next time screencfg is called.

If you call screencfg with any argument, for example screencfg('manual') then the manual method is always used instead of any of the automatic methods. One advantage of the manual method is that instead of maximizing the figure that appears you can resize and reposition the figure to cover the region where you want plots to appear. In that way you can restrict the area to be used for plotting, which is not possible with the automatic methods (although the need for this is somewhat unusual). Note that even when called with an argument, screencfg will still use a previous calculation of the screen configuration unless you first call figpos('reset').

The figure shown here will appear when the manual screen configuration is being used. Simply maximize the figure and then press the continue button. Alternatively (as mentioned above) you can specify a region smaller than the maximized figure, by moving and resizing the figure before pressing the continue button.

workingArea

(find usable screen area)
For Windows-based systems (Windows 10 or newer) workingArea.m is called from screencfg.m (described above) to determine the portion of the screen where figure windows can be placed without overlapping the taskbar. Unfortunately, workingArea.m does not work with for non-Windows-based systems so screencfg.m uses other methods for those systems. It's unlikely you will want to call workingArea.m directly when creating figure windows with plt, however, you may find it useful when you are creating figure windows directly with Matlab's figure command. Here are some examples:

figure('OuterPosition',workingArea); creates a figure filling the entire usable screen area.
v = workingArea + [8 8 -16 -16];
figure('OuterPosition',v); 		similar to above, except it gives you an 8-pixel margin on all sides
w = workingArea; p = [380 250];
figure('OuterPosition',[w(1:2) p]); 		creates a 380 by 250 figure in the lower left corner of the working area
w = workingArea; p = [380 250];
v = [w(1)+w(3)-380 w(2) p];
figure('OuterPosition',v); 		creates a 380 by 250 figure in the lower right corner of the working area
w = workingArea; p = [380 250];
v = [w(1) w(2)+w(4)-250 p];
figure('OuterPosition',v); 		creates a 380 by 250 figure in the upper left corner of the working area
w = workingArea; p = [380 250];
v = [w(1:2)+w(3:4)-p p];
figure('OuterPosition',v); 		creates a 380 by 250 figure in the upper right corner of the working area
w = workingArea;
v = [w(1)+w(3)-300 w(2) 300 w(4)];
figure('OuterPosition',v); 		creates the tallest 300-pixel wide figure as far to the right as possible
w = workingArea;
v = [w(1) w(2)+w(4)-200 w(3) 200];
figure('OuterPosition',v); 		creates the widest 200-pixel high figure as far to the top as possible

When called without an argument (as in the examples above) a 4-element row vector [x y width height] is returned.
When called with an argument, a scalar is returned. For example:

width = workingArea(3);
height = workingArea(4);

is equivalent to

w = workingArea; width = w(3); height = w(4);

These commands do not work with non-Windows operating systems or for Windows 8 or earlier, but figpos (described above) will give similar results and works with all operating systems. figpos returns values related to the inner position so they are somewhat smaller. For example figpos('width') is about 16 pixels smaller than workingArea(3) because it doesn't include the figure window border on the left and right. Also figpos('height') is about 40 pixels smaller than workingArea(4) because it doesn't include the figure window borders as well as the figure's title bar at the top of the figure.

As with figpos, workingArea will stop working correctly if you move or resize the taskbar. To fix that problem, type figpos('reset') in the command window. This will cause workingArea and figpos to recalculate the screen configuration based on the new taskbar position.

uic.m

(create
uicontrols)
uic behaves exactly like the native Matlab uicontrol function as long as all the property names begin with a lowercase letter. However if at least one of the property names begin with a capital later, then uic will create n uicontrols, where n is the length of the cell array value of that property. So for example:

uic('Pos',{p1 p2 p3},'string','QRA');

will create 3 buttons, all with the same name (QRA) located at p1, p2, p3 where p1, p2, p3, are each a 4-element row vector specifying the button position [left bottom width height]. Of course, it is somewhat unusual to give the same name to each button, so here is another command to give the 3 buttons different names:

uic('Pos',{p1 p2 p3},'String',{'QRK' 'QRS' 'QSA'});

Notice that both properties are capitalized, meaning that both the Pos and String properties are distributed, so that each button has different positions and string values. Now suppose I want the 3 objects to be text uicontrols instead of buttons. Just add the style property to the command:

uic('Pos',{p1 p2 p3},'String',{'QRK' 'QRS' 'QSA'},'style','text');

Notice that style is lowercase, so that property value ('text') will be applied to all 3 objects. In fact, if you had capitalized 'style', uic would give you an error message since it would expect the argument to be a cell array.

Now if we change 'String' to 'string' in the above command we will get a quite different result. Since 'string' is now lower case it will assign the entire cell array to each text uicontrol which will create 3 controls all with the same multiline string containing the 3 lines QRK, QRS, and QSA.

h = uic( ... ); will return the handle of the control if a single control was created. If multiple controls were created, h will be a column vector containing the control handles.

[h1 h2 h3] = uic( ... ); will assign h1 to the first control, h2 to the 2nd, and h3 to the third ... assuming that the uic command produces exactly 3 objects. When supplying more than one argument, you will get an error message if the number of output arguments is not the same as the number of objects created.

See the following programs for examples of the use of the uic function:
demo folder: | demoplt, gui1, movbar, pltmap, pltn, pub0, trigplt
math folder: | airspeed, carlo, curves, gauss, gpsLog, motion
sig folder: | afilt, editz, erip, winplt
util folder: | pltwater


MPP: A Matlab Preprocessor

MPP: A Matlab Preprocessor ver 05-May-2023

Synopsis

Pre-processors are part of most modern languages for good reasons, however, Matlab does not come with one. MPP was designed to fill this void. An example is given to show how to make a Matlab GUI application more readable and easier to modify. Execution speed is also often improved. An important feature of MPP is that line numbers are preserved. Thus when Matlab reports an error at line #n, you can correct the error by going to line n in the preprocessor source file. This MPP subfolder (under plt) includes the following files:

MPP.CPP | Source code for mpp. Compatible with most C and C++ compilers. Before using MPP you must compile this source to create an executable program. However you can skip this step if you are using a Windows system by making use of next file listed below.
MPP.b64 | Executable for Windows/DOS (compiled with the Borland C++ 5.5 compiler using the command bcc32 MPP.CPP). This file is in base64 format because binary files are not allowed on the file exchange. To convert this file to the binary file MPP.exe type the following commands: >> cd(fileparts(which('plt'))) >> cd MPP >> b64decode MPP.b64 .exe Or if you prefer you could use an online converter such as base64decode.org. The file size of the created exe file will be 65536 bytes if it has been decoded properly. If you are using a different operating system you must create the executable by compiling the C source file. Verify that it is compiled correctly by comparing the output .m files with the ones supplied for the 3 examples below. (I would appreciate it if you share your executable file with me so I can share it with other users.)
examp0.mi examp0.m | simple example 0 The .m file is the output of running MPP with the .mi source file This example (both the .mi and .m forms) is also shown below in section B of this document
examp1.mi examp1.m | demonstrates conditional compilation
modelA.m modelB.m modelC.m | used by examp1.mi
sum5s.mi sum5s.m | An GUI example program demonstrating many features of MPP

Motivation

In a large Matlab programming project, hundreds of constants may be needed to represent the various constraints and conditions of the problem to be solved as well as the myriad choices made by the programmer. If all these constants are sprinkled throughout the code, it quickly becomes unreadable and difficult to modify. The solution to this problem is to define variable names for each required constant at the beginning of the code (or in one or more separate .m script files).

For example, in your code, you may define a variable such as:

slider_width = .2;

Besides making the program more intelligible, this definition makes it trivial to change the slider width if that is ever required. This is nothing new. It's the commonly accepted style in any programming language. In fact, most languages provide a preprocessor to make it easy and efficient to define literals to represent constants (e.g. the #define statement in C).

Note that a definition such as the slider_width constant mentioned above does not require a preprocessor and most good Matlab programmers would already use many definitions such as this. Note however that if you have several functions in your .m file you would have to declare the variable as global so that you could use it in all the functions defined in the file. Also, nothing is preventing one of the functions from modifying the variable which may be confusing if you originally intended it to be a constant. Recent versions of Matlab allow the definition of constant class properties, but few Matlab programmers understand object oriented programming. Also making use of such constructs prevents you from distributing your program to users of older Matlab versions. Also since a new variable is defined for each constant, there is a time penalty due to Matlab's interpretive environment. In reality, this penalty is usually insignificant, but this concern and the global issues mentioned above often encourage an unreadable and difficult-to-maintain style with constants liberally scattered throughout the .m code. Another problem is that Matlab provides no easy way to define enumerated types. The MPP preprocessor was designed to solve these problems.

It is conceivable you could use a preprocessor designed for C or other languages on your Matlab source. However, MPP is far more suitable for this task compared to other preprocessors because of these two MPP features:
  • The number of lines in MPP's output file is the same as the number of lines in its input file. This is important so that when Matlab reports an error line number, it is easy to go to the line at fault in your .mi source code.
  • Most of the MPP preprocessor constructs are designed in such a way that the source code can be run by Matlab whether or not the source was passed through MPP. (Not all of the constructs fit that model however.)

Calling sequence: MPP FileIn [FileOut] [-s]

where:

FileIn | Full path and file name for the MPP input (required argument). If the extension is omitted, .mi will be assumed. If no path is given, the local directory is assumed.
FileOut | Full path and file name for MPP output (optional). If the extension is omitted, .m will be assumed. If this argument is not included, then MPP will write its output to a file with the same name as FileIn except that the extension is changed to .m. If the input file already has .m as its extension, then MPP will output its results to MPP.M.
-s | This option tells MPP to strip the comments from the output file. This switch may be placed anywhere after MPP on the command line. If one of the lines in the input file contains a comment and nothing else, then a line containing the single character % is written to the output file (thus maintaining the correspondence between input and output line numbers). Stripping comments saves disk space and may increase loading and execution speed as well. The comments of course are not lost, since the .mi file is the true source. NOTE: The comment lines at the top of the file are not removed since they are needed to provide the help text when you type help func_name from the command window.

MPP makes a single pass scan through input file FileIn (except with the -s switch, which uses an additional scan) and passes all characters to output file FileOut, unmodified except in the following four situations:

1. Include Files

When the string %include is found, an include section is begun. This string may be anywhere on a line, but it must be exact. (% include or %Include will not initiate an include section. The form of an include section is as follows:

     %include
       include1;  % comments are allowed here
       include2;  % This refers to an include file named "include2.m"
       includeN;
     %end_include
To find the include file, MPP will first look for the specified file (with a .m extension) in the current directory. If the file is not found, MPP will look for a list of include directories in a file called MPP.INI in the local directory. If MPP.INI is not in the local directory, MPP will try to find it in the same directory that contains MPP.EXE. Once MPP.INI is found, the first line of that file is read which should contain a path string such as: "c:\Matlab\projX\wcom".

MPP will search the specified directory for the include file. If it doesn't find it, the second line of MPP.INI will be read. If the end of MPP.INI is reached before finding the include file, an error message is reported.

Include files may contain only three types of lines:
  1. Comment lines
  2. blank lines
  3. lines of the form token = ReplacementText.
The last form will be treated in the same manner as lines within a define section as described in section 2 below. The replacement text may span multiple lines by using the Matlab line continuation symbol (three consecutive dots).

The %include and %end_include lines are passed to the output file, but the include file names in between are not. If the first line of the include file is a comment (which is a good practice), then that line will be inserted into the output file in place of the include file name (this preserves line numbers). Otherwise, the include file name is replaced with a line of the form: % IncludeFilename.m;.

2. Literal assignments

A define section is begun when an include file is opened for input or when the string %define is found. An example of a define section follows:

%define
   literal1 = RepTxt1;                % comments are allowed here too
   literal2 = RepTxt2;
   literal3 = RepTxt3;
   literal4 = literal1(1,a,literal2);
   literal5 = 10 * literal2 + literal3;
   c6 = 'abc';  c7 = 'def';            % multiple assignments per line are allowed
%end_define
What is a literal?
  • A literal can be any contiguous string of alphanumeric and underscore (_) characters that don't begin with a digit.
  • Also the $ character is allowed but only as either the first or the last character of the literal (see section C)
  • And finally the ( character is allowed in a literal but only as the last character. This is to allow function definitions (see last four lines of examp0 in section B).
Defining the replacement text:
  • The replacement text for the literal consists of all the characters after the equal sign until the end of the line, excluding the white space after the = and at the end of the line.
  • Comments are also not included in the replacement text.
  • The replacement text also does not include the semicolon used to terminate the replacement text (and actually, that semicolon is not required).
  • The replacement text may include any character except % and ;, although ; is allowed if it's used as a separator between elements of an array. If you need these characters as replacement text in other circumstances you may use the predefined literals mentioned below in section C.
  • As shown in the last line of the above example, you can make several assignments on one line by separating each with a semicolon.
If the replacement text contains a literal that is previously defined within a define section, then that literal is replaced with its previously defined replacement text. For example, the replacement text assigned to literal4 above would be:

RepTxt1(1,a,RepTxt2)

In a limited set of situations, MPP will also do arithmetic simplifications. For example, if RepTxt2 and ReplTxt3 were both numbers, then the replacement text assigned to literal5 will be reduced to a single number. MPP will simplify the replacement text if the expression after the equal sign consists only of these 5 operations:

Addition | +
Subtraction | -
Multiplication | *
Division | /
Association | ( )

Also, all the operands must be numbers, or literals which have previously been assigned to numbers (or expressions that have been reduced to numbers). If any of these conditions are not met, then MPP will simply associate the literal with the unmodified replacement text.

If the define section was part of the original MPP input file (i.e. it was not in an include file) then while MPP is saving the literal assignments it also passes the define section through to the output file modified by inserting an extra % character at the beginning of the line. Thus in the example above the following text would be output.

%define
  %literal1 = RepTxt1;                 % comments are allowed here too
  %literal2 = RepTxt2;
  %literal3 = RepTxt3;
  %literal4 = literal1(1,a,literal2);
  %literal5 = 10 * literal2 + literal3;
  %c6 = 'abc'; %c7 = 'def';            % multiple assignments per line are allowed
%end_define
When MPP opens an include file, it treats the contents as a define section in a similar way to that described above, except that no text is passed on to the output file while the literal assignments are being made. This behavior assures that the number of lines in InFile will be the same as the number of lines created in OutFile. This is important because of Matlab's use of line numbers for locating errors.

3. Literal substitutions

When MPP encounters a literal in the input stream that has been previously defined in a define section, then MPP outputs the replacement text to the output file instead of the original literal. There are three exceptions to this rule.:
  1. MPP will not replace the literal with its replacement text if the literal is within a comment. (i.e. on a line following a %).
  2. MPP will not replace literals that are within a quoted string (a group of characters beginning and ending with a ' character). Actually, that definition of a quoted string is overly simplistic since to represent a quote character within a quoted string Matlab uses two quotes. More accurately define a quoted string as a group of characters which begins with a ' and ends with an odd number of consecutive ' characters. For example, the following is a quoted string:'set(gcf,''WindowButtonUpFcn'','''');'
  3. You can prevent a literal replacement by following the literal with two underscore characters.
    For example: aa_ _ = func1_ _(arg66_ _);
    will be translated to: aa = func1(arg66);
    That translation remains the same even if any of these literals (aa, func1, arg66, aa_ _, func1_ _, arg666_ _) have been defined

4. Assignment operator simplifications

Even outside of a define section, when MPP encounters text of the form:

literal = expression;

where expression meets all the conditions specified above for the define sections, then MPP will replace the expression with a single number. For example, if InFile contains the string:

if val == 0 xy = 2/5; else xy = 1+5/2;

Then the following text will be written to OutFile:

if val == 0 xy = 0.4; else xy = 3.5;

Note that this is the only case where expressions are simplified outside of define sections. Thus the line:

if val == 0 xy = sqrt(1+5/2);

would not be simplified. (A possible extension to MPP?)

A. MPP's handling of the vector operator "[ ]"

Since the vector operator is not listed among the five expression operators, you would expect a left or right bracket to prevent any expression containing it from being evaluated. While this is true in general, there are a few special cases that are so useful, that MPP was made to recognize this operator in those situations. The first of these special cases allows MPP to simplify expressions that are elements of a vector within a define section. For example:

%define
   vec1 = [3, 2*2, 5+1];
%end_define
In this case, the replacement text for literal vec1 would be: [3, 4, 6]

The delimiters allowed between the elements of vectors defined within a define section are the comma (for row vectors) and the semicolon (for column vectors). In Matlab the two vectors

vec1 = [3, 2*2, 5+1];
vec1 = [3 2*2 5+1];

are equivalent. (i.e. the commas are not required). However, when defining a vector within a define section, the commas are required for MPP to recognize the replacement text as a vector. And likewise, the semicolon is a required delimiter for a column vector within a define section. Note that MPP will not simplify vectors occurring outside of a define section.

The second of these special cases allow MPP to extract an element of a previously defined vector. Thus if vec1 is defined as above, then vec1(2) will be replaced with the number 4. This replacement would take place wherever vec1(2) occurs, both inside and outside of define sections.

Although you may define literals that contain nested brackets, MPP will not be able to simplify expressions within such definitions. This will also prevent MPP's ability to extract a single element from the vector. If you do this anyway, you may get unintended results. For example, suppose one of the lines of a define section is:

a = [3, 4, [5, 6]];

Then later if you refer to a(3), MPP will substitute [5, 6].

That may not be what you intended, since in Matlab [3, 4, [5, 6]] is a different way of writing the 1 by 4 vector: [3, 4, 5, 6]

The following example (section B) will help you learn how MPP treats vectors.

B. An example: examp0.mi

Suppose the file examp0.mi contains the following:

%define
  pi = 3.1415926;    e = 2.7182818284590;
  big = 1e20 * pi * e;
  a = 3;
  car = 9999;
  i = -((2.0 - 1e4 + car) * a + 1) * 10 * 10 / 2;
  b = [2*3+a, 4*5, 6*7+10, car];
  x = [10.1, 10.2, 0.00001*a, 1e4];     % note that the leading 0 is needed
  v = [b(2), x(3), b(2)*x(3)];
  labels = ['Label01'; 'Label02'; 'Label03'; 'Label04'];
  bx = b + x;  % this will work, but the next line will be much more efficient
  bxx = [b(1)+x(1), b(2)+x(2), b(3)+x(3), b(4)+x(4)]; % see expansion of this
  EMPTY = isempty;                % synonym for isempty function
  NONE( = isempty(;               % another synonym for isempty function
  PHASE( = (180/pi) * angle(;     % phase of complex argument in degrees
  InputGain( = siglab('InpGain',; % synonym for siglab input gain call
%end_define

%  yy = big;  ii = i;
   yy = big;  ii = i;

%  bbb = b;   bbb = b(4) * b(1);
   bbb = b;   bbb = b(4) * b(1);

%  xx = x(1) + aaa + x(3) + x(2);   xx = x(1) + x(3) + x(2);
   xx = x(1) + aaa + x(3) + x(2);   xx = x(1) + x(3) + x(2);

%  x4 = v * v(3);
   x4 = v * v(3);

%  ebx = bx;
   ebx = bx;

%  ebxx = bxx;
   ebxx = bxx;

%  ee = 2e3 + 2.0e-2 * 3.e2;
   ee = 2e3 + 2.0e-2 * 3.e2;

%  xlabel([labels(2),labels(4)]);
   xlabel([labels(2),labels(4)]);

%  if q1 == q2  ex1 = 3/4; else ex1 = 4/3; end;
   if q1 == q2  ex1 = 3/4; else ex1 = 4/3; end;

%  if EMPTY(wg) wg = 1;   if NONE(wg) wg = 1;
   if EMPTY(wg) wg = 1;   if NONE(wg) wg = 1;

%  deg = PHASE(xx + yy*j);   InputGain(Chan,Level);
   deg = PHASE(xx + yy*j);   InputGain(Chan,Level);
Then after typing the dos command MPP examp0, the file examp0.m will be written as follows:

%define
  %pi = 3.1415926;    %e = 2.7182818284590;
  %big = 1e20 * pi * e;
  %a = 3;
  %car = 9999;
  %i = -((2.0 - 1e4 + car) * a + 1) * 10 * 10 / 2;
  %b = [2*3+a, 4*5, 6*7+10, car];
  %x = [10.1, 10.2, 0.00001*a, 1e4];     % note that the leading 0 is needed
  %v = [b(2), x(3), b(2)*x(3)];
  %labels = ['Label01'; 'Label02'; 'Label03'; 'Label04'];
  %bx = b + x;  % this will work, but next line will be much more efficient
  %bxx = [b(1)+x(1), b(2)+x(2), b(3)+x(3), b(4)+x(4)]; % see expansion of this
  %EMPTY = isempty;                % synonym for isempty function
  %NONE( = isempty(;               % another synonym for isempty function
  %PHASE( = (180/pi) * angle(;     % phase of complex argument in degrees
  %InputGain( = siglab('InpGain',; % synonym for siglab input gain call
%end_define

%  yy = big;  ii = i;
   yy = 8.539734077001e+20;  ii = -200;

%  bbb = b;   bbb = b(4) * b(1);
   bbb = [9,20,52,9999];   bbb = 89991;

%  xx = x(1) + aaa + x(3) + x(2);   xx = x(1) + x(3) + x(2);
   xx = 10.1 + aaa + 3e-05 + 10.2;   xx = 20.30003;

%  x4 = v * v(3);
   x4 = [20,3e-05,0.0006] * 0.0006;

%  ebx = bx;
   ebx = [9,20,52,9999] + [10.1,10.2,3e-05,10000];

%  ebxx = bxx;
   ebxx = [19.1,30.2,52.00003,19999];

%  ee = 2e3 + 2.0e-2 * 3.e2;
   ee = 2006;

%  xlabel([labels(2),labels(4)]);
   xlabel([ 'Label02', 'Label04']);

%  if q1 == q2  ex1 = 3/4; else ex1 = 4/3; end;
   if q1 == q2  ex1 = 0.75; else ex1 = 1.333333333333; end;

%  if EMPTY(wg) wg = 1;   if NONE(wg) wg = 1;
   if isempty(wg) wg = 1;   if isempty(wg) wg = 1;

%  deg = PHASE(xx + yy*j);   InputGain(Chan,Level);
   deg = (180/3.1415926) * angle(xx + yy*j);   siglab('InpGain',Chan,Level);

C. Pre-defined literals and their use

These four literals are pre-defined (MPP syntax would prevent you from defining them using a define section):

Literal | Value | Use
c$ | % | comment character
s$ | ; | statement terminator
$$ | ' | quote character
FileName$ |  | MPP's input file (without extension)

The comment character can be used to selectively comment out debugging code, or even as a way of conditional compilation. For example:
  %define
    IF_UNIX = c$;  % comment these lines out when compiling for Unix
    IF_DOS  = ;    % comment these lines out when compiling for Unix
  % IF_UNIX = ;    % comment these lines out when compiling for DOS
  % IF_DOS  = c$;  % comment these lines out when compiling for DOS
  %end_define
  IF_UNIX [s,w] = unix('!rm dir1/dir2/file');
  IF_DOS  eval('!del dir1\dir2\file.exe');
The example above shows what the code would look like when coding for a windows environment. The IF_UNIX literal is defined as a comment character, thus commenting out the line with the call to unix and the IF_DOS literal is defined as a null string so that the line with the eval would be executed.

Replacing Matlab if else clauses with these conditionals can make your program smaller and faster. Another example of conditional compilation can be found in the included examp1.mi example.

While debugging a program you may want to tell Matlab to display a specific set of variables. Usually, this is done by removing the semicolon at the end of each expression that you want in your debug printout. The drawback of this method is that once you no longer need the debug printout it may be difficult to find all the missing semicolons. Furthermore, once you have put these missing semicolons back in, later if you find that you want to reenable the debug printout, you don't have any record of which semicolons you need to remove again. You can avoid this cumbersome problem by using MPP to assign a set of expressions to a named debug group. However, finding these missing semicolons at clean-up time can be difficult. Then if you want to enable the debug printout, you have to repeat the whole process. A better way is:
  %define
    %Debug1 = s$;
     Debug1 = ;
  %end_define;

  a = foobar(barfoo(j.*k)) Debug1
  b = func1(a^2);
  c = func2(sqrt(a)+1) Debug1
Essentially what we have done is to assign the variables a and c to a debug variable group called Debug1. The define section shown above enables this debug group by assigning the Debug1 variable to a space. Later you can disable the debug group by replacing the define section with:
  %define
    Debug1 = s$;
    %Debug1 = ;
  %end_define;
Now Debug1 has been assigned to a semicolon instead of the space we used previously. Notice that a simple edit (moving the % character from one line to another) is all that is required to enable or disable the debugging group. Of course, each debug group can include as many variables as you want and you can have as many different debug groups as you need.

Since a semicolon terminates a literal assignment within a define section the replacement text can't normally include a semicolon character. However, there is a way around this if you really do want to include a semicolon in the replacement text. Simply use the s$ semicolon symbol wherever you want the semicolon included within the assignment text. Unlike the semicolon, the s$ doesn't terminate the replacement text.

As noted previously, mpp will not normally substitute a literal with its replacement text if the literal occurs within a quoted string. Such behavior would be dangerous and would destroy your ability to define arbitrary strings as needed. However, sometimes it is useful to be able to allow MPP to make such replacements. To allow replacements inside a quoted string, simply use $$ in place of the quote character. MPP will replace the $$ with a single quote, as well as make any replacements for all literals found within the quotes. You can also use $$ to define replacement text that has an odd number of quotes (although that is rarely useful). As explained previously, the $ character may be used in a literal only as the first or last character of the literal. The use of this special property of $ can be seen in the following somewhat extreme example (but if you can understand this one, everything else is easy):
  %define
    File$      = foobar;
    Extension$ = .ini;
    InputFile  = $$$$File$Extension$$$$$;  % file name, double quoted
    Permission = ''r+'';                   % open for reading & writing
    Action3    = 'Callback',$$fid = fopen(InputFile,Permission)$$;
  %end_define
  set(Handle1,Action3);
The output of mpp corresponding to this last line is:

set(Handle1,'Callback','fid = fopen(''foobar.ini'',''r+'')');

With less experience, we might have tried this simpler way of defining InputFile (the same as above but not use the trailing $ in File$ and Extension$ then we would have
  %define
    File      = foobar;
    Extension = .ini;
    InputFile = $$$$FileExtension$$$$;  % file name, double quoted
  %end_define
But this would not work because InputFile would no longer contain foobar or .ini because MPP would be looking up the literal FileExtension which of course has not been defined, so no substitution would occur.

Another special case that applies to define sections (or include files) is that you can specify a literal with no replacement text. In that case, MPP will assign a value to the literal equal to one plus the value of the previously defined numeric literal. The syntax of this is best described by example. Handle definitions are usually made sequentially and can be created using a define section such as:
  %define
    FIG    = 1;         % begin assigning handle definitions
    RUN    = FIG+1;     % run button
    STOP   = RUN+1;     % stop button
    SLIDEV = STOP+1;    % voltage slider
    AXIS1  = SLIDEV+1;  % axis 1
    AXIS2  = AXIS1+1;   % axis 2
    AXIS3  = AXIS2+1;   % axis 3
    PLOT1  = AXIS3+1;   % plot 1
    PLOT2  = PLOT1+1;   % plot 2
    PLOT3  = PLOT2+1;   % plot 3
  %end_define
Definitions such as this are referred to as enumerated types, and you could also use a classdef operator for this. However, that would make the code incompatible with older versions of Matlab (before the object-oriented constructs were added). Another advantage of using MPP for this purpose is that by using the special case mentioned above you could replace the above definition with the following more concise and readable one:
  %define
    FIG = 1;             % begin assigning handle definitions
    RUN; STOP;           % run, stop buttons
    SLIDEV;              % voltage slider
    AXIS1; AXIS2; AXIS3; % axis 1,2,3
    PLOT1; PLOT2; PLOT3; % plot 1,2,3
  %end_define
Caution: If you use auto-incrementing defines (as above), any of the pre-defined literals, or any literal containing the $ character, then the pre-processor source may not be run by Matlab directly. Therefore once you use any of these more advanced MPP features, you are committed to using MPP on your source code. Even without using these special features, many literal assignments will be legal and useful for MPP, which are illegal when used as input to Matlab directly. Once you resign yourself to always running your source through MPP, these problems become moot, and you will discover many ways to use MPP to make your code clearer and easier to modify.

Some editors allow you to automatically "compile" the source using MPP before exiting. That will make it less likely to accidentally skip the MPP process before running the .m file.

You may also want to invoke MPP from a make file, which might look something like this:
   .SUFFIXES:
   .SUFFIXES: .m .mi
   .mi.m:
           mpp $*.mi
   OBJS = program1.m program2.m
   all: $(OBJS)
   program1.m: program1.inc
   program2.m: program2.inc
You can learn some of these tricks for using MPP to make your code cleaner from the following example.

D. sum5s.m (a GUI example)

The previous examples merely give you an idea of the mechanics of what the preprocessor does, but this program (sum5s.mi) is an example that shows how to use MPP to achieve the benefits that I mentioned in the synopsis.

Before studying the code, run it first to see what it does. To do this, you must run the preprocessor to create sum5s.m by typing "mpp sum5s". Then run it by typing "sum5s" at the Matlab command prompt.

You will then see a figure like the one here except that the amplitude of the periodic function will be slowly varying. This movement will continue until you click on the stop button.

The goal of this example is merely to showcase ways of using MPP, so it does not use any of the pseudo objects or plotting techniques of the plt package. Only standard Matlab objects are used in this particular example. (You don't even need to have plt installed to run the sum5s program.)

Note that in this GUI example, I have chosen to use some preprocessor constructs (e.g. the definitions of the GUI objects & properties) which Matlab cannot handle directly. Therefore you cannot run sum5s without first running the source code through MPP.