Tutorials

Tutorials - Octave

Editing Bode Plot

Introduction

Bode plots (figure 1) are a standard graphic representation for dynamic linear systems. In Octave they are available through the function Bode. Though usually the plot produced is good sometimes you need to edit it, this it is not a straightforward task because standard tools like gca can not reach all the plots generated by Bode function. Instead a different approach needs to be used. Assuming you would like to modify the plot from figure 1 as that shown in figure 2 this tutorial will explain how to do it.

NB1: Bode is a function belonging to the Control toolbox therefore in order to use it you will need to install and/or load it in Octave.

NB2: In order for the script to work as expected make sure you have installed Octave 4.0 with default graphics toolkit set to qt. You can check and eventually change this with the function graphics_toolkit. The instructions on how to use it can be found with Octave command help or here.

Table of Contents

Tutorial Files

The Octave scripts created in this tutorial can be downloaded as a zip file here.

Standard Octave Bode Plot
Modified Octave Bode Plot

The handle to a figure

Let us start defining a transfer function and plotting its Bode plot

s = tf('s');
G = (s+10) / (s+1) / (s+1000);
bode(G);

Bode uses a figure to output its plots. Figures in Octave are defined as object types (type figure precisely) and these objects, called graphic objects, are the graphics data structures themselves. Getting access to these structures allows to modify any property of the plot.

It is important to note that figure is not the only type of graphic object, in the next section we will make use of the axes and line type. For a complete list of graphic object types available and and their properties see the official documentation.

Octave gives you access to graphic objects through pointers called graphic handles or simply handles. Therefore the first step to modify the plot is to capture the handle to the figure generated by Bode with the function

h1 = gcf; % Return a handle to the current figure, in this case the Bode plot

The handle to axes and line graphic objects

Now that we have gained access to figure's structure we can start retrieving the information necessary to modify the plot as requested. For Bode plots this information is stored in the graphic object types axes and line. Axes type objects are related to plot's axis and text properties while line contains plotted lines properties. Their handle can be obtained interrogating the figure object through its handle \(h1\) using the following function

ax = findall (h1, 'type', 'axes');% Return the handles to all objects of type = axes
pl = findall (h1, 'type', 'line');% Return the handles to all objects of type = line

The content of ax and pl is structured as follows

    • \(ax(1) = \text{handle to phase plot legend}\)
    • \(ax(2) = \text{handle to phase plot}\)
    • \(ax(3) = \text{handle to magnitude plot}\)
    • \(pl(1) = \text{NA}\)
    • \(pl(2) = \text{handle to phase plot line}\)
    • \(pl(3) = \text{handle to magnitude plot line}\)

Figure 3 shows to what plot's elements each handle gives you access to.

About pl(1), it has been signed as NA because its value is not used in this tutorial.

The details on how to find out and verify what ax and pl pointers are linked to can be found in section Checking ax and pl pointers content.

Handles in Octave Bode Plot
Added legen in Octave Bode Plot

Adding a legend to magnitude plot

As you can see from Figure 3, the legend of magnitude plot is missing. We can add it using the handle to magnitude plot \(ax(3)\) with the following code

L = legend (ax(3), '|G|'); % Adding a legend to magnitude plot

which creates a legend and assigns its handle to \(L\) (see figure 4). We will use this handle later to change magnitude legend properties.

A note on assigning plot line handles

Compared to legend, retrieving plot line handles requires some additional attention because if you use the function

pl = findall (h1, 'type', 'line'); % Find all the graphic object of type = line

after adding the magnitude plot legend you will find that, compared to the case where you use it before (see section The handle to axes and line graphic objects), pl size increases. The structure of the vector is now

  • \(pl(1) = \text{NA}\)
  • \(pl(2) = \text{NA}\)
  • \(pl(3) = \text{handle to phase plot line}\)
  • \(pl(4) = \text{handle to magnitude plot line}\)

and if you are not aware of this problem you might end up with some unexpected errors.

This issue can be solved observing that in both cases the following holds: the handles are always the last two elements of pl, pl(3) (or, written before findall(), pl(2)) is referring to the phase plot and pl(4) (or, written before findall(), pl(3)) is still related to magnitude plot. Therefore, if we use the following syntax

mag_line = pl(end); % Extracting magnitude line plot handle
phase_line = pl(end-1); % Extracting phase line plot handle

assigning handles will become independent from the size of pl making the plot lines properties always available for editing.

Getting access to all required elements

The changes that we want to apply to our plot require the modification of the following elements

  • Title
  • x and y labels
  • x and y tick values, these are the numbers found below abscissa and on the left of ordinate respectively
  • Legends
  • Plot lines
So far we have obtained access to legends (through \(L\) and \(ax(1)\) handles) and plot lines (through mag_line and phase_line handles). In order to reach the other elements we will need to understand how figure's graphic objects are structured and related.

Figure's objects structure

A simplified view of this structure (with elements only relevant to this tutorial) is given in figure 5. As you can see, title and labels are independent graphic objects of type text and their handles are stored into magnitude and phase axes objects. The picture also tells us that x and y tick values belong to axes objects, this means that they are available directly through ax(2) and ax(3) handles. get on the relevant axes object handle. But before we do that, let us clean up and order all the handles found so far

axes_handle = [ax(3); ax(2)]; % Rearranging axes handles to match Bode plot order
legend_handle = [L; ax(1)]; % all legend handles stored in vector legend_handle

Now we can start retrieving all the remaining handles:

For title:

title_handle = get (axes_handle(1), 'title'); % Obtain the handle to Bode title

For y labels:

b = get (axes_handle, 'ylabel'); % extract the handle to ylabel
ylabel_handle = [b{1}; b{2}]; % extract the handle to ylabel continued

For x labels:

xlabel_handle = get (axes_handle(2), 'xlabel'); % extract the handle to xlabel

For x and y tick values: as shown in figure 5, they are directly accessible through axes_handle vector.

If you are interested in the complete description of Octave's graphic object properties this can be found in the official documentation.

Changing plot's properties

At this point we are in the position to change the bode plot as required, that is

Modifying legends:

set(legend_handle(2), 'string', 'arg(H)'); % Changing the legend of phase plot
set(legend_handle, 'fontname', 'georgia', 'fontsize', 20) % Changing size and font

Modifying title:

set(title_handle,'fontsize', 25, 'fontname', 'Times') % Changing size and font

Modifying plot lines:

set(mag_line, 'linewidth', 2, 'color', 'red') % Changing width and color
set(phase_line, 'linewidth', 2, 'color', 'blue') % Changing width and color

Modifying axis tick values:

set(axes_handle, 'fontname', 'Arial', 'fontsize', 18) % Changing font and size

Modifying x and y labels:

compact = [ylabel_handle(2); xlabel_handle];
set(compact, 'fontsize', 15,'color','blue'); % Changing size and color
set(ylabel_handle(1), 'fontsize', 15,'color','red') % Changing size and color

And the job is done! Now your plot should look like that of figure 2.

Checking ax and pl pointers content

In this section we will check the content of ax and pl vectors.

For ax we will verify that the first element is a handle to the legend of phase graph (figure 1) and that the other two are handles to phase and magnitude plots respectively.

For pl we will verify that pl(2) is a handle to phase graph line (figure 1) and that pl(3) is a handles to magnitude graph line. pl(1) will be ignored since its value is not relevant to this tutorial.

First we build a linear system and retrieve axes and line handles (including the added magnitude legend):

s = tf('s');
G = (s+10) / (s+1) / (s+1000);
bode(G);
h1 = gcf; % Retrieve the handle to the Bode plot figure
ax = findall (h1, 'type', 'axes');% Return the handles to all objects of type = axes
pl = findall (h1, 'type', 'line');% Return the handles to all objects of type = line L=legend(ax(3),'test');

Now we perform the check

Check on ax(1)

disp('Check on ax handles')
disp('===================')
[r c] = size(ax);
ax_row_size = r
ax_column_size = c

disp('')
ax_1_check = get (ax(1),'tag') % Verify that a(1) is a legend
dummy = get (ax(1),'string');
ax_1_content = dummy{1} % Checks if ax(1) is the legend on the phase plot

Check on ax(2) and ax(3)

disp('')
b = get (ax(2:3),'ylabel'); % extract the pointer to ylabel, this is a cell array
b = cell2mat(b); % convert cell structure to matrix because of get function input
dummy = get(b,'string');
ax_2_ylabel = dummy{1} % Check that ax(2) is pointing at the phase plot
ax_3_ylabel = dummy{2} % Check that ax(3) is pointing at the magnitude plot

Check on pl

disp('Check on pl handles')
disp('===================')
[r c] = size(pl);
pl_row_size = r
pl_column_size = c

disp('')
pl2 = get (pl(end-1), 'displayname') % Checks that pl(2) is pointing at phase line
pl3 = get (pl(end), 'displayname') % Checks that pl(3) is pointing at magnitude line

And this ends the tutorial. You should now be able to modify the Bode plot and have the insight to customise it further if required.